Detecting and Cleaning WordPress Malware with SSH and Grep

Published On: March 16th, 2026|Categories: WordPress|4 min read|

Direct server access via SSH provides the most reliable method for auditing a compromised WordPress installation without the overhead of heavy security plugins.

When a site exhibits symptoms of infection, such as unauthorized redirects or a sudden spike in CPU usage, the underlying file system is the primary source of truth. You must verify the integrity of the core files by comparing local hashes against the official WordPress repository. If a core file like wp-settings.php shows a modification date that does not align with your update history, an injection is likely present. Accessing the server via terminal allows you to identify these anomalies using low-level tools that malware cannot easily hide from.

This approach bypasses the limitations of PHP-based scanners which can be manipulated by malicious code to report false negatives. You gain the ability to search across the entire directory structure in milliseconds using optimized binaries.

Identifying recently altered files is the first step in narrowing the search window to the exact moment of the breach.

Run the command find . -type f -mtime -7 to list every file modified within the last seven days across your entire web root. This command highlights theme files, plugins, and core components that should remain static during normal operation. If you see files in wp-content/uploads/ with a .php extension, you have identified a staging area for a web shell.

Cross-referencing these timestamps with server access logs reveals the IP addresses responsible for the modification. This data is critical for blocking the attacker at the firewall level before proceeding with the cleanup.

Attackers often use the touch command to forge file timestamps, making them appear as if they haven’t been modified in years.

You must execute find . -type f -ctime -7 to check for changes in the file status information, which is harder for automated scripts to falsify. This command tracks metadata changes, such as permission updates or ownership shifts, which frequently accompany the installation of a backdoor. Check for files with 777 permissions, as these indicate a severe security lapse that allows any user to execute code. Secure environments require 644 for files and 755 for directories to maintain a baseline of security. If the find command returns a large volume of results in the /cache/ or /tmp/ directories, focus your attention on executable scripts hidden within these transient folders.

Timely identification of these files prevents the malware from spreading to neighboring accounts on a shared hosting environment. Automated cleanup scripts often miss these edge cases, leading to reinfection within hours of a supposed fix.

Advanced grep Techniques for Malicious Patterns

Pattern matching with grep reveals the presence of functions typically used to execute encoded payloads hidden within legitimate PHP scripts.

Execute grep -rnw . -e "eval(" to find instances of the eval() function, which converts strings into executable PHP code. While some legitimate plugins use this for template engines, malicious actors rely on it to run obfuscated scripts that bypass static analysis. You should also search for base64_decode combined with gzuncompress to find compressed payloads hidden in the database or header files. If the search returns results in wp-config.php, the site’s foundation is compromised and requires immediate manual intervention.

Refine the search by excluding the node_modules or cache folders to reduce noise and decrease the time required for the scan. This focus ensures that you spend your time reviewing actual code rather than minified library assets.

# Find common malware signatures in PHP files
grep -rE "(eval|base64_decode|gzuncompress|str_rot13|shell_exec|passthru|system|exec|proc_open)" . --include=*.php

Identifying web shells and backdoors requires looking for specific function calls that allow system-level command execution.

Search for the GLOBALS superglobal being used with _POST or _REQUEST variables in unusual locations. This pattern is frequently used by attackers to create “stealth” backdoors that only execute when a specific parameter is sent via an HTTP request. For example, grep -r "GLOBALS.*_POST" . will flag scripts that might be accepting remote commands. If you find this in a file named class-wp-util.php that doesn’t belong to core, it is a confirmed shell.

Check for the use of extract($_POST) which allows an attacker to overwrite local variables and hijack the logic flow of a script. This technique is often used in vulnerable themes to gain administrative access without a password.

If the file size of a core component like index.php exceeds 1KB, it likely contains a prepended malicious block.

Malicious code blocks often start with followed by hundreds of empty lines to hide the payload from standard text editors. You must use cat -A index.php | head -n 20 to see hidden characters and long whitespace sequences that indicate a padding attack. Another common tactic is the use of hexadecimal or octal escape sequences to obscure the actual function names. Searching for \x or \1 within PHP files can help identify these obfuscated strings. If the grep results show long strings of seemingly random characters, you are looking at an encrypted payload.

Manual removal is the only way to ensure that the entire injection is purged without breaking the functionality of the site. You must be precise when deleting these blocks to avoid leaving trailing semicolons or open brackets that cause syntax errors.

Verifying Core Integrity and wp-config.php

The wp-config.php file and the root directory are the most common targets for persistent infections.

Check the very beginning of wp-config.php for any include or require statements that point to hidden files in the /tmp/ or /dev/shm/ directories. Attackers use these locations because they are often excluded from standard backups and are wiped on reboot, making forensic analysis difficult. You should also verify that the AUTH_KEY and SECURE_AUTH_KEY salts have not been modified to allow the attacker to maintain active sessions. If the salts have changed, all users will be logged out, but it forces the re-generation of session cookies which invalidates the attacker's access.

Resetting the salts is a mandatory step in the recovery process to ensure that stolen cookies cannot be reused. This action terminates all existing sessions across the entire site instantly.

/** 
 * Example of a malicious injection at the top of a file
 * If you see this, the file is compromised.
 */
$auth_pass = "63a9f0ea7bb98050796b649e85481845";
$color = "#df5";
$default_action = 'FilesMan';

Comparing your installation against a fresh download of the same WordPress version reveals modified core files immediately.

Use diff -r wordpress-core-directory/ current-site-directory/ to see a line-by-line comparison of every file. This command will list every modification, addition, or deletion made to the core software. If wp-login.php has been altered to log credentials to a text file, this comparison will highlight the added fopen() and fwrite() calls. You should replace any modified core files with original versions from the official repository immediately. This is faster and safer than trying to manually clean complex injections from thousands of lines of code.

Deleting the wp-admin and wp-includes directories and replacing them with fresh copies is the most efficient way to clear core infections. This process ensures that no hidden scripts remain in the deep subdirectories of the core framework.

Cleaning the Database and Post-Infection Hardening

Malicious scripts often inject JavaScript or unwanted URLs directly into the wp_posts and wp_options tables to facilitate SEO spam.

Use wp-cli to search for