# Remote Code Execution in .tgz File Upload

A .tgz upload endpoint validated inner filenames/extensions but the web server executed a double-extension .pdf.php shell, yielding RCE.

- Payout: $3,100
- Severity: high
- Program: -
- Credited: Nick Berrie
- Disclosed: 2022-01-30
- Write-up quality: 6/10
- Original: https://machevalia.blog/blog/remote-code-execution-in-tgz-file-upload
- Library page: https://bugbountylibrary.com/r/blog%3Amachevalia-blog-blog-remote-code-execution-in-tgz-file-upload
- Tags: domain:web impact:rce kind:bounty platform:blog sev:high technique:parser-differential technique:source-review vuln:file-upload vuln:rce

**Target:** .tgz archive upload endpoint with server-side unpacking and job folder
**Root cause:** The application validated the expected filename and first extension inside the unpacked .tgz, but the web server mapped the trailing .php extension, so a file named like FileName123.pdf.php passed validation and was served/executed as PHP.
**Steps:**
1. Obtain a legitimately formatted .tgz sample (expected 4 named files with specific extensions).
2. Upload a .tgz with an added shell: extra files are dropped, so it fails.
3. Replace an expected file with a same-named .php shell: rejected, revealing name+extension filtering.
4. Repack with `FileName123.pdf.php` (expected name, first extension .pdf, trailing .php).
5. Upload; app accepts, processes job, and writes the shell into an accessible job folder.
6. Request the shell and run commands to confirm RCE, then delete it.
**How found:** Noticed slow error responses suggesting the server unpacks and inspects archive contents rather than checking extension/MIME. Extra files were silently dropped, and replacing an expected file with a .php shell was rejected, revealing filtering on exact filename and extension. Hypothesis: filter checks first extension while server honors last.
**Exploitation:** Attacker uploads a crafted .tgz containing a file named with the expected base name plus a benign first extension and a .php trailing extension; the app's filter approves it, the web server executes it as PHP from the job folder, giving command execution.
**Insight:** When an upload is an archive, the validator and the executor may parse filenames differently: try double extensions where the validator reads the first suffix and the web server maps the last, and use slow error timing to infer server-side unpacking.
