mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2024-11-25 11:03:47 +03:00
First update for Issue #75 (error-based dumping)
This commit is contained in:
parent
3fd5119f3f
commit
65639cdda6
|
@ -1449,7 +1449,7 @@ def __setKnowledgeBaseAttributes(flushAll=True):
|
||||||
kb.dnsMode = False
|
kb.dnsMode = False
|
||||||
kb.dnsTest = None
|
kb.dnsTest = None
|
||||||
kb.docRoot = None
|
kb.docRoot = None
|
||||||
kb.dumpMode = False
|
kb.dumpTable = None
|
||||||
kb.dynamicMarkings = []
|
kb.dynamicMarkings = []
|
||||||
kb.dynamicParameters = False
|
kb.dynamicParameters = False
|
||||||
kb.endDetection = False
|
kb.endDetection = False
|
||||||
|
|
|
@ -506,3 +506,6 @@ GENERIC_SQL_COMMENT = "-- "
|
||||||
|
|
||||||
# Threshold value for turning back on time auto-adjustment mechanism
|
# Threshold value for turning back on time auto-adjustment mechanism
|
||||||
VALID_TIME_CHARS_RUN_THRESHOLD = 100
|
VALID_TIME_CHARS_RUN_THRESHOLD = 100
|
||||||
|
|
||||||
|
# Check for empty columns only if table is sufficiently large
|
||||||
|
CHECK_ZERO_COLUMNS_THRESHOLD = 10
|
||||||
|
|
|
@ -218,7 +218,7 @@ def decodePage(page, contentEncoding, contentType):
|
||||||
def processResponse(page, responseHeaders):
|
def processResponse(page, responseHeaders):
|
||||||
kb.processResponseCounter += 1
|
kb.processResponseCounter += 1
|
||||||
|
|
||||||
if not kb.dumpMode:
|
if not kb.dumpTable:
|
||||||
parseResponse(page, responseHeaders if kb.processResponseCounter < PARSE_HEADERS_LIMIT else None)
|
parseResponse(page, responseHeaders if kb.processResponseCounter < PARSE_HEADERS_LIMIT else None)
|
||||||
|
|
||||||
if conf.parseErrors:
|
if conf.parseErrors:
|
||||||
|
|
|
@ -32,9 +32,11 @@ from lib.core.data import logger
|
||||||
from lib.core.data import queries
|
from lib.core.data import queries
|
||||||
from lib.core.enums import DBMS
|
from lib.core.enums import DBMS
|
||||||
from lib.core.enums import PAYLOAD
|
from lib.core.enums import PAYLOAD
|
||||||
|
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
|
||||||
from lib.core.settings import FROM_DUMMY_TABLE
|
from lib.core.settings import FROM_DUMMY_TABLE
|
||||||
from lib.core.settings import MYSQL_ERROR_CHUNK_LENGTH
|
from lib.core.settings import MYSQL_ERROR_CHUNK_LENGTH
|
||||||
from lib.core.settings import MSSQL_ERROR_CHUNK_LENGTH
|
from lib.core.settings import MSSQL_ERROR_CHUNK_LENGTH
|
||||||
|
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
|
||||||
from lib.core.settings import SQL_SCALAR_REGEX
|
from lib.core.settings import SQL_SCALAR_REGEX
|
||||||
|
@ -44,7 +46,7 @@ from lib.core.threads import runThreads
|
||||||
from lib.core.unescaper import unescaper
|
from lib.core.unescaper import unescaper
|
||||||
from lib.request.connect import Connect as Request
|
from lib.request.connect import Connect as Request
|
||||||
|
|
||||||
def __oneShotErrorUse(expression, field):
|
def __oneShotErrorUse(expression, field=None):
|
||||||
offset = 1
|
offset = 1
|
||||||
partialValue = None
|
partialValue = None
|
||||||
threadData = getCurrentThreadData()
|
threadData = getCurrentThreadData()
|
||||||
|
@ -56,6 +58,12 @@ def __oneShotErrorUse(expression, field):
|
||||||
offset += len(partialValue)
|
offset += len(partialValue)
|
||||||
|
|
||||||
threadData.resumed = retVal is not None and not partialValue
|
threadData.resumed = retVal is not None and not partialValue
|
||||||
|
|
||||||
|
if Backend.isDbms(DBMS.MYSQL):
|
||||||
|
chunk_length = MYSQL_ERROR_CHUNK_LENGTH
|
||||||
|
elif Backend.isDbms(DBMS.MSSQL):
|
||||||
|
chunk_length = MSSQL_ERROR_CHUNK_LENGTH
|
||||||
|
else:
|
||||||
chunk_length = None
|
chunk_length = None
|
||||||
|
|
||||||
if retVal is None or partialValue:
|
if retVal is None or partialValue:
|
||||||
|
@ -64,20 +72,17 @@ def __oneShotErrorUse(expression, field):
|
||||||
check = "%s(?P<result>.*?)%s" % (kb.chars.start, kb.chars.stop)
|
check = "%s(?P<result>.*?)%s" % (kb.chars.start, kb.chars.stop)
|
||||||
trimcheck = "%s(?P<result>.*?)</" % (kb.chars.start)
|
trimcheck = "%s(?P<result>.*?)</" % (kb.chars.start)
|
||||||
|
|
||||||
|
if field:
|
||||||
nulledCastedField = agent.nullAndCastField(field)
|
nulledCastedField = agent.nullAndCastField(field)
|
||||||
|
|
||||||
if Backend.isDbms(DBMS.MYSQL):
|
if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)):
|
||||||
chunk_length = MYSQL_ERROR_CHUNK_LENGTH
|
nulledCastedField = queries[Backend.getIdentifiedDbms()].substring.query % (nulledCastedField, offset, chunk_length)
|
||||||
nulledCastedField = queries[DBMS.MYSQL].substring.query % (nulledCastedField, offset, chunk_length)
|
|
||||||
elif Backend.isDbms(DBMS.MSSQL):
|
|
||||||
chunk_length = MSSQL_ERROR_CHUNK_LENGTH
|
|
||||||
nulledCastedField = queries[DBMS.MSSQL].substring.query % (nulledCastedField, offset, chunk_length)
|
|
||||||
|
|
||||||
# Forge the error-based SQL injection request
|
# Forge the error-based SQL injection request
|
||||||
vector = kb.injection.data[PAYLOAD.TECHNIQUE.ERROR].vector
|
vector = kb.injection.data[PAYLOAD.TECHNIQUE.ERROR].vector
|
||||||
query = agent.prefixQuery(vector)
|
query = agent.prefixQuery(vector)
|
||||||
query = agent.suffixQuery(query)
|
query = agent.suffixQuery(query)
|
||||||
injExpression = expression.replace(field, nulledCastedField, 1)
|
injExpression = expression.replace(field, nulledCastedField, 1) if field else expression
|
||||||
injExpression = unescaper.unescape(injExpression)
|
injExpression = unescaper.unescape(injExpression)
|
||||||
injExpression = query.replace("[QUERY]", injExpression)
|
injExpression = query.replace("[QUERY]", injExpression)
|
||||||
payload = agent.payload(newValue=injExpression)
|
payload = agent.payload(newValue=injExpression)
|
||||||
|
@ -148,7 +153,7 @@ def __oneShotErrorUse(expression, field):
|
||||||
|
|
||||||
return safecharencode(retVal) if kb.safeCharEncode else retVal
|
return safecharencode(retVal) if kb.safeCharEncode else retVal
|
||||||
|
|
||||||
def __errorFields(expression, expressionFields, expressionFieldsList, expected=None, num=None):
|
def __errorFields(expression, expressionFields, expressionFieldsList, expected=None, num=None, emptyFields=None):
|
||||||
outputs = []
|
outputs = []
|
||||||
origExpr = None
|
origExpr = None
|
||||||
|
|
||||||
|
@ -169,14 +174,14 @@ def __errorFields(expression, expressionFields, expressionFieldsList, expected=N
|
||||||
else:
|
else:
|
||||||
expressionReplaced = expression.replace(expressionFields, field, 1)
|
expressionReplaced = expression.replace(expressionFields, field, 1)
|
||||||
|
|
||||||
output = __oneShotErrorUse(expressionReplaced, field)
|
output = NULL if emptyFields and field in emptyFields else __oneShotErrorUse(expressionReplaced, field)
|
||||||
|
|
||||||
if not kb.threadContinue:
|
if not kb.threadContinue:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if kb.fileReadMode and output and output.strip():
|
if kb.fileReadMode and output and output.strip():
|
||||||
print
|
print
|
||||||
elif output is not None and not (threadData.resumed and kb.suppressResumeInfo):
|
elif output is not None and not (threadData.resumed and kb.suppressResumeInfo) and not (emptyFields and field in emptyFields):
|
||||||
dataToStdout("[%s] [INFO] %s: %s\r\n" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(output)))
|
dataToStdout("[%s] [INFO] %s: %s\r\n" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(output)))
|
||||||
|
|
||||||
if isinstance(num, int):
|
if isinstance(num, int):
|
||||||
|
@ -222,6 +227,7 @@ def errorUse(expression, expected=None, dump=False):
|
||||||
|
|
||||||
abortedFlag = False
|
abortedFlag = False
|
||||||
count = None
|
count = None
|
||||||
|
emptyFields = []
|
||||||
start = time.time()
|
start = time.time()
|
||||||
startLimit = 0
|
startLimit = 0
|
||||||
stopLimit = None
|
stopLimit = None
|
||||||
|
@ -349,6 +355,14 @@ def errorUse(expression, expected=None, dump=False):
|
||||||
numThreads = min(conf.threads, (stopLimit - startLimit))
|
numThreads = min(conf.threads, (stopLimit - startLimit))
|
||||||
threadData.shared.outputs = BigArray()
|
threadData.shared.outputs = BigArray()
|
||||||
|
|
||||||
|
if kb.dumpTable and (len(expressionFieldsList) < (stopLimit - startLimit) > CHECK_ZERO_COLUMNS_THRESHOLD):
|
||||||
|
for field in expressionFieldsList:
|
||||||
|
if __oneShotErrorUse("SELECT COUNT(%s) FROM %s" % (field, kb.dumpTable)) == '0':
|
||||||
|
emptyFields.append(field)
|
||||||
|
debugMsg = "column '%s' for table '%s' appears to be empty. "
|
||||||
|
debugMsg += "It's values will not be dumped"
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
if stopLimit > TURN_OFF_RESUME_INFO_LIMIT:
|
if stopLimit > TURN_OFF_RESUME_INFO_LIMIT:
|
||||||
kb.suppressResumeInfo = True
|
kb.suppressResumeInfo = True
|
||||||
debugMsg = "suppressing possible resume console info because of "
|
debugMsg = "suppressing possible resume console info because of "
|
||||||
|
@ -366,7 +380,7 @@ def errorUse(expression, expected=None, dump=False):
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
break
|
break
|
||||||
|
|
||||||
output = __errorFields(expression, expressionFields, expressionFieldsList, expected, num)
|
output = __errorFields(expression, expressionFields, expressionFieldsList, expected, num, emptyFields)
|
||||||
|
|
||||||
if not kb.threadContinue:
|
if not kb.threadContinue:
|
||||||
break
|
break
|
||||||
|
|
|
@ -1573,7 +1573,7 @@ class Enumeration:
|
||||||
kb.data.cachedColumns = foundData
|
kb.data.cachedColumns = foundData
|
||||||
|
|
||||||
try:
|
try:
|
||||||
kb.dumpMode = True
|
kb.dumpTable = "%s.%s" % (conf.db, tbl)
|
||||||
|
|
||||||
if not safeSQLIdentificatorNaming(conf.db) in kb.data.cachedColumns \
|
if not safeSQLIdentificatorNaming(conf.db) in kb.data.cachedColumns \
|
||||||
or safeSQLIdentificatorNaming(tbl, True) not in \
|
or safeSQLIdentificatorNaming(tbl, True) not in \
|
||||||
|
@ -1782,7 +1782,7 @@ class Enumeration:
|
||||||
logger.critical(errMsg)
|
logger.critical(errMsg)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
kb.dumpMode = False
|
kb.dumpTable = None
|
||||||
|
|
||||||
def dumpAll(self):
|
def dumpAll(self):
|
||||||
if conf.db is not None and conf.tbl is None:
|
if conf.db is not None and conf.tbl is None:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user