errorlogs.net /Web Shell Detection
PHP shells backdoors
Web Shell Log Signatures

PHP in Uploads Directory (strongest indicator)

# PHP file served from uploads — almost never legitimate
185.220.101.45 - - [10/Apr/2025:02:14:00 +0000] "GET /wp-content/uploads/2025/03/image.php HTTP/1.1" 200 18420
185.220.101.45 - - [10/Apr/2025:02:14:01 +0000] "POST /wp-content/uploads/2025/03/image.php HTTP/1.1" 200 420

# Shell with cmd parameter (executing OS commands)
185.220.101.45 - - [10/Apr/2025:02:15:00 +0000] "GET /wp-content/uploads/2025/03/thumb.php?cmd=whoami HTTP/1.1" 200 8
185.220.101.45 - - [10/Apr/2025:02:15:30 +0000] "GET /wp-content/uploads/2025/03/thumb.php?cmd=cat+/etc/passwd HTTP/1.1" 200 1840

Known Shell File Names

# Classic named shells
91.108.4.12 - - [10/Apr/2025:03:00:00 +0000] "GET /c99.php HTTP/1.1" 200 45200
91.108.4.12 - - [10/Apr/2025:03:00:01 +0000] "GET /r57.php HTTP/1.1" 200 38100
91.108.4.12 - - [10/Apr/2025:03:00:02 +0000] "GET /b374k.php HTTP/1.1" 200 55000
# Random-looking names are also common — short, no relation to site content
91.108.4.12 - - [10/Apr/2025:03:01:00 +0000] "GET /wp-content/uploads/xbz.php HTTP/1.1" 200 22100

Shell Probe Before Execution (attacker confirming shell is alive)

# Attacker probing recently uploaded file — small response = echo output
185.220.101.45 - - [10/Apr/2025:02:13:55 +0000] "GET /wp-content/uploads/2025/03/image.php HTTP/1.1" 200 3
# 3-byte response is likely "ok\n" — a health check echo in the shell

# Followed immediately by exploitation
185.220.101.45 - - [10/Apr/2025:02:13:56 +0000] "POST /wp-content/uploads/2025/03/image.php HTTP/1.1" 200 1840

Encoded Shell Commands

# base64-encoded command (evades simple string matching)
185.220.101.45 - - [10/Apr/2025:02:20:00 +0000] "GET /admin/tmp.php?x=Y2F0IC9ldGMvcGFzc3dk HTTP/1.1" 200 1840
# Decode: echo 'Y2F0IC9ldGMvcGFzc3dk' | base64 -d  →  cat /etc/passwd

# Hex-encoded via PHP hex2bin or similar
185.220.101.45 - - [10/Apr/2025:02:20:30 +0000] "POST /include/config.php HTTP/1.1" 200 4200
Filesystem Checks (Beyond Logs)
bash
# Find PHP files in upload directories
find /var/www/html/wp-content/uploads -name "*.php" -o -name "*.phtml" -o -name "*.php5"

# PHP files modified or created in the last 7 days (anywhere in webroot)
find /var/www/html -name "*.php" -newer /var/www/html/index.php -mtime -7 -ls

# Search PHP files for shell indicators
grep -rEl "(eval|base64_decode|str_rot13|gzinflate|gzuncompress)\s*\(" /var/www/html/ 2>/dev/null

# Find files with shell_exec / system / passthru (execution functions)
grep -rEl "shell_exec|passthru|proc_open|popen|system\s*\(" /var/www/html/ 2>/dev/null

# Hidden files in web root (dot files shouldn't be there)
find /var/www/html -name ".*" -not -name ".htaccess" -ls

# Files owned by www-data that were recently modified (server wrote them = suspicious)
find /var/www/html -user www-data -name "*.php" -mtime -14 -ls
Log-Based Detection Commands
bash
LOG=/var/log/nginx/access.log

# PHP execution in upload/image/media directories
grep -iE "/(uploads|files|media|images|cache|tmp)/.*\.php" $LOG

# cmd= / exec= parameter to any PHP file
grep -iE "\.php\?(cmd|exec|command|run|shell|code)=" $LOG

# Known shell names
grep -iE "/(c99|r57|b374k|wso|alfa|indoxploit|priv8|symlink|shell|backdoor|hack)\.php" $LOG

# Large POST to unexpected PHP files (shell receiving data)
awk '$6=="\"POST" && $7 ~ /\.php/ && $10+0 > 500' $LOG | grep -v "wp-cron\|wp-login\|xmlrpc\|wp-admin\|upload\|contact"

# All IPs that accessed a PHP file in uploads
grep -iE "/uploads/.*\.php" $LOG | awk '{print $1}' | sort -u
🚨 Confirmed Web Shell
If you find a 200 response to a PHP file in /uploads/ or any non-PHP directory: your server is actively compromised. Immediately: take the site offline, preserve logs, remove the shell file, audit the entire filesystem with the commands above, rotate all credentials, and check for cron jobs added by the attacker (crontab -l -u www-data).