From 29b4c12bdbb9224a8c999d17af69b8078f532446 Mon Sep 17 00:00:00 2001 From: Huy Phan Date: Thu, 29 Nov 2012 17:21:56 +0800 Subject: [PATCH 1/7] add seo-delimiter, ignore-false-positive and use-hex-based-string parameters --- lib/parse/cmdline.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index ac1759d4dba..61590ba8a61 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -188,6 +188,9 @@ def cmdLineParser(): "for, provide custom injection payloads and " "optional tampering scripts") + injection.add_option("--seo-delimiter", dest="seoDelimiter", + help="delimiter between parameters (used for SEO URL)") + injection.add_option("-p", dest="testParameter", help="Testable parameter(s)") @@ -226,6 +229,12 @@ def cmdLineParser(): injection.add_option("--tamper", dest="tamper", help="Use given script(s) for tampering injection data") + injection.add_option("--ignore-false-positive", action="store_true", dest="ignoreFalsePositive", + help="ignore checking false positive") + + injection.add_option("--use-hex-based-string", action="store_true", dest="useHexBasedString", + help="use hex-based string") + # Detection options detection = OptionGroup(parser, "Detection", "These options can be " "used to specify how to parse " From 38903f09817fd5256884f0943caec6586a6238de Mon Sep 17 00:00:00 2001 From: Huy Phan Date: Thu, 29 Nov 2012 17:31:21 +0800 Subject: [PATCH 2/7] handle ignoreFalsePositive option --- 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 a1ea264348c..21e4b893fff 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -565,7 +565,9 @@ def genCmpPayload(): warnMsg += "problems during data retrieval" logger.warn(warnMsg) - injection = checkFalsePositives(injection) + if not conf.ignoreFalsePositive: + injection = checkFalsePositives(injection) + else: injection = None From 2c8138a6c101f90c442976cf36670320bf23ce39 Mon Sep 17 00:00:00 2001 From: Huy Phan Date: Thu, 29 Nov 2012 17:34:06 +0800 Subject: [PATCH 3/7] handle useHexBasedString option --- lib/request/inject.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/request/inject.py b/lib/request/inject.py index 2f740b07d10..d7591c91b78 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -369,6 +369,9 @@ def getValue(expression, blind=True, union=True, error=True, time=True, fromUser if conf.hexConvert: charsetType = CHARSET_TYPE.HEXADECIMAL + if conf.useHexBasedString: + expression = expression.replace("'","") + kb.safeCharEncode = safeCharEncode kb.resumeValues = resumeValue From 4dd21afbc8df42d4703515f946807e8f61c5eb4a Mon Sep 17 00:00:00 2001 From: Huy Phan Date: Fri, 30 Nov 2012 13:47:55 +0800 Subject: [PATCH 4/7] Support SEO URL --- lib/controller/controller.py | 20 ++++++--- lib/core/agent.py | 6 ++- lib/core/common.py | 84 ++++++++++++++++++++++++------------ lib/request/connect.py | 8 +++- 4 files changed, 81 insertions(+), 37 deletions(-) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 2a971f38a3e..321a17df902 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -270,12 +270,18 @@ 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 ";", conf.pDel or ";"), conf.parameters[PLACE.GET]): - paramKey = (conf.hostname, conf.path, PLACE.GET, parameter[0]) - - if paramKey not in kb.testedParams: - testSqlInj = True - break + if conf.seoDelimiter: + for count in range(len(conf.parameters[PLACE.GET].split(conf.seoDelimiter))): + if count not in kb.testedParams: + testSqlInj = True + break + else: + for parameter in re.findall(r"([^=]+)=([^%s]+%s?|\Z)" % (conf.pDel or ";", conf.pDel or ";"), conf.parameters[PLACE.GET]): + paramKey = (conf.hostname, conf.path, PLACE.GET, parameter[0]) + + if paramKey not in kb.testedParams: + testSqlInj = True + break else: paramKey = (conf.hostname, conf.path, None, None) if paramKey not in kb.testedParams: @@ -438,7 +444,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 str(parameter).upper() in IGNORE_PARAMETERS: testSqlInj = False infoMsg = "ignoring %s parameter '%s'" % (place, parameter) diff --git a/lib/core/agent.py b/lib/core/agent.py index c70794a4ea4..982f72adee1 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -127,9 +127,13 @@ def payload(self, place=None, parameter=None, value=None, newValue=None, where=N retVal = retVal.replace(CUSTOM_INJECTION_MARK_CHAR, "").replace(ASTERISK_MARKER, CUSTOM_INJECTION_MARK_CHAR) elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST): retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) + elif conf.seoDelimiter: + paramSplit = paramString[1:].split(conf.seoDelimiter) + paramSplit[int(parameter)] = self.addPayloadDelimiters(newValue) + retVal = paramString[0]+conf.seoDelimiter.join(paramSplit) else: retVal = paramString.replace("%s=%s" % (parameter, origValue), - "%s=%s" % (parameter, self.addPayloadDelimiters(newValue))) + "%s=%s" % (parameter, self.addPayloadDelimiters(newValue))) return retVal diff --git a/lib/core/common.py b/lib/core/common.py index c9e62936142..50f09ee1542 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -514,36 +514,55 @@ 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)) - - for element in splitParams: - element = re.sub(r"%s(.+?)%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), r"&\g<1>;", element) - elem = element.split("=") - - if len(elem) >= 2: - parameter = elem[0].replace(" ", "") + if conf.seoDelimiter and place == PLACE.GET: + splitParams = parameters[1:].split(conf.seoDelimiter) + for count in range(len(splitParams)): + parameter = str(count) condition = not conf.testParameter - condition |= parameter in conf.testParameter + condition |= parameter in conf.testParameter if condition: - testableParameters[parameter] = "=".join(elem[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, _): - warnMsg = "it appears that you have provided tainted parameter values " - warnMsg += "('%s') with most probably leftover " % element - warnMsg += "chars from manual SQL injection " - warnMsg += "tests (%s) or non-valid numerical value. " % DUMMY_SQL_INJECTION_CHARS - warnMsg += "Please, always use only valid parameter values " - warnMsg += "so sqlmap could be able to properly run " - logger.warn(warnMsg) - - message = "Are you sure you want to continue? [y/N] " - test = readInput(message, default="N") - if test[0] not in ("y", "Y"): - raise sqlmapSilentQuitException + testableParameters[parameter] = splitParams[count] + if testableParameters[parameter].strip(DUMMY_SQL_INJECTION_CHARS) != testableParameters[parameter]: + errMsg = "you have provided tainted parameter values " + errMsg += "(%s) with most probably leftover " % splitParams[count] + errMsg += "chars from manual sql injection " + errMsg += "tests (%s). " % DUMMY_SQL_INJECTION_CHARS + errMsg += "please, always use only valid parameter values " + errMsg += "so sqlmap could be able to do a valid run." + raise sqlmapSyntaxException, errMsg + else: + splitParams = parameters.split(conf.pDel or (DEFAULT_COOKIE_DELIMITER if place == PLACE.COOKIE else DEFAULT_GET_POST_DELIMITER)) + + for element in splitParams: + element = re.sub(r"%s(.+?)%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), r"&\g<1>;", element) + elem = element.split("=") + + if len(elem) >= 2: + parameter = elem[0].replace(" ", "") + + condition = not conf.testParameter + condition |= parameter in conf.testParameter + + if condition: + testableParameters[parameter] = "=".join(elem[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, _): + warnMsg = "it appears that you have provided tainted parameter values " + warnMsg += "('%s') with most probably leftover " % element + warnMsg += "chars from manual SQL injection " + warnMsg += "tests (%s) or non-valid numerical value. " % DUMMY_SQL_INJECTION_CHARS + warnMsg += "Please, always use only valid parameter values " + warnMsg += "so sqlmap could be able to properly run " + logger.warn(warnMsg) + + message = "Are you sure you want to continue? [y/N] " + test = readInput(message, default="N") + if test[0] not in ("y", "Y"): + raise sqlmapSilentQuitException if conf.testParameter and not testableParameters: paramStr = ", ".join(test for test in conf.testParameter) @@ -1066,7 +1085,12 @@ def parseTargetUrl(): hostnamePort = urlSplit[1].split(":") if not re.search("\[.+\]", urlSplit[1]) else filter(None, (re.search("\[.+\]", urlSplit[1]).group(0), re.search("\](:(?P\d+))?", urlSplit[1]).group("port"))) conf.scheme = urlSplit[0].strip().lower() if not conf.forceSSL else "https" - conf.path = urlSplit[2].strip() + + if conf.seoDelimiter: + conf.path = "/" + else: + conf.path = urlSplit[2].strip() + conf.hostname = hostnamePort[0].strip() conf.ipv6 = conf.hostname != conf.hostname.strip("[]") @@ -1094,6 +1118,10 @@ def parseTargetUrl(): if urlSplit[3]: conf.parameters[PLACE.GET] = urldecode(urlSplit[3]) if urlSplit[3] and urlencode(DEFAULT_GET_POST_DELIMITER, None) not in urlSplit[3] else urlSplit[3] + elif conf.seoDelimiter: + if len(urlSplit[2]) > 1: + conf.parameters[PLACE.GET] = urldecode(urlSplit[2][1:]) + conf.url = getUnicode("%s://%s:%d%s" % (conf.scheme, ("[%s]" % conf.hostname) if conf.ipv6 else conf.hostname, conf.port, conf.path)) conf.url = conf.url.replace(URI_QUESTION_MARKER, '?') @@ -2785,6 +2813,8 @@ def unsafeSQLIdentificatorNaming(name): prefix = "%s." % DEFAULT_MSSQL_SCHEMA if retVal.startswith(prefix): retVal = retVal[len(prefix):] + if conf.useHexBasedString: + return "0x"+"".join([hex(ord(c))[2:] for c in retVal]) return retVal diff --git a/lib/request/connect.py b/lib/request/connect.py index 87814567dc2..20a3ef784e4 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -264,8 +264,12 @@ def getPage(**kwargs): get = conf.parameters[PLACE.GET] if get: - url = "%s?%s" % (url, get) - requestMsg += "?%s" % get + if conf.seoDelimiter: + url = "%s%s" % (url, get) + requestMsg += "%s" % get + else: + url = "%s?%s" % (url, get) + requestMsg += "?%s" % get if conf.method == HTTPMETHOD.POST and not post: for place in (PLACE.POST,): From e791e9fe76e3707062105b056ce76b56c4a22475 Mon Sep 17 00:00:00 2001 From: Huy Phan Date: Fri, 30 Nov 2012 14:24:10 +0800 Subject: [PATCH 5/7] Update README.md --- README.md | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index aea30605a5a..c248e6a4a7c 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,8 @@ -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. +Description +=========== -**Links** - -* Homepage: http://sqlmap.org -* Download: [.tar.gz](https://github.com/sqlmapproject/sqlmap/tarball/master) or [.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 -* User's manual: https://github.com/sqlmapproject/sqlmap/wiki -* Frequently Asked Questions: 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 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) +Original CREDIT goes [here](https://github.com/sqlmapproject/sqlmap) +This fork adds some missing features from sqlmap: +* Supports SEO URL. +* Option to skip false positive check. +* Supports hex-based sql injection. \ No newline at end of file From ade4028eae2bba0137349211dbb226b9269be9f5 Mon Sep 17 00:00:00 2001 From: Huy Phan Date: Fri, 30 Nov 2012 14:25:56 +0800 Subject: [PATCH 6/7] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c248e6a4a7c..b7792ea9e77 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ Description =========== Original CREDIT goes [here](https://github.com/sqlmapproject/sqlmap) -This fork adds some missing features from sqlmap: + + +This fork adds some missing features of original sqlmap project. * Supports SEO URL. * Option to skip false positive check. * Supports hex-based sql injection. \ No newline at end of file From c03ea563fd98f64aa85da888a07d58a650bc195a Mon Sep 17 00:00:00 2001 From: Huy Phan Date: Fri, 30 Nov 2012 16:06:40 +0800 Subject: [PATCH 7/7] Don't urlencode GET requests if skip-url-encode option is turned on --- lib/request/connect.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/request/connect.py b/lib/request/connect.py index 20a3ef784e4..77e1980727c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -262,7 +262,7 @@ def getPage(**kwargs): elif target: if PLACE.GET in conf.parameters and not get: get = conf.parameters[PLACE.GET] - + if get: if conf.seoDelimiter: url = "%s%s" % (url, get) @@ -699,7 +699,9 @@ def _randomizeParameter(paramString, randomParameter): else: get += "%s%s=%s" % (delimiter, name, value) - get = urlencode(get, limit=True) + if not skipUrlEncode: + get = urlencode(get, limit=True) + 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)