diff --git a/lib/controller/action.py b/lib/controller/action.py index 5991fc282..c55e244ea 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -17,7 +17,6 @@ from lib.core.exception import sqlmapUnsupportedDBMSException from lib.core.settings import SUPPORTED_DBMS from lib.techniques.brute.use import columnExists from lib.techniques.brute.use import tableExists -from lib.techniques.inband.union.test import unionTest def action(): """ @@ -56,10 +55,6 @@ def action(): dataToStdout("%s\n" % conf.dbmsHandler.getFingerprint()) - # Techniques options - if conf.unionTest and kb.unionPosition is None: - conf.dumper.technic("inband injection payload", unionTest()) - # Enumeration options if conf.getBanner: conf.dumper.banner(conf.dbmsHandler.getBanner()) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index d0dc92b10..3aba7c579 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -235,7 +235,6 @@ def checkSqlInjection(place, parameter, value): # default) value # Parse boundary's if boundary.level > conf.level: - # NOTE: shall we report every single skipped boundary too? continue # Skip boundary if it does not match against test's @@ -377,9 +376,7 @@ def checkSqlInjection(place, parameter, value): # In case of UNION query SQL injection elif method == PAYLOAD.METHOD.UNION: - conf.uChar = test.request.char - conf.uCols = test.request.columns - configUnion() + configUnion(test.request.char, test.request.columns) reqPayload, unionVector = unionTest(comment, place, parameter, value, prefix, suffix) diff --git a/lib/core/common.py b/lib/core/common.py index dfeade11f..900bfe6ee 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -2066,34 +2066,37 @@ def openFile(filename, mode='r'): errMsg += "and that it's not locked by another process." raise sqlmapFilePathException, errMsg -def configUnion(): +def __configUnionChar(char): + if char.isdigit() or char == "NULL": + conf.uChar = char + elif not char.startswith("'") or not char.endswith("'"): + conf.uChar = "'%s'" % char + +def __configUnionCols(columns): + if "-" not in columns or len(columns.split("-")) != 2: + raise sqlmapSyntaxException, "--union-cols must be a range with hyphon (e.g. 1-10)" + + columns = columns.replace(" ", "") + conf.uColsStart, conf.uColsStop = columns.split("-") + + if not conf.uColsStart.isdigit() or not conf.uColsStop.isdigit(): + raise sqlmapSyntaxException, "--union-cols must be a range of integers" + + conf.uColsStart = int(conf.uColsStart) + conf.uColsStop = int(conf.uColsStop) + + if conf.uColsStart > conf.uColsStop: + errMsg = "--union-cols range has to be from lower to " + errMsg += "higher number of columns" + raise sqlmapSyntaxException, errMsg + +def configUnion(char, columns): + if isinstance(conf.uChar, basestring): + __configUnionChar(conf.uChar) + elif isinstance(char, basestring): + __configUnionChar(char) + if isinstance(conf.uCols, basestring): - debugMsg = "setting the UNION query SQL injection range of columns" - logger.debug(debugMsg) - - if "-" not in conf.uCols or len(conf.uCols.split("-")) != 2: - raise sqlmapSyntaxException, "--union-cols must be a range with hyphon (e.g. 1-10)" - - conf.uCols = conf.uCols.replace(" ", "") - conf.uColsStart, conf.uColsStop = conf.uCols.split("-") - - if not conf.uColsStart.isdigit() or not conf.uColsStop.isdigit(): - raise sqlmapSyntaxException, "--union-cols must be a range of integers" - - conf.uColsStart = int(conf.uColsStart) - conf.uColsStop = int(conf.uColsStop) - - if conf.uColsStart > conf.uColsStop: - errMsg = "--union-cols range has to be from lower to " - errMsg += "higher number of columns" - raise sqlmapSyntaxException, errMsg - - if isinstance(conf.uChar, basestring) and conf.uChar != "NULL": - debugMsg = "setting the UNION query SQL injection character to '%s'" % conf.uChar - logger.debug(debugMsg) - - if not conf.uChar.isdigit() and ( not conf.uChar.startswith("'") or not conf.uChar.endswith("'") ): - debugMsg = "forcing the UNION query SQL injection character to '%s'" % conf.uChar - logger.debug(debugMsg) - - conf.uChar = "'%s'" % conf.uChar + __configUnionCols(conf.uCols) + elif isinstance(columns, basestring): + __configUnionCols(columns) diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 8951204d5..5dfba3c54 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -76,7 +76,6 @@ optDict = { "Techniques": { "timeSec": "integer", - "unionTest": "boolean", "uCols": "string", "uChar": "string" }, diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index fa5c5510d..760ad9293 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -231,14 +231,10 @@ def cmdLineParser(): help="Seconds to delay the DBMS response " "(default 5)") - techniques.add_option("--union-test", dest="unionTest", - action="store_true", default=False, - help="Test for and use UNION query (inband) SQL injection") - techniques.add_option("--union-cols", dest="uCols", help="Range of columns to test for UNION query SQL injection") - techniques.add_option("--union-char", dest="uChar", default="NULL", + techniques.add_option("--union-char", dest="uChar", help="Character to use to bruteforce number of columns") # Fingerprint options diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index 3c1cd496f..8dfd14309 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -81,17 +81,11 @@ def __unionConfirm(comment, place, parameter, value, prefix, suffix, count): # Confirm the inband SQL injection and get the exact column # position which can be used to extract data if not isinstance(kb.unionPosition, int): - debugMsg = "testing full inband with %s columns" % count - logger.debug(debugMsg) - validPayload, unionVector = __unionPosition(comment, place, parameter, value, prefix, suffix, count) # Assure that the above function found the exploitable full inband # SQL injection position if not isinstance(kb.unionPosition, int): - debugMsg = "testing single-entry inband with %s columns" % count - logger.debug(debugMsg) - validPayload, unionVector = __unionPosition(comment, place, parameter, value, prefix, suffix, count, where=2) # Assure that the above function found the exploitable partial @@ -125,11 +119,9 @@ def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix if kb.dbms == DBMS.ORACLE: query += " FROM DUAL" - if conf.verbose in (1, 2): - status = '%d/%d (%d%s)' % (count, conf.uColsStop, round(100.0*count/conf.uColsStop), '%') - dataToStdout("\r[%s] [INFO] number of columns: %s" % (time.strftime("%X"), status), True) - - dataToStdout("\n") + status = '%d/%d (%d%s)' % (count, conf.uColsStop, round(100.0*count/conf.uColsStop), '%') + debugMsg = "testing number of columns: %s" % status + logger.debug(debugMsg) validPayload, unionVector = __unionConfirm(comment, place, parameter, value, prefix, suffix, count) @@ -152,12 +144,6 @@ def unionTest(comment, place, parameter, value, prefix, suffix): oldTechnique = kb.technique kb.technique = PAYLOAD.TECHNIQUE.UNION - - if conf.uChar == "NULL": - technique = "NULL bruteforcing" - else: - technique = "char (%s) bruteforcing" % conf.uChar - validPayload, unionVector = __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) if validPayload: diff --git a/sqlmap.conf b/sqlmap.conf index 36ef110e9..59bd37916 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -251,19 +251,15 @@ longestCommon = False # Default: 5 timeSec = 5 -# Test for and use UNION query (inband) SQL injection. -# Valid: True or False -unionTest = False - # Range of columns to test for # Valid: range of integers -# Default: 1-20 -uCols = 1-20 +# Example: 1-10 +uCols = # Character to use to bruteforce number of columns # Valid: string -# Default: NULL -uChar = NULL +# Example: NULL +uChar = [Fingerprint] diff --git a/xml/payloads.xml b/xml/payloads.xml index fe8d10108..e394629e5 100644 --- a/xml/payloads.xml +++ b/xml/payloads.xml @@ -1856,15 +1856,38 @@ Formats: + - MySQL NULL UNION query - 4 to 7 columns + MySQL NULL UNION query - 1 to 3 columns 3 1 1 1,2,3,4,5 1 [UNION] + + + # + NULL + 1-3 + + + + +
+ MySQL +
+
+ + + MySQL NULL UNION query - 4 to 7 columns + 3 + 2 + 1 + 1,2,3,4,5 + 1 + [UNION] # @@ -1879,6 +1902,72 @@ Formats: + + MySQL NULL UNION query - 8 to 12 columns + 3 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + NULL + 8-12 + + + + +
+ MySQL +
+
+ + + MySQL NULL UNION query - 13 to 18 columns + 3 + 4 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + NULL + 13-18 + + + + +
+ MySQL +
+
+ + + MySQL NULL UNION query - 19 to 25 columns + 3 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + # + NULL + 19-25 + + + + +
+ MySQL +
+
+ Generic NULL UNION query - 1 to 3 columns 3 @@ -1897,6 +1986,82 @@ Formats: + + + Generic NULL UNION query - 4 to 7 columns + 3 + 2 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + NULL + 4-7 + + + + + + + + Generic NULL UNION query - 8 to 12 columns + 3 + 3 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + NULL + 8-12 + + + + + + + + Generic NULL UNION query - 13 to 18 columns + 3 + 4 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + NULL + 13-18 + + + + + + + + Generic NULL UNION query - 19 to 25 columns + 3 + 5 + 1 + 1,2,3,4,5 + 1 + [UNION] + + + -- + NULL + 19-25 + + + + +