mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-06-10 08:03:05 +03:00
switching to SQLite resume support (on error and union techniques this moment)
This commit is contained in:
parent
2d7d84e16b
commit
744636a8c1
|
@ -1360,6 +1360,8 @@ def __setConfAttributes():
|
||||||
conf.dbmsConnector = None
|
conf.dbmsConnector = None
|
||||||
conf.dbmsHandler = None
|
conf.dbmsHandler = None
|
||||||
conf.dumpPath = None
|
conf.dumpPath = None
|
||||||
|
conf.hashDB = None
|
||||||
|
conf.hashDBFile = None
|
||||||
conf.httpHeaders = []
|
conf.httpHeaders = []
|
||||||
conf.hostname = None
|
conf.hostname = None
|
||||||
conf.multipleTargets = False
|
conf.multipleTargets = False
|
||||||
|
|
|
@ -40,6 +40,7 @@ from lib.core.settings import UNICODE_ENCODING
|
||||||
from lib.core.settings import URI_INJECTABLE_REGEX
|
from lib.core.settings import URI_INJECTABLE_REGEX
|
||||||
from lib.core.settings import URI_INJECTION_MARK_CHAR
|
from lib.core.settings import URI_INJECTION_MARK_CHAR
|
||||||
from lib.core.settings import USER_AGENT_ALIASES
|
from lib.core.settings import USER_AGENT_ALIASES
|
||||||
|
from lib.utils.hashdb import HashDB
|
||||||
from lib.core.xmldump import dumper as xmldumper
|
from lib.core.xmldump import dumper as xmldumper
|
||||||
from lib.request.connect import Connect as Request
|
from lib.request.connect import Connect as Request
|
||||||
|
|
||||||
|
@ -174,6 +175,9 @@ def __setOutputResume():
|
||||||
if not conf.sessionFile:
|
if not conf.sessionFile:
|
||||||
conf.sessionFile = "%s%ssession" % (conf.outputPath, os.sep)
|
conf.sessionFile = "%s%ssession" % (conf.outputPath, os.sep)
|
||||||
|
|
||||||
|
if not conf.hashDBFile:
|
||||||
|
conf.hashDBFile = "%s%shashdb" % (conf.outputPath, os.sep)
|
||||||
|
|
||||||
logger.info("using '%s' as session file" % conf.sessionFile)
|
logger.info("using '%s' as session file" % conf.sessionFile)
|
||||||
|
|
||||||
if os.path.exists(conf.sessionFile):
|
if os.path.exists(conf.sessionFile):
|
||||||
|
@ -223,6 +227,7 @@ def __setOutputResume():
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
os.remove(conf.sessionFile)
|
os.remove(conf.sessionFile)
|
||||||
|
os.remove(conf.hashDBFile)
|
||||||
logger.info("flushing session file")
|
logger.info("flushing session file")
|
||||||
except OSError, msg:
|
except OSError, msg:
|
||||||
errMsg = "unable to flush the session file (%s)" % msg
|
errMsg = "unable to flush the session file (%s)" % msg
|
||||||
|
@ -230,6 +235,7 @@ def __setOutputResume():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conf.sessionFP = codecs.open(conf.sessionFile, "a", UNICODE_ENCODING)
|
conf.sessionFP = codecs.open(conf.sessionFile, "a", UNICODE_ENCODING)
|
||||||
|
conf.hashDB = HashDB(conf.hashDBFile)
|
||||||
dataToSessionFile("\n[%s]\n" % time.strftime("%X %x"))
|
dataToSessionFile("\n[%s]\n" % time.strftime("%X %x"))
|
||||||
except IOError:
|
except IOError:
|
||||||
errMsg = "unable to write on the session file specified"
|
errMsg = "unable to write on the session file specified"
|
||||||
|
@ -338,12 +344,16 @@ def initTargetEnv():
|
||||||
if conf.sessionFP:
|
if conf.sessionFP:
|
||||||
conf.sessionFP.close()
|
conf.sessionFP.close()
|
||||||
|
|
||||||
|
if conf.hashDB:
|
||||||
|
conf.hashDB.close()
|
||||||
|
|
||||||
if conf.cj:
|
if conf.cj:
|
||||||
conf.cj.clear()
|
conf.cj.clear()
|
||||||
|
|
||||||
conf.paramDict = {}
|
conf.paramDict = {}
|
||||||
conf.parameters = {}
|
conf.parameters = {}
|
||||||
conf.sessionFile = None
|
conf.sessionFile = None
|
||||||
|
conf.hashDBFile = None
|
||||||
|
|
||||||
__setKnowledgeBaseAttributes(False)
|
__setKnowledgeBaseAttributes(False)
|
||||||
__restoreCmdLineOptions()
|
__restoreCmdLineOptions()
|
||||||
|
|
|
@ -53,83 +53,86 @@ def __oneShotErrorUse(expression, field):
|
||||||
|
|
||||||
threadData = getCurrentThreadData()
|
threadData = getCurrentThreadData()
|
||||||
|
|
||||||
retVal = None
|
retVal = conf.hashDB.retrieve(expression) if not conf.freshQueries else None
|
||||||
|
|
||||||
offset = 1
|
offset = 1
|
||||||
chunk_length = None
|
chunk_length = None
|
||||||
|
|
||||||
while True:
|
if not retVal:
|
||||||
check = "%s(?P<result>.*?)%s" % (kb.misc.start, kb.misc.stop)
|
while True:
|
||||||
trimcheck = "%s(?P<result>.*?)</" % (kb.misc.start)
|
check = "%s(?P<result>.*?)%s" % (kb.misc.start, kb.misc.stop)
|
||||||
|
trimcheck = "%s(?P<result>.*?)</" % (kb.misc.start)
|
||||||
|
|
||||||
nulledCastedField = agent.nullAndCastField(field)
|
nulledCastedField = agent.nullAndCastField(field)
|
||||||
|
|
||||||
if Backend.isDbms(DBMS.MYSQL):
|
if Backend.isDbms(DBMS.MYSQL):
|
||||||
chunk_length = MYSQL_ERROR_CHUNK_LENGTH
|
chunk_length = MYSQL_ERROR_CHUNK_LENGTH
|
||||||
nulledCastedField = queries[DBMS.MYSQL].substring.query % (nulledCastedField, offset, chunk_length)
|
nulledCastedField = queries[DBMS.MYSQL].substring.query % (nulledCastedField, offset, chunk_length)
|
||||||
elif Backend.isDbms(DBMS.MSSQL):
|
elif Backend.isDbms(DBMS.MSSQL):
|
||||||
chunk_length = MSSQL_ERROR_CHUNK_LENGTH
|
chunk_length = MSSQL_ERROR_CHUNK_LENGTH
|
||||||
nulledCastedField = queries[DBMS.MSSQL].substring.query % (nulledCastedField, offset, 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)
|
||||||
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)
|
||||||
|
|
||||||
# Perform the request
|
# Perform the request
|
||||||
page, headers = Request.queryPage(payload, content=True)
|
page, headers = Request.queryPage(payload, content=True)
|
||||||
|
|
||||||
reqCount += 1
|
reqCount += 1
|
||||||
|
|
||||||
# Parse the returned page to get the exact error-based
|
# Parse the returned page to get the exact error-based
|
||||||
# sql injection output
|
# sql injection output
|
||||||
output = reduce(lambda x, y: x if x is not None else y, [ \
|
output = reduce(lambda x, y: x if x is not None else y, [ \
|
||||||
extractRegexResult(check, page, re.DOTALL | re.IGNORECASE), \
|
extractRegexResult(check, page, re.DOTALL | re.IGNORECASE), \
|
||||||
extractRegexResult(check, listToStrValue(headers.headers \
|
extractRegexResult(check, listToStrValue(headers.headers \
|
||||||
if headers else None), re.DOTALL | re.IGNORECASE), \
|
if headers else None), re.DOTALL | re.IGNORECASE), \
|
||||||
extractRegexResult(check, threadData.lastRedirectMsg[1] \
|
extractRegexResult(check, threadData.lastRedirectMsg[1] \
|
||||||
if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \
|
if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \
|
||||||
threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)], \
|
threadData.lastRequestUID else None, re.DOTALL | re.IGNORECASE)], \
|
||||||
None)
|
None)
|
||||||
|
|
||||||
if output is not None:
|
if output is not None:
|
||||||
output = getUnicode(output, kb.pageEncoding)
|
output = getUnicode(output, kb.pageEncoding)
|
||||||
else:
|
else:
|
||||||
trimmed = extractRegexResult(trimcheck, page, re.DOTALL | re.IGNORECASE) \
|
trimmed = extractRegexResult(trimcheck, page, re.DOTALL | re.IGNORECASE) \
|
||||||
or extractRegexResult(trimcheck, listToStrValue(headers.headers \
|
or extractRegexResult(trimcheck, listToStrValue(headers.headers \
|
||||||
if headers else None), re.DOTALL | re.IGNORECASE) \
|
if headers else None), re.DOTALL | re.IGNORECASE) \
|
||||||
or extractRegexResult(trimcheck, threadData.lastRedirectMsg[1] \
|
or extractRegexResult(trimcheck, threadData.lastRedirectMsg[1] \
|
||||||
if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \
|
if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \
|
||||||
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 (due to its length): "
|
warnMsg = "possible server trimmed output detected (due to its length): "
|
||||||
warnMsg += trimmed
|
warnMsg += trimmed
|
||||||
logger.warn(warnMsg)
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
if any(map(lambda dbms: Backend.isDbms(dbms), [DBMS.MYSQL, DBMS.MSSQL])):
|
if any(map(lambda dbms: Backend.isDbms(dbms), [DBMS.MYSQL, DBMS.MSSQL])):
|
||||||
if offset == 1:
|
if offset == 1:
|
||||||
|
retVal = output
|
||||||
|
else:
|
||||||
|
retVal += output if output else ''
|
||||||
|
|
||||||
|
if output and len(output) >= chunk_length:
|
||||||
|
offset += chunk_length
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
retVal = output
|
retVal = output
|
||||||
else:
|
|
||||||
retVal += output if output else ''
|
|
||||||
|
|
||||||
if output and len(output) >= chunk_length:
|
|
||||||
offset += chunk_length
|
|
||||||
else:
|
|
||||||
break
|
break
|
||||||
else:
|
|
||||||
retVal = output
|
|
||||||
break
|
|
||||||
|
|
||||||
if isinstance(retVal, basestring):
|
if isinstance(retVal, basestring):
|
||||||
retVal = htmlunescape(retVal).replace("<br>", "\n")
|
retVal = htmlunescape(retVal).replace("<br>", "\n")
|
||||||
|
|
||||||
retVal = __errorReplaceChars(retVal)
|
retVal = __errorReplaceChars(retVal)
|
||||||
|
|
||||||
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(retVal)))
|
#dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(retVal)))
|
||||||
|
conf.hashDB.write(expression, retVal)
|
||||||
|
|
||||||
return safecharencode(retVal) if kb.safeCharEncode else retVal
|
return safecharencode(retVal) if kb.safeCharEncode else retVal
|
||||||
|
|
||||||
|
|
|
@ -50,54 +50,59 @@ reqCount = 0
|
||||||
def __oneShotUnionUse(expression, unpack=True, limited=False):
|
def __oneShotUnionUse(expression, unpack=True, limited=False):
|
||||||
global reqCount
|
global reqCount
|
||||||
|
|
||||||
check = "(?P<result>%s.*%s)" % (kb.misc.start, kb.misc.stop)
|
retVal = conf.hashDB.retrieve(expression) if not conf.freshQueries else None
|
||||||
trimcheck = "%s(?P<result>.*?)</" % (kb.misc.start)
|
|
||||||
|
|
||||||
# Prepare expression with delimiters
|
if not retVal:
|
||||||
expression = agent.concatQuery(expression, unpack)
|
check = "(?P<result>%s.*%s)" % (kb.misc.start, kb.misc.stop)
|
||||||
expression = unescaper.unescape(expression)
|
trimcheck = "%s(?P<result>.*?)</" % (kb.misc.start)
|
||||||
|
|
||||||
if conf.limitStart or conf.limitStop:
|
# Prepare expression with delimiters
|
||||||
where = PAYLOAD.WHERE.NEGATIVE
|
expression = agent.concatQuery(expression, unpack)
|
||||||
else:
|
expression = unescaper.unescape(expression)
|
||||||
where = None
|
|
||||||
|
|
||||||
# Forge the inband SQL injection request
|
if conf.limitStart or conf.limitStop:
|
||||||
vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector
|
where = PAYLOAD.WHERE.NEGATIVE
|
||||||
query = agent.forgeInbandQuery(expression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], None, limited)
|
else:
|
||||||
payload = agent.payload(newValue=query, where=where)
|
where = None
|
||||||
|
|
||||||
# Perform the request
|
# Forge the inband SQL injection request
|
||||||
page, headers = Request.queryPage(payload, content=True, raise404=False)
|
vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector
|
||||||
|
query = agent.forgeInbandQuery(expression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], None, limited)
|
||||||
|
payload = agent.payload(newValue=query, where=where)
|
||||||
|
|
||||||
reqCount += 1
|
# Perform the request
|
||||||
|
page, headers = Request.queryPage(payload, content=True, raise404=False)
|
||||||
|
|
||||||
# Parse the returned page to get the exact union-based
|
reqCount += 1
|
||||||
# sql injection output
|
|
||||||
output = reduce(lambda x, y: x if x is not None else y, [ \
|
|
||||||
extractRegexResult(check, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE), \
|
|
||||||
extractRegexResult(check, removeReflectiveValues(listToStrValue(headers.headers \
|
|
||||||
if headers else None), payload, True), re.DOTALL | re.IGNORECASE)], \
|
|
||||||
None)
|
|
||||||
|
|
||||||
if output is not None:
|
# Parse the returned page to get the exact union-based
|
||||||
output = getUnicode(output, kb.pageEncoding)
|
# sql injection output
|
||||||
else:
|
retVal = reduce(lambda x, y: x if x is not None else y, [ \
|
||||||
trimmed = extractRegexResult(trimcheck, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE) \
|
extractRegexResult(check, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE), \
|
||||||
or extractRegexResult(trimcheck, removeReflectiveValues(listToStrValue(headers.headers \
|
extractRegexResult(check, removeReflectiveValues(listToStrValue(headers.headers \
|
||||||
if headers else None), payload, True), re.DOTALL | re.IGNORECASE)
|
if headers else None), payload, True), re.DOTALL | re.IGNORECASE)], \
|
||||||
|
None)
|
||||||
|
|
||||||
if trimmed:
|
if retVal is not None:
|
||||||
warnMsg = "possible server trimmed output detected (due to its length): "
|
retVal = getUnicode(retVal, kb.pageEncoding)
|
||||||
warnMsg += trimmed
|
else:
|
||||||
logger.warn(warnMsg)
|
trimmed = extractRegexResult(trimcheck, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE) \
|
||||||
elif Backend.isDbms(DBMS.MYSQL) and not kb.multiThreadMode:
|
or extractRegexResult(trimcheck, removeReflectiveValues(listToStrValue(headers.headers \
|
||||||
warnMsg = "if the problem persists with 'None' values please try to use "
|
if headers else None), payload, True), re.DOTALL | re.IGNORECASE)
|
||||||
warnMsg += "hidden switch --no-cast (fixing problems with some collation "
|
|
||||||
warnMsg += "issues)"
|
|
||||||
singleTimeWarnMessage(warnMsg)
|
|
||||||
|
|
||||||
return output
|
if trimmed:
|
||||||
|
warnMsg = "possible server trimmed output detected (due to its length): "
|
||||||
|
warnMsg += trimmed
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
elif Backend.isDbms(DBMS.MYSQL) and not kb.multiThreadMode:
|
||||||
|
warnMsg = "if the problem persists with 'None' values please try to use "
|
||||||
|
warnMsg += "hidden switch --no-cast (fixing problems with some collation "
|
||||||
|
warnMsg += "issues)"
|
||||||
|
singleTimeWarnMessage(warnMsg)
|
||||||
|
|
||||||
|
conf.hashDB.write(expression, retVal)
|
||||||
|
|
||||||
|
return retVal
|
||||||
|
|
||||||
def configUnion(char=None, columns=None):
|
def configUnion(char=None, columns=None):
|
||||||
def __configUnionChar(char):
|
def __configUnionChar(char):
|
||||||
|
|
63
lib/utils/hashdb.py
Normal file
63
lib/utils/hashdb.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
Copyright (c) 2006-2011 sqlmap developers (http://www.sqlmap.org/)
|
||||||
|
See the file 'doc/COPYING' for copying permission
|
||||||
|
"""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
from lib.core.settings import UNICODE_ENCODING
|
||||||
|
|
||||||
|
class HashDB:
|
||||||
|
def __init__(self, filepath):
|
||||||
|
self.connection = sqlite3.connect(filepath)
|
||||||
|
self.cursor = self.connection.cursor()
|
||||||
|
self.cursor.execute("CREATE TABLE IF NOT EXISTS storage (id INTEGER PRIMARY KEY, value TEXT)")
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
try:
|
||||||
|
self.endTransaction()
|
||||||
|
self.connection.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def hashKey(self, key):
|
||||||
|
key = key.encode(UNICODE_ENCODING) if isinstance(key, unicode) else repr(key)
|
||||||
|
retVal = int(hashlib.md5(key).hexdigest()[:8], 16)
|
||||||
|
return retVal
|
||||||
|
|
||||||
|
def beginTransaction(self):
|
||||||
|
"""
|
||||||
|
Great speed improvement can be gained by using explicit transactions around multiple inserts.
|
||||||
|
Reference: http://stackoverflow.com/questions/4719836/python-and-sqlite3-adding-thousands-of-rows
|
||||||
|
"""
|
||||||
|
self.cursor.execute('BEGIN TRANSACTION')
|
||||||
|
|
||||||
|
def endTransaction(self):
|
||||||
|
try:
|
||||||
|
self.cursor.execute('END TRANSACTION')
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def retrieve(self, key):
|
||||||
|
retVal = None
|
||||||
|
if key:
|
||||||
|
hash_ = self.hashKey(key)
|
||||||
|
for row in self.cursor.execute("SELECT value FROM storage WHERE id=?", (hash_,)):
|
||||||
|
retVal = row[0]
|
||||||
|
return retVal
|
||||||
|
|
||||||
|
def write(self, key, value):
|
||||||
|
if key:
|
||||||
|
hash_ = self.hashKey(key)
|
||||||
|
try:
|
||||||
|
self.cursor.execute("INSERT INTO storage VALUES (?, ?)", (hash_, value,))
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
self.cursor.execute("UPDATE storage SET value=? WHERE id=?", (value, hash_,))
|
Loading…
Reference in New Issue
Block a user