More drei stuff

This commit is contained in:
Miroslav Stampar 2019-05-02 16:54:54 +02:00
parent 2791ea51ea
commit ff968c2331
13 changed files with 113 additions and 94 deletions

View File

@ -64,7 +64,9 @@ def safecharencode(value):
for char in SAFE_ENCODE_SLASH_REPLACEMENTS:
retVal = retVal.replace(char, repr(char).strip('\''))
retVal = reduce(lambda x, y: x + (y if (y in string.printable or isinstance(value, text_type) and ord(y) >= 160) else '\\x%02x' % ord(y)), retVal, type(value)())
for char in set(retVal):
if not (char in string.printable or isinstance(value, text_type) and ord(char) >= 160):
retVal = retVal.replace(char, '\\x%02x' % ord(char))
retVal = retVal.replace(SLASH_MARKER, "\\\\")
retVal = retVal.replace(HEX_ENCODED_PREFIX_MARKER, HEX_ENCODED_PREFIX)

View File

@ -1377,7 +1377,7 @@ def checkWaf():
conf.timeout = IDS_WAF_CHECK_TIMEOUT
try:
retVal = Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True, disableTampering=True)[1] < IDS_WAF_CHECK_RATIO
retVal = (Request.queryPage(place=place, value=value, getRatioValue=True, noteResponseTime=False, silent=True, disableTampering=True)[1] or 0) < IDS_WAF_CHECK_RATIO
except SqlmapConnectionException:
retVal = True
finally:

View File

