How to Find Hidden WordPress Backdoors Using WP-CLI and SQL
30 Juli 2026
0
Internet TechnologyComments (0)
Log in to leave a comment
No posts yet
Log in to leave a comment
No posts yet
Clicking the core update button on an outsourced WordPress site can give anyone pause. Delaying updates out of fear that a broken theme or custom plugin will turn the screen white eventually leaves the site riddled with vulnerabilities. When anxiety strikes that you might have already been compromised, here is a practical inspection workflow to audit your database and server as quickly as possible.
Attackers usually enter quietly. A common trick is directly modifying the database's wp_usermeta table to stealthily grant administrator privileges to an existing account. These accounts often stay hidden from the standard login list, but querying the DB directly exposes them immediately.
`sql
SELECT
u.ID,
u.user_login,
u.user_email,
u.user_registered,
m.meta_value AS capabilities
FROM
wp_users u
INNER JOIN
wp_usermeta m ON u.ID = m.user_id
WHERE
m.meta_key = 'wp_capabilities'
AND (
m.meta_value LIKE '%"administrator"%'
OR m.meta_value LIKE '%"administrator":true%'
)
ORDER BY
u.user_registered DESC;
`
Next is the file system. If there are PHP files sitting in the wp-content/uploads directory—a place reserved strictly for images and documents—nine times out of ten, it's a backdoor. Open an SSH terminal and scan for suspicious files.
`bash
find wp-content/uploads/ -type f -name "*.php" -ls
find . -type f -name "*.php" -exec grep -HnE "(eval(|base64_decode(|gzinflate(|passthru(|shell_exec()" {} ;
wp core verify-checksums --include-root
`
You also need to check the logs. Attacks targeting the REST API typically flood the server with POST requests in a short time frame, aiming to get 200 or 201 HTTP responses. Extract suspicious requests from your Nginx access.log.
`bash
grep -E "POST|PUT" /var/log/nginx/access.log | grep -E "/wp-json/|rest_route=" | awk '/ {print $1, $4, $6, $7, $9}' | sort | uniq -c | sort -nr | head -n 30
`
Applying patches directly to a live production site isn't bravery—it's reckless. Always create a staging environment first by cloning both the database and the file system.
`bash
wp db export production_backup.sql --add-drop-table
rsync -avz --exclude='wp-content/cache' /var/www/html/ staging:/var/www/staging/
wp db import production_backup.sql
wp search-replace 'https://example.com' 'https://staging.example.com' --skip-columns=guid
wp plugin list --update=available --fields=name,version,update_version,requires_php --format=table
`
Once replication is complete, run the updates and spend just five minutes testing core features. Manually testing user registration, login, adding items to the cart, opening the checkout page, and submitting contact forms will give you true peace of mind.
There are frustrating situations where a plugin developer hasn't issued an update, preventing you from bumping the core version right away. In those cases, buy time by blocking access to specific paths at the server level or WAF. If you're using Nginx, block REST API user creation and batch processing endpoints in nginx.conf, excluding your own IP address.
`nginx
location ~* ^/wp-json/(batch/v1|wp/v2/users) {
limit_except GET {
allow 192.0.2.1;
deny all;
}
try_files $uri args;
}
`
As an extra safety net, set up a backup script to upload the DB and uploads folder to S3 in case the unexpected happens.
`bash
#!/bin/bash
WP_PATH="/var/www/html"
BACKUP_DIR="/tmp/wp_backups"
DATE=$(date +%Y%m%d_%H%M%S)
S3_BUCKET="s3://my-wordpress-secure-backups"
mkdir -p $BACKUP_DIR
wp db export BACKUP_DIR/db_DATE.sql --path=$WP_PATH --quiet
tar -czf BACKUP_DIR/files_DATE.tar.gz -C $WP_PATH wp-content/uploads/
aws s3 cp BACKUP_DIR/db_DATE.sql S3_BUCKET/db_DATE.sql
aws s3 cp BACKUP_DIR/files_DATE.tar.gz S3_BUCKET/files_DATE.tar.gz
find $BACKUP_DIR -type f -mtime +7 -delete
`
Clearing malicious code and applying security patches isn't the end of the story. An attacker could still regain access using stolen session cookies. Flush out all active sessions and completely regenerate the salt keys in wp-config.php.
`bash
wp user list --field=ID | xargs -n 1 wp user session destroy --all
wp config shuffle-salts
`
Disable the built-in capability to edit plugin or theme files directly from the admin dashboard. This serves as a minimal safeguard to prevent attackers from injecting code even if they manage to gain administrative privileges. Add these two lines to wp-config.php:
`php
define( 'DISALLOW_FILE_EDIT', true );
define( 'DISALLOW_FILE_MODS', true );
`
Finally, drop a custom filter into the mu-plugins directory to prevent unauthenticated users from scraping the user list via the REST API.
`php
<?php
/**
`