From b269e8418fbea74cb47a4b5240411e801eef107b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 15 Dec 2015 10:46:37 +0100 Subject: [PATCH 01/13] Fixes #1608 --- lib/core/common.py | 4 ++-- plugins/dbms/mysql/takeover.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index 77fc7bfa4..0159c53a0 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -1738,7 +1738,7 @@ def posixToNtSlashes(filepath): 'C:\\\\Windows' """ - return filepath.replace('/', '\\') + return filepath.replace('/', '\\') if filepath else filepath def ntToPosixSlashes(filepath): """ @@ -1749,7 +1749,7 @@ def ntToPosixSlashes(filepath): 'C:/Windows' """ - return filepath.replace('\\', '/') + return filepath.replace('\\', '/') if filepath else filepath def isHexEncodedString(subject): """ diff --git a/plugins/dbms/mysql/takeover.py b/plugins/dbms/mysql/takeover.py index ebd7bdc03..9c02c7a68 100644 --- a/plugins/dbms/mysql/takeover.py +++ b/plugins/dbms/mysql/takeover.py @@ -60,7 +60,7 @@ class Takeover(GenericTakeover): else: self.__plugindir = "%s/lib/mysql/plugin" % self.__basedir - self.__plugindir = ntToPosixSlashes(normalizePath(self.__plugindir)) + self.__plugindir = ntToPosixSlashes(normalizePath(self.__plugindir)) or '.' self.udfRemoteFile = "%s/%s.%s" % (self.__plugindir, self.udfSharedLibName, self.udfSharedLibExt) @@ -74,7 +74,7 @@ class Takeover(GenericTakeover): # NOTE: specifying the relative path as './udf.dll' # saves in @@datadir on both MySQL 4.1 and MySQL 5.0 - self.__datadir = "." + self.__datadir = '.' self.__datadir = ntToPosixSlashes(normalizePath(self.__datadir)) # The DLL can be in either C:\WINDOWS, C:\WINDOWS\system, From d7d786d3b548a6a2a0df9ab1b753326fe0d0e367 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 15 Dec 2015 11:29:37 +0100 Subject: [PATCH 02/13] Fixes #1607 --- lib/request/connect.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/request/connect.py b/lib/request/connect.py index 93414961e..ccb3588d1 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -456,6 +456,9 @@ class Connect(object): for cookie in conf.cj: if cookie.value is None: cookie.value = "" + else: + for char in (r"\r", r"\n"): + cookie.value = re.sub(r"(%s)([^ \t])" % char, r"\g<1>\t\g<2>", cookie.value) conn = urllib2.urlopen(req) From aee47d32c5b21ac71483a558f9bff520ec04947a Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 15 Dec 2015 12:13:03 +0100 Subject: [PATCH 03/13] Patch for #1601 --- 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 0159c53a0..ff981fc77 100755 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -86,6 +86,7 @@ from lib.core.exception import SqlmapSilentQuitException from lib.core.exception import SqlmapSyntaxException from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapUserQuitException +from lib.core.exception import SqlmapValueException from lib.core.log import LOGGER_HANDLER from lib.core.optiondict import optDict from lib.core.settings import BANNER @@ -1638,7 +1639,9 @@ def safeStringFormat(format_, params): 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") + warnMsg = "wrong number of parameters during string formatting. " + warnMsg += "Please report by e-mail content \"%r | %r | %r\" to 'dev@sqlmap.org'" % (format_, params, retVal) + raise SqlmapValueException(warnMsg) 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 From e4ed1c058b3f69619e72aee519d880495f163711 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 18 Dec 2015 17:15:59 +0100 Subject: [PATCH 04/13] Minor error message improvement (SSL issues) --- lib/request/httpshandler.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/request/httpshandler.py b/lib/request/httpshandler.py index 5cf0613ca..9ada558d2 100644 --- a/lib/request/httpshandler.py +++ b/lib/request/httpshandler.py @@ -5,6 +5,7 @@ Copyright (c) 2006-2015 sqlmap developers (http://sqlmap.org/) See the file 'doc/COPYING' for copying permission """ +import distutils.version import httplib import socket import urllib2 @@ -13,6 +14,7 @@ from lib.core.common import getSafeExString from lib.core.data import kb from lib.core.data import logger from lib.core.exception import SqlmapConnectionException +from lib.core.settings import PYVERSION ssl = None try: @@ -84,7 +86,10 @@ class HTTPSConnection(httplib.HTTPSConnection): logger.debug("SSL connection error occurred ('%s')" % getSafeExString(ex)) if not success: - raise SqlmapConnectionException("can't establish SSL connection") + errMsg = "can't establish SSL connection" + if distutils.version.LooseVersion(PYVERSION) < distutils.version.LooseVersion("2.7.10"): + errMsg += " (please retry with Python >= 2.7.10)" + raise SqlmapConnectionException(errMsg) class HTTPSHandler(urllib2.HTTPSHandler): def https_open(self, req): From 7ed7497fdaf8289f3a30006d25cb8e192365e51d Mon Sep 17 00:00:00 2001 From: getcode2git Date: Sat, 19 Dec 2015 15:39:14 +0800 Subject: [PATCH 05/13] update safedog.py update some feature --- waf/safedog.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/waf/safedog.py b/waf/safedog.py index 31b706e18..f4bc339ec 100644 --- a/waf/safedog.py +++ b/waf/safedog.py @@ -7,6 +7,7 @@ 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__ = "Safedog Web Application Firewall (Safedog)" @@ -17,6 +18,8 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) retval = re.search(r"WAF/2.0", headers.get("X-Powered-By", ""), re.I) is not None + retval |= re.search(r"Safedog", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + retval |= re.search(r"safedog", headers.get("Set-Cookie", ""), re.I) is not None if retval: break From eb79b0aae89beaf66282dc359b8a1f2eafd11eeb Mon Sep 17 00:00:00 2001 From: getcode2git Date: Sat, 19 Dec 2015 15:41:25 +0800 Subject: [PATCH 06/13] Create safe3.py Safe3 Web Application Firewall --- waf/safe3.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 waf/safe3.py diff --git a/waf/safe3.py b/waf/safe3.py new file mode 100644 index 000000000..d1aaacfc5 --- /dev/null +++ b/waf/safe3.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2015 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__ = "Safe3 Web Application Firewall" + +def detect(get_page): + retval = False + + for vector in WAF_ATTACK_VECTORS: + page, headers, code = get_page(get=vector) + retval = re.search(r"Safe3WAF", headers.get("X-Powered-By", ""), re.I) is not None + retval |= re.search(r"Safe3 Web Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None + if retval: + break + + return retval + From 89e0fc8ffa77bcb5d495751f266c8b1d72305c17 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 19 Dec 2015 17:50:12 +0100 Subject: [PATCH 07/13] Minor update --- lib/core/enums.py | 1 + waf/safe3.py | 2 +- waf/safedog.py | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/enums.py b/lib/core/enums.py index 785daf95e..64e074ca2 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -177,6 +177,7 @@ class HTTP_HEADER: TRANSFER_ENCODING = "Transfer-Encoding" URI = "URI" VIA = "Via" + X_POWERED_BY = "X-Powered-By" class EXPECTED: BOOL = "bool" diff --git a/waf/safe3.py b/waf/safe3.py index d1aaacfc5..70db14140 100644 --- a/waf/safe3.py +++ b/waf/safe3.py @@ -17,7 +17,7 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = re.search(r"Safe3WAF", headers.get("X-Powered-By", ""), re.I) is not None + retval = re.search(r"Safe3WAF", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None retval |= re.search(r"Safe3 Web Firewall", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None if retval: break diff --git a/waf/safedog.py b/waf/safedog.py index f4bc339ec..8d11c511c 100644 --- a/waf/safedog.py +++ b/waf/safedog.py @@ -17,9 +17,9 @@ def detect(get_page): for vector in WAF_ATTACK_VECTORS: page, headers, code = get_page(get=vector) - retval = re.search(r"WAF/2.0", headers.get("X-Powered-By", ""), re.I) is not None + retval = re.search(r"WAF/2\.0", headers.get(HTTP_HEADER.X_POWERED_BY, ""), re.I) is not None retval |= re.search(r"Safedog", headers.get(HTTP_HEADER.SERVER, ""), re.I) is not None - retval |= re.search(r"safedog", headers.get("Set-Cookie", ""), re.I) is not None + retval |= re.search(r"safedog", headers.get(HTTP_HEADER.SET_COOKIE, ""), re.I) is not None if retval: break From ae7481081e74a7ed8ff99dd2d709b276afccd2f1 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Sat, 19 Dec 2015 23:45:10 +0100 Subject: [PATCH 08/13] Patch for an Issue reported via email --- lib/request/basic.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/request/basic.py b/lib/request/basic.py index 21ecd2a34..e87bce1ee 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -156,6 +156,8 @@ def checkCharEncoding(encoding, warn=True): if delimiter in encoding: encoding = encoding[:encoding.find(delimiter)].strip() + encoding = encoding.replace(""", "") + # popular typos/errors if "8858" in encoding: encoding = encoding.replace("8858", "8859") # iso-8858 -> iso-8859 @@ -189,6 +191,8 @@ def checkCharEncoding(encoding, warn=True): encoding = "ascii" elif encoding.find("utf8") > 0: encoding = "utf8" + elif encoding.find("utf-8") > 0: + encoding = "utf-8" # Reference: http://philip.html5.org/data/charsets-2.html if encoding in translate: From 7411ff93e561eea8c07d4ec3bc7acd5cdabf0431 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 23 Dec 2015 08:14:18 +0100 Subject: [PATCH 09/13] Minor update related to the #1620 --- xml/errors.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/xml/errors.xml b/xml/errors.xml index eb4670fd9..a06f67a79 100644 --- a/xml/errors.xml +++ b/xml/errors.xml @@ -17,6 +17,7 @@ + From 3454e356f931eb049357158e5197a06d17422ab2 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Wed, 23 Dec 2015 08:55:45 +0100 Subject: [PATCH 10/13] Fixes #1621 --- lib/parse/payloads.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/parse/payloads.py b/lib/parse/payloads.py index 89e077098..24e481b12 100644 --- a/lib/parse/payloads.py +++ b/lib/parse/payloads.py @@ -20,13 +20,13 @@ def cleanupVals(text, tag): text = text.split(',') if isinstance(text, basestring): - text = int(text) if text.isdigit() else str(text) + text = int(text) if text.isdigit() else text elif isinstance(text, list): count = 0 for _ in text: - text[count] = int(_) if _.isdigit() else str(_) + text[count] = int(_) if _.isdigit() else _ count += 1 if len(text) == 1 and tag not in ("clause", "where"): From 24d95ab6b3b9a00b15f48da5950443e223ef8468 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Thu, 24 Dec 2015 10:34:42 +0100 Subject: [PATCH 11/13] Fixes #1624 --- 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 e87bce1ee..d5bcc96d4 100755 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -103,7 +103,7 @@ def forgeHeaders(items=None): kb.mergeCookies = not _ or _[0] in ("y", "Y") if kb.mergeCookies and kb.injection.place != PLACE.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) + _ = 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))).replace('\\', r'\\'), x) headers[HTTP_HEADER.COOKIE] = _(headers[HTTP_HEADER.COOKIE]) if PLACE.COOKIE in conf.parameters: From 849babaf8d58f60d2c6d0f9c1db9e0c3071d2a2b Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 28 Dec 2015 11:39:46 +0100 Subject: [PATCH 12/13] Minor patch for too fast Ctrl-C(-ers) --- sqlmap.py | 70 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/sqlmap.py b/sqlmap.py index fdbc7afa1..af95008e9 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -111,7 +111,10 @@ def main(): except SqlmapUserQuitException: errMsg = "user quit" - logger.error(errMsg) + try: + logger.error(errMsg) + except KeyboardInterrupt: + pass except (SqlmapSilentQuitException, bdb.BdbQuit): pass @@ -121,18 +124,30 @@ def main(): except SqlmapBaseException as ex: errMsg = getSafeExString(ex) - logger.critical(errMsg) + try: + logger.critical(errMsg) + except KeyboardInterrupt: + pass + raise SystemExit except KeyboardInterrupt: print + errMsg = "user aborted" - logger.error(errMsg) + try: + logger.error(errMsg) + except KeyboardInterrupt: + pass except EOFError: print errMsg = "exit" - logger.error(errMsg) + + try: + logger.error(errMsg) + except KeyboardInterrupt: + pass except SystemExit: pass @@ -142,32 +157,35 @@ def main(): errMsg = unhandledExceptionMessage() excMsg = traceback.format_exc() - if any(_ in excMsg for _ in ("No space left", "Disk quota exceeded")): - errMsg = "no space left on output device" - logger.error(errMsg) - raise SystemExit + try: + if any(_ in excMsg for _ in ("No space left", "Disk quota exceeded")): + errMsg = "no space left on output device" + logger.error(errMsg) + raise SystemExit - elif "bad marshal data (unknown type code)" in excMsg: - match = re.search(r"\s*(.+)\s+ValueError", excMsg) - errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "") - errMsg += ". Please delete .pyc files on your system to fix the problem" - logger.error(errMsg) - raise SystemExit + elif "bad marshal data (unknown type code)" in excMsg: + match = re.search(r"\s*(.+)\s+ValueError", excMsg) + errMsg = "one of your .pyc files are corrupted%s" % (" ('%s')" % match.group(1) if match else "") + errMsg += ". Please delete .pyc files on your system to fix the problem" + logger.error(errMsg) + raise SystemExit - for match in re.finditer(r'File "(.+?)", line', excMsg): - 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_) + for match in re.finditer(r'File "(.+?)", line', excMsg): + 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_) - errMsg = maskSensitiveData(errMsg) - excMsg = maskSensitiveData(excMsg) + errMsg = maskSensitiveData(errMsg) + excMsg = maskSensitiveData(excMsg) - logger.critical(errMsg) - kb.stickyLevel = logging.CRITICAL - dataToStdout(excMsg) - createGithubIssue(errMsg, excMsg) + logger.critical(errMsg) + kb.stickyLevel = logging.CRITICAL + dataToStdout(excMsg) + createGithubIssue(errMsg, excMsg) + except KeyboardInterrupt: + pass finally: if conf.get("showTime"): From fc5802f461958a2387a035c3b5d2d7246d56ea9c Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Tue, 29 Dec 2015 13:19:25 +0100 Subject: [PATCH 13/13] Fixes #1628 --- plugins/dbms/maxdb/enumeration.py | 60 +++++++++++++++++++++++++++++- plugins/dbms/sybase/enumeration.py | 45 +++++++++++++++++++++- plugins/generic/databases.py | 2 +- 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/plugins/dbms/maxdb/enumeration.py b/plugins/dbms/maxdb/enumeration.py index 95ec6a385..7b9d1d20a 100644 --- a/plugins/dbms/maxdb/enumeration.py +++ b/plugins/dbms/maxdb/enumeration.py @@ -7,16 +7,20 @@ See the file 'doc/COPYING' for copying permission from lib.core.common import Backend from lib.core.common import randomStr +from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.data import paths from lib.core.data import queries from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapNoneDataException +from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB from lib.utils.pivotdumptable import pivotDumpTable +from lib.techniques.brute.use import columnExists from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): @@ -91,7 +95,7 @@ class Enumeration(GenericEnumeration): return kb.data.cachedTables - def getColumns(self, onlyColNames=False): + def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: @@ -111,6 +115,17 @@ class Enumeration(GenericEnumeration): conf.db = safeSQLIdentificatorNaming(conf.db) + if conf.col: + 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) + if conf.tbl: tblList = conf.tbl.split(",") else: @@ -129,6 +144,43 @@ class Enumeration(GenericEnumeration): for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) + if bruteForce: + resumeAvailable = False + + for tbl in tblList: + for db, table, colName, colType in kb.brute.columns: + if db == conf.db and table == tbl: + resumeAvailable = True + break + + if resumeAvailable and not conf.freshQueries or colList: + columns = {} + + for column in colList: + columns[column] = None + + for tbl in tblList: + for db, table, colName, colType in kb.brute.columns: + if db == conf.db and table == tbl: + columns[colName] = colType + + if conf.db in kb.data.cachedColumns: + kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns + else: + kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): columns} + + return kb.data.cachedColumns + + message = "do you want to use common column existence check? [y/N/q] " + test = readInput(message, default="Y" if "Y" in message else "N") + + if test[0] in ("n", "N"): + return + elif test[0] in ("q", "Q"): + raise SqlmapUserQuitException + else: + return columnExists(paths.COMMON_COLUMNS) + rootQuery = queries[Backend.getIdentifiedDbms()].columns for tbl in tblList: @@ -141,6 +193,12 @@ class Enumeration(GenericEnumeration): return {conf.db: kb.data.cachedColumns[conf.db]} + if dumpMode and colList: + table = {} + table[safeSQLIdentificatorNaming(tbl)] = dict((_, None) for _ in colList) + kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table + continue + infoMsg = "fetching columns " infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming(tbl) infoMsg += "on database '%s'" % unsafeSQLIdentificatorNaming(conf.db) diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index 09a0356af..e0707d9b4 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -9,19 +9,23 @@ from lib.core.common import Backend from lib.core.common import filterPairValues from lib.core.common import isTechniqueAvailable from lib.core.common import randomStr +from lib.core.common import readInput from lib.core.common import safeSQLIdentificatorNaming from lib.core.common import unArrayizeValue from lib.core.common import unsafeSQLIdentificatorNaming from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger +from lib.core.data import paths from lib.core.data import queries from lib.core.dicts import SYBASE_TYPES from lib.core.enums import PAYLOAD from lib.core.exception import SqlmapMissingMandatoryOptionException from lib.core.exception import SqlmapNoneDataException +from lib.core.exception import SqlmapUserQuitException from lib.core.settings import CURRENT_DB from lib.utils.pivotdumptable import pivotDumpTable +from lib.techniques.brute.use import columnExists from plugins.generic.enumeration import Enumeration as GenericEnumeration class Enumeration(GenericEnumeration): @@ -159,7 +163,7 @@ class Enumeration(GenericEnumeration): return kb.data.cachedTables - def getColumns(self, onlyColNames=False): + def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: @@ -208,6 +212,43 @@ class Enumeration(GenericEnumeration): for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl) + if bruteForce: + resumeAvailable = False + + for tbl in tblList: + for db, table, colName, colType in kb.brute.columns: + if db == conf.db and table == tbl: + resumeAvailable = True + break + + if resumeAvailable and not conf.freshQueries or colList: + columns = {} + + for column in colList: + columns[column] = None + + for tbl in tblList: + for db, table, colName, colType in kb.brute.columns: + if db == conf.db and table == tbl: + columns[colName] = colType + + if conf.db in kb.data.cachedColumns: + kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)] = columns + else: + kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = {safeSQLIdentificatorNaming(tbl, True): columns} + + return kb.data.cachedColumns + + message = "do you want to use common column existence check? [y/N/q] " + test = readInput(message, default="Y" if "Y" in message else "N") + + if test[0] in ("n", "N"): + return + elif test[0] in ("q", "Q"): + raise SqlmapUserQuitException + else: + return columnExists(paths.COMMON_COLUMNS) + rootQuery = queries[Backend.getIdentifiedDbms()].columns if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: @@ -225,7 +266,7 @@ class Enumeration(GenericEnumeration): return {conf.db: kb.data.cachedColumns[conf.db]} - if colList: + if dumpMode and colList: table = {} table[safeSQLIdentificatorNaming(tbl)] = dict((_, None) for _ in colList) kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] = table diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py index 8070fc0ad..23e0b4cf6 100644 --- a/plugins/generic/databases.py +++ b/plugins/generic/databases.py @@ -238,7 +238,7 @@ class Databases: return kb.data.cachedTables - message = "do you want to use common table existence check? %s" % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") + message = "do you want to use common table existence check? %s " % ("[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS,) else "[y/N/q]") test = readInput(message, default="Y" if "Y" in message else "N") if test[0] in ("n", "N"):