diff --git a/lib/core/agent.py b/lib/core/agent.py index 73a121c00..d802f4c97 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -45,6 +45,7 @@ from lib.core.exception import SqlmapNoneDataException from lib.core.settings import BOUNDED_BASE64_MARKER from lib.core.settings import BOUNDARY_BACKSLASH_MARKER from lib.core.settings import BOUNDED_INJECTION_MARKER +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 @@ -890,11 +891,16 @@ class Agent(object): if element > 0: unionQuery += ',' - if element == position: + if conf.uValues: + unionQuery += conf.uValues.split(',')[element] + elif element == position: unionQuery += query else: unionQuery += char + if conf.uValues: + unionQuery = unionQuery.replace(CUSTOM_INJECTION_MARK_CHAR, query) + if fromTable and not unionQuery.endswith(fromTable): unionQuery += fromTable diff --git a/lib/core/option.py b/lib/core/option.py index 63cfb6d3f..951399fc0 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1801,6 +1801,9 @@ def _cleanupOptions(): conf.dbms = dbms if conf.dbms and ',' not in conf.dbms else None break + if conf.uValues: + conf.uCols = "%d-%d" % (1 + conf.uValues.count(','), 1 + conf.uValues.count(',')) + if conf.testFilter: conf.testFilter = conf.testFilter.strip('*+') conf.testFilter = re.sub(r"([^.])([*+])", r"\g<1>.\g<2>", conf.testFilter) @@ -2582,6 +2585,10 @@ def _basicOptionValidation(): errMsg = "switch '--text-only' is incompatible with switch '--null-connection'" raise SqlmapSyntaxException(errMsg) + if conf.uValues and conf.uChar: + errMsg = "option '--union-values' is incompatible with option '--union-char'" + raise SqlmapSyntaxException(errMsg) + if conf.base64Parameter and conf.tamper: errMsg = "option '--base64' is incompatible with option '--tamper'" raise SqlmapSyntaxException(errMsg) @@ -2804,6 +2811,11 @@ def _basicOptionValidation(): errMsg = "option '--dump-format' accepts one of following values: %s" % ", ".join(getPublicTypeMembers(DUMP_FORMAT, True)) raise SqlmapSyntaxException(errMsg) + if conf.uValues and (not re.search(r"\A['\w\s.,()%s-]+\Z" % CUSTOM_INJECTION_MARK_CHAR, conf.uValues) or conf.uValues.count(CUSTOM_INJECTION_MARK_CHAR) != 1): + errMsg = "option '--union-values' must contain valid UNION column values, along with the injection position " + errMsg += "(e.g. 'NULL,1,%s,NULL')" % CUSTOM_INJECTION_MARK_CHAR + raise SqlmapSyntaxException(errMsg) + if conf.skip and conf.testParameter: if intersect(conf.skip, conf.testParameter): errMsg = "option '--skip' is incompatible with option '-p'" diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 761ee9955..d41e85b5e 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -118,6 +118,7 @@ optDict = { "uCols": "string", "uChar": "string", "uFrom": "string", + "uValues": "string", "dnsDomain": "string", "secondUrl": "string", "secondReq": "string", diff --git a/lib/core/settings.py b/lib/core/settings.py index 2bbf50715..a0f5f759c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from thirdparty import six from thirdparty.six import unichr as _unichr # sqlmap version (...) -VERSION = "1.7.8.11" +VERSION = "1.7.9.0" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index b1074166c..95493b9b9 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -414,6 +414,9 @@ def cmdLineParser(argv=None): techniques.add_argument("--union-from", dest="uFrom", help="Table to use in FROM part of UNION query SQL injection") + techniques.add_argument("--union-values", dest="uValues", + help="Column values to use for UNION query SQL injection") + techniques.add_argument("--dns-domain", dest="dnsDomain", help="Domain name used for DNS exfiltration attack") diff --git a/lib/techniques/union/test.py b/lib/techniques/union/test.py index c7a3f5948..91ffa13b8 100644 --- a/lib/techniques/union/test.py +++ b/lib/techniques/union/test.py @@ -340,7 +340,7 @@ def _unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix) warnMsg = "if UNION based SQL injection is not detected, " warnMsg += "please consider " - if not conf.uChar and count > 1 and kb.uChar == NULL: + if not conf.uChar and count > 1 and kb.uChar == NULL and conf.uValues is None: message = "injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] " if not readInput(message, default='Y', boolean=True): diff --git a/sqlmap.conf b/sqlmap.conf index 895b60115..df95a9e7f 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -412,6 +412,11 @@ uChar = # Example: INFORMATION_SCHEMA.COLLATIONS uFrom = +# Column values to use for UNION query SQL injection. +# Valid: string +# Example: NULL,1,*,NULL +uChar = + # Domain name used for DNS exfiltration attack. # Valid: string dnsDomain =