Implements ARRAY_AGG for PostgreSQL

This commit is contained in:
Miroslav Stampar 2020-10-28 14:36:25 +01:00
parent 73d0c67a80
commit a5852390f7
2 changed files with 31 additions and 24 deletions

View File

@ -18,7 +18,7 @@ from lib.core.enums import OS
from thirdparty.six import unichr as _unichr from thirdparty.six import unichr as _unichr
# sqlmap version (<major>.<minor>.<month>.<monthly commit>) # sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.4.10.23" VERSION = "1.4.10.24"
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

View File

@ -135,11 +135,17 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
else: else:
retVal = getUnicode(retVal) retVal = getUnicode(retVal)
elif kb.jsonAggMode: elif kb.jsonAggMode:
if Backend.isDbms(DBMS.PGSQL):
output = extractRegexResult(r"(?P<result>%s.*%s)" % (kb.chars.start, kb.chars.stop), page or "")
if output:
retVal = output
else:
output = extractRegexResult(r"(?P<result>%s.*?%s)" % (kb.chars.start, kb.chars.stop), page or "") output = extractRegexResult(r"(?P<result>%s.*?%s)" % (kb.chars.start, kb.chars.stop), page or "")
if output: if output:
output = output[len(kb.chars.start):-len(kb.chars.stop)]
try: try:
retVal = "" retVal = ""
for row in json.loads(output[len(kb.chars.start):-len(kb.chars.stop)]): for row in json.loads(output):
retVal += "%s%s%s" % (kb.chars.start, row, kb.chars.stop) retVal += "%s%s%s" % (kb.chars.start, row, kb.chars.stop)
except: except:
pass pass
@ -248,16 +254,24 @@ def unionUse(expression, unpack=True, dump=False):
# Set kb.partRun in case the engine is called from the API # Set kb.partRun in case the engine is called from the API
kb.partRun = getPartRun(alias=False) if conf.api else None kb.partRun = getPartRun(alias=False) if conf.api else None
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ORACLE) and expressionFields: if expressionFieldsList and len(expressionFieldsList) > 1 and "ORDER BY" in expression.upper():
# Removed ORDER BY clause because UNION does not play well with it
expression = re.sub(r"(?i)\s*ORDER BY\s+[\w,]+", "", expression)
debugMsg = "stripping ORDER BY clause from statement because "
debugMsg += "it does not play well with UNION query SQL injection"
singleTimeDebugMessage(debugMsg)
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ORACLE, DBMS.PGSQL) and expressionFields:
match = re.search(r"SELECT\s*(.+?)\bFROM", expression, re.I) match = re.search(r"SELECT\s*(.+?)\bFROM", expression, re.I)
if match and not (Backend.isDbms(DBMS.ORACLE) and FROM_DUMMY_TABLE[DBMS.ORACLE] in expression): if match and not (Backend.isDbms(DBMS.ORACLE) and FROM_DUMMY_TABLE[DBMS.ORACLE] in expression):
kb.jsonAggMode = True kb.jsonAggMode = True
if Backend.isDbms(DBMS.MYSQL): if Backend.isDbms(DBMS.MYSQL):
_ = expression.replace(expressionFields, "CONCAT('%s',JSON_ARRAYAGG(CONCAT_WS('%s',%s)),'%s')" % (kb.chars.start, kb.chars.delimiter, expressionFields, kb.chars.stop), 1) query = expression.replace(expressionFields, "CONCAT('%s',JSON_ARRAYAGG(CONCAT_WS('%s',%s)),'%s')" % (kb.chars.start, kb.chars.delimiter, expressionFields, kb.chars.stop), 1)
else: elif Backend.isDbms(DBMS.ORACLE):
_ = expression.replace(expressionFields, "'%s'||JSON_ARRAYAGG(%s)||'%s'" % (kb.chars.start, ("||'%s'||" % kb.chars.delimiter).join(expressionFieldsList), kb.chars.stop), 1) query = expression.replace(expressionFields, "'%s'||JSON_ARRAYAGG(%s)||'%s'" % (kb.chars.start, ("||'%s'||" % kb.chars.delimiter).join(expressionFieldsList), kb.chars.stop), 1)
_ = re.sub(r"(?i)\s*ORDER BY ROWNUM", "", _) elif Backend.isDbms(DBMS.PGSQL):
output = _oneShotUnionUse(_, False) query = expression.replace(expressionFields, "ARRAY_AGG('%s'||%s||'%s')::text" % (kb.chars.start, ("||'%s'||" % kb.chars.delimiter).join("COALESCE(%s::text,' ')" % field for field in expressionFieldsList), kb.chars.stop), 1)
output = _oneShotUnionUse(query, False)
value = parseUnionPage(output) value = parseUnionPage(output)
kb.jsonAggMode = False kb.jsonAggMode = False
elif Backend.isDbms(DBMS.MSSQL) and kb.dumpColumns: elif Backend.isDbms(DBMS.MSSQL) and kb.dumpColumns:
@ -267,13 +281,6 @@ def unionUse(expression, unpack=True, dump=False):
value = parseUnionPage(output) value = parseUnionPage(output)
kb.rowXmlMode = False kb.rowXmlMode = False
if expressionFieldsList and len(expressionFieldsList) > 1 and "ORDER BY" in expression.upper():
# Removed ORDER BY clause because UNION does not play well with it
expression = re.sub(r"(?i)\s*ORDER BY\s+[\w,]+", "", expression)
debugMsg = "stripping ORDER BY clause from statement because "
debugMsg += "it does not play well with UNION query SQL injection"
singleTimeDebugMessage(debugMsg)
# We have to check if the SQL query might return multiple entries # We have to check if the SQL query might return multiple entries
# if the technique is partial UNION query and in such case forge the # if the technique is partial UNION query and in such case forge the
# SQL limiting the query output one entry at a time # SQL limiting the query output one entry at a time