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 (localhost vs 127.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

  1. Uploads folder = biggest risk
  2. Always check for rogue admins
  3. Don’t blindly automate config edits
  4. Backups are lifesavers
  5. 2FA is non-negotiable
  6. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *