Clean WordPress database infected with Malware
I just finished a new job to clean a WordPress website that was compromised with malicious codes. Removing the malicious codes from the theme files is easy, but the complex job is to remove them from MySQL database tables.
Here is the step-by-step process.
Note: for referene purpose, I have used table prefix as wp_. In your case, you need to use your actutal table prefix.
Check for hidden admin users (again, properly)
Even if you deleted users, sometimes roles remain or are reassigned.
SELECT u.ID, u.user_login, u.user_email FROM wt_users u JOIN wt_usermeta um ON u.ID = um.user_id WHERE um.meta_key = 'wt_capabilities' AND um.meta_value LIKE '%administrator%';
Only expected admins should appear.
Scan for suspicious content (VERY IMPORTANT)
Malware often hides in:
- posts
- options
- widgets
- plugin settings
Search for common malicious patterns
SELECT * FROM wt_posts
WHERE post_content LIKE '%<script%'
OR post_content LIKE '%eval(%'
OR post_content LIKE '%base64_decode%'
OR post_content LIKE '%iframe%';
SELECT * FROM wt_options
WHERE option_value LIKE '%<script%'
OR option_value LIKE '%base64%'
OR option_value LIKE '%eval(%';
Check wp_options (most common infection point)
Focus on autoloaded options:
SELECT option_name FROM wt_options WHERE autoload = 'yes';
Then inspect suspicious ones manually:
SELECT option_name, option_value FROM wt_options WHERE option_name IN ('siteurl','home','active_plugins');
Look for:
- Unknown URLs
- Injected JS
- Unknown plugins
Check for rogue admin-related options
SELECT * FROM wt_options WHERE option_name LIKE '%user%' OR option_name LIKE '%role%';
Check scheduled malware (Action Scheduler / Cron)
Since you saw actionscheduler tables:
SELECT * FROM wt_actionscheduler_actions WHERE hook LIKE '%eval%' OR hook LIKE '%malicious%' OR hook LIKE '%wp_%';
Also check WP Cron:
SELECT option_value FROM wt_options WHERE option_name = 'cron';
Malware often hides scheduled reinfection scripts here.
Check for hidden injected links (SEO spam)
SELECT ID, post_title FROM wt_posts WHERE post_content LIKE '%href=% AND post_content LIKE '%http%';
Look for casino / pharma / unknown domains.
Check usermeta for hidden capabilities
SELECT * FROM wt_usermeta WHERE meta_key LIKE '%capabilities%' AND meta_value NOT LIKE '%administrator%' AND meta_value LIKE '%admin%';
Look for suspicious option names
Hackers often create random options:
SELECT option_name FROM wt_options WHERE option_name REGEXP '^[a-zA-Z0-9]{20,}$';
Clean orphaned data (safe cleanup)
DELETE pm FROM wt_postmeta pm LEFT JOIN wt_posts p ON pm.post_id = p.ID WHERE p.ID IS NULL;
DELETE um FROM wt_usermeta um LEFT JOIN wt_users u ON um.user_id = u.ID WHERE u.ID IS NULL;
Add security
- Wordfence / Sucuri
- Disable file editing:
define(‘DISALLOW_FILE_EDIT’, true);