diff --git a/data/xml/queries.xml b/data/xml/queries.xml
index 33267e870..0e60c3173 100644
--- a/data/xml/queries.xml
+++ b/data/xml/queries.xml
@@ -198,11 +198,11 @@
-
+
-
+
diff --git a/lib/core/option.py b/lib/core/option.py
index 6b28901ca..222e2aa3c 100644
--- a/lib/core/option.py
+++ b/lib/core/option.py
@@ -2113,7 +2113,6 @@ def _setKnowledgeBaseAttributes(flushAll=True):
kb.responseTimeMode = None
kb.responseTimePayload = None
kb.resumeValues = True
- kb.rowXmlMode = False
kb.safeCharEncode = False
kb.safeReq = AttribDict()
kb.secondReq = None
diff --git a/lib/core/settings.py b/lib/core/settings.py
index e54302067..4db5996fc 100644
--- a/lib/core/settings.py
+++ b/lib/core/settings.py
@@ -18,7 +18,7 @@ from lib.core.enums import OS
from thirdparty.six import unichr as _unichr
# sqlmap version (...)
-VERSION = "1.4.10.25"
+VERSION = "1.4.10.26"
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)
diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py
index ddb772901..241fcea7e 100644
--- a/lib/techniques/union/use.py
+++ b/lib/techniques/union/use.py
@@ -75,7 +75,7 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
if retVal is None:
vector = kb.injection.data[PAYLOAD.TECHNIQUE.UNION].vector
- if not any((kb.rowXmlMode, kb.jsonAggMode)):
+ if not kb.jsonAggMode:
injExpression = unescaper.escape(agent.concatQuery(expression, unpack))
kb.unionDuplicates = vector[7]
kb.forcePartialUnion = vector[8]
@@ -100,49 +100,26 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
incrementCounter(PAYLOAD.TECHNIQUE.UNION)
- if kb.rowXmlMode:
- output = extractRegexResult(r"(?P()+)", page or "")
- if output:
- try:
- root = xml.etree.ElementTree.fromstring(safeStringFormat("%s", getBytes(output)))
- retVal = ""
- for column in kb.dumpColumns:
- base64 = True
- for child in root:
- value = child.attrib.get(column, "").strip()
- if value and not re.match(r"\A[a-zA-Z0-9+/]+={0,2}\Z", value):
- base64 = False
- break
-
- try:
- decodeBase64(value)
- except (binascii.Error, TypeError):
- base64 = False
- break
-
- if base64:
- for child in root:
- child.attrib[column] = decodeBase64(child.attrib.get(column, ""), binary=False) or NULL
-
- for child in root:
- row = []
- for column in kb.dumpColumns:
- row.append(child.attrib.get(column, NULL))
- retVal += "%s%s%s" % (kb.chars.start, kb.chars.delimiter.join(row), kb.chars.stop)
-
- except:
- pass
- else:
- retVal = getUnicode(retVal)
- elif kb.jsonAggMode:
- if Backend.isDbms(DBMS.PGSQL):
+ if kb.jsonAggMode:
+ if Backend.isDbms(DBMS.MSSQL):
+ output = extractRegexResult(r"%s(?P.*)%s" % (kb.chars.start, kb.chars.stop), page or "")
+ if output:
+ try:
+ retVal = ""
+ fields = re.findall(r'"([^"]+)":', extractRegexResult(r"{(?P[^}]+)}", output))
+ for row in json.loads(output):
+ retVal += "%s%s%s" % (kb.chars.start, kb.chars.delimiter.join(getUnicode(row[field] or NULL) for field in fields), kb.chars.stop)
+ except:
+ pass
+ else:
+ retVal = getUnicode(retVal)
+ elif Backend.isDbms(DBMS.PGSQL):
output = extractRegexResult(r"(?P%s.*%s)" % (kb.chars.start, kb.chars.stop), page or "")
if output:
retVal = output
else:
- output = extractRegexResult(r"(?P%s.*?%s)" % (kb.chars.start, kb.chars.stop), page or "")
+ output = extractRegexResult(r"%s(?P.*?)%s" % (kb.chars.start, kb.chars.stop), page or "")
if output:
- output = output[len(kb.chars.start):-len(kb.chars.stop)]
try:
retVal = ""
for row in json.loads(output):
@@ -177,7 +154,7 @@ def _oneShotUnionUse(expression, unpack=True, limited=False):
hashDBWrite("%s%s" % (conf.hexConvert or False, expression), retVal)
- elif not any((kb.rowXmlMode, kb.jsonAggMode)):
+ elif not kb.jsonAggMode:
trimmed = _("%s(?P.*?)<" % (kb.chars.start))
if trimmed:
@@ -261,7 +238,7 @@ def unionUse(expression, unpack=True, dump=False):
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:
+ if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.ORACLE, DBMS.PGSQL, DBMS.MSSQL) and expressionFields:
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):
kb.jsonAggMode = True
@@ -271,15 +248,11 @@ def unionUse(expression, unpack=True, dump=False):
query = expression.replace(expressionFields, "'%s'||JSON_ARRAYAGG(%s)||'%s'" % (kb.chars.start, ("||'%s'||" % kb.chars.delimiter).join(expressionFieldsList), kb.chars.stop), 1)
elif Backend.isDbms(DBMS.PGSQL): # Note: ARRAY_AGG does CSV alike output, thus enclosing start/end inside each item
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)
+ elif Backend.isDbms(DBMS.MSSQL):
+ query = "'%s'+(%s FOR JSON AUTO, INCLUDE_NULL_VALUES)+'%s'" % (kb.chars.start, expression, kb.chars.stop)
output = _oneShotUnionUse(query, 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)
- value = parseUnionPage(output)
- kb.rowXmlMode = False
# 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
diff --git a/plugins/generic/databases.py b/plugins/generic/databases.py
index 7a3dfe4dd..eb2061753 100644
--- a/plugins/generic/databases.py
+++ b/plugins/generic/databases.py
@@ -639,18 +639,6 @@ class Databases(object):
logger.info(infoMsg)
values = None
- if Backend.isDbms(DBMS.MSSQL) and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION):
- expression = query
- kb.dumpColumns = []
- kb.rowXmlMode = True
-
- for column in (extractRegexResult(r"SELECT (?P.+?) FROM", query) or "").split(','):
- kb.dumpColumns.append(randomStr().lower())
- expression = expression.replace(column, "%s AS %s" % (column, kb.dumpColumns[-1]), 1)
-
- values = unionUse(expression)
- kb.rowXmlMode = False
- kb.dumpColumns = None
if values is None:
values = inject.getValue(query, blind=False, time=False)