@ -11,6 +11,7 @@ import collections
import contextlib
import copy
import distutils
import functools
import getpass
import hashlib
import inspect
@ -1849,7 +1850,7 @@ def safeFilepathEncode(filepath):
retVal = filepath
if filepath and isinstance(filepath, six.text_type):
if filepath and six.PY2 and isinstance(filepath, six.text_type):
retVal = filepath.encode(sys.getfilesystemencoding() or UNICODE_ENCODING)
return retVal
@ -1929,8 +1930,8 @@ def getFilteredPageContent(page, onlyText=True, split=" "):
Returns filtered page content without script, style and/or comments
or all HTML tags
>>> getFilteredPageContent(u'<html><title>foobar</title><body>test</body></html>')
u'foobar test'
>>> getFilteredPageContent(u'<html><title>foobar</title><body>test</body></html>') == "foobar test"
True
"""
retVal = page
@ -1947,8 +1948,8 @@ def getPageWordSet(page):
"""
Returns word set used in page content
>>> sorted(getPageWordSet(u'<html><title>foobar</title><body>test</body></html>'))
[u'foobar', u'test']
>>> sorted(getPageWordSet(u'<html><title>foobar</title><body>test</body></html>')) == [u'foobar', u'test']
True
"""
retVal = set()
@ -2459,13 +2460,13 @@ def decodeHex(value):
True
"""
return bytes.fromhex(value) if hasattr(bytes, "fromhex") else value.decode("hex")
return bytes.fromhex(getUnicode(value)) if hasattr(bytes, "fromhex") else value.decode("hex")
def getBytes(value, encoding=UNICODE_ENCODING, errors="strict"):
"""
Returns byte representation of provided Unicode value
>>> getBytes(getUnicode("foo\x01\x83\xffbar")) == b"foo\x01\x83\xffbar"
>>> getBytes(getUnicode(b"foo\\x01\\x83\\xffbar")) == b"foo\\x01\\x83\\xffbar"
True
"""
@ -2488,9 +2489,9 @@ def getOrds(value):
"""
Returns ORD(...) representation of provided string value
>>> getOrds(u'fo\xf6bar')
>>> getOrds(u'fo\\xf6bar')
[102, 111, 246, 98, 97, 114]
>>> getOrds(b"fo\xc3\xb6bar")
>>> getOrds(b"fo\\xc3\\xb6bar")
[102, 111, 195, 182, 98, 97, 114]
"""
@ -2642,8 +2643,8 @@ def extractErrorMessage(page):
"""
Returns reported error message from page if it founds one
>>> extractErrorMessage(u'<html><title>Test</title>\\n<b>Warning</b>: oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated<br><p>Only a test page</p></html>')
u'oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated'
>>> extractErrorMessage(u'<html><title>Test</title>\\n<b>Warning</b>: oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated<br><p>Only a test page</p></html>') == u'oci_parse() [function.oci-parse]: ORA-01756: quoted string not properly terminated'
True
"""
retVal = None
@ -2716,10 +2717,10 @@ def urldecode(value, encoding=None, unsafe="%%&=;+%s" % CUSTOM_INJECTION_MARK_CH
"""
URL decodes given value
>>> urldecode('AND%201%3E%282%2B3%29%23', convall=True)
u'AND 1>(2+3)#'
>>> urldecode('AND%201%3E%282%2B3%29%23', convall=False)
u'AND 1>(2%2B3)#'
>>> urldecode('AND%201%3E%282%2B3%29%23', convall=True) == 'AND 1>(2+3)#'
True
>>> urldecode('AND%201%3E%282%2B3%29%23', convall=False) == 'AND 1>(2%2B3)#'
True
"""
result = value
@ -2738,7 +2739,7 @@ def urldecode(value, encoding=None, unsafe="%%&=;+%s" % CUSTOM_INJECTION_MARK_CH
charset = set(string.printable) - set(unsafe)
def _(match):
char = chr(ord(match.group(1).decode("hex")))
char = getUnicode(decodeHex(match.group(1)))
return char if char in charset else match.group(0)
if spaceplus:
@ -3020,13 +3021,15 @@ def findDynamicContent(firstPage, secondPage):
prefix = prefix[-DYNAMICITY_BOUNDARY_LENGTH:]
suffix = suffix[:DYNAMICITY_BOUNDARY_LENGTH]
infix = max(re.search(r"(?s)%s(.+)%s" % (re.escape(prefix), re.escape(suffix)), _) for _ in (firstPage, secondPage)).group(1)
if infix[0].isalnum():
prefix = trimAlphaNum(prefix)
if infix[-1].isalnum():
suffix = trimAlphaNum(suffix)
for _ in (firstPage, secondPage):
match = re.search(r"(?s)%s(.+)%s" % (re.escape(prefix), re.escape(suffix)), _)
if match:
infix = match.group(1)
if infix[0].isalnum():
prefix = trimAlphaNum(prefix)
if infix[-1].isalnum():
suffix = trimAlphaNum(suffix)
break
kb.dynamicMarkings.append((prefix if prefix else None, suffix if suffix else None))
@ -3557,7 +3560,7 @@ def getLatestRevision():
req = _urllib.request.Request(url="https://raw.githubusercontent.com/sqlmapproject/sqlmap/master/lib/core/settings.py")
try:
content = _urllib.request.urlopen(req).read()
content = getUnicode(_urllib.request.urlopen(req).read())
retVal = extractRegexResult(r"VERSION\s*=\s*[\"'](?P<result>[\d.]+)", content)
except:
pass
@ -4423,12 +4426,8 @@ def serializeObject(object_):
"""
Serializes given object
>>> serializeObject([1, 2, 3, ('a', 'b')])
'gAJdcQEoSwFLAksDVQFhVQFihnECZS4='
>>> serializeObject(None)
'gAJOLg=='
>>> serializeObject('foobar')
'gAJVBmZvb2JhcnEBLg=='
>>> type(serializeObject([1, 2, 3, ('a', 'b')])) == six.binary_type
True
"""
return base64pickle(object_)
@ -4668,7 +4667,10 @@ def prioritySortColumns(columns):
def _(column):
return column and "id" in column.lower()
return sorted(sorted(columns, key=len), lambda x, y: -1 if _(x) and not _(y) else 1 if not _(x) and _(y) else 0)
if six.PY2:
return sorted(sorted(columns, key=len), lambda x, y: -1 if _(x) and not _(y) else 1 if not _(x) and _(y) else 0)
else:
return sorted(sorted(columns, key=len), key=functools.cmp_to_key(lambda x, y: -1 if _(x) and not _(y) else 1 if not _(x) and _(y) else 0))
def getRequestHeader(request, name):
"""
@ -4975,12 +4977,12 @@ def safeVariableNaming(value):
"""
Returns escaped safe-representation of a given variable name that can be used in Python evaluated code
>>> safeVariableNaming("class.id")
'EVAL_636c6173732e6964'
>>> safeVariableNaming("class.id") == "EVAL_636c6173732e6964"
True
"""
if value in keyword.kwlist or re.search(r"\A[^a-zA-Z]|[^\w]", value):
value = "%s%s" % (EVALCODE_ENCODED_PREFIX, value.encode(UNICODE_ENCODING).encode("hex"))
value = "%s%s" % (EVALCODE_ENCODED_PREFIX, getUnicode(binascii.hexlify(getBytes(value))))
return value
@ -4988,12 +4990,12 @@ def unsafeVariableNaming(value):
"""
Returns unescaped safe-representation of a given variable name
>>> unsafeVariableNaming("EVAL_636c6173732e6964")
u'class.id'
>>> unsafeVariableNaming("EVAL_636c6173732e6964") == "class.id"
True
"""
if value.startswith(EVALCODE_ENCODED_PREFIX):
value = value[len(EVALCODE_ENCODED_PREFIX):].decode("hex").decode(UNICODE_ENCODING)
value = getUnicode(decodeHex(value[len(EVALCODE_ENCODED_PREFIX):]))
return value

View File

@ -162,6 +162,10 @@ class WichmannHill(random.Random):
z = (z + a) % 256 or 1
self.__whseed(x, y, z)
def patchHeaders(headers):
if not hasattr(headers, "headers"):
headers.headers = ["%s: %s\r\n" % (header, headers[header]) for header in headers]
# Reference: https://github.com/urllib3/urllib3/blob/master/src/urllib3/filepost.py
def choose_boundary():
return uuid.uuid4().hex

View File

@ -11,6 +11,7 @@ except:
import pickle
import base64
import binascii
import json
import re
import sys
@ -24,8 +25,8 @@ def base64decode(value):
"""
Decodes string value from Base64 to plain format
>>> base64decode('Zm9vYmFy')
'foobar'
>>> base64decode('Zm9vYmFy') == b'foobar'
True
"""
return base64.b64decode(unicodeencode(value))
@ -34,8 +35,8 @@ def base64encode(value):
"""
Encodes string value from plain to Base64 format
>>> base64encode('foobar')
'Zm9vYmFy'
>>> base64encode('foobar') == b'Zm9vYmFy'
True
"""
return base64.b64encode(unicodeencode(value))
@ -44,8 +45,8 @@ def base64pickle(value):
"""
Serializes (with pickle) and encodes to Base64 format supplied (binary) value
>>> base64pickle('foobar')
'gAJVBmZvb2JhcnEBLg=='
>>> base64unpickle(base64pickle([1, 2, 3])) == [1, 2, 3]
True
"""
retVal = None
@ -68,8 +69,8 @@ def base64unpickle(value):
"""
Decodes value from Base64 to plain format and deserializes (with pickle) its content
>>> base64unpickle('gAJVBmZvb2JhcnEBLg==')
'foobar'
>>> type(base64unpickle('gAJjX19idWlsdGluX18Kb2JqZWN0CnEBKYFxAi4=')) == object
True
"""
retVal = None
@ -85,8 +86,8 @@ def hexdecode(value):
"""
Decodes string value from hex to plain format
>>> hexdecode('666f6f626172')
'foobar'
>>> hexdecode('666f6f626172') == b'foobar'
True
"""
value = value.lower()
@ -103,16 +104,12 @@ def hexencode(value, encoding=None):
"""
Encodes string value from plain to hex format
>>> hexencode('foobar')
'666f6f626172'
>>> hexencode('foobar') == b'666f6f626172'
True
"""
retVal = unicodeencode(value, encoding)
if six.PY2:
retVal = retVal.encode("hex")
else:
retVal = retVal.hex()
retVal = binascii.hexlify(retVal)
return retVal
@ -120,8 +117,8 @@ def unicodeencode(value, encoding=None):
"""
Returns 8-bit string representation of the supplied unicode value
>>> unicodeencode(u'foobar')
'foobar'
>>> unicodeencode(u'foobar') == b'foobar'
True
"""
retVal = value
@ -138,8 +135,8 @@ def utf8encode(value):
"""
Returns 8-bit string representation of the supplied UTF-8 value
>>> utf8encode(u'foobar')
'foobar'
>>> utf8encode(u'foobar') == b'foobar'
True
"""
return unicodeencode(value, "utf-8")
@ -148,11 +145,16 @@ def utf8decode(value):
"""
Returns UTF-8 representation of the supplied 8-bit string representation
>>> utf8decode('foobar')
>>> utf8decode(b'foobar')
u'foobar'
"""
return value.decode("utf-8")
retVal = value
if isinstance(value, six.binary_type):
retVal = value.decode("utf-8")
return retVal
def htmlunescape(value):
"""
@ -217,8 +219,8 @@ def dejsonize(data):
"""
Returns JSON deserialized data
>>> dejsonize('{\\n "foo": "bar"\\n}')
{u'foo': u'bar'}
>>> dejsonize('{\\n "foo": "bar"\\n}') == {u'foo': u'bar'}
True
"""
return json.loads(data)

View File

@ -1765,7 +1765,8 @@ def _cleanupOptions():
conf.string = decodeStringEscape(conf.string)
if conf.getAll:
map(lambda _: conf.__setitem__(_, True), WIZARD.ALL)
for _ in WIZARD.ALL:
conf.__setitem__(_, True)
if conf.noCast:
for _ in list(DUMP_REPLACEMENTS.keys()):

View File

@ -17,7 +17,7 @@ from lib.core.enums import DBMS_DIRECTORY_NAME
from lib.core.enums import OS
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.3.5.3"
VERSION = "1.3.5.4"
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)
@ -839,8 +839,8 @@ for key, value in os.environ.items():
def _reversible(ex):
if isinstance(ex, UnicodeDecodeError):
if INVALID_UNICODE_PRIVATE_AREA:
return ("".join(unichr(int('000f00%2x' % ord(_), 16)) for _ in ex.object[ex.start:ex.end]), ex.end)
return (u"".join(unichr(int('000f00%2x' % (_ if isinstance(_, int) else ord(_)), 16)) for _ in ex.object[ex.start:ex.end]), ex.end)
else:
return ("".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in ex.object[ex.start:ex.end]).decode(UNICODE_ENCODING), ex.end)
return (u"".join(INVALID_UNICODE_CHAR_FORMAT % (_ if isinstance(_, int) else ord(_)) for _ in ex.object[ex.start:ex.end]), ex.end)
codecs.register_error("reversible", _reversible)

