mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-02-03 05:04:11 +03:00
lots of refactoring regarding removal of already obsolete session file mechanism
This commit is contained in:
parent
1e67b4f0b9
commit
ec44e88db8
|
@ -738,13 +738,6 @@ def dataToStdout(data, forceOutput=False):
|
|||
logging._releaseLock()
|
||||
setFormatterPrependFlag(len(data) == 1 and data not in ('\n', '\r') or len(data) > 2 and data[0] == '\r' and data[-1] != '\n')
|
||||
|
||||
def dataToSessionFile(data):
|
||||
if not conf.sessionFile or kb.suppressSession:
|
||||
return
|
||||
|
||||
conf.sessionFP.write(data)
|
||||
conf.sessionFP.flush()
|
||||
|
||||
def dataToTrafficFile(data):
|
||||
if not conf.trafficFile:
|
||||
return
|
||||
|
|
|
@ -143,14 +143,16 @@ class EXPECTED:
|
|||
INT = "int"
|
||||
|
||||
class HASHDB_KEYS:
|
||||
KB_ABS_FILE_PATHS = "KB_ABS_FILE_PATHS"
|
||||
KB_CHARS = "KB_CHARS"
|
||||
KB_BRUTE_TABLES = "KB_BRUTE_TABLES"
|
||||
KB_BRUTE_COLUMNS = "KB_BRUTE_COLUMNS"
|
||||
DBMS = "DBMS"
|
||||
CONF_TMP_PATH = "CONF_TMP_PATH"
|
||||
KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE"
|
||||
KB_INJECTIONS = "KB_INJECTIONS"
|
||||
KB_ABS_FILE_PATHS = "KB_ABS_FILE_PATHS"
|
||||
KB_BRUTE_COLUMNS = "KB_BRUTE_COLUMNS"
|
||||
KB_BRUTE_TABLES = "KB_BRUTE_TABLES"
|
||||
KB_CHARS = "KB_CHARS"
|
||||
KB_DYNAMIC_MARKINGS = "KB_DYNAMIC_MARKINGS"
|
||||
KB_INJECTIONS = "KB_INJECTIONS"
|
||||
KB_XP_CMDSHELL_AVAILABLE = "KB_XP_CMDSHELL_AVAILABLE"
|
||||
OS = "OS"
|
||||
|
||||
class REDIRECTION:
|
||||
YES = "Y"
|
||||
|
|
|
@ -1497,13 +1497,11 @@ def __setKnowledgeBaseAttributes(flushAll=True):
|
|||
kb.reflectiveMechanism = True
|
||||
kb.reflectiveCounters = {REFLECTIVE_COUNTER.MISS:0, REFLECTIVE_COUNTER.HIT:0}
|
||||
kb.responseTimes = []
|
||||
kb.resumedQueries = {}
|
||||
kb.resumeValues = True
|
||||
kb.safeCharEncode = False
|
||||
kb.singleLogFlags = set()
|
||||
kb.skipOthersDbms = None
|
||||
kb.stickyFlag = False
|
||||
kb.suppressSession = False
|
||||
kb.suppressResumeInfo = False
|
||||
kb.technique = None
|
||||
kb.testMode = False
|
||||
|
|
|
@ -11,7 +11,7 @@ import re
|
|||
|
||||
from lib.core.common import Backend
|
||||
from lib.core.common import Format
|
||||
from lib.core.common import dataToSessionFile
|
||||
from lib.core.common import hashDBWrite
|
||||
from lib.core.common import intersect
|
||||
from lib.core.common import readInput
|
||||
from lib.core.common import singleTimeWarnMessage
|
||||
|
@ -20,42 +20,25 @@ from lib.core.convert import base64unpickle
|
|||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.enums import HASHDB_KEYS
|
||||
from lib.core.enums import OS
|
||||
from lib.core.settings import SUPPORTED_DBMS
|
||||
from lib.core.settings import UNKNOWN_DBMS_VERSION
|
||||
|
||||
def safeFormatString(value):
|
||||
retVal = value
|
||||
if retVal:
|
||||
retVal = retVal.replace("[", "__LEFT_SQUARE_BRACKET__").replace("]", "__RIGHT_SQUARE_BRACKET__")
|
||||
return retVal
|
||||
|
||||
def unSafeFormatString(value):
|
||||
retVal = value
|
||||
if retVal:
|
||||
retVal = retVal.replace("__LEFT_SQUARE_BRACKET__", "[").replace("__RIGHT_SQUARE_BRACKET__", "]")
|
||||
return retVal
|
||||
|
||||
def setDbms(dbms):
|
||||
"""
|
||||
@param dbms: database management system to be set into the knowledge
|
||||
base as fingerprint.
|
||||
@type dbms: C{str}
|
||||
"""
|
||||
condition = (
|
||||
not kb.resumedQueries
|
||||
or ( kb.resumedQueries.has_key(conf.url) and
|
||||
not kb.resumedQueries[conf.url].has_key("DBMS") )
|
||||
)
|
||||
|
||||
if condition:
|
||||
dataToSessionFile("[%s][%s][%s][DBMS][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(dbms)))
|
||||
hashDBWrite(HASHDB_KEYS.DBMS, dbms)
|
||||
|
||||
firstRegExp = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS]))
|
||||
dbmsRegExp = re.search("^%s" % firstRegExp, dbms, re.I)
|
||||
_ = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS]))
|
||||
_ = re.search("^%s" % _, dbms, re.I)
|
||||
|
||||
if dbmsRegExp:
|
||||
dbms = dbmsRegExp.group(1)
|
||||
if _:
|
||||
dbms = _.group(1)
|
||||
|
||||
Backend.setDbms(dbms)
|
||||
|
||||
|
@ -76,11 +59,6 @@ def setOs():
|
|||
"""
|
||||
|
||||
infoMsg = ""
|
||||
condition = (
|
||||
not kb.resumedQueries
|
||||
or ( kb.resumedQueries.has_key(conf.url) and
|
||||
not kb.resumedQueries[conf.url].has_key("OS") )
|
||||
)
|
||||
|
||||
if not kb.bannerFp:
|
||||
return
|
||||
|
@ -105,82 +83,4 @@ def setOs():
|
|||
if infoMsg:
|
||||
logger.info(infoMsg)
|
||||
|
||||
if condition:
|
||||
dataToSessionFile("[%s][%s][%s][OS][%s]\n" % (conf.url, kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]), Backend.getOs()))
|
||||
|
||||
def resumeConfKb(expression, url, value):
|
||||
if expression == "Dynamic markings" and url == conf.url:
|
||||
kb.dynamicMarkings = base64unpickle(value[:-1])
|
||||
infoMsg = "resuming dynamic markings from session file"
|
||||
logger.info(infoMsg)
|
||||
|
||||
elif expression == "DBMS" and url == conf.url:
|
||||
dbms = unSafeFormatString(value[:-1])
|
||||
dbms = dbms.lower()
|
||||
dbmsVersion = [UNKNOWN_DBMS_VERSION]
|
||||
|
||||
firstRegExp = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS]))
|
||||
dbmsRegExp = re.search("%s ([\d\.]+)" % firstRegExp, dbms)
|
||||
|
||||
if dbmsRegExp:
|
||||
dbms = dbmsRegExp.group(1)
|
||||
dbmsVersion = [ dbmsRegExp.group(2) ]
|
||||
|
||||
if conf.dbms and conf.dbms.lower() != dbms:
|
||||
message = "you provided '%s' as back-end DBMS, " % conf.dbms
|
||||
message += "but from a past scan information on the target URL "
|
||||
message += "sqlmap assumes the back-end DBMS is %s. " % dbms
|
||||
message += "Do you really want to force the back-end "
|
||||
message += "DBMS value? [y/N] "
|
||||
test = readInput(message, default="N")
|
||||
|
||||
if not test or test[0] in ("n", "N"):
|
||||
conf.dbms = None
|
||||
Backend.setDbms(dbms)
|
||||
Backend.setVersionList(dbmsVersion)
|
||||
else:
|
||||
infoMsg = "resuming back-end DBMS '%s' " % dbms
|
||||
infoMsg += "from session file"
|
||||
logger.info(infoMsg)
|
||||
|
||||
Backend.setDbms(dbms)
|
||||
Backend.setVersionList(dbmsVersion)
|
||||
|
||||
elif expression == "OS" and url == conf.url:
|
||||
os = unSafeFormatString(value[:-1])
|
||||
|
||||
if os and os != 'None':
|
||||
infoMsg = "resuming back-end DBMS operating system '%s' " % os
|
||||
infoMsg += "from session file"
|
||||
logger.info(infoMsg)
|
||||
|
||||
if conf.os and conf.os.lower() != os.lower():
|
||||
message = "you provided '%s' as back-end DBMS operating " % conf.os
|
||||
message += "system, but from a past scan information on the "
|
||||
message += "target URL sqlmap assumes the back-end DBMS "
|
||||
message += "operating system is %s. " % os
|
||||
message += "Do you really want to force the back-end DBMS "
|
||||
message += "OS value? [y/N] "
|
||||
test = readInput(message, default="N")
|
||||
|
||||
if not test or test[0] in ("n", "N"):
|
||||
conf.os = os
|
||||
else:
|
||||
conf.os = os
|
||||
|
||||
Backend.setOs(conf.os)
|
||||
|
||||
elif expression == "Remote temp path" and url == conf.url and conf.tmpPath is None:
|
||||
conf.tmpPath = unSafeFormatString(value[:-1])
|
||||
|
||||
infoMsg = "resuming remote absolute path of temporary "
|
||||
infoMsg += "files directory '%s' from session file" % conf.tmpPath
|
||||
logger.info(infoMsg)
|
||||
|
||||
elif conf.freshQueries:
|
||||
pass
|
||||
|
||||
elif expression == "xp_cmdshell availability" and url == conf.url:
|
||||
kb.xpCmdshellAvailable = True if unSafeFormatString(value[:-1]).lower() == "true" else False
|
||||
infoMsg = "resuming xp_cmdshell availability"
|
||||
logger.info(infoMsg)
|
||||
hashDBWrite(HASHDB_KEYS.OS, Backend.getOs())
|
||||
|
|
|
@ -14,7 +14,7 @@ import re
|
|||
import tempfile
|
||||
import time
|
||||
|
||||
from lib.core.common import dataToSessionFile
|
||||
from lib.core.common import Backend
|
||||
from lib.core.common import hashDBRetrieve
|
||||
from lib.core.common import intersect
|
||||
from lib.core.common import paramToDict
|
||||
|
@ -37,14 +37,15 @@ from lib.core.exception import sqlmapUserQuitException
|
|||
from lib.core.option import authHandler
|
||||
from lib.core.option import __setDBMS
|
||||
from lib.core.option import __setKnowledgeBaseAttributes
|
||||
from lib.core.session import resumeConfKb
|
||||
from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR
|
||||
from lib.core.settings import HOST_ALIASES
|
||||
from lib.core.settings import REFERER_ALIASES
|
||||
from lib.core.settings import RESULTS_FILE_FORMAT
|
||||
from lib.core.settings import SOAP_REGEX
|
||||
from lib.core.settings import SUPPORTED_DBMS
|
||||
from lib.core.settings import UNENCODED_ORIGINAL_VALUE
|
||||
from lib.core.settings import UNICODE_ENCODING
|
||||
from lib.core.settings import UNKNOWN_DBMS_VERSION
|
||||
from lib.core.settings import URI_INJECTABLE_REGEX
|
||||
from lib.core.settings import USER_AGENT_ALIASES
|
||||
from lib.utils.hashdb import HashDB
|
||||
|
@ -243,78 +244,79 @@ def __resumeHashDBValues():
|
|||
if injection not in kb.injections:
|
||||
kb.injections.append(injection)
|
||||
|
||||
def __setOutputResume():
|
||||
__resumeDBMS()
|
||||
__resumeOS()
|
||||
|
||||
def __resumeDBMS():
|
||||
"""
|
||||
Check and set the output text file and the resume functionality.
|
||||
Resume stored DBMS information from HashDB
|
||||
"""
|
||||
|
||||
if not conf.sessionFile:
|
||||
conf.sessionFile = "%s%ssession" % (conf.outputPath, os.sep)
|
||||
value = hashDBRetrieve(HASHDB_KEYS.DBMS)
|
||||
|
||||
logger.info("using '%s' as a session file" % conf.sessionFile)
|
||||
if not value:
|
||||
return
|
||||
|
||||
if os.path.exists(conf.sessionFile):
|
||||
if not conf.flushSession:
|
||||
try:
|
||||
readSessionFP = codecs.open(conf.sessionFile, "r", UNICODE_ENCODING, 'replace')
|
||||
__url_cache = set()
|
||||
__expression_cache = {}
|
||||
dbms = value.lower()
|
||||
dbmsVersion = [UNKNOWN_DBMS_VERSION]
|
||||
_ = "(%s)" % ("|".join([alias for alias in SUPPORTED_DBMS]))
|
||||
_ = re.search("%s ([\d\.]+)" % _, dbms, re.I)
|
||||
|
||||
for line in readSessionFP.readlines(): # xreadlines doesn't return unicode strings when codec.open() is used
|
||||
if line.count("][") == 4:
|
||||
line = line.split("][")
|
||||
if _:
|
||||
dbms = _.group(1).lower()
|
||||
dbmsVersion = [_.group(2)]
|
||||
|
||||
if len(line) != 5:
|
||||
continue
|
||||
if conf.dbms:
|
||||
if conf.dbms.lower() != dbms:
|
||||
message = "you provided '%s' as back-end DBMS, " % conf.dbms
|
||||
message += "but from a past scan information on the target URL "
|
||||
message += "sqlmap assumes the back-end DBMS is %s. " % dbms
|
||||
message += "Do you really want to force the back-end "
|
||||
message += "DBMS value? [y/N] "
|
||||
test = readInput(message, default="N")
|
||||
|
||||
url, _, _, expression, value = line
|
||||
if not test or test[0] in ("n", "N"):
|
||||
conf.dbms = None
|
||||
Backend.setDbms(dbms)
|
||||
Backend.setVersionList(dbmsVersion)
|
||||
else:
|
||||
infoMsg = "resuming back-end DBMS '%s' " % dbms
|
||||
logger.info(infoMsg)
|
||||
|
||||
if not value:
|
||||
continue
|
||||
Backend.setDbms(dbms)
|
||||
Backend.setVersionList(dbmsVersion)
|
||||
|
||||
if url[0] == "[":
|
||||
url = url[1:]
|
||||
def __resumeOS():
|
||||
"""
|
||||
Resume stored OS information from HashDB
|
||||
"""
|
||||
|
||||
value = value.rstrip('\r\n') # Strips both chars independently
|
||||
value = hashDBRetrieve(HASHDB_KEYS.OS)
|
||||
|
||||
if url not in ( conf.url, conf.hostname ):
|
||||
continue
|
||||
if not value:
|
||||
return
|
||||
|
||||
if url not in __url_cache:
|
||||
kb.resumedQueries[url] = {}
|
||||
kb.resumedQueries[url][expression] = value
|
||||
__url_cache.add(url)
|
||||
__expression_cache[url] = set(expression)
|
||||
os = value
|
||||
|
||||
resumeConfKb(expression, url, value)
|
||||
if os and os != 'None':
|
||||
infoMsg = "resuming back-end DBMS operating system '%s' " % os
|
||||
logger.info(infoMsg)
|
||||
|
||||
if expression not in __expression_cache[url]:
|
||||
kb.resumedQueries[url][expression] = value
|
||||
__expression_cache[url].add(value)
|
||||
elif len(value) >= len(kb.resumedQueries[url][expression]):
|
||||
kb.resumedQueries[url][expression] = value
|
||||
if conf.os and conf.os.lower() != os.lower():
|
||||
message = "you provided '%s' as back-end DBMS operating " % conf.os
|
||||
message += "system, but from a past scan information on the "
|
||||
message += "target URL sqlmap assumes the back-end DBMS "
|
||||
message += "operating system is %s. " % os
|
||||
message += "Do you really want to force the back-end DBMS "
|
||||
message += "OS value? [y/N] "
|
||||
test = readInput(message, default="N")
|
||||
|
||||
if kb.injection.place is not None and kb.injection.parameter is not None:
|
||||
kb.injections.append(kb.injection)
|
||||
except IOError, msg:
|
||||
errMsg = "unable to properly open the session file (%s)" % msg
|
||||
raise sqlmapFilePathException, errMsg
|
||||
else:
|
||||
readSessionFP.close()
|
||||
if not test or test[0] in ("n", "N"):
|
||||
conf.os = os
|
||||
else:
|
||||
try:
|
||||
os.remove(conf.sessionFile)
|
||||
logger.info("flushing session file")
|
||||
except OSError, msg:
|
||||
errMsg = "unable to flush the session file (%s)" % msg
|
||||
raise sqlmapFilePathException, errMsg
|
||||
conf.os = os
|
||||
|
||||
try:
|
||||
conf.sessionFP = codecs.open(conf.sessionFile, "a", UNICODE_ENCODING)
|
||||
dataToSessionFile("\n[%s]\n" % time.strftime("%X %x"))
|
||||
except IOError:
|
||||
errMsg = "unable to write on the session file specified"
|
||||
raise sqlmapFilePathException, errMsg
|
||||
Backend.setOs(conf.os)
|
||||
|
||||
def __setResultsFile():
|
||||
"""
|
||||
|
@ -435,7 +437,6 @@ def initTargetEnv():
|
|||
|
||||
conf.paramDict = {}
|
||||
conf.parameters = {}
|
||||
conf.sessionFile = None
|
||||
conf.hashDBFile = None
|
||||
|
||||
__setKnowledgeBaseAttributes(False)
|
||||
|
@ -445,7 +446,6 @@ def initTargetEnv():
|
|||
def setupTargetEnv():
|
||||
__createTargetDirs()
|
||||
__setRequestParams()
|
||||
__setOutputResume()
|
||||
__setHashDB()
|
||||
__resumeHashDBValues()
|
||||
__setResultsFile()
|
||||
|
|
|
@ -126,7 +126,6 @@ def liveTest():
|
|||
|
||||
name = None
|
||||
log = []
|
||||
session = []
|
||||
switches = dict(global_)
|
||||
|
||||
if case.hasAttribute("name"):
|
||||
|
@ -143,14 +142,9 @@ def liveTest():
|
|||
if item.hasAttribute("value"):
|
||||
log.append(replaceVars(item.getAttribute("value"), vars_))
|
||||
|
||||
if case.getElementsByTagName("session"):
|
||||
for item in case.getElementsByTagName("session")[0].getElementsByTagName("item"):
|
||||
if item.hasAttribute("value"):
|
||||
session.append(replaceVars(item.getAttribute("value"), vars_))
|
||||
|
||||
msg = "running live test case '%s' (%d/%d)" % (name, count, length)
|
||||
logger.info(msg)
|
||||
result = runCase(switches, log, session)
|
||||
result = runCase(switches, log)
|
||||
if result:
|
||||
logger.info("test passed")
|
||||
else:
|
||||
|
@ -178,7 +172,6 @@ def initCase(switches=None):
|
|||
if key in cmdLineOptions.__dict__:
|
||||
cmdLineOptions.__dict__[key] = value
|
||||
|
||||
conf.sessionFile = None
|
||||
init(cmdLineOptions, True)
|
||||
__setVerbosity()
|
||||
|
||||
|
@ -190,7 +183,7 @@ def cleanCase():
|
|||
conf.verbose = 1
|
||||
__setVerbosity()
|
||||
|
||||
def runCase(switches=None, log=None, session=None):
|
||||
def runCase(switches=None, log=None):
|
||||
retVal = True
|
||||
initCase(switches)
|
||||
|
||||
|
@ -198,19 +191,6 @@ def runCase(switches=None, log=None, session=None):
|
|||
if result == False: #if None ignore
|
||||
retVal = False
|
||||
|
||||
if session and retVal:
|
||||
ifile = open(conf.sessionFile, 'r')
|
||||
content = ifile.read()
|
||||
ifile.close()
|
||||
for item in session:
|
||||
if item.startswith("r'") and item.endswith("'"):
|
||||
if not re.search(item[2:-1], content, re.DOTALL):
|
||||
retVal = False
|
||||
break
|
||||
elif content.find(item) < 0:
|
||||
retVal = False
|
||||
break
|
||||
|
||||
if log and retVal:
|
||||
ifile = open(conf.dumper.getOutputFile(), 'r')
|
||||
content = ifile.read()
|
||||
|
|
|
@ -50,12 +50,11 @@ from lib.core.unescaper import unescaper
|
|||
from lib.request.connect import Connect as Request
|
||||
from lib.request.direct import direct
|
||||
from lib.techniques.blind.inference import bisection
|
||||
from lib.techniques.blind.inference import queryOutputLength
|
||||
from lib.techniques.dns.test import dnsTest
|
||||
from lib.techniques.dns.use import dnsUse
|
||||
from lib.techniques.error.use import errorUse
|
||||
from lib.techniques.union.use import unionUse
|
||||
from lib.utils.resume import queryOutputLength
|
||||
from lib.utils.resume import resume
|
||||
|
||||
def __goInference(payload, expression, charsetType=None, firstChar=None, lastChar=None, dump=False):
|
||||
start = time.time()
|
||||
|
@ -488,8 +487,4 @@ def goStacked(expression, silent=False):
|
|||
Request.queryPage(payload, content=False, silent=silent, noteResponseTime=False, timeBasedCompare=True)
|
||||
|
||||
def checkBooleanExpression(expression, expectingNone=True):
|
||||
kb.suppressSession = True
|
||||
value = getValue(unescaper.unescape(expression), expected=EXPECTED.BOOL, suppressOutput=True, expectingNone=expectingNone)
|
||||
kb.suppressSession = False
|
||||
|
||||
return value
|
||||
return getValue(unescaper.unescape(expression), expected=EXPECTED.BOOL, suppressOutput=True, expectingNone=expectingNone)
|
||||
|
|
|
@ -7,12 +7,14 @@ Copyright (c) 2006-2012 sqlmap developers (http://www.sqlmap.org/)
|
|||
See the file 'doc/COPYING' for copying permission
|
||||
"""
|
||||
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
|
||||
from extra.safe2bin.safe2bin import safecharencode
|
||||
from lib.core.agent import agent
|
||||
from lib.core.common import Backend
|
||||
from lib.core.common import calculateDeltaSeconds
|
||||
from lib.core.common import dataToStdout
|
||||
from lib.core.common import decodeHexValue
|
||||
from lib.core.common import decodeIntToUnicode
|
||||
|
@ -24,6 +26,7 @@ from lib.core.common import getPartRun
|
|||
from lib.core.common import hashDBRetrieve
|
||||
from lib.core.common import hashDBWrite
|
||||
from lib.core.common import incrementCounter
|
||||
from lib.core.common import randomStr
|
||||
from lib.core.common import safeStringFormat
|
||||
from lib.core.common import setFormatterPrependFlag
|
||||
from lib.core.common import singleTimeWarnMessage
|
||||
|
@ -31,6 +34,7 @@ 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 CHARSET_TYPE
|
||||
from lib.core.enums import DBMS
|
||||
from lib.core.enums import PAYLOAD
|
||||
from lib.core.exception import sqlmapThreadException
|
||||
|
@ -546,3 +550,56 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None
|
|||
|
||||
_ = finalValue or partialValue
|
||||
return getCounter(kb.technique), safecharencode(_) if kb.safeCharEncode else _
|
||||
|
||||
def queryOutputLength(expression, payload):
|
||||
"""
|
||||
Returns the query output length.
|
||||
"""
|
||||
|
||||
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)
|
||||
|
||||
if selectTopExpr or selectDistinctExpr or selectFromExpr or selectExpr:
|
||||
if selectTopExpr:
|
||||
regExpr = selectTopExpr.groups()[0]
|
||||
elif selectDistinctExpr:
|
||||
regExpr = selectDistinctExpr.groups()[0]
|
||||
elif selectFromExpr:
|
||||
regExpr = selectFromExpr.groups()[0]
|
||||
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:
|
||||
lengthExpr = "SELECT %s FROM (%s)" % (lengthQuery % regExpr, expression)
|
||||
|
||||
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
|
||||
lengthExpr += " AS %s" % randomStr(lowercase=True)
|
||||
elif select:
|
||||
lengthExpr = expression.replace(regExpr, lengthQuery % regExpr, 1)
|
||||
else:
|
||||
lengthExpr = lengthQuery % expression
|
||||
|
||||
infoMsg = "retrieving the length of query output"
|
||||
logger.info(infoMsg)
|
||||
|
||||
start = time.time()
|
||||
lengthExprUnescaped = unescaper.unescape(lengthExpr)
|
||||
count, length = bisection(payload, lengthExprUnescaped, charsetType=CHARSET_TYPE.DIGITS)
|
||||
|
||||
debugMsg = "performed %d queries in %d seconds" % (count, calculateDeltaSeconds(start))
|
||||
logger.debug(debugMsg)
|
||||
|
||||
if length == " ":
|
||||
length = 0
|
||||
|
||||
return count, length, regExpr
|
||||
|
|
|
@ -1,188 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id$
|
||||
|
||||
Copyright (c) 2006-2012 sqlmap developers (http://www.sqlmap.org/)
|
||||
See the file 'doc/COPYING' for copying permission
|
||||
"""
|
||||
|
||||
import re
|
||||
import time
|
||||
|
||||
from lib.core.common import calculateDeltaSeconds
|
||||
from lib.core.common import dataToSessionFile
|
||||
from lib.core.common import dataToStdout
|
||||
from lib.core.common import Backend
|
||||
from lib.core.common import safeStringFormat
|
||||
from lib.core.common import randomStr
|
||||
from lib.core.common import replaceNewlineTabs
|
||||
from lib.core.common import restoreDumpMarkedChars
|
||||
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
|
||||
from lib.core.unescaper import unescaper
|
||||
from lib.techniques.blind.inference import bisection
|
||||
|
||||
def queryOutputLength(expression, payload):
|
||||
"""
|
||||
Returns the query output length.
|
||||
"""
|
||||
|
||||
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)
|
||||
|
||||
if selectTopExpr or selectDistinctExpr or selectFromExpr or selectExpr:
|
||||
if selectTopExpr:
|
||||
regExpr = selectTopExpr.groups()[0]
|
||||
elif selectDistinctExpr:
|
||||
regExpr = selectDistinctExpr.groups()[0]
|
||||
elif selectFromExpr:
|
||||
regExpr = selectFromExpr.groups()[0]
|
||||
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:
|
||||
lengthExpr = "SELECT %s FROM (%s)" % (lengthQuery % regExpr, expression)
|
||||
|
||||
if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ):
|
||||
lengthExpr += " AS %s" % randomStr(lowercase=True)
|
||||
elif select:
|
||||
lengthExpr = expression.replace(regExpr, lengthQuery % regExpr, 1)
|
||||
else:
|
||||
lengthExpr = lengthQuery % expression
|
||||
|
||||
infoMsg = "retrieving the length of query output"
|
||||
logger.info(infoMsg)
|
||||
|
||||
start = time.time()
|
||||
lengthExprUnescaped = unescaper.unescape(lengthExpr)
|
||||
count, length = bisection(payload, lengthExprUnescaped, charsetType=CHARSET_TYPE.DIGITS)
|
||||
|
||||
debugMsg = "performed %d queries in %d seconds" % (count, calculateDeltaSeconds(start))
|
||||
logger.debug(debugMsg)
|
||||
|
||||
if length == " ":
|
||||
length = 0
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
try:
|
||||
if "sqlmapfile" in expression or "sqlmapoutput" in expression or conf.freshQueries:
|
||||
return None
|
||||
|
||||
condition = (
|
||||
kb.resumedQueries and conf.url in kb.resumedQueries
|
||||
and expression in kb.resumedQueries[conf.url]
|
||||
)
|
||||
|
||||
if not condition:
|
||||
return None
|
||||
|
||||
resumedValue = kb.resumedQueries[conf.url][expression]
|
||||
|
||||
if not resumedValue:
|
||||
return None
|
||||
|
||||
resumedValue = restoreDumpMarkedChars(resumedValue, True)
|
||||
|
||||
if resumedValue[-1] == "]":
|
||||
resumedValue = resumedValue[:-1]
|
||||
|
||||
infoMsg = "read from file '%s': " % conf.sessionFile
|
||||
|
||||
if "\n" in resumedValue:
|
||||
infoMsg += "%s..." % resumedValue.split("\n")[0]
|
||||
else:
|
||||
infoMsg += resumedValue
|
||||
|
||||
if not kb.suppressResumeInfo:
|
||||
dataToStdout("[%s] [INFO] %s\n" % (time.strftime("%X"), infoMsg))
|
||||
|
||||
return resumedValue
|
||||
|
||||
# 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.
|
||||
if not payload:
|
||||
return None
|
||||
|
||||
if not Backend.getIdentifiedDbms():
|
||||
return None
|
||||
|
||||
substringQuery = queries[Backend.getIdentifiedDbms()].substring.query
|
||||
select = re.search("\ASELECT ", expression, re.I)
|
||||
|
||||
_, length, regExpr = queryOutputLength(expression, payload)
|
||||
|
||||
if not length:
|
||||
return None
|
||||
|
||||
if len(resumedValue) == int(length):
|
||||
infoMsg = "read from file '%s': " % conf.sessionFile
|
||||
infoMsg += "%s" % resumedValue.split("\n")[0]
|
||||
logger.info(infoMsg)
|
||||
|
||||
dataToSessionFile("[%s][%s][%s][%s][%s]\n" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(resumedValue)))
|
||||
|
||||
return resumedValue
|
||||
elif len(resumedValue) < int(length):
|
||||
infoMsg = "resumed from file '%s': " % conf.sessionFile
|
||||
infoMsg += "%s..." % resumedValue.split("\n")[0]
|
||||
logger.info(infoMsg)
|
||||
|
||||
dataToSessionFile("[%s][%s][%s][%s][%s" % (conf.url, kb.injection.place, conf.parameters[kb.injection.place], expression, replaceNewlineTabs(resumedValue)))
|
||||
|
||||
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)))
|
||||
|
||||
missingCharsLength = int(length) - len(resumedValue)
|
||||
|
||||
infoMsg = "retrieving pending %d query " % missingCharsLength
|
||||
infoMsg += "output characters"
|
||||
logger.info(infoMsg)
|
||||
|
||||
start = time.time()
|
||||
count, finalValue = bisection(payload, newExpr, length=missingCharsLength)
|
||||
|
||||
debugMsg = "performed %d queries in %d seconds" % (count, calculateDeltaSeconds(start))
|
||||
logger.debug(debugMsg)
|
||||
|
||||
if len(finalValue) != ( int(length) - len(resumedValue) ):
|
||||
warnMsg = "the total length of the query is not "
|
||||
warnMsg += "right, sqlmap is going to retrieve the "
|
||||
warnMsg += "query value from the beginning now"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
return None
|
||||
|
||||
return "%s%s" % (resumedValue, finalValue)
|
||||
|
||||
return None
|
||||
except ValueError:
|
||||
errMsg = "invalid resume value for expression: '%s'" % expression
|
||||
logger.error(errMsg)
|
||||
return None
|
Loading…
Reference in New Issue
Block a user