Skip to content

Instantly share code, notes, and snippets.

@daviddarke
Created December 2, 2025 15:53
Show Gist options
  • Select an option

  • Save daviddarke/078c8f4e79e0542d7dce6c09a65aef6e to your computer and use it in GitHub Desktop.

Select an option

Save daviddarke/078c8f4e79e0542d7dce6c09a65aef6e to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
#
# Scan a WordPress database for suspicious content.
#
# Usage (from WP root):
# bash scan_wp_db.sh
#
# Or specify a path to the WP root:
# bash scan_wp_db.sh /var/www/html
#
WP_ROOT="${1:-.}"
WP_CONFIG="$WP_ROOT/wp-config.php"
if [ ! -f "$WP_CONFIG" ]; then
echo "Could not find wp-config.php at: $WP_CONFIG" >&2
exit 1
fi
if ! command -v php >/dev/null 2>&1; then
echo "php CLI is required to read wp-config.php" >&2
exit 1
fi
if ! command -v mysql >/dev/null 2>&1; then
echo "mysql CLI client is required" >&2
exit 1
fi
echo "Reading DB credentials from: $WP_CONFIG"
# Extract DB credentials and table prefix via PHP
CONF_STR=$(php -r "
define('SHORTINIT', true);
include '$WP_CONFIG';
global \$table_prefix;
echo DB_NAME . '|' . DB_USER . '|' . DB_PASSWORD . '|' . DB_HOST . '|' . \$table_prefix;
" 2>/dev/null)
if [ -z "$CONF_STR" ]; then
echo "Failed to read DB settings from wp-config.php" >&2
exit 1
fi
IFS='|' read -r DB_NAME DB_USER DB_PASSWORD DB_HOST TABLE_PREFIX <<< "$CONF_STR"
echo "DB_NAME: $DB_NAME"
echo "DB_USER: $DB_USER"
echo "DB_HOST: $DB_HOST"
echo "TABLE_PREFIX: $TABLE_PREFIX"
echo
mysql_cmd=(mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME")
echo "Testing DB connection..."
if ! "${mysql_cmd[@]}" -e "SELECT 1" >/dev/null 2>&1; then
echo "Could not connect to MySQL with the credentials from wp-config.php" >&2
exit 1
fi
echo "Connection OK."
echo
###############################################
# 1) Suspicious patterns in wp_options table #
###############################################
echo "=== Suspicious patterns in ${TABLE_PREFIX}options.option_value ==="
"${mysql_cmd[@]}" -e "
SELECT option_id,
option_name,
LEFT(option_value, 200) AS sample_value
FROM ${TABLE_PREFIX}options
WHERE option_value REGEXP
'(base64_decode|gzinflate|eval\\(|fromCharCode|document\\.cookie|
<script|<iframe|onerror=|onload=|javascript:|data:text/html)'
ORDER BY option_id DESC
LIMIT 200;
"
echo
#########################################################
# 2) Suspicious patterns in posts (post_content) #
#########################################################
echo '=== Suspicious patterns in ${TABLE_PREFIX}posts.post_content ==='
"${mysql_cmd[@]}" -e "
SELECT ID,
post_type,
post_status,
LEFT(post_title, 60) AS title,
LEFT(post_content, 200) AS sample_content
FROM ${TABLE_PREFIX}posts
WHERE post_content REGEXP
'(base64_decode|gzinflate|eval\\(|fromCharCode|document\\.cookie|
<script|<iframe|onerror=|onload=|javascript:|data:text/html)'
ORDER BY ID DESC
LIMIT 200;
"
echo
#########################################################
# 3) Admin users (check for unexpected administrator) #
#########################################################
echo "=== Administrator accounts (check for unexpected users) ==="
"${mysql_cmd[@]}" -e "
SELECT u.ID,
u.user_login,
u.user_email,
m.meta_value AS capabilities
FROM ${TABLE_PREFIX}users u
JOIN ${TABLE_PREFIX}usermeta m
ON u.ID = m.user_id
WHERE m.meta_key LIKE '%capabilities'
AND m.meta_value LIKE '%\"administrator\"%';
"
echo
#########################################################
# 4) Site URL / Home URL (check for hijacked URLs) #
#########################################################
echo "=== Site URL / Home URL (ensure these are correct) ==="
"${mysql_cmd[@]}" -e "
SELECT option_id, option_name, option_value
FROM ${TABLE_PREFIX}options
WHERE option_name IN ('siteurl', 'home');
"
echo
#########################################################
# 5) Option names that look suspicious #
#########################################################
echo "=== Suspicious-looking option names (often used by malware) ==="
"${mysql_cmd[@]}" -e "
SELECT option_id, option_name, LEFT(option_value, 200) AS sample_value
FROM ${TABLE_PREFIX}options
WHERE option_name REGEXP '(malware|backdoor|shell|spam|inject|redir|redirect)'
ORDER BY option_id DESC
LIMIT 200;
"
echo
echo "Scan complete. Review the above output carefully."
echo "Anything returned here is *suspicious*, not automatically malicious."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment