mcpField
W
wordpress-plugin-hunt

wordpress-plugin-hunt

github.com/uphiagoSecurity1.3k41 installsUpdated 8w ago
Repository

About wordpress-plugin-hunt

wordpress-plugin-hunt is a WordPress security skill for identifying installed plugins and checking their versions against known CVEs. It guides authorized testers through multiple detection methods and validation steps.

Key features

  1. 01REST API namespace probing
  2. 02readme.txt version checks
  3. 03HTML and JavaScript fingerprinting
  4. 04Cross-reference versions with CVEs
  5. 05Validate findings using multiple signals

Use cases

  1. 01Identify plugins on a confirmed WordPress target
  2. 02Check plugin versions for known vulnerabilities
  3. 03Find a detection fallback when readme.txt is blocked

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/uphiago/recon-skills --skill wordpress-plugin-hunt

After installing, type/wordpress-plugin-huntin Claude Code to confirm it loaded.

Details

From the project README

WordPress Plugin Hunt Skill

Discover installed WordPress plugins through REST API namespace probing, readme.txt version detection, and HTML/JS source analysis. Cross-reference discovered versions against known CVEs for exploitation. WordPress plugin vulnerabilities are one of the most reliable paths to RCE — confirmed CVEs include Elementor, Slider Revolution, ElementsKit, Gravity Forms, Jetpack, WooCommerce, and LiteSpeed Cache.

When to Use

  • WordPress confirmed on target (via wp-mass-recon).
  • Running deep-invade Phase 3.
  • You need an exploitation vector beyond CORS/XMLRPC.
  • Target has a plugin-heavy WordPress site (e-commerce, page builder, forms).

Prerequisites

  • terminal with curl, python3.
  • WordPress target confirmed (/wp-json/ or /wp-login.php accessible).
  • For CVE exploitation: knowledge of specific CVE PoCs (reference security-arsenal skill).

How to Run

# Quick plugin namespace scan (30+ plugins)
TARGET="example.com"
for ns in "revslider/v1" "elementskit/v1" "elementor/v1" "gf/v2" "wc/v3" \
  "jetpack/v4" "litespeed/v1" "yoast/v1" "acf/v3" "contact-form-7/v1" \
  "solidwp-mail/v1" "wpsl/v1" "redirection/v1" "rankmath/v1"; do
  code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 "https://$TARGET/wp-json/$ns")
  [[ "$code" != "404" ]] && echo "FOUND: /wp-json/$ns (HTTP $code)"
done

Quick Reference

Top Exploitable Plugins (US SMB targets — confirmed CVEs from mass recon)
PluginVulnerable VersionCVEImpactFrequency
Slider Revolution< 6.6.20CVE-2024-2534RCE via file upload~10%
ElementsKit< 2.9.4CVE-2023-6851SQLi~5%
ElementsKit< 2.9.4CVE-2023-6853File Upload (unauthenticated)~5%
Gravity Forms< 2.8.2CVE-2024-6115PHP Object Injection → Auth Bypass~8%
Jetpack< 13.1CVE-2024-1782SSRF28%
Elementor< 3.24.0CVE-2024-xxxxInfo disclosure → Auth bypass28%
LiteSpeed Cache< 6.5.0CVE-2024-50550Privilege escalation~15%
WooCommerceN/A (API exposure)N/AOrder/customer data42%
Yoast SEON/A (sitemap enum)N/AAuthor email leak42%
Detection Methods
MethodWhat It RevealsReliability
REST namespace probePlugin presence + dataHigh (if plugin registers REST routes)
readme.txt versionExact version numberMedium (many sites block readme.txt)
HTML source grepPlugin CSS/JS handlesMedium
robots.txtPlugin-generated entriesLow (only if plugin adds entries)
/wp-content/plugins/<slug>/Directory listing or assetsMedium

Procedure

Step 1 — REST Namespace Brute Force (40+ plugins)
TARGET="$1"
OUTDIR="$OUTDIR/plugins/$TARGET"
mkdir -p "$OUTDIR"

# Comprehensive plugin namespace list
declare -A PLUGIN_NAMESPACES
PLUGIN_NAMESPACES["revslider"]="revslider/v1/slides"
PLUGIN_NAMESPACES["elementskit"]="elementskit/v1/layouts"
PLUGIN_NAMESPACES["elementor"]="elementor/v1/globals"
PLUGIN_NAMESPACES["gravityforms"]="gf/v2/forms"
PLUGIN_NAMESPACES["woocommerce"]="wc/v3/products"
PLUGIN_NAMESPACES["jetpack"]="jetpack/v4/settings"
PLUGIN_NAMESPACES["litespeed"]="litespeed/v1/token"
PLUGIN_NAMESPACES["yoast"]="yoast/v1/indexing"
PLUGIN_NAMESPACES["acf"]="acf/v3/posts"
PLUGIN_NAMESPACES["contactform7"]="contact-form-7/v1/contact-forms"
PLUGIN_NAMESPACES["solidwp"]="solidwp-mail/v1/export"
PLUGIN_NAMESPACES["wpsl"]="wpsl/v1/locations"
PLUGIN_NAMESPACES["redirection"]="redirection/v1/redirect"
PLUGIN_NAMESPACES["rankmath"]="rankmath/v1/getHead"
PLUGIN_NAMESPACES["fusionbuilder"]="fusion-builder/v1/elements"
PLUGIN_NAMESPACES["visualcomposer"]="visualcomposer/v1/posts"
PLUGIN_NAMESPACES["ninjaforms"]="ninja-forms/v1/forms"
PLUGIN_NAMESPACES["wpforms"]="wpforms/v1/forms"
PLUGIN_NAMESPACES["mailchimp"]="mailchimp-for-woocommerce/v1"
PLUGIN_NAMESPACES["automatewoo"]="automatewoo/v1"
PLUGIN_NAMESPACES["give"]="give-api/v1/forms"
PLUGIN_NAMESPACES["buddypress"]="buddypress/v1/members"
PLUGIN_NAMESPACES["learndash"]="ldlms/v1/courses"
PLUGIN_NAMESPACES["restrictcontent"]="rcp/v1/memberships"
PLUGIN_NAMESPACES["eventscalendar"]="tribe/events/v1/events"
PLUGIN_NAMESPACES["woosubscriptions"]="wc/v1/subscriptions"
PLUGIN_NAMESPACES["woomemberships"]="wc/v1/memberships"
PLUGIN_NAMESPACES["wpml"]="wpml/v1/languages"
PLUGIN_NAMESPACES["polylang"]="pll/v1/languages"
PLUGIN_NAMESPACES["translatepress"]="trp/v1/languages"
PLUGIN_NAMESPACES["nextgen"]="nextgen-gallery/v1"
PLUGIN_NAMESPACES["envira"]="envira-gallery/v1"
PLUGIN_NAMESPACES["essentialgrid"]="essential-grid/v1/grids"
PLUGIN_NAMESPACES["thegrid"]="the-grid/v1/grids"
PLUGIN_NAMESPACES["masterslider"]="masterslider/v1/sliders"
PLUGIN_NAMESPACES["smartslider3"]="smart-slider-3/v1/sliders"
PLUGIN_NAMESPACES["metaslider"]="ml-slider/v1/slideshows"
PLUGIN_NAMESPACES["duplicator"]="duplicator/v1"
PLUGIN_NAMESPACES["updraft"]="updraftplus/v1"
PLUGIN_NAMESPACES["backupbuddy"]="backupbuddy/v1"
PLUGIN_NAMESPACES["aioseo"]="aioseo/v1/settings"
PLUGIN_NAMESPACES["seopress"]="seopress/v1/settings"

echo "[*] Probing ${#PLUGIN_NAMESPACES[@]} plugin namespaces on $TARGET..."
echo ""

for plugin in "${!PLUGIN_NAMESPACES[@]}"; do
  ns="${PLUGIN_NAMESPACES[$plugin]}"
  resp=$(curl -sk --max-time 5 --connect-timeout 5 -o /tmp/plugin_check_$$.tmp -w "%{http_code}" "https://$TARGET/wp-json/$ns" 2>/dev/null)

  if [[ "$resp" == "200" ]]; then
    size=$(wc -c < /tmp/plugin_check_$$.tmp)
    # Check if response is real plugin data, not SPA catch-all
    if grep -qiE 'id|slides|forms|products|settings|locations|name' /tmp/plugin_check_$$.tmp 2>/dev/null; then
      echo "[PLUGIN] $plugin — REST API active (${size} bytes)"
      cp /tmp/plugin_check_$$.tmp "$OUTDIR/${plugin}_rest.json"
    fi
  elif [[ "$resp" == "401" ]]; then
    echo "[PLUGIN] $plugin — requires auth (HTTP 401)"
  elif [[ "$resp" == "403" ]]; then
    echo "[PLUGIN] $plugin — access forbidden (HTTP 403)"
  fi
