diff --git a/lib/core/agent.py b/lib/core/agent.py index 6a2ddbeea..435c58eb6 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -55,11 +55,11 @@ class Agent: retValue = "" newValue = urlencode(newValue) - if negative or conf.paramNegative: + if negative or kb.unionNegative: negValue = "-" - elif falseCond or conf.paramFalseCond: + elif falseCond or kb.unionFalseCond: randInt = randomInt() - falseValue = " AND %d=%d" % (randInt, randInt + 1) + falseValue = urlencode(" AND %d=%d" % (randInt, randInt + 1)) # After identifing the injectable parameter if kb.injPlace == "User-Agent": diff --git a/lib/core/option.py b/lib/core/option.py index 009632b26..22acf4f4b 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -872,8 +872,6 @@ def __setConfAttributes(): conf.outputPath = None conf.paramDict = {} conf.parameters = {} - conf.paramFalseCond = False - conf.paramNegative = False conf.path = None conf.port = None conf.progressWidth = 54 @@ -932,6 +930,9 @@ def __setKnowledgeBaseAttributes(): kb.unionComment = "" kb.unionCount = None kb.unionPosition = None + kb.unionNegative = False + kb.unionFalseCond = False + def __saveCmdline(): """ diff --git a/lib/core/session.py b/lib/core/session.py index 9f58ce925..50ef376f7 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -199,7 +199,7 @@ def setStacked(): if condition: dataToSessionFile("[%s][%s][%s][Stacked queries][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.stackedTest)) -def setUnion(comment=None, count=None, position=None): +def setUnion(comment=None, count=None, position=None, negative=False, falseCond=False): """ @param comment: union comment to save in session file @type comment: C{str} @@ -226,7 +226,7 @@ def setUnion(comment=None, count=None, position=None): kb.unionComment = comment kb.unionCount = count - elif position: + if position: condition = ( not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and ( not kb.resumedQueries[conf.url].has_key("Union position") @@ -238,6 +238,30 @@ def setUnion(comment=None, count=None, position=None): kb.unionPosition = position + if negative: + condition = ( + not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and + ( not kb.resumedQueries[conf.url].has_key("Union negative") + ) ) + ) + + if condition: + dataToSessionFile("[%s][%s][%s][Union negative][Yes]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace])) + + kb.unionNegative = True + + if falseCond: + condition = ( + not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and + ( not kb.resumedQueries[conf.url].has_key("Union false condition") + ) ) + ) + + if condition: + dataToSessionFile("[%s][%s][%s][Union false condition][Yes]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace])) + + kb.unionFalseCond = True + def setRemoteTempPath(): condition = ( not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and @@ -430,6 +454,20 @@ def resumeConfKb(expression, url, value): logMsg += "%s from session file" % kb.unionPosition logger.info(logMsg) + 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 + 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 + logger.info(logMsg) + elif expression == "Remote temp path" and url == conf.url: conf.tmpPath = value[:-1] diff --git a/lib/request/inject.py b/lib/request/inject.py index 21a0b84af..81c0962e0 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -368,16 +368,16 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None, warnMsg += "technique, sqlmap is going blind" logger.warn(warnMsg) - oldParamFalseCond = conf.paramFalseCond - oldParamNegative = conf.paramNegative - conf.paramFalseCond = False - conf.paramNegative = False + oldParamFalseCond = kb.unionFalseCond + oldParamNegative = kb.unionNegative + kb.unionFalseCond = False + kb.unionNegative = False if blind and not value: value = __goInferenceProxy(expression, fromUser, expected, batch, resumeValue, unpack, charsetType, firstChar, lastChar) - conf.paramFalseCond = oldParamFalseCond - conf.paramNegative = oldParamNegative + kb.unionFalseCond = oldParamFalseCond + kb.unionNegative = oldParamNegative if value and isinstance(value, str): value = value.strip() diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index 9c0966a33..b45b09114 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -33,7 +33,26 @@ from lib.core.unescaper import unescaper from lib.parse.html import htmlParser from lib.request.connect import Connect as Request +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 __unionPosition(negative=False, falseCond=False): + value = None + if negative or falseCond: negLogMsg = "partial (single entry)" else: @@ -73,6 +92,7 @@ def __unionPosition(negative=False, falseCond=False): if randQuery in resultPage and not htmlParsed: setUnion(position=exprPosition) + value = __forgeUserFriendlyValue(payload) break @@ -90,22 +110,26 @@ def __unionPosition(negative=False, falseCond=False): logger.warn(warnMsg) + return value + def __unionConfirm(): + value = None + # Confirm the inband SQL injection and get the exact column # position if not isinstance(kb.unionPosition, int): - __unionPosition() + value = __unionPosition() # Assure that the above function found the exploitable full inband # SQL injection position if not isinstance(kb.unionPosition, int): - __unionPosition(falseCond=True) + value = __unionPosition(falseCond=True) # Assure that the above function found the exploitable partial # (single entry) inband SQL injection position by appending # a false condition after the parameter value if not isinstance(kb.unionPosition, int): - __unionPosition(negative=True) + value = __unionPosition(negative=True) # Assure that the above function found the exploitable partial # (single entry) inband SQL injection position with negative @@ -113,24 +137,9 @@ def __unionConfirm(): if not isinstance(kb.unionPosition, int): return else: - conf.paramNegative = True + setUnion(negative=True) else: - conf.paramFalseCond = True - -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 + setUnion(falseCond=True) return value @@ -142,7 +151,6 @@ def __unionTestByNULLBruteforce(comment): """ columns = None - value = None query = agent.prefixQuery(" UNION ALL SELECT NULL") for count in range(0, 50): @@ -161,15 +169,13 @@ def __unionTestByNULLBruteforce(comment): if seqMatcher >= 0.6: columns = count + 1 - value = __forgeUserFriendlyValue(payload) break - return value, columns + return columns def __unionTestByOrderBy(comment): columns = None - value = None prevPayload = "" for count in range(1, 51): @@ -182,13 +188,11 @@ def __unionTestByOrderBy(comment): columns = count elif columns: - value = __forgeUserFriendlyValue(prevPayload) - break prevPayload = payload - return value, columns + return columns def unionTest(): """ @@ -205,25 +209,28 @@ def unionTest(): infoMsg += "'%s' with %s technique" % (kb.injParameter, technique) logger.info(infoMsg) - value = "" + value = None columns = None for comment in (queries[kb.dbms].comment, ""): if conf.uTech == "orderby": - value, columns = __unionTestByOrderBy(comment) + columns = __unionTestByOrderBy(comment) else: - value, columns = __unionTestByNULLBruteforce(comment) + columns = __unionTestByNULLBruteforce(comment) if columns: - setUnion(comment, columns) + setUnion(comment=comment, count=columns) break if kb.unionCount: - __unionConfirm() + value = __unionConfirm() else: warnMsg = "the target url is not affected by an " warnMsg += "inband sql injection vulnerability" logger.warn(warnMsg) + if value is None: + value = "" + return value diff --git a/lib/techniques/inband/union/use.py b/lib/techniques/inband/union/use.py index 0ab6bb3f5..b3fada7bc 100644 --- a/lib/techniques/inband/union/use.py +++ b/lib/techniques/inband/union/use.py @@ -70,7 +70,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh expression = agent.concatQuery(expression, unpack) expression = unescaper.unescape(expression) - if ( conf.paramNegative or conf.paramFalseCond ) and not direct: + if ( kb.unionNegative or kb.unionFalseCond ) and not direct: _, _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr) if len(expressionFieldsList) > 1: