diff --git a/lib/core/agent.py b/lib/core/agent.py index 227113670..c00db48f2 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -341,15 +341,19 @@ class Agent: @return: query fields (columns) and more details @rtype: C{str} """ + prefixRegex = "(?:\s+(?:FIRST|SKIP)\s+\d+)*" fieldsSelectTop = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", query, re.I) fieldsSelectDistinct = re.search("\ASELECT%s\s+DISTINCT\((.+?)\)\s+FROM" % prefixRegex, query, re.I) fieldsSelectCase = re.search("\ASELECT%s\s+(\(CASE WHEN\s+.+\s+END\))" % prefixRegex, query, re.I) fieldsSelectFrom = re.search("\ASELECT%s\s+(.+?)\s+FROM\s+" % prefixRegex, query, re.I) + fieldsExists = re.search("EXISTS(.*)", query, re.I) fieldsSelect = re.search("\ASELECT%s\s+(.*)" % prefixRegex, query, re.I) fieldsNoSelect = query - if fieldsSelectTop: + if fieldsExists: + fieldsToCastStr = fieldsSelect.groups()[0] + elif fieldsSelectTop: fieldsToCastStr = fieldsSelectTop.groups()[0] elif fieldsSelectDistinct: fieldsToCastStr = fieldsSelectDistinct.groups()[0] @@ -368,7 +372,7 @@ class Agent: fieldsToCastList = fieldsToCastStr.replace(", ", ",") fieldsToCastList = fieldsToCastList.split(",") - return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, fieldsToCastList, fieldsToCastStr + return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, fieldsToCastList, fieldsToCastStr, fieldsExists def simpleConcatQuery(self, query1, query2): concatenatedQuery = "" @@ -414,15 +418,18 @@ class Agent: concatenatedQuery = "" query = query.replace(", ", ",") - fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr = self.getFields(query) + fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr, fieldsExists = self.getFields(query) castedFields = self.nullCastConcatFields(fieldsToCastStr) concatenatedQuery = query.replace(fieldsToCastStr, castedFields, 1) else: concatenatedQuery = query - fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr = self.getFields(query) + fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr, fieldsExists = self.getFields(query) if getIdentifiedDBMS() == DBMS.MYSQL: - if fieldsSelectFrom: + if fieldsExists: + concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.misc.start, 1) + concatenatedQuery += ",'%s')" % kb.misc.stop + elif fieldsSelectFrom: concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.misc.start, 1) concatenatedQuery = concatenatedQuery.replace(" FROM ", ",'%s') FROM " % kb.misc.stop, 1) elif fieldsSelect or fieldsSelectCase: @@ -432,7 +439,10 @@ class Agent: concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.misc.start, concatenatedQuery, kb.misc.stop) elif getIdentifiedDBMS() in ( DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE ): - if fieldsSelectFrom: + if fieldsExists: + concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.misc.start, 1) + concatenatedQuery += "||'%s'" % kb.misc.stop + elif fieldsSelectFrom: concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.misc.start, 1) concatenatedQuery = concatenatedQuery.replace(" FROM ", "||'%s' FROM " % kb.misc.stop, 1) elif fieldsSelect or fieldsSelectCase: @@ -445,7 +455,10 @@ class Agent: concatenatedQuery += " FROM DUAL" elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE): - if fieldsSelectTop: + if fieldsExists: + concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.misc.start, 1) + concatenatedQuery += "+'%s'" % kb.misc.stop + elif fieldsSelectTop: topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+", concatenatedQuery, re.I).group(1) concatenatedQuery = concatenatedQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, kb.misc.start), 1) concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % kb.misc.stop, 1) diff --git a/lib/request/inject.py b/lib/request/inject.py index 99b9fea8b..2ea5c3871 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -160,7 +160,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r return __goInference(payload, expression, charsetType, firstChar, lastChar) if kb.dbmsDetected: - _, _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(expression) + _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) rdbRegExp = re.search("RDB\$GET_CONTEXT\([^)]+\)", expression, re.I) if rdbRegExp and getIdentifiedDBMS() == DBMS.FIREBIRD: diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index f43408043..bbca3bcd5 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -75,12 +75,12 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None lastChar = int(lastChar) if kb.dbmsDetected: - _, _, _, _, _, _, fieldToCastStr = agent.getFields(expression) - nulledCastedField = agent.nullAndCastField(fieldToCastStr) - expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1) - expressionUnescaped = unescaper.unescape(expressionReplaced) + _, _, _, _, _, _, fieldToCastStr, _ = agent.getFields(expression) + nulledCastedField = agent.nullAndCastField(fieldToCastStr) + expressionReplaced = expression.replace(fieldToCastStr, nulledCastedField, 1) + expressionUnescaped = unescaper.unescape(expressionReplaced) else: - expressionUnescaped = unescaper.unescape(expression) + expressionUnescaped = unescaper.unescape(expression) if length and not isinstance(length, int) and length.isdigit(): length = int(length) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 4d823ced3..1bf5bc2a7 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -42,7 +42,7 @@ def errorUse(expression): query = agent.suffixQuery(query) check = "%s(?P.*?)%s" % (kb.misc.start, kb.misc.stop) - _, _, _, _, _, _, fieldToCastStr = agent.getFields(expression) + _, _, _, _, _, _, fieldToCastStr, _ = agent.getFields(expression) nulledCastedField = agent.nullAndCastField(fieldToCastStr) if getIdentifiedDBMS() == DBMS.MYSQL: diff --git a/lib/techniques/inband/union/use.py b/lib/techniques/inband/union/use.py index 6d2f5fb4d..ee0246861 100644 --- a/lib/techniques/inband/union/use.py +++ b/lib/techniques/inband/union/use.py @@ -95,7 +95,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, unpack expression = unescaper.unescape(expression) if kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == 2 and not direct: - _, _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr) + _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(origExpr) # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one