Script in the Uploads Folder: Catch the Webshell, Not the App
MediumThreat Detection25-35 minAn attacker got a file-upload past the app's filter and dropped a webshell — a server-side script they now drive over HTTP to run commands. It shows up in the nginx access log (/var/log/nginx/access.log) as requests to a .php file living under the site's writable upload directory, returning 200. The tell isn't the volume — it's the LOCATION: a script executing from where user uploads land, a place a real app never serves code from.
The trap: this is a WordPress-style site, so legitimate traffic hammers /wp-admin/admin-ajax.php with POSTs all day, and a busy shopper POSTs repeatedly to /api/checkout. A rule that flags 'any .php that takes POSTs' or 'any source with many POSTs' lights up on those real endpoints — a false positive. What separates the webshell is that it is a script under an UPLOAD/writable path, not an application route.
1. Identify the webshell's request path and record it in /root/case/webshell.txt.
2. Write a detector at /root/detect.sh that reads the access log on stdin and prints the webshell path(s) it flags, one per line. Grading runs bash /root/detect.sh < /var/log/nginx/access.log. Grading also feeds it a second, unseen log the same way, so it must work from what it reads on stdin, not from a hardcoded file path or answer.
3. Tune on LOCATION + TYPE: a script extension (.php/.phtml/.jsp/.aspx…) served from an upload/writable directory (uploads, media, images, files, tmp). MITRE ATT&CK: T1505.003 (Web Shell).
- Find the script executing from the upload/writable directory (the webshell)
- Record the webshell request path to /root/case/webshell.txt
- Write /root/detect.sh: reads the access log on stdin, prints the flagged webshell path(s)
- Tune so the webshell is flagged but the app's own POST endpoints (admin-ajax.php, /api/checkout) are not
Hands-on lab: you work on a real Linux box in your browser, graded on the machine's actual state.
