mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-02-02 20:54:13 +03:00
Implementation for an Issue #1360
This commit is contained in:
parent
2c2f83f67b
commit
a33b0454cd
|
@ -197,6 +197,7 @@ class HASHDB_KEYS:
|
||||||
KB_CHARS = "KB_CHARS"
|
KB_CHARS = "KB_CHARS"
|
||||||
KB_DYNAMIC_MARKINGS = "KB_DYNAMIC_MARKINGS"
|
KB_DYNAMIC_MARKINGS = "KB_DYNAMIC_MARKINGS"
|
||||||
KB_INJECTIONS = "KB_INJECTIONS"
|
KB_INJECTIONS = "KB_INJECTIONS"
|
||||||
|
KB_ERROR_CHUNK_LENGTH = "KB_ERROR_CHUNK_LENGTH"
|
||||||
KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE"
|
KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE"
|
||||||
OS = "OS"
|
OS = "OS"
|
||||||
|
|
||||||
|
|
|
@ -1792,6 +1792,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
|
||||||
kb.endDetection = False
|
kb.endDetection = False
|
||||||
kb.explicitSettings = set()
|
kb.explicitSettings = set()
|
||||||
kb.extendTests = None
|
kb.extendTests = None
|
||||||
|
kb.errorChunkLength = None
|
||||||
kb.errorIsNone = True
|
kb.errorIsNone = True
|
||||||
kb.fileReadMode = False
|
kb.fileReadMode = False
|
||||||
kb.followSitemapRecursion = None
|
kb.followSitemapRecursion = None
|
||||||
|
|
|
@ -323,11 +323,11 @@ CUSTOM_INJECTION_MARK_CHAR = '*'
|
||||||
# Other way to declare injection position
|
# Other way to declare injection position
|
||||||
INJECT_HERE_MARK = '%INJECT HERE%'
|
INJECT_HERE_MARK = '%INJECT HERE%'
|
||||||
|
|
||||||
# Maximum length used for retrieving data over MySQL error based payload due to "known" problems with longer result strings
|
# Minimum chunk length used for retrieving data over error based payloads
|
||||||
MYSQL_ERROR_CHUNK_LENGTH = 50
|
MIN_ERROR_CHUNK_LENGTH = 8
|
||||||
|
|
||||||
# Maximum length used for retrieving data over MSSQL error based payload due to trimming problems with longer result strings
|
# Maximum chunk length used for retrieving data over error based payloads
|
||||||
MSSQL_ERROR_CHUNK_LENGTH = 100
|
MAX_ERROR_CHUNK_LENGTH = 1024
|
||||||
|
|
||||||
# Do not escape the injected statement if it contains any of the following SQL keywords
|
# Do not escape the injected statement if it contains any of the following SQL keywords
|
||||||
EXCLUDE_UNESCAPE = ("WAITFOR DELAY ", " INTO DUMPFILE ", " INTO OUTFILE ", "CREATE ", "BULK ", "EXEC ", "RECONFIGURE ", "DECLARE ", "'%s'" % CHAR_INFERENCE_MARK)
|
EXCLUDE_UNESCAPE = ("WAITFOR DELAY ", " INTO DUMPFILE ", " INTO OUTFILE ", "CREATE ", "BULK ", "EXEC ", "RECONFIGURE ", "DECLARE ", "'%s'" % CHAR_INFERENCE_MARK)
|
||||||
|
|
|
@ -403,12 +403,18 @@ def _resumeHashDBValues():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kb.absFilePaths = hashDBRetrieve(HASHDB_KEYS.KB_ABS_FILE_PATHS, True) or kb.absFilePaths
|
kb.absFilePaths = hashDBRetrieve(HASHDB_KEYS.KB_ABS_FILE_PATHS, True) or kb.absFilePaths
|
||||||
kb.chars = hashDBRetrieve(HASHDB_KEYS.KB_CHARS, True) or kb.chars
|
|
||||||
kb.dynamicMarkings = hashDBRetrieve(HASHDB_KEYS.KB_DYNAMIC_MARKINGS, True) or kb.dynamicMarkings
|
|
||||||
kb.brute.tables = hashDBRetrieve(HASHDB_KEYS.KB_BRUTE_TABLES, True) or kb.brute.tables
|
kb.brute.tables = hashDBRetrieve(HASHDB_KEYS.KB_BRUTE_TABLES, True) or kb.brute.tables
|
||||||
kb.brute.columns = hashDBRetrieve(HASHDB_KEYS.KB_BRUTE_COLUMNS, True) or kb.brute.columns
|
kb.brute.columns = hashDBRetrieve(HASHDB_KEYS.KB_BRUTE_COLUMNS, True) or kb.brute.columns
|
||||||
|
kb.chars = hashDBRetrieve(HASHDB_KEYS.KB_CHARS, True) or kb.chars
|
||||||
|
kb.dynamicMarkings = hashDBRetrieve(HASHDB_KEYS.KB_DYNAMIC_MARKINGS, True) or kb.dynamicMarkings
|
||||||
kb.xpCmdshellAvailable = hashDBRetrieve(HASHDB_KEYS.KB_XP_CMDSHELL_AVAILABLE) or kb.xpCmdshellAvailable
|
kb.xpCmdshellAvailable = hashDBRetrieve(HASHDB_KEYS.KB_XP_CMDSHELL_AVAILABLE) or kb.xpCmdshellAvailable
|
||||||
|
|
||||||
|
kb.errorChunkLength = hashDBRetrieve(HASHDB_KEYS.KB_ERROR_CHUNK_LENGTH)
|
||||||
|
if kb.errorChunkLength and kb.errorChunkLength.isdigit():
|
||||||
|
kb.errorChunkLength = int(kb.errorChunkLength)
|
||||||
|
else:
|
||||||
|
kb.errorChunkLength = None
|
||||||
|
|
||||||
conf.tmpPath = conf.tmpPath or hashDBRetrieve(HASHDB_KEYS.CONF_TMP_PATH)
|
conf.tmpPath = conf.tmpPath or hashDBRetrieve(HASHDB_KEYS.CONF_TMP_PATH)
|
||||||
|
|
||||||
for injection in hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or []:
|
for injection in hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or []:
|
||||||
|
|
|
@ -35,10 +35,11 @@ from lib.core.data import logger
|
||||||
from lib.core.data import queries
|
from lib.core.data import queries
|
||||||
from lib.core.dicts import FROM_DUMMY_TABLE
|
from lib.core.dicts import FROM_DUMMY_TABLE
|
||||||
from lib.core.enums import DBMS
|
from lib.core.enums import DBMS
|
||||||
|
from lib.core.enums import HASHDB_KEYS
|
||||||
from lib.core.enums import HTTP_HEADER
|
from lib.core.enums import HTTP_HEADER
|
||||||
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
|
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
|
||||||
from lib.core.settings import MYSQL_ERROR_CHUNK_LENGTH
|
from lib.core.settings import MIN_ERROR_CHUNK_LENGTH
|
||||||
from lib.core.settings import MSSQL_ERROR_CHUNK_LENGTH
|
from lib.core.settings import MAX_ERROR_CHUNK_LENGTH
|
||||||
from lib.core.settings import NULL
|
from lib.core.settings import NULL
|
||||||
from lib.core.settings import PARTIAL_VALUE_MARKER
|
from lib.core.settings import PARTIAL_VALUE_MARKER
|
||||||
from lib.core.settings import SLOW_ORDER_COUNT_THRESHOLD
|
from lib.core.settings import SLOW_ORDER_COUNT_THRESHOLD
|
||||||
|
@ -50,7 +51,7 @@ from lib.core.unescaper import unescaper
|
||||||
from lib.request.connect import Connect as Request
|
from lib.request.connect import Connect as Request
|
||||||
from lib.utils.progress import ProgressBar
|
from lib.utils.progress import ProgressBar
|
||||||
|
|
||||||
def _oneShotErrorUse(expression, field=None):
|
def _oneShotErrorUse(expression, field=None, chunkTest=False):
|
||||||
offset = 1
|
offset = 1
|
||||||
partialValue = None
|
partialValue = None
|
||||||
threadData = getCurrentThreadData()
|
threadData = getCurrentThreadData()
|
||||||
|
@ -63,12 +64,28 @@ def _oneShotErrorUse(expression, field=None):
|
||||||
|
|
||||||
threadData.resumed = retVal is not None and not partialValue
|
threadData.resumed = retVal is not None and not partialValue
|
||||||
|
|
||||||
if Backend.isDbms(DBMS.MYSQL):
|
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and kb.errorChunkLength is None and not chunkTest and not kb.testMode:
|
||||||
chunkLength = MYSQL_ERROR_CHUNK_LENGTH
|
debugMsg = "searching for error chunk length..."
|
||||||
elif Backend.isDbms(DBMS.MSSQL):
|
logger.debug(debugMsg)
|
||||||
chunkLength = MSSQL_ERROR_CHUNK_LENGTH
|
|
||||||
else:
|
current = MAX_ERROR_CHUNK_LENGTH
|
||||||
chunkLength = None
|
while current >= MIN_ERROR_CHUNK_LENGTH:
|
||||||
|
testChar = str(current % 10)
|
||||||
|
testQuery = "SELECT %s('%s',%d)" % ("REPEAT" if Backend.isDbms(DBMS.MYSQL) else "REPLICATE", testChar, current)
|
||||||
|
result = unArrayizeValue(_oneShotErrorUse(testQuery, chunkTest=True))
|
||||||
|
if result and testChar in result:
|
||||||
|
if result == testChar * current:
|
||||||
|
kb.errorChunkLength = current
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
current = len(result) - len(kb.chars.stop)
|
||||||
|
else:
|
||||||
|
current = current / 2
|
||||||
|
|
||||||
|
if kb.errorChunkLength:
|
||||||
|
hashDBWrite(HASHDB_KEYS.KB_ERROR_CHUNK_LENGTH, kb.errorChunkLength)
|
||||||
|
else:
|
||||||
|
kb.errorChunkLength = 0
|
||||||
|
|
||||||
if retVal is None or partialValue:
|
if retVal is None or partialValue:
|
||||||
try:
|
try:
|
||||||
|
@ -79,12 +96,12 @@ def _oneShotErrorUse(expression, field=None):
|
||||||
if field:
|
if field:
|
||||||
nulledCastedField = agent.nullAndCastField(field)
|
nulledCastedField = agent.nullAndCastField(field)
|
||||||
|
|
||||||
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and not any(_ in field for _ in ("COUNT", "CASE")): # skip chunking of scalar expression (unneeded)
|
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)) and not any(_ in field for _ in ("COUNT", "CASE")) and kb.errorChunkLength and not chunkTest:
|
||||||
extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(field), expression).group(0)
|
extendedField = re.search(r"[^ ,]*%s[^ ,]*" % re.escape(field), expression).group(0)
|
||||||
if extendedField != field: # e.g. MIN(surname)
|
if extendedField != field: # e.g. MIN(surname)
|
||||||
nulledCastedField = extendedField.replace(field, nulledCastedField)
|
nulledCastedField = extendedField.replace(field, nulledCastedField)
|
||||||
field = extendedField
|
field = extendedField
|
||||||
nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, chunkLength)
|
nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, kb.errorChunkLength)
|
||||||
|
|
||||||
# Forge the error-based SQL injection request
|
# Forge the error-based SQL injection request
|
||||||
vector = kb.injection.data[kb.technique].vector
|
vector = kb.injection.data[kb.technique].vector
|
||||||
|
@ -125,10 +142,11 @@ def _oneShotErrorUse(expression, field=None):
|
||||||
threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)
|
threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)
|
||||||
|
|
||||||
if trimmed:
|
if trimmed:
|
||||||
warnMsg = "possible server trimmed output detected "
|
if not chunkTest:
|
||||||
warnMsg += "(due to its length and/or content): "
|
warnMsg = "possible server trimmed output detected "
|
||||||
warnMsg += safecharencode(trimmed)
|
warnMsg += "(due to its length and/or content): "
|
||||||
logger.warn(warnMsg)
|
warnMsg += safecharencode(trimmed)
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
if not kb.testMode:
|
if not kb.testMode:
|
||||||
check = "(?P<result>.*?)%s" % kb.chars.stop[:2]
|
check = "(?P<result>.*?)%s" % kb.chars.stop[:2]
|
||||||
|
@ -146,8 +164,8 @@ def _oneShotErrorUse(expression, field=None):
|
||||||
else:
|
else:
|
||||||
retVal += output if output else ''
|
retVal += output if output else ''
|
||||||
|
|
||||||
if output and len(output) >= chunkLength:
|
if output and kb.errorChunkLength and len(output) >= kb.errorChunkLength and not chunkTest:
|
||||||
offset += chunkLength
|
offset += kb.errorChunkLength
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user