View File

@ -58,6 +58,7 @@ from lib.core.common import wasLastResponseDelayed
from lib.core.common import unsafeVariableNaming
from lib.core.common import urldecode
from lib.core.common import urlencode
from lib.core.compat import patchHeaders
from lib.core.compat import xrange
from lib.core.data import conf
from lib.core.data import kb
@ -517,6 +518,7 @@ class Connect(object):
code = (code or conn.code) if conn.code == kb.originalCode else conn.code # do not override redirection code (for comparison purposes)
responseHeaders = conn.info()
responseHeaders[URI_HTTP_HEADER] = conn.geturl()
patchHeaders(responseHeaders)
kb.serverHeader = responseHeaders.get(HTTP_HEADER.SERVER, kb.serverHeader)
else:
code = None
@ -592,6 +594,7 @@ class Connect(object):
page = ex.read() if not skipRead else None
responseHeaders = ex.info()
responseHeaders[URI_HTTP_HEADER] = ex.geturl()
patchHeaders(responseHeaders)
page = decodePage(page, responseHeaders.get(HTTP_HEADER.CONTENT_ENCODING), responseHeaders.get(HTTP_HEADER.CONTENT_TYPE))
except socket.timeout:
warnMsg = "connection timed out while trying "
@ -1349,8 +1352,7 @@ class Connect(object):
kb.permissionFlag = True
singleTimeWarnMessage("potential permission problems detected ('%s')" % message)
if not hasattr(headers, "headers"):
headers.headers = ["%s: %s\r\n" % (header, headers[header]) for header in headers]
patchHeaders(headers)
if content or response:
return page, headers, code

