Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6df4d73

Browse files
committed
Implementation for an Issue #2025
1 parent 2aaa486 commit 6df4d73

4 files changed

Lines changed: 68 additions & 25 deletions

File tree

lib/core/option.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,6 +1858,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
18581858
kb.dnsMode = False
18591859
kb.dnsTest = None
18601860
kb.docRoot = None
1861+
kb.dumpColumns = None
18611862
kb.dumpTable = None
18621863
kb.dumpKeyboardInterrupt = False
18631864
kb.dynamicMarkings = []
@@ -1941,6 +1942,7 @@ def _setKnowledgeBaseAttributes(flushAll=True):
19411942
kb.responseTimeMode = None
19421943
kb.responseTimePayload = None
19431944
kb.resumeValues = True
1945+
kb.rowXmlMode = False
19441946
kb.safeCharEncode = False
19451947
kb.safeReq = AttribDict()
19461948
kb.singleLogFlags = set()

lib/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from lib.core.revision import getRevisionNumber
2020

2121
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
22-
VERSION = "1.0.7.18"
22+
VERSION = "1.0.7.19"
2323
REVISION = getRevisionNumber()
2424
STABLE = VERSION.count('.') <= 2
2525
VERSION_STRING = "sqlmap/%s#%s" % (VERSION, "stable" if STABLE else "dev")

