Merge branch 'master' into feature/add-SAP-HANA-support

This commit is contained in:
Zakaria Zoulati 2026-06-13 11:35:02 +01:00 committed by zakaria-zoulati
commit 9e92d3cc94
68 changed files with 34950 additions and 2635 deletions

View file

@ -1088,7 +1088,7 @@ def heuristicCheckSqlInjection(place, parameter):
if casting:
errMsg = "possible %s casting detected (e.g. '" % ("integer" if origValue.isdigit() else "type")
platform = conf.url.split('.')[-1].lower()
platform = (extractRegexResult(r"\.(?P<result>\w+)(?:\?|\Z)", conf.url) or conf.url.split('.')[-1]).lower()
if platform == WEB_PLATFORM.ASP:
errMsg += "%s=CInt(request.querystring(\"%s\"))" % (parameter, parameter)
elif platform == WEB_PLATFORM.ASPX:
@ -1238,7 +1238,7 @@ def checkDynamicContent(firstPage, secondPage):
kb.heavilyDynamic = True
secondPage, _, _ = Request.queryPage(content=True)
findDynamicContent(firstPage, secondPage)
findDynamicContent(firstPage, secondPage, merge=True)
def checkStability():
"""

View file

@ -1229,6 +1229,9 @@ class Agent(object):
def removePayloadDelimiters(self, value):
"""
Removes payload delimiters from inside the input string
>>> agent.removePayloadDelimiters(agent.addPayloadDelimiters("1 AND 1=1")) == "1 AND 1=1"
True
"""
return value.replace(PAYLOAD_DELIMITER, "") if value else value
@ -1236,6 +1239,9 @@ class Agent(object):
def extractPayload(self, value):
"""
Extracts payload from inside of the input string
>>> agent.extractPayload("prefix" + agent.addPayloadDelimiters("1 AND 1=1") + "suffix") == "1 AND 1=1"
True
"""
_ = re.escape(PAYLOAD_DELIMITER)

View file

@ -655,7 +655,7 @@ def paramToDict(place, parameters=None):
kb.base64Originals[parameter] = oldValue = value
value = urldecode(value, convall=True)
value = decodeBase64(value, binary=False, encoding=conf.encoding or UNICODE_ENCODING)
parameters = re.sub(r"\b%s(\b|\Z)" % re.escape(oldValue), value, parameters)
parameters = re.sub(r"\b%s(\b|\Z)" % re.escape(oldValue), value.replace('\\', r'\\'), parameters)
except:
errMsg = "parameter '%s' does not contain " % parameter
errMsg += "valid Base64 encoded value ('%s')" % value
@ -2467,7 +2467,7 @@ def getSQLSnippet(dbms, sfile, **variables):
retVal = retVal.replace(_, randomStr())
for _ in re.findall(r"%RANDINT\d+%", retVal, re.I):
retVal = retVal.replace(_, randomInt())
retVal = retVal.replace(_, getText(randomInt()))
variables = re.findall(r"(?<!\bLIKE ')%(\w+)%", retVal, re.I)
@ -3237,11 +3237,15 @@ def aliasToDbmsEnum(dbms):
return retVal
def findDynamicContent(firstPage, secondPage):
def findDynamicContent(firstPage, secondPage, merge=False):
"""
This function checks if the provided pages have dynamic content. If they
are dynamic, proper markings will be made
Note: with merge=True the newly found markings are accumulated into the
existing ones (e.g. when refining across multiple original-page samples)
instead of replacing them
>>> findDynamicContent("Lorem ipsum dolor sit amet, congue tation referrentur ei sed. Ne nec legimus habemus recusabo, natum reque et per. Facer tritani reprehendunt eos id, modus constituam est te. Usu sumo indoctum ad, pri paulo molestiae complectitur no.", "Lorem ipsum dolor sit amet, congue tation referrentur ei sed. Ne nec legimus habemus recusabo, natum reque et per. <script src='ads.js'></script>Facer tritani reprehendunt eos id, modus constituam est te. Usu sumo indoctum ad, pri paulo molestiae complectitur no.")
>>> kb.dynamicMarkings
[('natum reque et per. ', 'Facer tritani repreh')]
@ -3254,7 +3258,9 @@ def findDynamicContent(firstPage, secondPage):
singleTimeLogMessage(infoMsg)
blocks = list(SequenceMatcher(None, firstPage, secondPage).get_matching_blocks())
kb.dynamicMarkings = []
if not merge:
kb.dynamicMarkings = []
# Removing too small matching blocks
for block in blocks[:]:
@ -3283,7 +3289,7 @@ def findDynamicContent(firstPage, secondPage):
suffix = suffix[:DYNAMICITY_BOUNDARY_LENGTH]
for _ in (firstPage, secondPage):
match = re.search(r"(?s)%s(.+)%s" % (re.escape(prefix), re.escape(suffix)), _)
match = re.search(r"(?s)%s(.+?)%s" % (re.escape(prefix), re.escape(suffix)), _)
if match:
infix = match.group(1)
if infix[0].isalnum():
@ -3292,7 +3298,9 @@ def findDynamicContent(firstPage, secondPage):
suffix = trimAlphaNum(suffix)
break
kb.dynamicMarkings.append((prefix if prefix else None, suffix if suffix else None))
marking = (prefix if prefix else None, suffix if suffix else None)
if marking not in kb.dynamicMarkings: # Note: avoiding duplicates (e.g. when accumulating markings across samples)
kb.dynamicMarkings.append(marking)
if len(kb.dynamicMarkings) > 0:
infoMsg = "dynamic content marked for removal (%d region%s)" % (len(kb.dynamicMarkings), 's' if len(kb.dynamicMarkings) > 1 else '')
@ -3311,11 +3319,11 @@ def removeDynamicContent(page):
if prefix is None and suffix is None:
continue
elif prefix is None:
page = re.sub(r"(?s)^.+%s" % re.escape(suffix), suffix.replace('\\', r'\\'), page)
page = re.sub(r"(?s)^.+?%s" % re.escape(suffix), suffix.replace('\\', r'\\'), page)
elif suffix is None:
page = re.sub(r"(?s)%s.+$" % re.escape(prefix), prefix.replace('\\', r'\\'), page)
else:
page = re.sub(r"(?s)%s.+%s" % (re.escape(prefix), re.escape(suffix)), "%s%s" % (prefix.replace('\\', r'\\'), suffix.replace('\\', r'\\')), page)
page = re.sub(r"(?s)%s.+?%s" % (re.escape(prefix), re.escape(suffix)), "%s%s" % (prefix.replace('\\', r'\\'), suffix.replace('\\', r'\\')), page)
return page
@ -4122,6 +4130,9 @@ def intersect(containerA, containerB, lowerCase=False):
def decodeStringEscape(value):
"""
Decodes escaped string values (e.g. "\\t" -> "\t")
>>> decodeStringEscape("a" + chr(92) + "tb") == "a" + chr(9) + "b"
True
"""
retVal = value
@ -4136,6 +4147,9 @@ def decodeStringEscape(value):
def encodeStringEscape(value):
"""
Encodes escaped string values (e.g. "\t" -> "\\t")
>>> encodeStringEscape("a" + chr(9) + "b") == "a" + chr(92) + "tb"
True
"""
retVal = value
@ -4283,7 +4297,10 @@ def safeSQLIdentificatorNaming(name, isTable=False):
'[begin]'
>>> getText(safeSQLIdentificatorNaming("foobar"))
'foobar'
>>> kb.forceDbms = popValue()
>>> kb.forcedDbms = DBMS.FIREBIRD
>>> getText(safeSQLIdentificatorNaming("foo bar"))
'"foo bar"'
>>> kb.forcedDbms = popValue()
"""
retVal = name
@ -4303,9 +4320,9 @@ def safeSQLIdentificatorNaming(name, isTable=False):
if not conf.noEscape:
retVal = unsafeSQLIdentificatorNaming(retVal)
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE, DBMS.SPANNER): # Note: in SQLite double-quotes are treated as string if column/identifier is non-existent (e.g. SELECT "foobar" FROM users)
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE, DBMS.SPANNER, DBMS.CLICKHOUSE): # Note: in SQLite double-quotes are treated as string if column/identifier is non-existent (e.g. SELECT "foobar" FROM users)
retVal = "`%s`" % retVal
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE, DBMS.RAIMA, DBMS.VIRTUOSO, DBMS.SNOWFLAKE):
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE, DBMS.RAIMA, DBMS.VIRTUOSO, DBMS.SNOWFLAKE, DBMS.FIREBIRD, DBMS.DERBY, DBMS.MAXDB):
retVal = "\"%s\"" % retVal
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.ALTIBASE, DBMS.MIMERSQL, DBMS.HANA):
retVal = "\"%s\"" % retVal.upper()
@ -4342,9 +4359,9 @@ def unsafeSQLIdentificatorNaming(name):
retVal = name
if isinstance(name, six.string_types):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE, DBMS.SPANNER):
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS, DBMS.CUBRID, DBMS.SQLITE, DBMS.SPANNER, DBMS.CLICKHOUSE):
retVal = name.replace("`", "")
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE, DBMS.RAIMA, DBMS.VIRTUOSO, DBMS.SNOWFLAKE):
elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2, DBMS.HSQLDB, DBMS.H2, DBMS.INFORMIX, DBMS.MONETDB, DBMS.VERTICA, DBMS.MCKOI, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.EXTREMEDB, DBMS.FRONTBASE, DBMS.RAIMA, DBMS.VIRTUOSO, DBMS.SNOWFLAKE, DBMS.FIREBIRD, DBMS.DERBY, DBMS.MAXDB):
retVal = name.replace("\"", "")
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.ALTIBASE, DBMS.MIMERSQL, DBMS.HANA):
retVal = name.replace("\"", "").upper()

View file

@ -299,8 +299,8 @@ class Dump(object):
colType = columns[column]
column = unsafeSQLIdentificatorNaming(column)
maxlength1 = max(maxlength1, len(column or ""))
maxlength2 = max(maxlength2, len(colType or ""))
maxlength1 = max(maxlength1, getConsoleLength(column or ""))
maxlength2 = max(maxlength2, getConsoleLength(colType or ""))
maxlength1 = max(maxlength1, len("COLUMN"))
lines1 = "-" * (maxlength1 + 2)
@ -337,10 +337,10 @@ class Dump(object):
colType = columns[column]
column = unsafeSQLIdentificatorNaming(column)
blank1 = " " * (maxlength1 - len(column))
blank1 = " " * (maxlength1 - getConsoleLength(column))
if colType is not None:
blank2 = " " * (maxlength2 - len(colType))
blank2 = " " * (maxlength2 - getConsoleLength(colType))
self._write("| %s%s | %s%s |" % (column, blank1, colType, blank2))
else:
self._write("| %s%s |" % (column, blank1))

View file

@ -2071,7 +2071,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.cache = AttribDict()
kb.cache.addrinfo = {}
kb.cache.content = LRUDict(capacity=16)
kb.cache.comparison = {}
kb.cache.comparison = LRUDict(capacity=256)
kb.cache.encoding = LRUDict(capacity=256)
kb.cache.alphaBoundaries = None
kb.cache.hashRegex = None

View file

@ -20,7 +20,7 @@ from lib.core.enums import OS
from thirdparty import six
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.10.6.50"
VERSION = "1.10.6.95"
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
@ -62,7 +62,7 @@ LOWER_RATIO_BOUND = 0.02
UPPER_RATIO_BOUND = 0.98
# For filling in case of dumb push updates
DUMMY_JUNK = "fooj0Zo4"
DUMMY_JUNK = "Phah5jue"
# Markers for special cases when parameter values contain html encoded characters
PARAMETER_AMP_MARKER = "__PARAMETER_AMP__"
@ -476,7 +476,7 @@ URI_INJECTABLE_REGEX = r"//[^/]*/([^\.*?]+)\Z"
SENSITIVE_DATA_REGEX = r"(\s|=)(?P<result>[^\s=]*\b%s\b[^\s]*)\s"
# Options to explicitly mask in anonymous (unhandled exception) reports (along with anything carrying the <hostname> inside)
SENSITIVE_OPTIONS = ("hostname", "answers", "data", "dnsDomain", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy", "fileRead", "fileWrite", "fileDest", "testParameter", "authCred", "sqlQuery", "requestFile", "csrfToken", "csrfData", "csrfUrl", "testParameter")
SENSITIVE_OPTIONS = ("hostname", "answers", "data", "dnsDomain", "googleDork", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy", "fileRead", "fileWrite", "fileDest", "authCred", "sqlQuery", "requestFile", "csrfToken", "csrfData", "csrfUrl", "testParameter")
# Maximum number of threads (avoiding connection issues and/or DoS)
MAX_NUMBER_OF_THREADS = 10
@ -554,7 +554,7 @@ UNSAFE_DUMP_FILEPATH_REPLACEMENT = '_'
RESTORE_MERGED_OPTIONS = ("col", "db", "dbms", "os", "dnsDomain", "privEsc", "tbl", "regexp", "string", "textOnly", "threads", "timeSec", "tmpPath", "uChar", "user")
# Parameters to be ignored in detection phase (upper case)
IGNORE_PARAMETERS = ("__VIEWSTATE", "__VIEWSTATEENCRYPTED", "__VIEWSTATEGENERATOR", "__EVENTARGUMENT", "__EVENTTARGET", "__EVENTVALIDATION", "ASPSESSIONID", "ASP.NET_SESSIONID", "JSESSIONID", "CFID", "CFTOKEN")
IGNORE_PARAMETERS = ("__VIEWSTATE", "__VIEWSTATEENCRYPTED", "__VIEWSTATEGENERATOR", "__EVENTARGUMENT", "__EVENTTARGET", "__EVENTVALIDATION", "__SCROLLPOSITIONX", "__SCROLLPOSITIONY", "__PREVIOUSPAGE", "ASPSESSIONID", "ASP.NET_SESSIONID", "JSESSIONID", "PHPSESSID", "SESSID", "CFID", "CFTOKEN")
# Regular expression used for recognition of ASP.NET control parameters
ASP_NET_CONTROL_REGEX = r"(?i)\Actl\d+\$"
@ -626,7 +626,7 @@ DUMMY_SQL_INJECTION_CHARS = ";()'"
DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]|\bUNION\b.+\bSELECT\b|\bSELECT\b.+\bFROM\b|\b(CONCAT|information_schema|SLEEP|DELAY|FLOOR\(RAND)\b"
# Extensions skipped by crawler
CRAWL_EXCLUDE_EXTENSIONS = frozenset(("3ds", "3g2", "3gp", "7z", "DS_Store", "a", "aac", "accdb", "access", "adp", "ai", "aif", "aiff", "apk", "ar", "asf", "au", "avi", "bak", "bin", "bin", "bk", "bkp", "bmp", "btif", "bz2", "c", "cab", "caf", "cfg", "cgm", "cmx", "com", "conf", "config", "cpio", "cpp", "cr2", "cue", "dat", "db", "dbf", "deb", "debug", "djvu", "dll", "dmg", "dmp", "dng", "doc", "docx", "dot", "dotx", "dra", "dsk", "dts", "dtshd", "dvb", "dwg", "dxf", "dylib", "ear", "ecelp4800", "ecelp7470", "ecelp9600", "egg", "elf", "env", "eol", "eot", "epub", "error", "exe", "f4v", "fbs", "fh", "fla", "flac", "fli", "flv", "fpx", "fst", "fvt", "g3", "gif", "go", "gz", "h", "h261", "h263", "h264", "ico", "ief", "img", "ini", "ipa", "iso", "jar", "java", "jpeg", "jpg", "jpgv", "jpm", "js", "jxr", "ktx", "lock", "log", "lvp", "lz", "lzma", "lzo", "m3u", "m4a", "m4v", "mar", "mdb", "mdi", "mid", "mj2", "mka", "mkv", "mmr", "mng", "mov", "movie", "mp3", "mp4", "mp4a", "mpeg", "mpg", "mpga", "msi", "mxu", "nef", "npx", "nrg", "o", "oga", "ogg", "ogv", "old", "otf", "ova", "ovf", "pbm", "pcx", "pdf", "pea", "pgm", "php", "pic", "pid", "pkg", "png", "pnm", "ppm", "pps", "ppt", "pptx", "ps", "psd", "py", "pya", "pyc", "pyo", "pyv", "qt", "rar", "ras", "raw", "rb", "rgb", "rip", "rlc", "rs", "run", "rz", "s3m", "s7z", "scm", "scpt", "service", "sgi", "shar", "sil", "smv", "so", "sock", "socket", "sqlite", "sqlitedb", "sub", "svc", "swf", "swo", "swp", "sys", "tar", "tbz2", "temp", "tga", "tgz", "tif", "tiff", "tlz", "tmp", "toast", "torrent", "ts", "ts", "ttf", "uvh", "uvi", "uvm", "uvp", "uvs", "uvu", "vbox", "vdi", "vhd", "vhdx", "viv", "vmdk", "vmx", "vob", "vxd", "war", "wav", "wax", "wbmp", "wdp", "weba", "webm", "webp", "whl", "wm", "wma", "wmv", "wmx", "woff", "woff2", "wvx", "xbm", "xif", "xls", "xlsx", "xlt", "xm", "xpi", "xpm", "xwd", "xz", "yaml", "yml", "z", "zip", "zipx"))
CRAWL_EXCLUDE_EXTENSIONS = frozenset(("3ds", "3g2", "3gp", "7z", "DS_Store", "a", "aac", "accdb", "access", "adp", "ai", "aif", "aiff", "apk", "ar", "asf", "au", "avi", "bak", "bin", "bk", "bkp", "bmp", "btif", "bz2", "c", "cab", "caf", "cfg", "cgm", "cmx", "com", "conf", "config", "cpio", "cpp", "cr2", "cue", "dat", "db", "dbf", "deb", "debug", "djvu", "dll", "dmg", "dmp", "dng", "doc", "docx", "dot", "dotx", "dra", "dsk", "dts", "dtshd", "dvb", "dwg", "dxf", "dylib", "ear", "ecelp4800", "ecelp7470", "ecelp9600", "egg", "elf", "env", "eol", "eot", "epub", "error", "exe", "f4v", "fbs", "fh", "fla", "flac", "fli", "flv", "fpx", "fst", "fvt", "g3", "gif", "go", "gz", "h", "h261", "h263", "h264", "ico", "ief", "img", "ini", "ipa", "iso", "jar", "java", "jpeg", "jpg", "jpgv", "jpm", "js", "jxr", "ktx", "lock", "log", "lvp", "lz", "lzma", "lzo", "m3u", "m4a", "m4v", "mar", "mdb", "mdi", "mid", "mj2", "mka", "mkv", "mmr", "mng", "mov", "movie", "mp3", "mp4", "mp4a", "mpeg", "mpg", "mpga", "msi", "mxu", "nef", "npx", "nrg", "o", "oga", "ogg", "ogv", "old", "otf", "ova", "ovf", "pbm", "pcx", "pdf", "pea", "pgm", "php", "pic", "pid", "pkg", "png", "pnm", "ppm", "pps", "ppt", "pptx", "ps", "psd", "py", "pya", "pyc", "pyo", "pyv", "qt", "rar", "ras", "raw", "rb", "rgb", "rip", "rlc", "rs", "run", "rz", "s3m", "s7z", "scm", "scpt", "service", "sgi", "shar", "sil", "smv", "so", "sock", "socket", "sqlite", "sqlitedb", "sub", "svc", "swf", "swo", "swp", "sys", "tar", "tbz2", "temp", "tga", "tgz", "tif", "tiff", "tlz", "tmp", "toast", "torrent", "ts", "ttf", "uvh", "uvi", "uvm", "uvp", "uvs", "uvu", "vbox", "vdi", "vhd", "vhdx", "viv", "vmdk", "vmx", "vob", "vxd", "war", "wav", "wax", "wbmp", "wdp", "weba", "webm", "webp", "whl", "wm", "wma", "wmv", "wmx", "woff", "woff2", "wvx", "xbm", "xif", "xls", "xlsx", "xlt", "xm", "xpi", "xpm", "xwd", "xz", "yaml", "yml", "z", "zip", "zipx"))
# Patterns often seen in HTTP headers containing custom injection marking character '*'
PROBLEMATIC_CUSTOM_INJECTION_PATTERNS = r"(;q=[^;']+)|(\*/\*)"
@ -828,7 +828,7 @@ MAX_HELP_OPTION_LENGTH = 18
MAX_CONNECT_RETRIES = 100
# Strings for detecting formatting errors
FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", "CF_SQL_NUMERIC", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "Attribute validation error for tag", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "unable to interpret text value", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException", "Arguments are of the wrong type", "Invalid conversion")
FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Please enter a", "Conversion failed", "String or binary data would be truncated", "Failed to convert", "unable to interpret text value", "Input string was not in a correct format", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal", "TypeMismatchException", "CF_SQL_INTEGER", "CF_SQL_NUMERIC", " for CFSQLTYPE ", "cfqueryparam cfsqltype", "InvalidParamTypeException", "Invalid parameter type", "Attribute validation error for tag", "is not of type numeric", "<cfif Not IsNumeric(", "invalid input syntax for integer", "invalid input syntax for type", "invalid number", "character to number conversion error", "String was not recognized as a valid", "Convert.ToInt", "cannot be converted to a ", "InvalidDataException", "Arguments are of the wrong type", "Invalid conversion")
# Regular expression used for extracting ASP.NET view state values
VIEWSTATE_REGEX = r'(?i)(?P<name>__VIEWSTATE[^"]*)[^>]+value="(?P<result>[^"]+)'
@ -842,13 +842,13 @@ LIMITED_ROWS_TEST_NUMBER = 15
# Default adapter to use for bottle server
RESTAPI_DEFAULT_ADAPTER = "wsgiref"
# Default REST-JSON API server listen address
# Default REST API server listen address
RESTAPI_DEFAULT_ADDRESS = "127.0.0.1"
# Default REST-JSON API server listen port
# Default REST API server listen port
RESTAPI_DEFAULT_PORT = 8775
# Unsupported options by REST-JSON API server
# Unsupported options by REST API server
RESTAPI_UNSUPPORTED_OPTIONS = ("sqlShell", "wizard", "evalCode", "alert")
# Use "Supplementary Private Use Area-A"
@ -973,14 +973,14 @@ for key, value in os.environ.items():
_ = key[len(SQLMAP_ENVIRONMENT_PREFIX) + 1:].upper()
if _ in globals():
original = globals()[_]
if isinstance(original, int):
if isinstance(original, bool):
globals()[_] = value.lower() in ('1', 'true')
elif isinstance(original, int):
try:
globals()[_] = int(value)
except ValueError:
pass
elif isinstance(original, bool):
globals()[_] = value.lower() in ('1', 'true')
elif isinstance(original, (list, tuple)):
globals()[_] = [__.strip() for __ in _.split(',')]
globals()[_] = [__.strip() for __ in value.split(',')]
else:
globals()[_] = value

View file

@ -87,8 +87,10 @@ class Wordlist(six.Iterator):
errMsg += "sure that you haven't made any changes to it"
raise SqlmapInstallationException(errMsg)
except StopIteration:
self.adjust()
retVal = next(self.iter).rstrip()
if self.index > len(self.filenames): # Note: no more sources (filenames + custom) to switch to
raise
self.adjust() # Note: switch to the next source and retry (gracefully skipping empty ones)
continue
if not self.proc_count or self.counter % self.proc_count == self.proc_id:
break
return retVal

View file

@ -235,13 +235,13 @@ def checkCharEncoding(encoding, warn=True):
# Reference: http://docs.python.org/library/codecs.html
try:
codecs.lookup(encoding)
except:
except LookupError:
encoding = None
if encoding:
try:
six.text_type(getBytes(randomStr()), encoding)
except:
except (UnicodeDecodeError, LookupError):
if warn:
warnMsg = "invalid web page charset '%s'" % encoding
singleTimeLogMessage(warnMsg, logging.WARN, encoding)
@ -258,10 +258,11 @@ def getHeuristicCharEncoding(page):
>>> getHeuristicCharEncoding(b"<html></html>")
'ascii'
>>> getHeuristicCharEncoding(b'<!DOCTYPE html><html><head><meta charset="windows-1251"><title>\\xd2\\xe5\\xf1\\xf2</title></head><body>\\xc2 \\xf1\\xee\\xee\\xf2\\xe2\\xe5\\xf2\\xf1\\xf2\\xe2\\xe8\\xe8 \\xf1 \\xef\\xf0\\xe8\\xed\\xf6\\xe8\\xef\\xe0\\xec\\xe8 \\xf0\\xe0\\xe1\\xee\\xf2\\xfb \\xf3\\xf2\\xe8\\xeb\\xe8\\xf2\\xfb \\xe0\\xe2\\xf2\\xee\\xec\\xe0\\xf2\\xe8\\xf7\\xe5\\xf1\\xea\\xee\\xe3\\xee \\xee\\xef\\xf0\\xe5\\xe4\\xe5\\xeb\\xe5\\xed\\xe8\\xff \\xea\\xee\\xe4\\xe8\\xf0\\xee\\xe2\\xea\\xe8, \\xed\\xe0\\xec \\xf2\\xf0\\xe5\\xe1\\xf3\\xe5\\xf2\\xf1\\xff \\xef\\xf0\\xe5\\xe4\\xee\\xf1\\xf2\\xe0\\xe2\\xe8\\xf2\\xfc \\xe7\\xed\\xe0\\xf7\\xe8\\xf2\\xe5\\xeb\\xfc\\xed\\xee \\xe1\\xee\\xeb\\xe5\\xe5 \\xe4\\xeb\\xe8\\xed\\xed\\xfb\\xe9 \\xf4\\xf0\\xe0\\xe3\\xec\\xe5\\xed\\xf2 \\xf2\\xe5\\xea\\xf1\\xf2\\xe0 \\xed\\xe0 \\xf0\\xf3\\xf1\\xf1\\xea\\xee\\xec \\xff\\xe7\\xfb\\xea\\xe5. \\xdd\\xf2\\xee \\xed\\xe5\\xee\\xb1\\xf5\\xee\\xe4\\xe8\\xec\\xee \\xe4\\xeb\\xff \\xf2\\xee\\xe3\\xee, \\xf7\\xf2\\xee\\xf1\\xfb \\xf1\\xf2\\xe0\\xf2\\xe8\\xf1\\xf2\\xe8\\xf7\\xe5\\xf1\\xea\\xe8\\xe9 \\xe0\\xed\\xe0\\xeb\\xe8\\xe7\\xe0\\xf2\\xee\\xf0 \\xf7\\xe0\\xf1\\xf2\\xee\\xf2\\xed\\xee\\xf1\\xf2\\xe8 \\xf1\\xe8\\xec\\xe2\\xee\\xeb\\xee\\xe2 \\xe8 \\xe4\\xe2\\xf3\\xf5\\xe1\\xf3\\xea\\xe2\\xe5\\xed\\xed\\xfb\\xf5 \\xf1\\xee\\xf7\\xe5\\xf2\\xe0\\xed\\xe8\\xb9 \\xf1\\xec\\xee\\xe3 \\xf1 \\xe2\\xfb\\xf1\\xee\\xea\\xee\\xb9 \\xf1\\xf2\\xe5\\xef\\xe5\\xed\\xfc\\xf2 \\xf3\\xe2\\xe5\\xf0\\xe5\\xed\\xed\\xee\\xf1\\xf2\\xe8 \\xe7\\xe0\\xf4\\xe8\\xea\\xf1\\xe8\\xf0\\xee\\xe2\\xe0\\xf2\\xfc \\xe8\\xec\\xe5\\xed\\xed\\xee \\xf1\\xf2\\xe0\\xed\\xe4\\xe0\\xf0\\xf2 Windows-1251, \\xe0 \\xed\\xe5 MacCyrillic \\xe8\\xeb\\xe8 ISO-8859-5. \\xd0\\xf3\\xf1\\xf1\\xea\\xe8\\xb9 \\xff\\xe7\\xfb\\xea \\xee\\xe1\\xbb\\xe0\\xe4\\xe0\\xe5\\xf2 \\xf3\\xed\\xe8\\xea\\xe0\\xeb\\xfc\\xed\\xfb\\xec \\xf0\\xe0\\xf1\\xef\\xf0\\xe5\\xe4\\xe5\\xeb\\xe5\\xed\\xe8\\xe5\\xec \\xe3\\xeb\\xe0\\xf1\\xed\\xfb\\xf5 \\xe8 \\xf1\\xee\\xe3\\xeb\\xe0\\xf1\\xed\\xfb\\xf5 \\xe1\\xf3\\xea\\xe2, \\xf2\\xe0\\xea\\xe8\\xf5 \\xea\\xe0\\xea \\xee, \\xe5, \\xe0, \\xe8, \\xed, \\xf2, \\xea\\xee\\xf2\\xee\\xf0\\xfb\\xe5 \\xe2 \\xf0\\xe0\\xe7\\xed\\xfb\\xf5 \\xea\\xee\\xe4\\xee\\xe2\\xfb\\xf5 \\xf1\\xf2\\xf0\\xe0\\xed\\xe8\\xf6\\xe0\\xf5 \\xe7\\xe0\\xed\\xe8\\xec\\xe0\\xf3\\xf2 \\xf1\\xee\\xe2\\xe5\\xf0\\xf8\\xe5\\xed\\xed\\xee \\xf0\\xe0\\xe7\\xed\\xfb\\xe5 \\xef\\xee\\xf7\\xe8\\xf6\\xe8\\xe8 \\xe2 \\xf2\\xe0\\xe1\\xeb\\xe8\\xf6\\xe5 \\xe1\\xe0\\xb9\\xf2\\xee\\xe2. \\xca\\xee\\xe3\\xe4\\xe0 \\xf2\\xe5\\xea\\xf1\\xf2\\xe0 \\xf1\\xf2\\xe0\\xed\\xee\\xe2\\xe8\\xf2\\xf1\\xff \\xe4\\xee\\xf1\\xf2\\xe0\\xf2\\xee\\xf7\\xed\\xee \\xec\\xed\\xee\\xe3\\xee, \\xe2\\xe5\\xf0\\xee\\xff\\xf2\\xed\\xee\\xf1\\xf2\\xfc \\xee\\xf8\\xe8\\xe1\\xea\\xe8 \\xf1\\xed\\xe8\\xe6\\xe0\\xe5\\xf2\\xf1\\xff \\xef\\xf0\\xe0\\xea\\xf2\\xe8\\xf7\\xe5\\xf1\\xea\\xe8 \\xe4\\xee \\xed\\xf3\\xeb\\xff. \\xcc\\xfb \\xe4\\xee\\xe1\\xe0\\xe2\\xeb\\xff\\xe5\\xec \\xe5\\xf9\\xe5 \\xed\\xe5\\xf1\\xea\\xee\\xeb\\xfc\\xea\\xee \\xef\\xf0\\xe5\\xe4\\xeb\\xee\\xe6\\xe5\\xed\\xe8\\xb9, \\xf7\\xf2\\xee\\xf1\\xfb \\xf0\\xe0\\xf1\\xf8\\xe8\\xf0\\xe8\\xf2\\xfc \\xe2\\xfb\\xe1\\xee\\xf0\\xea\\xf3 \\xe4\\xe0\\xed\\xed\\xfb\\xf5 \\xe4\\xeb\\xff \\xea\\xee\\xf0\\xf0\\xe5\\xea\\xf2\\xed\\xee\\xe3\\xee \\xf2\\xe5\\xf1\\xf2\\xe8\\xf0\\xee\\xe2\\xe0\\xed\\xe8\\xff \\xe2\\xe0\\xf8\\xe5\\xb9 \\xe1\\xe8\\xe1\\xeb\\xe8\\xee\\xf2\\xe5\\xea\\xe8 \\xe2 \\xf1\\xf0\\xe5\\xe4\\xe5 Python.</body></html>')
'windows-1251'
"""
key = (len(page), hash(page))
retVal = kb.cache.encoding.get(key)
if retVal is None:
retVal = detect(page[:HEURISTIC_PAGE_SIZE_THRESHOLD])["encoding"]
@ -281,6 +282,8 @@ def decodePage(page, contentEncoding, contentType, percentDecode=True):
'<html>foo&bar</html>'
>>> getText(decodePage(b"&#x9;", None, "text/html; charset=utf-8"))
'\\t'
>>> getText(decodePage(b"&#x4A;", None, "text/html; charset=utf-8"))
'J'
"""
if not page or (conf.nullConnection and len(page) < 2):
@ -345,14 +348,13 @@ def decodePage(page, contentEncoding, contentType, percentDecode=True):
if not kb.disableHtmlDecoding:
# e.g. &#x9;&#195;&#235;&#224;&#226;&#224;
if b"&#" in page:
page = re.sub(b"&#x([0-9a-f]{1,2});", lambda _: decodeHex(_.group(1) if len(_.group(1)) == 2 else b"0%s" % _.group(1)), page)
page = re.sub(b"(?i)&#x([0-9a-f]{1,2});", lambda _: decodeHex(_.group(1) if len(_.group(1)) == 2 else b"0%s" % _.group(1)), page)
page = re.sub(b"&#(\\d{1,3});", lambda _: six.int2byte(int(_.group(1))) if int(_.group(1)) < 256 else _.group(0), page)
# e.g. %20%28%29
if percentDecode:
if b"%" in page:
page = re.sub(b"%([0-9a-f]{2})", lambda _: decodeHex(_.group(1)), page)
page = re.sub(b"%([0-9A-F]{2})", lambda _: decodeHex(_.group(1)), page) # Note: %DeepSee_SQL in CACHE
page = re.sub(b"(?i)%([0-9a-f]{2})", lambda _: decodeHex(_.group(1)), page)
# e.g. &amp;
page = re.sub(b"&([^;]+);", lambda _: six.int2byte(HTML_ENTITIES[getText(_.group(1))]) if HTML_ENTITIES.get(getText(_.group(1)), 256) < 256 else _.group(0), page)

View file

@ -1163,7 +1163,7 @@ class Connect(object):
postUrlEncode = False
if conf.hpp:
if not any(conf.url.lower().endswith(_.lower()) for _ in (WEB_PLATFORM.ASP, WEB_PLATFORM.ASPX)):
if (extractRegexResult(r"\.(?P<result>\w+)(?:\?|\Z)", conf.url) or "").lower() not in (WEB_PLATFORM.ASP, WEB_PLATFORM.ASPX):
warnMsg = "HTTP parameter pollution should work only against "
warnMsg += "ASP(.NET) targets"
singleTimeWarnMessage(warnMsg)

View file

@ -156,7 +156,7 @@ class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler):
try:
result = _urllib.request.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
except _urllib.error.HTTPError as ex:
result = ex
result = error = ex
# Dirty hack for https://github.com/sqlmapproject/sqlmap/issues/4046
try:
@ -178,7 +178,7 @@ class SmartRedirectHandler(_urllib.request.HTTPRedirectHandler):
if not hasattr(result, "read"):
def _(self, length=None):
try:
retVal = getSafeExString(ex) # Note: pyflakes mistakenly marks 'ex' as undefined (NOTE: tested in both Python2 and Python3)
retVal = getSafeExString(error)
except:
retVal = ""
return getBytes(retVal)

View file

@ -231,8 +231,8 @@ class UDF(object):
errMsg += "but the database underlying operating system is Linux"
raise SqlmapMissingMandatoryOptionException(errMsg)
self.udfSharedLibName = os.path.basename(self.udfLocalFile).split(".")[0]
self.udfSharedLibExt = os.path.basename(self.udfLocalFile).split(".")[1]
self.udfSharedLibName = os.path.splitext(os.path.basename(self.udfLocalFile))[0]
self.udfSharedLibExt = os.path.splitext(self.udfLocalFile)[1][1:]
msg = "how many user-defined functions do you want to create "
msg += "from the shared library? "

View file

@ -152,6 +152,8 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
lastChar = 0
elif conf.lastChar is not None and (isinstance(conf.lastChar, int) or (hasattr(conf.lastChar, "isdigit") and conf.lastChar.isdigit())):
lastChar = int(conf.lastChar)
if kb.fileReadMode: # Note: file content is retrieved hex-encoded (2 chars per byte), mirroring the firstChar handling above
lastChar <<= 1
elif hasattr(lastChar, "isdigit") and lastChar.isdigit() or isinstance(lastChar, int):
lastChar = int(lastChar)
else:
@ -512,6 +514,8 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
threadData.shared.value = [None] * length
threadData.shared.index = [firstChar] # As list for python nested function scoping
threadData.shared.start = firstChar
threadData.shared.retrieved = 0
threadData.shared.endIndex = 0
try:
def blindThread():
@ -537,7 +541,11 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
break
with kb.locks.value:
threadData.shared.value[currentCharIndex - 1 - firstChar] = val
idx = currentCharIndex - 1 - firstChar
threadData.shared.value[idx] = val
threadData.shared.retrieved += 1
if idx > threadData.shared.endIndex:
threadData.shared.endIndex = idx
currentValue = list(threadData.shared.value)
if kb.threadContinue:
@ -545,25 +553,18 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
progress.progress(threadData.shared.index[0])
elif conf.verbose >= 1:
startCharIndex = 0
endCharIndex = 0
for i in xrange(length):
if currentValue[i] is not None:
endCharIndex = max(endCharIndex, i)
endCharIndex = threadData.shared.endIndex
output = ''
if endCharIndex > conf.progressWidth:
startCharIndex = endCharIndex - conf.progressWidth
count = threadData.shared.start
count = threadData.shared.start + threadData.shared.retrieved
for i in xrange(startCharIndex, endCharIndex + 1):
output += '_' if currentValue[i] is None else filterControlChars(currentValue[i] if len(currentValue[i]) == 1 else ' ', replacement=' ')
for i in xrange(length):
count += 1 if currentValue[i] is not None else 0
if startCharIndex > 0:
output = ".." + output[2:]

View file

@ -78,6 +78,8 @@ class DataStore(object):
username = None
password = None
RESTAPI_READONLY_OPTIONS = ("api", "taskid", "database")
# API objects
class Database(object):
filepath = None
@ -91,7 +93,7 @@ class Database(object):
self.connection = sqlite3.connect(self.database, timeout=3, isolation_level=None, check_same_thread=False)
self.cursor = self.connection.cursor()
self.lock = threading.Lock()
logger.debug("REST-JSON API %s connected to IPC database" % who)
logger.debug("REST API %s connected to IPC database" % who)
def disconnect(self):
if self.cursor:
@ -296,6 +298,19 @@ def setRestAPILog():
def is_admin(token):
return safeCompareStrings(DataStore.admin_token, token)
def validate_task_options(taskid, options, caller):
if not isinstance(options, dict):
logger.warning("[%s] Invalid JSON options provided to %s()" % (taskid, caller))
return "Invalid JSON options"
for key in options:
if key in RESTAPI_UNSUPPORTED_OPTIONS or key in RESTAPI_READONLY_OPTIONS:
logger.warning("[%s] Unsupported option '%s' provided to %s()" % (taskid, key, caller))
return "Unsupported option '%s'" % key
elif key not in DataStore.tasks[taskid].options:
logger.warning("[%s] Unknown option '%s' provided to %s()" % (taskid, key, caller))
return "Unknown option '%s'" % key
@hook('before_request')
def check_authentication():
if not any((DataStore.username, DataStore.password)):
@ -490,6 +505,10 @@ def option_set(taskid):
logger.warning("[%s] Invalid JSON options provided to option_set()" % taskid)
return jsonize({"success": False, "message": "Invalid JSON options"})
message = validate_task_options(taskid, request.json, "option_set")
if message:
return jsonize({"success": False, "message": message})
for option, value in request.json.items():
DataStore.tasks[taskid].set_option(option, value)
@ -511,10 +530,13 @@ def scan_start(taskid):
logger.warning("[%s] Invalid JSON options provided to scan_start()" % taskid)
return jsonize({"success": False, "message": "Invalid JSON options"})
for key in request.json:
if key in RESTAPI_UNSUPPORTED_OPTIONS:
logger.warning("[%s] Unsupported option '%s' provided to scan_start()" % (taskid, key))
return jsonize({"success": False, "message": "Unsupported option '%s'" % key})
if DataStore.tasks[taskid].engine_process() is not None and not DataStore.tasks[taskid].engine_has_terminated():
logger.warning("[%s] Scan already running" % taskid)
return jsonize({"success": False, "message": "Scan already running"})
message = validate_task_options(taskid, request.json, "scan_start")
if message:
return jsonize({"success": False, "message": message})
# Initialize sqlmap engine's options with user's provided options, if any
for option, value in request.json.items():
@ -596,7 +618,7 @@ def scan_data(taskid):
json_data_message.append({"status": status, "type": content_type, "value": dejsonize(value)})
# Read all error messages from the IPC database
for error in DataStore.current_db.execute("SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", (taskid,)):
for error, in DataStore.current_db.execute("SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", (taskid,)):
json_errors_message.append(error)
logger.debug("(%s) Retrieved scan data and error messages" % taskid)
@ -684,9 +706,12 @@ def version(token=None):
def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=RESTAPI_DEFAULT_ADAPTER, username=None, password=None, database=None):
"""
REST-JSON API server
REST API server
"""
if not all((username, password)):
logger.critical("REST API server requires both username and password")
DataStore.admin_token = encodeHex(os.urandom(16), binary=False)
DataStore.username = username
DataStore.password = password
@ -702,7 +727,7 @@ def server(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, adapter=REST
s.bind((host, 0))
port = s.getsockname()[1]
logger.info("Running REST-JSON API server at '%s:%d'.." % (host, port))
logger.info("Running REST API server at '%s:%d'.." % (host, port))
logger.info("Admin (secret) token: %s" % DataStore.admin_token)
logger.debug("IPC database: '%s'" % Database.filepath)
@ -762,7 +787,7 @@ def _client(url, options=None):
def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=None, password=None):
"""
REST-JSON API client
REST API client
"""
DataStore.username = username
@ -770,20 +795,20 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non
dbgMsg = "Example client access from command line:"
dbgMsg += "\n\t$ taskid=$(curl http://%s:%d/task/new 2>1 | grep -o -I '[a-f0-9]\\{16\\}') && echo $taskid" % (host, port)
dbgMsg += "\n\t$ curl -H \"Content-Type: application/json\" -X POST -d '{\"url\": \"http://testphp.vulnweb.com/artists.php?artist=1\"}' http://%s:%d/scan/$taskid/start" % (host, port)
dbgMsg += "\n\t$ curl -H \"Content-Type: application/json\" -X POST -d '{\"url\": \"http://testasp.vulnweb.com/showforum.asp?id=1\"}' http://%s:%d/scan/$taskid/start" % (host, port)
dbgMsg += "\n\t$ curl http://%s:%d/scan/$taskid/data" % (host, port)
dbgMsg += "\n\t$ curl http://%s:%d/scan/$taskid/log" % (host, port)
logger.debug(dbgMsg)
addr = "http://%s:%d" % (host, port)
logger.info("Starting REST-JSON API client to '%s'..." % addr)
logger.info("Starting REST API client to '%s'..." % addr)
try:
_client(addr)
except Exception as ex:
if not isinstance(ex, _urllib.error.HTTPError) or ex.code == _http_client.UNAUTHORIZED:
errMsg = "There has been a problem while connecting to the "
errMsg += "REST-JSON API server at '%s' " % addr
errMsg += "REST API server at '%s' " % addr
errMsg += "(%s)" % getSafeExString(ex)
logger.critical(errMsg)
return
@ -900,7 +925,7 @@ def client(host=RESTAPI_DEFAULT_ADDRESS, port=RESTAPI_DEFAULT_PORT, username=Non
elif command in ("help", "?"):
msg = "help Show this help message\n"
msg += "new ARGS Start a new scan task with provided arguments (e.g. 'new -u \"http://testphp.vulnweb.com/artists.php?artist=1\"')\n"
msg += "new ARGS Start a new scan task with provided arguments (e.g. 'new -u \"http://testasp.vulnweb.com/showforum.asp?id=1\"')\n"
msg += "use TASKID Switch current context to different task (e.g. 'use c04d8c5c7582efb4')\n"
msg += "data Retrieve and show data for current task\n"
msg += "log Retrieve and show log for current task\n"

View file

@ -616,7 +616,7 @@ def _finalize(retVal, results, processes, attack_info=None):
removals.add((user, hash_))
hashDBWrite(hash_, word)
for item in attack_info or []:
for item in list(attack_info or []):
if (item[0][0], item[0][1]) in removals:
attack_info.remove(item)
@ -1081,7 +1081,7 @@ def dictionaryAttack(attack_dict):
if item and hash_ not in keys:
resumed = hashDBRetrieve(hash_)
if not resumed:
if resumed is None:
attack_info.append(item)
user_hash.append(item[0])
else:

View file

@ -106,7 +106,7 @@ class HashDB(object):
if retVal is None:
retVal = self._read_cache.get(hash_)
if not retVal:
if retVal is None:
for _ in xrange(HASHDB_RETRIEVE_RETRIES):
try:
for row in self.cursor.execute("SELECT value FROM storage WHERE id=?", (hash_,)):

View file

@ -18,6 +18,7 @@ from lib.core.common import singleTimeWarnMessage
from lib.core.common import unArrayizeValue
from lib.core.common import unsafeSQLIdentificatorNaming
from lib.core.compat import xrange
from lib.core.convert import getConsoleLength
from lib.core.convert import getUnicode
from lib.core.data import conf
from lib.core.data import kb
@ -58,7 +59,7 @@ def pivotDumpTable(table, colList, count=None, blind=True, alias=None):
logger.info(infoMsg)
for column in colList:
lengths[column] = len(column)
lengths[column] = getConsoleLength(column)
entries[column] = []
return entries, lengths
@ -169,7 +170,7 @@ def pivotDumpTable(table, colList, count=None, blind=True, alias=None):
value = "" if isNoneValue(value) else unArrayizeValue(value)
lengths[column] = max(lengths[column], len(DUMP_REPLACEMENTS.get(getUnicode(value), getUnicode(value))))
lengths[column] = max(lengths[column], getConsoleLength(DUMP_REPLACEMENTS.get(getUnicode(value), getUnicode(value))))
entries[column].append(value)
except KeyboardInterrupt:

View file

@ -74,6 +74,11 @@ def safecharencode(value):
def safechardecode(value, binary=False):
"""
Reverse function to safecharencode
>>> safechardecode(u'test123') == u'test123'
True
>>> safechardecode(safecharencode(u'test\x01\x02\xaf')) == u'test\x01\x02\xaf'
True
"""
retVal = value

View file

@ -11,7 +11,6 @@ import socket
from lib.core.common import getSafeExString
from lib.core.common import popValue
from lib.core.common import pushValue
from lib.core.common import readInput
from lib.core.common import urlencode
from lib.core.convert import getBytes
from lib.core.convert import getUnicode
@ -24,7 +23,6 @@ from lib.core.enums import HTTP_HEADER
from lib.core.enums import REDIRECTION
from lib.core.exception import SqlmapBaseException
from lib.core.exception import SqlmapConnectionException
from lib.core.exception import SqlmapUserQuitException
from lib.core.settings import BING_REGEX
from lib.core.settings import DUCKDUCKGO_REGEX
from lib.core.settings import DUMMY_SEARCH_USER_AGENT
@ -37,152 +35,102 @@ from thirdparty.six.moves import http_client as _http_client
from thirdparty.six.moves import urllib as _urllib
from thirdparty.socks import socks
def _fetch(url, headers, data=None):
"""
Fetches and returns the (decoded) content of a search engine results page
(or None in case of a connection issue)
"""
retVal = None
try:
req = _urllib.request.Request(url, data=getBytes(data) if data else None, headers=headers)
conn = _urllib.request.urlopen(req)
requestMsg = "HTTP request:\n%s %s" % ("POST" if data else "GET", url)
requestMsg += " %s" % _http_client.HTTPConnection._http_vsn_str
logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg)
page = conn.read()
responseHeaders = conn.info()
responseMsg = "HTTP response (%s - %d):\n" % (conn.msg, conn.code)
if conf.verbose <= 4:
responseMsg += getUnicode(responseHeaders, UNICODE_ENCODING)
elif conf.verbose > 4:
responseMsg += "%s\n%s\n" % (responseHeaders, page)
logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg)
page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE))
retVal = getUnicode(page) # Note: if decodePage call fails (Issue #4202)
except _urllib.error.HTTPError as ex:
try:
retVal = getUnicode(ex.read())
except Exception:
pass
except (_urllib.error.URLError, _http_client.error, socket.error, socket.timeout, socks.ProxyError):
pass
return retVal
def _search(dork):
"""
This method performs the effective search on Google providing
the google dork and the Google session cookie
This method performs the effective search using the provided dork,
trying the available search engines in order of (current) scraping
reliability and returning the results of the first one that yields any
(so that the failure of a single engine does not break the feature)
"""
if not dork:
return None
page = None
data = None
requestHeaders = {}
responseHeaders = {}
retVal = []
seen = set()
requestHeaders[HTTP_HEADER.USER_AGENT] = dict(conf.httpHeaders).get(HTTP_HEADER.USER_AGENT, DUMMY_SEARCH_USER_AGENT)
requestHeaders[HTTP_HEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE
requestHeaders[HTTP_HEADER.COOKIE] = GOOGLE_CONSENT_COOKIE
try:
req = _urllib.request.Request("https://www.google.com/ncr", headers=requestHeaders)
conn = _urllib.request.urlopen(req)
except Exception as ex:
errMsg = "unable to connect to Google ('%s')" % getSafeExString(ex)
raise SqlmapConnectionException(errMsg)
requestHeaders = {
HTTP_HEADER.USER_AGENT: dict(conf.httpHeaders).get(HTTP_HEADER.USER_AGENT, DUMMY_SEARCH_USER_AGENT),
HTTP_HEADER.ACCEPT_ENCODING: HTTP_ACCEPT_ENCODING_HEADER_VALUE,
HTTP_HEADER.COOKIE: GOOGLE_CONSENT_COOKIE,
}
gpage = conf.googlePage if conf.googlePage > 1 else 1
logger.info("using search result page #%d" % gpage)
url = "https://www.google.com/search?" # NOTE: if consent fails, try to use the "http://"
url += "q=%s&" % urlencode(dork, convall=True)
url += "num=100&hl=en&complete=0&safe=off&filter=0&btnG=Search"
url += "&start=%d" % ((gpage - 1) * 100)
encoded = urlencode(dork, convall=True)
try:
req = _urllib.request.Request(url, headers=requestHeaders)
conn = _urllib.request.urlopen(req)
# Note: (name, url, POST data, regex, regex flags, match->link). Ordered by current scraping reliability; tried in turn until one yields results (DuckDuckGo currently being the only consistently scrapeable one)
engines = (
("DuckDuckGo", "https://html.duckduckgo.com/html/", "q=%s&s=%d" % (encoded, (gpage - 1) * 30), DUCKDUCKGO_REGEX, re.I | re.S, lambda match: match.group(1).replace("&amp;", "&")),
("Bing", "https://www.bing.com/search?q=%s&first=%d" % (encoded, (gpage - 1) * 10 + 1), None, BING_REGEX, re.I | re.S, lambda match: match.group(1)),
("Google", "https://www.google.com/search?q=%s&num=100&hl=en&complete=0&safe=off&filter=0&btnG=Search&start=%d" % (encoded, (gpage - 1) * 100), None, GOOGLE_REGEX, re.I, lambda match: match.group(1) or match.group(2)),
)
requestMsg = "HTTP request:\nGET %s" % url
requestMsg += " %s" % _http_client.HTTPConnection._http_vsn_str
logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg)
for name, url, data, regex, flags, extract in engines:
page = _fetch(url, requestHeaders, data)
page = conn.read()
code = conn.code
status = conn.msg
responseHeaders = conn.info()
if not page:
continue
responseMsg = "HTTP response (%s - %d):\n" % (status, code)
count = 0
for match in re.finditer(regex, page, flags):
link = _urllib.parse.unquote(extract(match))
if link and link not in seen:
seen.add(link)
retVal.append(link)
count += 1
if conf.verbose <= 4:
responseMsg += getUnicode(responseHeaders, UNICODE_ENCODING)
elif conf.verbose > 4:
responseMsg += "%s\n%s\n" % (responseHeaders, page)
if count:
logger.info("found %d usable link%s using %s" % (count, 's' if count != 1 else "", name))
break # Note: stop at the first engine that actually returns results (others are only fallbacks)
logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg)
except _urllib.error.HTTPError as ex:
try:
page = ex.read()
responseHeaders = ex.info()
except Exception as _:
warnMsg = "problem occurred while trying to get "
warnMsg += "an error page information (%s)" % getSafeExString(_)
logger.critical(warnMsg)
return None
except (_urllib.error.URLError, _http_client.error, socket.error, socket.timeout, socks.ProxyError):
errMsg = "unable to connect to Google"
raise SqlmapConnectionException(errMsg)
page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE))
page = getUnicode(page) # Note: if decodePage call fails (Issue #4202)
retVal = [_urllib.parse.unquote(match.group(1) or match.group(2)) for match in re.finditer(GOOGLE_REGEX, page, re.I)]
if not retVal and "detected unusual traffic" in page:
warnMsg = "Google has detected 'unusual' traffic from "
warnMsg += "used IP address disabling further searches"
if conf.proxyList:
# Note: switch proxy (if available) when an abuse/captcha page was served (instead of pointlessly falling through to the next engine from the same blocked IP)
if conf.proxyList and (("detected unusual traffic" in page) or ("issue with the Tor Exit Node you are currently using" in page)):
warnMsg = "%s has detected 'unusual' traffic from the used IP address" % name
raise SqlmapBaseException(warnMsg)
else:
logger.critical(warnMsg)
if not retVal:
message = "no usable links found. What do you want to do?"
message += "\n[1] (re)try with DuckDuckGo (default)"
message += "\n[2] (re)try with Bing"
message += "\n[3] quit"
choice = readInput(message, default='1')
if choice == '3':
raise SqlmapUserQuitException
elif choice == '2':
url = "https://www.bing.com/search?q=%s&first=%d" % (urlencode(dork, convall=True), (gpage - 1) * 10 + 1)
regex = BING_REGEX
else:
url = "https://html.duckduckgo.com/html/"
data = "q=%s&s=%d" % (urlencode(dork, convall=True), (gpage - 1) * 30)
regex = DUCKDUCKGO_REGEX
try:
req = _urllib.request.Request(url, data=getBytes(data), headers=requestHeaders)
conn = _urllib.request.urlopen(req)
requestMsg = "HTTP request:\nGET %s" % url
requestMsg += " %s" % _http_client.HTTPConnection._http_vsn_str
logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg)
page = conn.read()
code = conn.code
status = conn.msg
responseHeaders = conn.info()
page = decodePage(page, responseHeaders.get("Content-Encoding"), responseHeaders.get("Content-Type"))
responseMsg = "HTTP response (%s - %d):\n" % (status, code)
if conf.verbose <= 4:
responseMsg += getUnicode(responseHeaders, UNICODE_ENCODING)
elif conf.verbose > 4:
responseMsg += "%s\n%s\n" % (responseHeaders, page)
logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg)
except _urllib.error.HTTPError as ex:
try:
page = ex.read()
page = decodePage(page, ex.headers.get("Content-Encoding"), ex.headers.get("Content-Type"))
except socket.timeout:
warnMsg = "connection timed out while trying "
warnMsg += "to get error page information (%d)" % ex.code
logger.critical(warnMsg)
return None
except:
errMsg = "unable to connect"
raise SqlmapConnectionException(errMsg)
page = getUnicode(page) # Note: if decodePage call fails (Issue #4202)
retVal = [_urllib.parse.unquote(match.group(1).replace("&amp;", "&")) for match in re.finditer(regex, page, re.I | re.S)]
if not retVal and "issue with the Tor Exit Node you are currently using" in page:
warnMsg = "DuckDuckGo has detected 'unusual' traffic from "
warnMsg += "used (Tor) IP address"
if conf.proxyList:
raise SqlmapBaseException(warnMsg)
else:
logger.critical(warnMsg)
warnMsg = "no usable links found (search engines might be blocking the used IP address)"
logger.critical(warnMsg)
return retVal
@ -206,6 +154,7 @@ def search(dork):
return search(dork)
else:
raise
finally:
kb.choices.redirect = popValue()

View file

@ -122,6 +122,8 @@ class SQLAlchemy(GenericConnector):
try:
self.cursor = self.connector.execute(query)
if hasattr(self.connector, "commit"): # Note: SQLAlchemy 2.0+ dropped implicit autocommit (otherwise DML changes - e.g. via --sql-query - would be silently lost)
self.connector.commit()
retVal = True
except (_sqlalchemy.exc.OperationalError, _sqlalchemy.exc.ProgrammingError) as ex:
logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % getSafeExString(ex))

View file

@ -26,6 +26,8 @@ class xrange(object):
True
>>> list(xrange(0, 7, 2)) == list(range(0, 7, 2))
True
>>> list(xrange(8, 0, -2)) == list(range(8, 0, -2))
True
>>> foobar = xrange(1, 10)
>>> 7 in foobar
True
@ -33,6 +35,12 @@ class xrange(object):
False
>>> foobar[0]
1
>>> 6 in xrange(8, 0, -2)
True
>>> 0 in xrange(8, 0, -2)
False
>>> xrange(0, 10, 2).index(4)
2
"""
__slots__ = ['_slice']
@ -71,10 +79,17 @@ class xrange(object):
return self._len()
def _len(self):
return max(0, 1 + int((self.stop - 1 - self.start) // self.step))
if self.step > 0:
lo, hi, step = self.start, self.stop, self.step
else: # Note: normalizing for descending ranges (negative step)
lo, hi, step = self.stop, self.start, -self.step
return max(0, (hi - lo + step - 1) // step)
def __contains__(self, value):
return (self.start <= value < self.stop) and (value - self.start) % self.step == 0
if self.step > 0:
return self.start <= value < self.stop and (value - self.start) % self.step == 0
else:
return self.stop < value <= self.start and (value - self.start) % self.step == 0
def __getitem__(self, index):
if isinstance(index, slice):
@ -98,7 +113,7 @@ class xrange(object):
return self.start + self.step * i
def index(self, i):
if self.start <= i < self.stop:
return i - self.start
if i in self:
return (i - self.start) // self.step # Note: also accounts for step != 1 (and descending ranges)
else:
raise ValueError("%d is not in list" % i)