How to find a webshell on a Linux web server
A webshell is a small script that lets an attacker run commands on your server from a web browser. It is often only a few lines long, hidden among thousands of legitimate files, and it can stay there for months. If your server hosts a website, an API with file uploads or a CMS like WordPress, this is the threat to check first.
How webshells get onto a server
- An upload form that accepts a
.phpfile disguised as an image; - an outdated plugin or theme with a known vulnerability;
- stolen FTP, SSH or admin panel credentials;
- a vulnerable dependency that allows remote code execution.
Once in place, the webshell is the attacker's door: they use it to read your configuration, dump the database, install a cryptominer or move to other servers.
Step 1: Search for dangerous functions
Webshells rely on a handful of functions to run commands or decode hidden payloads. For PHP:
grep -rIn --include=*.php -E "eval\(|assert\(|base64_decode\(|gzinflate\(|str_rot13\(|shell_exec\(|passthru\(|system\(|proc_open\(" /var/www 2>/dev/null | cut -c1-200
Legitimate code uses some of these, so focus on combinations: eval(base64_decode(...)), eval(gzinflate(...)) or assert($_POST[...]) are almost never legitimate.
For Python and Node.js applications, look for code that executes what it receives:
grep -rIn --include=*.py -E "exec\(|eval\(|subprocess\..*request" /srv /opt 2>/dev/null
grep -rIn --include=*.js -E "child_process|eval\(req\.|Function\(req\." /srv /opt --exclude-dir=node_modules 2>/dev/null
Step 2: Look for files that should not be there
# PHP files in upload folders
find /var/www -path "*upload*" -name "*.php" 2>/dev/null
# Files with double extensions
find /var/www -regextype egrep -regex ".*\.(jpg|png|gif|txt)\.php" 2>/dev/null
# Recently changed code files
find /var/www -type f \( -name "*.php" -o -name "*.js" -o -name "*.py" \) -mtime -7 -ls 2>/dev/null
Images do not need to be executable code. A .php file inside wp-content/uploads is a red flag on its own.
Step 3: Spot obfuscated code
Attackers hide webshells by encoding them. Two quick ways to surface them:
# Files with extremely long lines (typical of encoded payloads)
find /var/www -name "*.php" -exec awk 'length > 1000 {print FILENAME; exit}' {} \; 2>/dev/null
# Long base64 strings
grep -rIl --include=*.php -E "[A-Za-z0-9+/]{300,}={0,2}" /var/www 2>/dev/null
Step 4: Check the access logs
A webshell has to be called to be used. Look for POST requests to files that should not receive them, especially in upload or cache folders:
grep "POST" /var/log/nginx/access.log* 2>/dev/null | grep -E "uploads|cache|tmp|\.php" | awk '{print $1, $7}' | sort | uniq -c | sort -rn | head -20
The same IP repeatedly posting to one obscure file is a webshell being used. Note the IP and the dates: they tell you when the compromise started.
Step 5: Compare with a clean copy
For a CMS, the most reliable test is a comparison. Download the exact same version of the CMS and its plugins, then diff:
diff -rq /var/www/site /tmp/clean-copy | grep -v "uploads"
Any core file that differs from the official release has been modified.
Step 6: Clean up and close the hole
- Save a copy of the malicious files and the logs (for the investigation).
- Delete the webshells, or restore the site from a backup made before the compromise date.
- Update the CMS, plugins, themes and dependencies.
- Change every password: admin accounts, database, FTP/SSH, API keys stored in configuration files.
- Block code execution in upload folders. For nginx:
location ~* /uploads/.*\.php$ { deny all; }
- Check the rest of the server: a webshell is often the first step before a cryptominer or a backdoor. Our step-by-step malware scan covers what to check next.
Frequently asked questions
Can an antivirus find webshells?
It finds known webshells well, because many attackers reuse the same scripts. Custom or freshly obfuscated webshells can slip through, which is why the manual checks above and log review matter.
My site works normally. Can it still have a webshell?
Yes. That is the point of a webshell: it does not break anything. Many are found months later, during an unrelated audit or after a hosting provider complaint about spam.
How do I prevent webshells?
Keep the CMS and plugins updated, never allow code execution in upload folders, validate uploaded file types on the server side, use strong unique admin passwords, and scan web directories every day.
FounderMode scans your web directories every day, flags vulnerable versions of what you run, and alerts you in Slack when something changes. Start the free trial.