Introduction
A web shell is a malicious script uploaded to a web server through a vulnerable file upload feature. Once placed in a web-accessible directory, the attacker can execute arbitrary commands on the server by visiting the script URL. Web shells are one of the most common post-exploitation tools because they provide persistent, stealthy remote access through the web server's normal HTTP port.
Symptoms
- Unknown PHP/ASP/JSP files found in upload directories
- Web server access log shows POST requests to upload form followed by GET requests to unusual files
- Unexpected files with names like
shell.php,cmd.php,c99.php,r57.php - Server making outbound connections to suspicious external IPs
- File upload directory contains files with executable extensions
Common Causes
- File upload form not validating file extensions or MIME types
- Upload directory configured with execute permissions
- Image upload vulnerability allowing PHP code in image metadata (EXIF)
- No file type whitelist implemented on the server
- Double extension bypass (file.php.jpg) not handled correctly
Step-by-Step Fix
- 1.Search for web shells on the server:
- 2.```bash
- 3.# Search for common web shell patterns
- 4.sudo grep -r --include="*.php" -l "eval(|base64_decode(|system(|passthru(|exec(|shell_exec(" /var/www/html/
- 5.# Search for recently modified PHP files
- 6.sudo find /var/www/html -name "*.php" -mtime -7 -ls
- 7.# Search in upload directories specifically
- 8.sudo find /var/www/html/uploads -type f -name "*.php" -o -name "*.phtml" -o -name "*.phar"
- 9.
` - 10.Analyze the web shell to understand attacker activity:
- 11.```bash
- 12.# Check the web shell access log
- 13.sudo grep "shell.php" /var/log/nginx/access.log
- 14.sudo grep "shell.php" /var/log/apache2/access.log
- 15.# Check when it was uploaded
- 16.stat /var/www/html/uploads/shell.php
- 17.# Review the shell's capabilities
- 18.cat /var/www/html/uploads/shell.php
- 19.
` - 20.Remove the web shell and any other suspicious files:
- 21.```bash
- 22.# Remove the identified web shell
- 23.sudo rm /var/www/html/uploads/shell.php
- 24.# Remove any other unexpected executable files in upload dirs
- 25.sudo find /var/www/html/uploads -type f \( -name "*.php" -o -name "*.phtml" \) -delete
- 26.
` - 27.Fix the file upload vulnerability:
- 28.```php
- 29.// BEFORE (vulnerable):
- 30.move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $_FILES['file']['name']);
// AFTER (secure): $allowed = ['jpg', 'jpeg', 'png', 'gif', 'pdf']; $ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION)); if (!in_array($ext, $allowed)) { die('Invalid file type'); } // Generate a random filename $filename = bin2hex(random_bytes(16)) . '.' . $ext; move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $filename); ```
- 1.Remove execute permissions from the upload directory:
- 2.```nginx
- 3.# Nginx - disable PHP execution in uploads
- 4.location /uploads/ {
- 5.location ~ \.php$ {
- 6.deny all;
- 7.return 403;
- 8.}
- 9.}
- 10.
` - 11.Check for additional compromise indicators:
- 12.```bash
- 13.# Check for new user accounts
- 14.sudo grep $(date +%Y-%m-%d) /var/log/auth.log | grep "useradd|adduser"
- 15.# Check for new cron jobs
- 16.sudo crontab -l
- 17.for user in $(cut -d: -f1 /etc/passwd); do
- 18.sudo crontab -u $user -l 2>/dev/null
- 19.done
- 20.# Check for reverse shell connections
- 21.sudo ss -tlnp | grep -v "nginx|apache|sshd"
- 22.
`
Prevention
- Implement strict file type whitelisting on all upload forms
- Store uploaded files outside the web root or in a directory without execute permissions
- Rename uploaded files to random names to prevent direct URL access
- Use Content Security Policy (CSP) headers to limit script execution
- Implement Web Application Firewall (WAF) rules to detect web shell patterns
- Scan uploaded files with antivirus/malware detection engines