done

rm -f /tmp/plugin_check_$$.tmp
Step 2 — Version Detection via readme.txt
TARGET="$1"
OUTDIR="$OUTDIR/plugins/$TARGET"

# Common plugin slugs to check
SLUGS=(
  "elementor" "revslider" "js_composer" "wp-rocket" "wordfence"
  "woocommerce" "jetpack" "litespeed-cache" "yoast-seo" "advanced-custom-fields"
  "contact-form-7" "gravityforms" "ninja-forms" "wpforms-lite"
  "all-in-one-wp-migration" "updraftplus" "duplicator" "backupbuddy"
  "essential-grid" "smart-slider-3" "masterslider" "metaslider"
  "fusion-builder" "visualcomposer" "elementor-pro" "essential-addons-for-elementor-lite"
  "redux-framework" "buddypress" "learndash" "give"
  "the-events-calendar" "events-manager" "wpml-string-translation"
  "mailchimp-for-woocommerce" "automatewoo" "woocommerce-subscriptions"
  "woocommerce-memberships" "restrict-content-pro" "easy-digital-downloads"
  "rank-math-seo" "seo-by-rank-math" "all-in-one-seo-pack" "wp-seopress"
  "monsterinsights" "pixel-caffeine" "facebook-for-woocommerce"
  "popup-maker" "optinmonster" "mailpoet" "fluentform"
  "wp-fastest-cache" "w3-total-cache" "sg-cachepress" "autoptimize"
  "redirection" "better-wp-security" "sucuri-scanner" "wp-cerber"
  "solid-security" "solidwp-mail" "post-smtp" "wp-mail-smtp"
)

echo "[*] Checking readme.txt for ${#SLUGS[@]} plugins..."
echo ""

for slug in "${SLUGS[@]}"; do
  readme=$(curl -sk --max-time 5 --connect-timeout 5 "https://$TARGET/wp-content/plugins/$slug/readme.txt" 2>/dev/null)

  if [[ -n "$readme" ]]; then
    version=$(echo "$readme" | grep -i "stable tag:" | sed 's/.*: //' | tr -d '\r' | head -1)
    name=$(echo "$readme" | grep -i "=== .* ===" | head -1 | sed 's/=== //;s/ ===//')

    if [[ -n "$version" ]]; then
      echo "[VERSION] $slug: $version ($name)"

      # Check for known vulnerable versions
      case "$slug" in
        revslider)
          [[ "$version" < "6.6.20" ]] && echo "  [VULN] Slider Revolution < 6.6.20 — CVE-2024-2534 (RCE)" ;;
        elementskit|elementskit-lite)
          [[ "$version" < "2.9.4" ]] && echo "  [VULN] ElementsKit < 2.9.4 — CVE-2023-6851/6853 (RCE)" ;;
        litespeed-cache)
          [[ "$version" < "6.5.0" ]] && echo "  [VULN] LiteSpeed Cache < 6.5.0 — CVE-2024-50550 (priv esc)" ;;
        elementor|elementor-pro)
          [[ "$version" < "3.24.0" ]] && echo "  [VULN] Elementor < 3.24.0 — info disclosure / auth bypass" ;;
        gravityforms)
          [[ "$version" < "2.8.2" ]] && echo "  [VULN] Gravity Forms < 2.8.2 — CVE-2024-6115 (auth bypass)" ;;
        jetpack)
          [[ "$version" < "13.1" ]] && echo "  [VULN] Jetpack < 13.1 — CVE-2024-1782 (info disc)" ;;
      esac
    fi
  fi
done
Step 3 — HTML/JS Source Plugin Detection
TARGET="$1"

echo "[*] Scanning HTML source for plugin fingerprints..."

PAGE=$(curl -sk --max-time 10 --connect-timeout 10 "https://$TARGET/" 2>/dev/null)

# CSS/JS handles
echo "$PAGE" | grep -Eo "(?:/wp-content/plugins/|/wp-content/themes/)[a-zA-Z0-9_-]+" | sort -u | while read -r path; do
  plugin=$(echo "$path" | grep -Eo 'plugins/\K[a-zA-Z0-9_-]+|themes/\K[a-zA-Z0-9_-]+')
  echo "  [SOURCE] $plugin (found in HTML)"
