sqlmap/lib/utils/resume.py

189 lines
6.7 KiB
Python
Raw Normal View History

2008-10-15 19:38:22 +04:00
#!/usr/bin/env python
"""
2008-10-15 19:56:32 +04:00
$Id$
2008-10-15 19:38:22 +04:00
2012-01-11 18:59:46 +04:00
Copyright (c) 2006-2012 sqlmap developers (http://www.sqlmap.org/)
2010-10-15 03:18:29 +04:00
See the file 'doc/COPYING' for copying permission
2008-10-15 19:38:22 +04:00
"""
import re
import time
2008-10-15 19:38:22 +04:00
from lib.core.common import calculateDeltaSeconds
2008-10-15 19:38:22 +04:00
from lib.core.common import dataToSessionFile
2011-01-07 19:39:47 +03:00
from lib.core.common import dataToStdout
from lib.core.common import Backend
2010-01-15 19:06:59 +03:00
from lib.core.common import safeStringFormat
from lib.core.common import randomStr
from lib.core.common import replaceNewlineTabs
2010-10-21 13:51:07 +04:00
from lib.core.common import restoreDumpMarkedChars
2008-10-15 19:38:22 +04:00
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import queries
from lib.core.enums import DBMS
from lib.core.enums import CHARSET_TYPE
2008-10-15 19:38:22 +04:00
from lib.core.unescaper import unescaper
from lib.techniques.blind.inference import bisection
2008-10-15 19:38:22 +04:00
def queryOutputLength(expression, payload):
"""
Returns the query output length.
"""
2011-04-30 17:20:05 +04:00
lengthQuery = queries[Backend.getIdentifiedDbms()].length.query
select = re.search("\ASELECT\s+", expression, re.I)
selectTopExpr = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", expression, re.I)
selectDistinctExpr = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I)
selectFromExpr = re.search("\ASELECT\s+(.+?)\s+FROM", expression, re.I)
selectExpr = re.search("\ASELECT\s+(.+)$", expression, re.I)
miscExpr = re.search("\A(.+)", expression, re.I)
2008-10-15 19:38:22 +04:00
if selectTopExpr or selectDistinctExpr or selectFromExpr or selectExpr:
2008-10-15 19:38:22 +04:00
if selectTopExpr:
regExpr = selectTopExpr.groups()[0]
elif selectDistinctExpr:
regExpr = selectDistinctExpr.groups()[0]
elif selectFromExpr:
regExpr = selectFromExpr.groups()[0]
2008-10-15 19:38:22 +04:00
elif selectExpr:
regExpr = selectExpr.groups()[0]
elif miscExpr:
regExpr = miscExpr.groups()[0]
if ( select and re.search("\A(COUNT|LTRIM)\(", regExpr, re.I) ) or len(regExpr) <= 1:
return None, None, None
if selectDistinctExpr:
2010-03-11 01:08:11 +03:00
lengthExpr = "SELECT %s FROM (%s)" % (lengthQuery % regExpr, expression)
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
2010-03-11 01:08:11 +03:00
lengthExpr += " AS %s" % randomStr(lowercase=True)
elif select:
2008-10-15 19:38:22 +04:00
lengthExpr = expression.replace(regExpr, lengthQuery % regExpr, 1)
else:
lengthExpr = lengthQuery % expression
infoMsg = "retrieving the length of query output"
logger.info(infoMsg)
start = time.time()
2008-10-15 19:38:22 +04:00
lengthExprUnescaped = unescaper.unescape(lengthExpr)
2012-04-05 03:32:20 +04:00
count, length = bisection(payload, lengthExprUnescaped, charsetType=CHARSET_TYPE.DIGITS)
debugMsg = "performed %d queries in %d seconds" % (count, calculateDeltaSeconds(start))
logger.debug(debugMsg)
2008-10-15 19:38:22 +04:00
if length == " ":
length = 0
2008-10-15 19:38:22 +04:00
return count, length, regExpr
def resume(expression, payload):
"""
This function can be called to resume part or entire output of a
SQL injection query output.
"""
2010-12-03 13:39:36 +03:00
2010-10-14 00:54:18 +04:00
try:
2011-03-24 13:08:47 +03:00
if "sqlmapfile" in expression or "sqlmapoutput" in expression or conf.freshQueries:
2010-10-14 00:54:18 +04:00
return None
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
condition = (
2011-04-23 01:00:42 +04:00
kb.resumedQueries and conf.url in kb.resumedQueries
and expression in kb.resumedQueries[conf.url]
2010-10-14 00:54:18 +04:00
)
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
if not condition:
return None
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
resumedValue = kb.resumedQueries[conf.url][expression]
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
if not resumedValue:
return None
2008-10-15 19:38:22 +04:00
2010-10-21 13:51:07 +04:00
resumedValue = restoreDumpMarkedChars(resumedValue, True)
2010-10-14 00:54:18 +04:00
if resumedValue[-1] == "]":
resumedValue = resumedValue[:-1]
2008-10-15 19:38:22 +04:00
2011-04-30 17:20:05 +04:00
infoMsg = "read from file '%s': " % conf.sessionFile
2008-10-15 19:38:22 +04:00
2011-12-21 15:50:49 +04:00
if "\n" in resumedValue:
infoMsg += "%s..." % resumedValue.split("\n")[0]
2010-10-14 00:54:18 +04:00
else:
2011-12-21 15:50:49 +04:00
infoMsg += resumedValue
2008-10-15 19:38:22 +04:00
if not kb.suppressResumeInfo:
dataToStdout("[%s] [INFO] %s\n" % (time.strftime("%X"), infoMsg))
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
return resumedValue
2008-10-15 19:38:22 +04:00
2010-12-03 13:39:36 +03:00
# If we called this function without providing a payload it means
# that we have called it from lib/request/inject __goInband() or
# from __goError() function so we return to the calling function
# so that the query output will be retrieved taking advantage
# of either error-based or inband SQL injection vulnerability.
2010-10-14 00:54:18 +04:00
if not payload:
return None
2008-10-15 19:38:22 +04:00
if not Backend.getIdentifiedDbms():
2010-10-14 00:54:18 +04:00
return None
substringQuery = queries[Backend.getIdentifiedDbms()].substring.query
select = re.search("\ASELECT ", expression, re.I)
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
_, length, regExpr = queryOutputLength(expression, payload)
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
if not length:
return None
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
if len(resumedValue) == int(length):
2011-04-30 17:20:05 +04:00
infoMsg = "read from file '%s': " % conf.sessionFile
2010-10-14 00:54:18 +04:00
infoMsg += "%s" % resumedValue.split("\n")[0]
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(resumedValue)))
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
return resumedValue
elif len(resumedValue) < int(length):
2011-04-30 17:20:05 +04:00
infoMsg = "resumed from file '%s': " % conf.sessionFile
2010-10-14 00:54:18 +04:00
infoMsg += "%s..." % resumedValue.split("\n")[0]
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
dataToSessionFile("[%s][%s][%s][%s][%s" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(resumedValue)))
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
if select:
newExpr = expression.replace(regExpr, safeStringFormat(substringQuery, (regExpr, len(resumedValue) + 1, int(length))), 1)
else:
newExpr = safeStringFormat(substringQuery, (expression, len(resumedValue) + 1, int(length)))
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
missingCharsLength = int(length) - len(resumedValue)
2008-10-15 19:38:22 +04:00
2011-04-30 17:20:05 +04:00
infoMsg = "retrieving pending %d query " % missingCharsLength
2010-10-14 00:54:18 +04:00
infoMsg += "output characters"
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
start = time.time()
count, finalValue = bisection(payload, newExpr, length=missingCharsLength)
2010-10-14 00:54:18 +04:00
debugMsg = "performed %d queries in %d seconds" % (count, calculateDeltaSeconds(start))
logger.debug(debugMsg)
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
if len(finalValue) != ( int(length) - len(resumedValue) ):
2011-04-30 17:20:05 +04:00
warnMsg = "the total length of the query is not "
2010-10-14 00:54:18 +04:00
warnMsg += "right, sqlmap is going to retrieve the "
warnMsg += "query value from the beginning now"
logger.warn(warnMsg)
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
return None
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
return "%s%s" % (resumedValue, finalValue)
2008-10-15 19:38:22 +04:00
2010-10-14 00:54:18 +04:00
return None
2010-10-14 19:23:42 +04:00
except ValueError:
2010-10-14 00:54:18 +04:00
errMsg = "invalid resume value for expression: '%s'" % expression
logger.error(errMsg)
return None