View File

@ -158,7 +158,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
length = None
showEta = conf.eta and isinstance(length, int)
numThreads = min(conf.threads, length) or 1
numThreads = min(conf.threads or 0, length or 0) or 1
if showEta:
progress = ProgressBar(maxValue=length)
@ -198,8 +198,10 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
else:
posValue = ord(hintValue[idx - 1])
markingValue = "'%s'" % CHAR_INFERENCE_MARK
unescapedCharValue = unescaper.escape("'%s'" % decodeIntToUnicode(posValue))
forgedPayload = agent.extractPayload(payload)
forgedPayload = safeStringFormat(forgedPayload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue))
forgedPayload = safeStringFormat(forgedPayload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, posValue)).replace(markingValue, unescapedCharValue)
result = Request.queryPage(agent.replacePayload(payload, forgedPayload), timeBasedCompare=timeBasedCompare, raise404=False)
incrementCounter(kb.technique)
@ -293,12 +295,13 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
lastChar = [_ for _ in threadData.shared.value if _ is not None][-1]
except IndexError:
lastChar = None
if 'a' <= lastChar <= 'z':
position = charTbl.index(ord('a') - 1) # 96
elif 'A' <= lastChar <= 'Z':
position = charTbl.index(ord('A') - 1) # 64
elif '0' <= lastChar <= '9':
position = charTbl.index(ord('0') - 1) # 47
else:
if 'a' <= lastChar <= 'z':
position = charTbl.index(ord('a') - 1) # 96
elif 'A' <= lastChar <= 'Z':
position = charTbl.index(ord('A') - 1) # 64
elif '0' <= lastChar <= '9':
position = charTbl.index(ord('0') - 1) # 47
except ValueError:
pass
finally:
@ -633,7 +636,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
dataToStdout(filterControlChars(val))
# some DBMSes (e.g. Firebird, DB2, etc.) have issues with trailing spaces
if len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace():
if Backend.getIdentifiedDbms() in (DBMS.FIREBIRD, DBMS.DB2, DBMS.MAXDB) and len(partialValue) > INFERENCE_BLANK_BREAK and partialValue[-INFERENCE_BLANK_BREAK:].isspace():
finalValue = partialValue[:-INFERENCE_BLANK_BREAK]
break
elif charsetType and partialValue[-1:].isspace():

