| name | diagnostic-malware-scan |
| description | Scans locally synced WordPress files for suspicious code patterns and potential malware using pattern matching. Detects obfuscation chains, backdoor signatures, suspicious file placements, and dangerous function usage with false positive reduction. |
Diagnostic: Malware & Suspicious Code Scan
You perform pattern-based malware and suspicious code detection on locally synced WordPress files. This diagnostic identifies potential security threats through pattern matching without requiring remote server access.
IMPORTANT: This is a SEPARATE category from core integrity checking (which uses WP-CLI verify-checksums). This skill scans for suspicious patterns in ALL synced files, not just core files.
Overview
This skill scans locally synced WordPress files at .sites/{site-name}/ for four categories of suspicious patterns:
- Obfuscation Chains — Critical severity, indicates encoded/hidden malicious code
- Known Backdoor Signatures — Critical severity, matches known malware patterns
- Suspicious File Placements — Warning severity, files in unexpected locations
- Dangerous Function Usage with User Input — Warning severity, risky coding patterns
How It Works
Prerequisites
Before running scans, you need:
- Site name from connection profile (e.g.,
example-com)
- Local sync directory:
.sites/{site-name}/
- Files must have been synced via the
/connect command
If the local sync directory doesn't exist or is empty, return an error finding and skip scans.
Scan Location
CRITICAL DISTINCTION: This skill scans LOCAL files at .sites/{site-name}/, NOT remote files via SSH. Files have already been synced by the /connect command. This makes grep operations fast and avoids SSH overhead.
Pattern Categories
Category 1: Obfuscation Chains (Critical)
These patterns indicate deliberate code obfuscation, commonly used by malware to hide payloads.
Pattern 1a: eval with base64_decode
grep -rn 'eval\s*(\s*base64_decode' .sites/{site-name}/ --include="*.php"
What it finds: Code that decodes base64 content and immediately executes it with eval(). This is a common malware pattern.
Pattern 1b: Double base64_decode
grep -rn 'base64_decode.*base64_decode' .sites/{site-name}/ --include="*.php"
What it finds: Multiple layers of base64 encoding. Legitimate code rarely needs double encoding.
Pattern 1c: gzinflate/gzuncompress with base64_decode
grep -rn 'gzinflate\s*(\s*base64_decode\|gzuncompress\s*(\s*base64_decode' .sites/{site-name}/ --include="*.php"
What it finds: Compressed AND encoded content. Often used to hide malicious scripts.
Pattern 1d: str_rot13 with eval or base64_decode
grep -rn 'str_rot13.*eval\|str_rot13.*base64_decode' .sites/{site-name}/ --include="*.php"
What it finds: ROT13 encoding combined with execution functions. Another obfuscation technique.
IMPORTANT: Do NOT flag standalone base64_decode without eval/exec/double-encoding. Too many false positives (legitimate uses: email encoding, data URIs, image embedding).
For each match in Category 1:
Finding ID: SUSP-OBFUSCATE-{3-char-md5-of-filepath:line}
Severity: Critical
Category: Suspicious Code
Example Finding:
{
"id": "SUSP-OBFUSCATE-7a9",
"severity": "Critical",
"category": "Suspicious Code",
"title": "Obfuscated code execution detected",
"summary": "This file contains hidden code that decodes and executes itself, a common malware technique.",
"detail": "Found pattern 'eval(base64_decode(' at line 42. Context:\n\n40: // Plugin initialization\n41: function init_plugin() {\n42: eval(base64_decode('ZXZhbCgnZWNobyAi...'));\n43: register_hooks();\n44: }\n\nThis pattern is commonly used by malware to hide malicious code from security scans.",
"location": "wp-content/plugins/suspicious-plugin/init.php:42",
"fix": "Investigate this file immediately. If you did not add this code intentionally, it is likely malware. Compare with the original plugin source from WordPress.org or the theme developer. If confirmed malicious, delete the file and scan for other compromised files. Consider restoring from a clean backup."
}
Category 2: Known Backdoor Signatures (Critical)
These patterns match known backdoor and webshell signatures.
Pattern 2a: Common backdoor names (case-insensitive)
grep -rin 'FilesMan\|WSO\|c99shell\|r57shell\|b374k' .sites/{site-name}/ --include="*.php"
What it finds: References to well-known PHP backdoors and webshells.
Pattern 2b: Shell execution with user input
grep -rn 'shell_exec\s*(\s*\$_\|system\s*(\s*\$_\|passthru\s*(\s*\$_' .sites/{site-name}/ --include="*.php"
What it finds: Direct execution of shell commands from user input ($_GET, $_POST, $_REQUEST). Major security risk.
For each match in Category 2:
Finding ID: SUSP-BACKDOOR-{3-char-md5-of-filepath:line}
Severity: Critical
Category: Suspicious Code
Example Finding:
{
"id": "SUSP-BACKDOOR-3c1",
"severity": "Critical",
"category": "Suspicious Code",
"title": "Known backdoor signature detected",
"summary": "This file contains code matching a known malicious backdoor.",
"detail": "Found backdoor signature 'FilesMan' at line 156. Context:\n\n154: <?php\n155: if (isset($_GET['action'])) {\n156: eval(FilesMan($_GET['cmd']));\n157: }\n158: ?>\n\nFilesMan is a known PHP backdoor that allows remote file management and code execution.",
"location": "wp-content/themes/child-theme/functions.php:156",
"fix": "This is a confirmed backdoor. Remove this code immediately. The file may have been compromised. Restore functions.php from a clean backup or the original theme source. After removing, scan the entire site for other infected files and review server access logs for unauthorized access."
}
Example Finding (Shell Execution):
{
"id": "SUSP-BACKDOOR-8d4",
"severity": "Critical",
"category": "Suspicious Code",
"title": "Shell command execution from user input",
"summary": "This file executes system commands directly from user-provided data, allowing code execution.",
"detail": "Found pattern 'system($_GET' at line 23. Context:\n\n21: function run_command() {\n22: $cmd = $_GET['command'];\n23: system($_GET['command']);\n24: }\n\nThis allows an attacker to run arbitrary system commands by visiting a URL like: yoursite.com/file.php?command=rm%20-rf%20/",
"location": "wp-content/plugins/vulnerable-plugin/admin.php:23",
"fix": "Remove this code immediately. If this is a legitimate admin function, rewrite it to use a whitelist of allowed commands and proper authentication. Never execute user input directly. If this is not your code, the plugin is malicious and should be deleted."
}
Category 3: Suspicious File Placements (Warning)
Files in locations where they shouldn't exist.
Pattern 3a: PHP files in uploads directory
find .sites/{site-name}/wp-content/uploads -name "*.php" -type f 2>/dev/null
What it finds: PHP files in wp-content/uploads/. WordPress uploads directory should only contain media files (images, PDFs, etc.), not executable PHP.
Pattern 3b: Unexpected PHP files in wp-includes/
This requires comparing found PHP files against a known good list of core wp-includes/ files. For v1 implementation:
find .sites/{site-name}/wp-includes -name "*.php" -type f | wc -l
Count total PHP files in wp-includes/. WordPress 6.x core has approximately 400-450 PHP files in wp-includes/. If count is significantly higher (e.g., >500), flag for manual review.
Alternative: Check for files with suspicious names in wp-includes/:
find .sites/{site-name}/wp-includes -name "*.php" -type f | grep -E '(shell|backdoor|c99|wso|eval|exec|system|cmd)'
For each match in Category 3:
Finding ID: SUSP-PLACEMENT-{3-char-md5-of-filepath}
Severity: Warning
Category: Suspicious Code
Example Finding (PHP in uploads):
{
"id": "SUSP-PLACEMENT-5f2",
"severity": "Warning",
"category": "Suspicious Code",
"title": "PHP file found in uploads directory",
"summary": "A PHP script was found in the uploads folder, where only media files belong.",
"detail": "Found PHP file: wp-content/uploads/2024/03/shell.php\n\nThe uploads directory should only contain media files (images, videos, documents). PHP files here can be executed by visiting their URL directly, bypassing WordPress security.",
"location": "wp-content/uploads/2024/03/shell.php",
"fix": "Investigate this file. If you did not upload it intentionally, delete it immediately. Check server access logs to determine how it was uploaded. Consider disabling PHP execution in uploads directory via .htaccess:\n\n<Files *.php>\ndeny from all\n</Files>"
}
Example Finding (Suspicious wp-includes file):
{
"id": "SUSP-PLACEMENT-9a7",
"severity": "Warning",
"category": "Suspicious Code",
"title": "Unexpected file in wp-includes directory",
"summary": "A file with a suspicious name was found in WordPress core directory.",
"detail": "Found file: wp-includes/wp-shell.php\n\nThis file name does not match standard WordPress core files. It may have been added by malware.",
"location": "wp-includes/wp-shell.php",
"fix": "Verify whether this is a legitimate WordPress core file by checking WordPress.org source code for your version. If not found in official source, delete this file and run a full malware scan."
}
Category 4: Dangerous Function Usage with User Input (Warning)
Coding patterns that are risky even if not immediately malicious.
Pattern 4a: eval with variable (not string literal)
grep -rn 'eval\s*(\s*\$' .sites/{site-name}/ --include="*.php"
What it finds: eval() with variable input. Even without base64, eval($var) is dangerous if $var comes from user input.
Pattern 4b: assert with variable input
grep -rn 'assert\s*(\s*\$' .sites/{site-name}/ --include="*.php"
What it finds: assert() with variable input. Can be exploited similarly to eval() in older PHP versions.
Pattern 4c: preg_replace with /e modifier (deprecated)
grep -rn 'preg_replace.*\/.*e' .sites/{site-name}/ --include="*.php"
What it finds: preg_replace() with /e modifier, which evaluates matched code. Deprecated since PHP 5.5, removed in PHP 7, commonly exploited.
Pattern 4d: create_function (deprecated)
grep -rn 'create_function' .sites/{site-name}/ --include="*.php"
What it finds: create_function(), deprecated in PHP 7.2, removed in PHP 8.0. Often used in exploits.
For each match in Category 4:
Finding ID: SUSP-DANGEROUS-{3-char-md5-of-filepath:line}
Severity: Warning
Category: Suspicious Code
Example Finding:
{
"id": "SUSP-DANGEROUS-2e8",
"severity": "Warning",
"category": "Suspicious Code",
"title": "Use of deprecated and dangerous function",
"summary": "This code uses a deprecated PHP function that is commonly exploited by attackers.",
"detail": "Found 'create_function' at line 87. Context:\n\n85: function dynamic_callback($input) {\n86: $callback = create_function('$val', 'return $val * 2;');\n87: return array_map($callback, $input);\n88: }\n\ncreate_function() is deprecated (PHP 7.2+) and removed (PHP 8.0+). It uses eval() internally and is commonly exploited for code injection.",
"location": "wp-content/plugins/old-plugin/helpers.php:87",
"fix": "Replace create_function() with an anonymous function (closure):\n\nBefore: $callback = create_function('$val', 'return $val * 2;');\nAfter: $callback = function($val) { return $val * 2; };\n\nIf this plugin is from WordPress.org, check for an updated version."
}
False Positive Reduction
To reduce false positives, apply these filters:
Exclusion Rules
Skip these directories:
node_modules/ — Build tools may use legitimate base64 for data
vendor/ — Third-party libraries may have legitimate uses
- Files matching
*.min.js — Minified JS often contains legitimate base64 for data URIs
Implementation:
grep -rn 'pattern' .sites/{site-name}/ --include="*.php" \
--exclude-dir=node_modules \
--exclude-dir=vendor \
--exclude='*.min.js'
Context-Aware Filtering
For base64_decode matches:
- Only flag when combined with eval/exec/double-encoding
- Do NOT flag standalone base64_decode
For WordPress.org plugins:
- Note in finding if the file is in a WP.org plugin directory
- Add to detail: "Note: This is in a plugin from WordPress.org (pluginname). This may be a legitimate use case or a plugin vulnerability. Check the plugin's changelog and reviews."
- Lower confidence indicator
Detection:
Output Format
Return findings as a JSON array. Each finding must include:
id (string) — Deterministic ID: SUSP-{PATTERN_TYPE}-{3-char-md5-of-filepath:line}
severity (string) — "Critical" for Categories 1-2, "Warning" for Categories 3-4
category (string) — "Suspicious Code" (NOT "Security")
title (string) — Short descriptive title
summary (string) — One non-technical sentence explaining the risk
detail (string) — Technical detail including:
- The matched line content
- 2 lines before and after for context
- File path and line number
- Explanation of why this pattern is suspicious
location (string) — Relative file path with line number (e.g., "wp-content/plugins/plugin-name/file.php:123")
fix (string) — Specific remediation recommendation
Example Complete Output:
[
{
"id": "SUSP-OBFUSCATE-7a9",
"severity": "Critical",
"category": "Suspicious Code",
"title": "Obfuscated code execution detected",
"summary": "This file contains hidden code that decodes and executes itself, a common malware technique.",
"detail": "Found pattern 'eval(base64_decode(' at line 42 in wp-content/plugins/suspicious-plugin/init.php.\n\nContext:\n40: // Plugin initialization\n41: function init_plugin() {\n42: eval(base64_decode('ZXZhbCgnZWNobyAi...'));\n43: register_hooks();\n44: }\n\nThis pattern is commonly used by malware to hide code from security scans.",
"location": "wp-content/plugins/suspicious-plugin/init.php:42",
"fix": "Investigate immediately. Compare with original plugin source. If malicious, delete file and scan for other compromised files."
},
{
"id": "SUSP-PLACEMENT-5f2",
"severity": "Warning",
"category": "Suspicious Code",
"title": "PHP file found in uploads directory",
"summary": "A PHP script was found in the uploads folder, where only media files belong.",
"detail": "Found PHP file: wp-content/uploads/2024/03/shell.php. The uploads directory should only contain media files.",
"location": "wp-content/uploads/2024/03/shell.php",
"fix": "Investigate this file. If not intentionally uploaded, delete immediately. Consider disabling PHP execution in uploads via .htaccess."
}
]
Error Handling
Local Sync Directory Missing
If .sites/{site-name}/ doesn't exist:
{
"id": "SUSP-SYNC-001",
"severity": "Warning",
"category": "Suspicious Code",
"title": "Local files not synced",
"summary": "Cannot perform malware scan because files have not been synced locally.",
"detail": "The local sync directory '.sites/{site-name}/' does not exist. Files must be synced via the /connect command before malware scanning can be performed.",
"location": "Local Sync",
"fix": "Run the /connect command to sync site files locally, then retry this diagnostic."
}
Empty Sync Directory
If directory exists but has no PHP files:
{
"id": "SUSP-SYNC-002",
"severity": "Info",
"category": "Suspicious Code",
"title": "No PHP files found to scan",
"summary": "The local sync directory exists but contains no PHP files.",
"detail": "Found 0 PHP files in '.sites/{site-name}/'. This may indicate an incomplete sync or a non-PHP site.",
"location": "Local Sync",
"fix": "Verify the site was synced correctly. Re-run /connect if needed."
}
Grep Command Failures
If grep/find commands fail:
{
"id": "SUSP-CMD-003",
"severity": "Warning",
"category": "Suspicious Code",
"title": "Scan command failed",
"summary": "An error occurred while scanning for suspicious code patterns.",
"detail": "Command 'grep -rn pattern' failed with error: {error_output}. This may indicate file permission issues or corrupted local files.",
"location": "Scan Execution",
"fix": "Check file permissions in '.sites/{site-name}/'. Re-sync files if needed."
}
Performance Considerations
- Local scanning is fast: grep on local files is significantly faster than remote SSH commands
- Parallel pattern matching: Different pattern categories can be run in parallel if the framework supports it
- Large file handling: If a single file has many matches (>10), group them into a single finding to avoid cluttering the report
- Line context: Only include 2 lines before/after the match to keep findings concise
Success Criteria
Malware scan is complete when:
- All 4 pattern categories have been scanned
- All matches processed through false positive filters
- WordPress.org plugin context added where applicable
- All findings returned in structured JSON format with deterministic IDs
- Error handling applied for missing/empty sync directories
- No standalone base64_decode flagged without suspicious context