If you run WordPress sites, this is something you hope never happens.
Recently, two of my websites (pakspace.com and zubair.pk) were compromised. Instead of just cleaning them, I took it further β full recovery + security hardening.
This guide walks you through everything I did, in a practical, real-world way.
π¨ Signs Your WordPress Site Is Hacked
Hereβs what raised the alarm:
- Unknown admin user:
bbp_support_agent - PHP files inside
/wp-content/uploads - Suspicious activity in database
- Possible unauthorized access
These are classic indicators of compromise.
π§© How WordPress Hacks Typically Work

π Step 1: Identify Unauthorized Access
I checked users directly via database:
php -r '
require "/path/to/wp-config.php";
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
$res = $mysqli->query("SELECT user_login, user_email FROM wp_users");
while($row = $res->fetch_assoc()){
print_r($row);
}'
π Found:
- Legit users β
- Rogue admin β
βοΈ Deleted malicious admin immediately
π Step 2: Remove Malicious Files
Biggest red flag:
PHP files inside
/uploads
Scan:
find /home/... -type f -path "*/wp-content/uploads/*.php"
Removed suspicious files.
Verify cleanup:
find /home/... -type f -path "*/wp-content/uploads/*.php" ! -name "index.php"
βοΈ Clean
ποΈ Step 3: Scan Database for Malware
Used safe queries:
SELECT option_name, option_value
FROM wp_options
WHERE option_value LIKE '%<script%'
OR option_value LIKE '%iframe%'
OR option_value LIKE '%base64_decode%'
OR option_value LIKE '%eval(%';
π Result:
- Only ad scripts
- No obfuscated payloads
βοΈ Database clean
π Step 4: Inspect MU Plugins
Found:
wp-content/mu-plugins/woocommerce-analytics-proxy-speed-module.php
Checked manually:
- readable code
- no obfuscation
βοΈ Safe
π Step 5: Block PHP Execution in Uploads
Added .htaccess:
<Files *.php>
deny from all
</Files>
βοΈ Prevents future shell execution
π Step 6: Rotate WordPress Salts
Generated:
curl -s https://api.wordpress.org/secret-key/1.1/salt/
Replaced in wp-config.php
βοΈ Logged out all sessions
β οΈ Step 7: Database Password Rotation (What Went Wrong)
Tried automating password rotationβ¦
π₯ Broke both sites.
Root cause:
- MySQL host mismatch (
localhostvs127.0.0.1) - Script corrupted
wp-config.php
Fix:
- Restored backup
- Recovered instantly
π οΈ Recovery Flow

π Step 8: Security Hardening (CRITICAL)
After cleanup, I didnβt stop there.
β Enabled 2FA Everywhere
- WordPress admin (via plugin)
- SSH server access
[ Password ]
+
[ 2FA Code ]
=
Strong Authentication
π This alone blocks most attacks.
β Server Hardening Done
Key improvements:
- Disabled unnecessary services
- Secured SSH (no weak auth)
- Limited access points
- Ensured proper file permissions
- Checked cron jobs for persistence
- Verified no hidden processes
β WordPress Hardening
Added:
define('DISALLOW_FILE_EDIT', true);
Also:
- removed unused plugins/themes
- updated everything
- enforced strong passwords
β Final State
- β Malware removed
- β Rogue admin removed
- β Database clean
- β Uploads secured
- β Salts rotated
- β 2FA enabled (WP + SSH)
- β Server hardened
- β Sites fully operational
π‘οΈ Key Lessons
- Uploads folder = biggest risk
- Always check for rogue admins
- Donβt blindly automate config edits
- Backups are lifesavers
- 2FA is non-negotiable
- Server-level security matters as much as WordPress
π Ongoing Monitoring
Run periodically:
find /home/... -type f \( -path "*/uploads/*.php" -o -name ".*.php" -o -name "*.phtml" \)
Should always return nothing.
π¬ Final Thoughts
Cleaning a hacked WordPress site isnβt just about removing malware.
Itβs about:
- understanding the attack
- removing persistence
- hardening everything afterward
If you skip hardening, the attacker will come back.

