diff --git a/README.md b/README.md index aea30605a5a..b7792ea9e77 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,10 @@ -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** +Original CREDIT goes [here](https://github.com/sqlmapproject/sqlmap) -* 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) + +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 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 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/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 " diff --git a/lib/request/connect.py b/lib/request/connect.py index 87814567dc2..77e1980727c 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -262,10 +262,14 @@ def getPage(**kwargs): elif target: if PLACE.GET in conf.parameters and not get: 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,): @@ -695,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) 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