Skip to content

Commit

Permalink
Implements sqlmapproject#4403
Browse files Browse the repository at this point in the history
  • Loading branch information
stamparm committed Oct 27, 2020
1 parent 673a5af commit 585645e
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 23 deletions.
1 change: 1 addition & 0 deletions lib/core/option.py
Original file line number Diff line number Diff line change
Expand Up @@ -2058,6 +2058,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.identifiedWafs = set()
kb.injection = InjectionDict()
kb.injections = []
kb.jsonAggMode = False
kb.laggingChecked = False
kb.lastParserStatus = None

Expand Down
2 changes: 1 addition & 1 deletion lib/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from thirdparty.six import unichr as _unichr

# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.4.10.19"
VERSION = "1.4.10.20"
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
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)
Expand Down
57 changes: 36 additions & 21 deletions lib/techniques/union/use.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""

import binascii
import json
import re
import time
import xml.etree.ElementTree
Expand Down Expand Up @@ -74,7 +75,7 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
if retVal is None:
vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector

if not kb.rowXmlMode:
if not any((kb.rowXmlMode, kb.jsonAggMode)):
injExpression = unescaper.escape(agent.concatQuery(expression, unpack))
kb.unionDuplicates = vector[7]
kb.forcePartialUnion = vector[8]
Expand All @@ -99,24 +100,8 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):

incrementCounter(PAYLOAD.TECHNIQUE.UNION)

if not kb.rowXmlMode:
# Parse the returned page to get the exact UNION-based
# SQL injection output
def _(regex):
return firstNotNone(
extractRegexResult(regex, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE),
extractRegexResult(regex, removeReflectiveValues(listToStrValue((_ for _ in headers.headers if not _.startswith(HTTP_HEADER.URI)) if headers else None), payload, True), re.DOTALL | re.IGNORECASE)
)

# Automatically patching last char trimming cases
if kb.chars.stop not in (page or "") and kb.chars.stop[:-1] in (page or ""):
warnMsg = "automatically patching output having last char trimmed"
singleTimeWarnMessage(warnMsg)
page = page.replace(kb.chars.stop[:-1], kb.chars.stop)

retVal = _("(?P<result>%s.*%s)" % (kb.chars.start, kb.chars.stop))
else:
output = extractRegexResult(r"(?P<result>(<row.+?/>)+)", page)
if kb.rowXmlMode:
output = extractRegexResult(r"(?P<result>(<row.+?/>)+)", page or "")
if output:
try:
root = xml.etree.ElementTree.fromstring(safeStringFormat("<root>%s</root>", getBytes(output)))
Expand Down Expand Up @@ -149,6 +134,28 @@ def _(regex):
pass
else:
retVal = getUnicode(retVal)
elif kb.jsonAggMode:
output = extractRegexResult(r"(?P<result>%s.*?%s)" % (kb.chars.start, kb.chars.stop), page or "")
if output:
retVal = ""
for row in json.loads(output[len(kb.chars.start):-len(kb.chars.stop)]):
retVal += "%s%s%s" % (kb.chars.start, row, kb.chars.stop)
else:
# Parse the returned page to get the exact UNION-based
# SQL injection output
def _(regex):
return firstNotNone(
extractRegexResult(regex, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE),
extractRegexResult(regex, removeReflectiveValues(listToStrValue((_ for _ in headers.headers if not _.startswith(HTTP_HEADER.URI)) if headers else None), payload, True), re.DOTALL | re.IGNORECASE)
)

# Automatically patching last char trimming cases
if kb.chars.stop not in (page or "") and kb.chars.stop[:-1] in (page or ""):
warnMsg = "automatically patching output having last char trimmed"
singleTimeWarnMessage(warnMsg)
page = page.replace(kb.chars.stop[:-1], kb.chars.stop)

retVal = _("(?P<result>%s.*%s)" % (kb.chars.start, kb.chars.stop))

if retVal is not None:
retVal = getUnicode(retVal, kb.pageEncoding)
Expand All @@ -159,7 +166,7 @@ def _(regex):

hashDBWrite("%s%s" % (conf.hexConvert or False, expression), retVal)

elif not kb.rowXmlMode:
elif not any((kb.rowXmlMode, kb.jsonAggMode)):
trimmed = _("%s(?P<result>.*?)<" % (kb.chars.start))

if trimmed:
Expand Down Expand Up @@ -236,7 +243,15 @@ def unionUse(expression, unpack=True, dump=False):
# Set kb.partRun in case the engine is called from the API
kb.partRun = getPartRun(alias=False) if conf.api else None

if Backend.isDbms(DBMS.MSSQL) and kb.dumpColumns:
if Backend.isDbms(DBMS.MYSQL) and expressionFields:
match = re.search(r"SELECT\s*(.+?)\bFROM", expression, re.I)
if match:
kb.jsonAggMode = True
_ = expression.replace(expressionFields, "CONCAT('%s',JSON_ARRAYAGG(CONCAT_WS('%s',%s)),'%s')" % (kb.chars.start, kb.chars.delimiter, expressionFields, kb.chars.stop), 1)
output = _oneShotUnionUse(_, False)
value = parseUnionPage(output)
kb.jsonAggMode = False
elif Backend.isDbms(DBMS.MSSQL) and kb.dumpColumns:
kb.rowXmlMode = True
_ = "(%s FOR XML RAW, BINARY BASE64)" % expression
output = _oneShotUnionUse(_, False)
Expand Down
2 changes: 1 addition & 1 deletion plugins/generic/entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def dumpTable(self, foundData=None):
continue

kb.dumpColumns = [unsafeSQLIdentificatorNaming(_) for _ in colList]
colNames = colString = ", ".join(column for column in colList)
colNames = colString = ','.join(column for column in colList)
rootQuery = queries[Backend.getIdentifiedDbms()].dump_table

infoMsg = "fetching entries"
Expand Down

0 comments on commit 585645e

Please sign in to comment.