diff --git a/lib/core/common.py b/lib/core/common.py index bd1756c79..521a7c0fa 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1086,6 +1086,40 @@ def isBase64EncodedString(subject): def isHexEncodedString(subject): return re.match(r"\A[0-9a-fA-F]+\Z", subject) is not None +def profile(profileOutputFile='sqlmap.profile', imageOutputFile='profile.png'): + import cProfile + cProfile.run("start()", profileOutputFile) + + graphScript = 'gprof2dot.py' + graphScriptRepositoryUrl = 'http://gprof2dot.jrfonseca.googlecode.com/hg/' + graphScriptPath = os.path.join(paths.SQLMAP_ROOT_PATH, graphScript) + if not os.path.exists(graphScriptPath): + errMsg = "unable to find Jose Fonseca's '%s' graph " % graphScript + errMsg += "conversion script. please download it from " + errMsg += "official repository at '%s' " % graphScriptRepositoryUrl + errMsg += "and put it inside sqlmap's root directory ('%s')." % paths.SQLMAP_ROOT_PATH + logger.error(errMsg) + return + + infoMsg = "converting profile data to an image." + logger.info(infoMsg) + + if os.path.exists(imageOutputFile): + os.remove(imageOutputFile) + + msg = subprocess.Popen('python %s -f pstats %s | dot -Tpng -o %s' % (graphScriptPath, profileOutputFile, imageOutputFile), shell=True, stderr=subprocess.PIPE).stderr.read() + + if msg: + errMsg = "there was an error while converting ('%s')." % msg.strip() + logger.error(errMsg) + else: + if os.name == 'mac': + subprocess.call(('open', imageOutputFile)) + elif os.name == 'posix': + subprocess.call(('xdg-open', imageOutputFile)) + elif os.name == 'nt': + subprocess.call(('start', imageOutputFile)) + def getConsoleWidth(default=80): width = None @@ -1118,3 +1152,51 @@ def parseXmlFile(xmlFile, handler): def calculateDeltaSeconds(start, epsilon=0.05): return int(time.time() - start + epsilon) + +def getCommonPredictionTables(value, originalTable): + if not kb.commonTables: + kb.commonTables = {} + fileName = os.path.join(paths.SQLMAP_TXT_PATH, 'common-tables.txt') + file = open(fileName, 'r') + key = None + for line in file.xreadlines(): + line = line.strip() + if len(line) > 1: + if line[0] == '[' and line[-1] == ']': + key = line[1:-1] + elif key: + if key not in kb.commonTables: + kb.commonTables[key] = [] + kb.commonTables[key].append(line.strip()) + + predictionSet = set() + wildIndexes = [] + + kb.dbms = 'MySQL' + + if value[-1] != '.': + value += '.' + charIndex = 0 + findIndex = value.find('.', charIndex) + while findIndex != -1: + wildIndexes.append(findIndex) + charIndex += 1 + findIndex = value.find('.', charIndex) + if kb.dbms in kb.commonTables: + for item in kb.commonTables[kb.dbms]: + if re.search('\A%s' % value, item): + for index in wildIndexes: + char = item[index] + if char not in predictionSet: + predictionSet.add(char) + predictionTable = [] + otherTable = [] + for ordChar in originalTable: + if chr(ordChar) not in predictionSet: + otherTable.append(ordChar) + else: + predictionTable.append(ordChar) + predictionTable.sort() + return predictionTable, otherTable + else: + return None, originalTable diff --git a/lib/core/option.py b/lib/core/option.py index 019cc2685..c67c57c6c 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -882,7 +882,6 @@ def __setConfAttributes(): debugMsg = "initializing the configuration" logger.debug(debugMsg) - conf.cpuThrottleDelay = 0.001 conf.cj = None conf.dbmsConnector = None conf.dbmsHandler = None @@ -929,6 +928,7 @@ def __setKnowledgeBaseAttributes(): kb.absFilePaths = set() kb.bannerFp = advancedDict() kb.data = advancedDict() + kb.commonTables = None # Basic back-end DBMS fingerprint kb.dbms = None diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 5e27bded4..518503b14 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -27,6 +27,7 @@ import sys from optparse import OptionError from optparse import OptionGroup from optparse import OptionParser +from optparse import SUPPRESS_HELP from lib.core.data import logger from lib.core.settings import VERSION_STRING @@ -415,10 +416,10 @@ def cmdLineParser(): miscellaneous.add_option("-s", dest="sessionFile", help="Save and resume all data retrieved " "on a session file") - + miscellaneous.add_option("--flush-session", dest="flushSession", action="store_true", help="Flush session file for current target") - + miscellaneous.add_option("--eta", dest="eta", action="store_true", help="Display for each output the " "estimated time of arrival") @@ -439,6 +440,16 @@ def cmdLineParser(): help="Clean up the DBMS by sqlmap specific " "UDF and tables") + # Hidden and/or experimental options + parser.add_option("--profile", dest="profile", action="store_true", + help=SUPPRESS_HELP) + + parser.add_option("--cpu-throttle", dest="cpuThrottle", type="int", default=10, + help=SUPPRESS_HELP) + + parser.add_option("--common-prediction", dest="useCommonPrediction", action="store_true", + help=SUPPRESS_HELP) + parser.add_option_group(target) parser.add_option_group(request) parser.add_option_group(injection) diff --git a/lib/request/connect.py b/lib/request/connect.py index 44e1ef7a9..7b60cd6bf 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -260,7 +260,10 @@ class Connect: logger.log(8, responseMsg) - time.sleep(conf.cpuThrottleDelay) + if conf.cpuThrottle: + minThrottleDelay, maxThrottleDelay = 0.0001, 0.1 + delay = minThrottleDelay + (maxThrottleDelay-minThrottleDelay) * conf.cpuThrottle + time.sleep(delay) return page, responseHeaders diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 53120cfff..9ec1c49c2 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -340,7 +340,14 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None while True: index += 1 charStart = time.time() - val = getChar(index, asciiTbl) + + if conf.useCommonPrediction: + commonTbl, otherTbl = getCommonPredictionTables(finalValue, asciiTbl) + val = getChar(index, commonTbl) if commonTbl else None + if not val: + val = getChar(index, otherTbl) + else: + val = getChar(index, asciiTbl) if val is None or ( lastChar > 0 and index > lastChar ): break diff --git a/sqlmap.py b/sqlmap.py index cb1629db8..a0cee99c1 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -39,6 +39,7 @@ except ImportError, _: from lib.controller.controller import start from lib.core.common import banner +from lib.core.common import profile from lib.core.common import setPaths from lib.core.common import weAreFrozen from lib.core.data import conf @@ -75,8 +76,10 @@ def main(): try: init(cmdLineOptions) - start() - + if not conf.profile: + start() + else: + profile() except exceptionsTuple, e: e = str(e) logger.error(e)