View File

@ -724,19 +724,20 @@ def attackDumpedTable():
def hashRecognition(value):
retVal = None
isOracle, isMySQL = Backend.isDbms(DBMS.ORACLE), Backend.isDbms(DBMS.MYSQL)
if six.PY2: # currently only supported on Python2
isOracle, isMySQL = Backend.isDbms(DBMS.ORACLE), Backend.isDbms(DBMS.MYSQL)
if isinstance(value, six.string_types):
for name, regex in getPublicTypeMembers(HASH):
# Hashes for Oracle and old MySQL look the same hence these checks
if isOracle and regex == HASH.MYSQL_OLD or isMySQL and regex == HASH.ORACLE_OLD:
continue
elif regex == HASH.CRYPT_GENERIC:
if any((value.lower() == value, value.upper() == value)):
if isinstance(value, six.string_types):
for name, regex in getPublicTypeMembers(HASH):
# Hashes for Oracle and old MySQL look the same hence these checks
if isOracle and regex == HASH.MYSQL_OLD or isMySQL and regex == HASH.ORACLE_OLD:
continue
elif re.match(regex, value):
retVal = regex
break
elif regex == HASH.CRYPT_GENERIC:
if any((value.lower() == value, value.upper() == value)):
continue
elif re.match(regex, value):
retVal = regex
break
return retVal

View File

@ -34,6 +34,8 @@ def tamper(payload, **kwargs):
'1 AND A NOT BETWEEN 0 AND B--'
>>> tamper('1 AND A = B--')
'1 AND A BETWEEN B AND B--'
>>> tamper('1 AND LAST_INSERT_ROWID()=LAST_INSERT_ROWID()')
'1 AND LAST_INSERT_ROWID() BETWEEN LAST_INSERT_ROWID() AND LAST_INSERT_ROWID()'
"""
retVal = payload
@ -48,7 +50,7 @@ def tamper(payload, **kwargs):
retVal = re.sub(r"\s*>\s*(\d+|'[^']+'|\w+\(\d+\))", r" NOT BETWEEN 0 AND \g<1>", payload)
if retVal == payload:
match = re.search(r"(?i)(\b(AND|OR)\b\s+)(?!.*\b(AND|OR)\b)([^=]+?)\s*=\s*(\w+)\s*", payload)
match = re.search(r"(?i)(\b(AND|OR)\b\s+)(?!.*\b(AND|OR)\b)([^=]+?)\s*=\s*([\w()]+)\s*", payload)
if match:
_ = "%s %s BETWEEN %s AND %s" % (match.group(2), match.group(4), match.group(5), match.group(5))

View File

@ -8,6 +8,7 @@ See the file 'LICENSE' for copying permission
import re
from lib.core.common import decodeHex
from lib.core.common import getOrds
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL
@ -37,7 +38,7 @@ def tamper(payload, **kwargs):
if payload:
for match in re.finditer(r"\b0x([0-9a-f]+)\b", retVal):
if len(match.group(1)) > 2:
result = "CONCAT(%s)" % ','.join("CHAR(%d)" % ord(_) for _ in decodeHex(match.group(1)))
result = "CONCAT(%s)" % ','.join("CHAR(%d)" % _ for _ in getOrds(decodeHex(match.group(1))))
else:
result = "CHAR(%d)" % ord(decodeHex(match.group(1)))
retVal = retVal.replace(match.group(0), result)

View File

@ -6,7 +6,6 @@ See the file 'LICENSE' for copying permission
"""
import re
import urllib
from lib.core.enums import PRIORITY
@ -26,6 +25,6 @@ def tamper(payload, **kwargs):
retVal = payload
if payload:
retVal = re.sub(r"(?i)\bAND\b", urllib.quote("&&"), re.sub(r"(?i)\bOR\b", urllib.quote("||"), payload))
retVal = re.sub(r"(?i)\bAND\b", "%26%26", re.sub(r"(?i)\bOR\b", "%7C%7C", payload))
return retVal