done

# Elementor specific
if echo "$PAGE" | grep -q "elementor-element\|data-elementor-id"; then
  echo "  [SOURCE] Elementor (page builder in use)"
fi

# WooCommerce specific
if echo "$PAGE" | grep -q "woocommerce\|wc-forward\|add_to_cart_button"; then
  echo "  [SOURCE] WooCommerce (e-commerce active)"
fi

# WP Rocket
if echo "$PAGE" | grep -q "wpr-minify\|rocket-lazyload"; then
  echo "  [SOURCE] WP Rocket (caching plugin)"
fi

# Cloudflare
if echo "$PAGE" | grep -q "cloudflare\|cf-browser-verification"; then
  echo "  [SOURCE] Cloudflare (CDN/WAF detected)"
fi
Step 4 — CVE Exploitation Quick Reference

When a vulnerable plugin version is confirmed:

TARGET="$1"

# Slider Revolution CVE-2024-2534 (RCE via file upload)
# Requires: revslider < 6.6.20
# Attack: POST to /wp-json/revslider/v1/upload with ZIP containing PHP
# See: security-arsenal skill for full PoC

# ElementsKit CVE-2023-6853 (RCE via file upload, unauthenticated)
# Requires: elementskit < 2.9.4
# Attack: POST to /wp-json/elementskit/v1/upload with specially crafted file
# See: security-arsenal skill for full PoC

# Gravity Forms CVE-2024-6115 (auth bypass)
# Requires: gravityforms < 2.8.2
# Attack: Unauthenticated access to form entries via REST API
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/wp-json/gf/v2/forms" 2>/dev/null | python3 -m json.tool | head -20

# LiteSpeed Cache CVE-2024-50550 (privilege escalation)
# Requires: litespeed < 6.5.0
# Attack: Crawler token manipulation to gain admin access
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/wp-json/litespeed/v1/token" 2>/dev/null

Pitfalls

  • REST namespace 200 ≠ plugin present. Some themes and security plugins return 200 for all /wp-json/ paths. Verify response content has actual plugin data (JSON with id, name, or slug fields).
  • readme.txt blocked on many hosts. WP Engine, Hostinger, and Cloudflare often block readme.txt at the CDN level. Fall back to REST namespaces or HTML source grep.
  • Custom plugin slugs. Premium plugins may have custom directory names. gravityforms may be gravityforms-clientsite. Check HTML source for actual slugs via wp-content/plugins/ paths.
  • SliderRev v1 endpoints may return 404 on 6.x. Slider Revolution renamed REST endpoints while the plugin remained active. Probe current non-v1 paths, including /wp-json/sliderrevolution/sliders/.
  • Plugin version comparison needs semantic versioning. Bash string comparison (<) fails on 10.x vs 2.x. Use sort -V or python for complex comparisons.
  • Elementor 500 is only a lead. /wp-json/elementor/v1/favorites is an information disclosure only when the response contains a stack trace, server paths, or other non-public details.

Hosting Provider Pattern (P-23 — critical for plugin detection)

Different hosting providers have distinct vulnerability profiles for plugin detection:

HostREST Usersreadme.txtCORSXMLRPCBest Plugin Detection Method
GoDaddyUsually exposedUsually accessibleOften reflectsUsually openreadme.txt (most accessible)
Cloudflare + WP EngineUsually blockedBlocked at CDNMay workBlockedHTML source grep + REST namespace brute force
HostingerExposedAccessibleOften reflectsOpenreadme.txt + REST namespace
WP Engine (direct)Blocked (401)BlockedMixedBlockedHTML source only
BluehostExposedAccessibleOften reflectsOpenAll methods work
SiteGroundMixedOften accessibleMixedMixedREST namespace + readme.txt

Verification

  • Every detected plugin MUST be confirmed via at least 2 detection methods (REST + readme, or REST + HTML source).
  • Every CVE MUST be matched against the exact version number, not just plugin presence.
  • CVE exploitation MUST be verified with a PoC that demonstrates impact (not just version detection).
  • Plugin vulnerabilities that require authentication must have a credential acquisition path (CORS, brute force, open reg) documented.

Comments

Markdown supported