lib/techniques/union/use.py

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
See the file 'doc/COPYING' for copying permission
66
"""
77

8+
import binascii
89
import re
910
import time
11+
import xml.etree.ElementTree
1012

1113
from extra.safe2bin.safe2bin import safecharencode
1214
from lib.core.agent import agent
@@ -46,6 +48,7 @@
4648
from lib.core.exception import SqlmapDataException
4749
from lib.core.exception import SqlmapSyntaxException
4850
from lib.core.settings import MAX_BUFFERED_PARTIAL_UNION_LENGTH
51+
from lib.core.settings import NULL
4952
from lib.core.settings import SQL_SCALAR_REGEX
5053
from lib.core.settings import TURN_OFF_RESUME_INFO_LIMIT
5154
from lib.core.threads import getCurrentThreadData
@@ -62,38 +65,66 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
6265
threadData.resumed = retVal is not None
6366

6467
if retVal is None:
65-
# Prepare expression with delimiters
66-
injExpression = unescaper.escape(agent.concatQuery(expression, unpack))
67-
68-
# Forge the UNION SQL injection request
6968
vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector
70-
kb.unionDuplicates = vector[7]
71-
kb.forcePartialUnion = vector[8]
72-
query = agent.forgeUnionQuery(injExpression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6], None, limited)
73-
where = PAYLOAD.WHERE.NEGATIVE if conf.limitStart or conf.limitStop else vector[6]
69+
70+
if not kb.rowXmlMode:
71+
injExpression = unescaper.escape(agent.concatQuery(expression, unpack))
72+
kb.unionDuplicates = vector[7]
73+
kb.forcePartialUnion = vector[8]
74+
query = agent.forgeUnionQuery(injExpression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6], None, limited)
75+
where = PAYLOAD.WHERE.NEGATIVE if conf.limitStart or conf.limitStop else vector[6]
76+
else:
77+
where = vector[6]
78+
query = agent.forgeUnionQuery(expression, vector[0], vector[1], vector[2], vector[3], vector[4], vector[5], vector[6], None, False)
79+
7480
payload = agent.payload(newValue=query, where=where)
7581

7682
# Perform the request
7783
page, headers = Request.queryPage(payload, content=True, raise404=False)
7884

7985
incrementCounter(PAYLOAD.TECHNIQUE.UNION)
8086

81-
# Parse the returned page to get the exact UNION-based
82-
# SQL injection output
83-
def _(regex):
84-
return reduce(lambda x, y: x if x is not None else y, (\
85-
extractRegexResult(regex, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE), \
86-
extractRegexResult(regex, removeReflectiveValues(listToStrValue(headers.headers \
87-
if headers else None), payload, True), re.DOTALL | re.IGNORECASE)), \
88-
None)
87+
if not kb.rowXmlMode:
88+
# Parse the returned page to get the exact UNION-based
89+
# SQL injection output
90+
def _(regex):
91+
return reduce(lambda x, y: x if x is not None else y, (\
92+
extractRegexResult(regex, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE), \
93+
extractRegexResult(regex, removeReflectiveValues(listToStrValue(headers.headers \
94+
if headers else None), payload, True), re.DOTALL | re.IGNORECASE)), \
95+
None)
96+
97+
# Automatically patching last char trimming cases
98+
if kb.chars.stop not in (page or "") and kb.chars.stop[:-1] in (page or ""):
99+
warnMsg = "automatically patching output having last char trimmed"
100+
singleTimeWarnMessage(warnMsg)
101+
page = page.replace(kb.chars.stop[:-1], kb.chars.stop)
102+
103+
retVal = _("(?P<result>%s.*%s)" % (kb.chars.start, kb.chars.stop))
104+
else:
105+
output = extractRegexResult(r"(?P<result>(<row[^>]+>)+)", page)
106+
if output:
107+
retVal = ""
108+
root = xml.etree.ElementTree.fromstring("<root>%s</root>" % output)
109+
for column in kb.dumpColumns:
110+
base64 = True
111+
for child in root:
112+
child.attrib[column] = child.attrib.get(column, "").encode("base64")
113+
try:
114+
child.attrib.get(column, "").decode("base64")
115+
except binascii.Error:
116+
base64 = False
117+
break
89118

90-
# Automatically patching last char trimming cases
91-
if kb.chars.stop not in (page or "") and kb.chars.stop[:-1] in (page or ""):
92-
warnMsg = "automatically patching output having last char trimmed"
93-
singleTimeWarnMessage(warnMsg)
94-
page = page.replace(kb.chars.stop[:-1], kb.chars.stop)
119+
if base64:
120+
for child in root:
121+
child.attrib[column] = child.attrib.get(column, "").decode("base64") or NULL
95122

96-
retVal = _("(?P<result>%s.*%s)" % (kb.chars.start, kb.chars.stop))
123+
for child in root:
124+
row = []
125+
for column in kb.dumpColumns:
126+
row.append(child.attrib.get(column, NULL))
127+
retVal += "%s%s%s" % (kb.chars.start, kb.chars.delimiter.join(row), kb.chars.stop)
97128

98129
if retVal is not None:
99130
retVal = getUnicode(retVal, kb.pageEncoding)
@@ -103,7 +134,8 @@ def _(regex):
103134
retVal = htmlunescape(retVal).replace("<br>", "\n")
104135

105136
hashDBWrite("%s%s" % (conf.hexConvert or False, expression), retVal)
106-
else:
137+
138+
elif not kb.rowXmlMode:
107139
trimmed = _("%s(?P<result>.*?)<" % (kb.chars.start))
108140

109141
if trimmed:
@@ -174,6 +206,13 @@ def unionUse(expression, unpack=True, dump=False):
174206
# Set kb.partRun in case the engine is called from the API
175207
kb.partRun = getPartRun(alias=False) if hasattr(conf, "api") else None
176208

209+
if Backend.isDbms(DBMS.MSSQL) and kb.dumpColumns:
210+
kb.rowXmlMode = True
211+
_ = "(%s FOR XML RAW, BINARY BASE64)" % expression
212+
output = _oneShotUnionUse(_, False)
213+
value = parseUnionPage(output)
214+
kb.rowXmlMode = False
215+
177216
if expressionFieldsList and len(expressionFieldsList) > 1 and "ORDER BY" in expression.upper():
178217
# Removed ORDER BY clause because UNION does not play well with it
179218
expression = re.sub("\s*ORDER BY\s+[\w,]+", "", expression, re.I)
@@ -186,7 +225,7 @@ def unionUse(expression, unpack=True, dump=False):
186225
# SQL limiting the query output one entry at a time
187226
# NOTE: we assume that only queries that get data from a table can
188227
# return multiple entries
189-
if (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \
228+
if value is None and (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \
190229
kb.forcePartialUnion or \
191230
(dump and (conf.limitStart or conf.limitStop)) or "LIMIT " in expression.upper()) and \
192231
" FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \

plugins/generic/entries.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ def dumpTable(self, foundData=None):
137137
logger.warn(warnMsg)
138138
continue
139139

140+
kb.dumpColumns = colList
140141
colNames = colString = ", ".join(column for column in colList)
141142
rootQuery = queries[Backend.getIdentifiedDbms()].dump_table
142143

@@ -370,6 +371,7 @@ def dumpTable(self, foundData=None):
370371
logger.critical(errMsg)
371372

372373
finally:
374+
kb.dumpColumns = None
373375
kb.dumpTable = None
374376

375377
def dumpAll(self):

0 commit comments

Comments
 (0)