name: wp-mass-recon
description: Batch WP recon: users, CORS, XMLRPC, leaks across domains.
version: 1.0.0
author: uphiago
license: MIT
platforms: [linux]
compatibility: Requires curl, nmap, python3, masscan, subfinder, httpx, nuclei
metadata:
tags: [recon, wordpress, mass-scan, us-companies]
category: recon
related_skills:
- cors-credential-wordpress
- xmlrpc-exploitation
- source-leak-hunt
- wordpress-plugin-hunt
- staging-subdomain-hunt
- wordpress-full-compromise
- deep-invade
- recon-playbook
- port-service-discovery
WP Mass Recon Skill
Batch WordPress vulnerability detection pipeline for scanning dozens to hundreds of domains in parallel. Detects WordPress presence, REST API user enumeration, CORS credential reflection, XMLRPC exposure, open registration, and sensitive file leaks in a single pass. Proven on 600+ US company domains across 28 sectors.
When to Use
- Starting recon on a batch of 10+ domains.
- Sector-wide vulnerability mapping (law firms, pest control, landscaping, pools, roofing, HVAC, etc.).
- After
subfinder/crt.sh produces a target list and you need to triage.
- You want maximum findings per minute with a parallelizable pipeline.
Prerequisites
terminal tool with access to the worker container (curl, httpx, python3, jq).
- Target list file at
/root/output/targets.txt in format domain|company|sector (one per line).
- Worker container has
parallel_batch.py available or you use the inline commands below.
How to Run
httpx -silent -l targets.txt -threads 50 -tech-detect -status-code -title -o /root/output/alive.txt
python3 /root/output/recon_us/new_targets/parallel_batch.py /root/output/targets.txt 20
Or run the 4-phase pipeline manually using the commands in Procedure.
Quick Reference
| Check | Command | Positive Signal |
|---|
| WP detection | curl -skI "https://TARGET/wp-login.php" | HTTP 200/301/302 |
| User enum | curl -sk "https://TARGET/wp-json/wp/v2/users" | JSON with id, name, slug |
| CORS | curl -skI "https://TARGET/wp-json/wp/v2/users" -H "Origin: https://evil.com" | Access-Control-Allow-Credentials: true |
| XMLRPC | curl -sk -X POST "https://TARGET/xmlrpc.php" -d '<methodCall><methodName>demo.sayHello</methodName></methodCall>' | Hello! in body |
| Open reg | curl -sk "https://TARGET/wp-login.php?action=register" | Form with user_login field |
| Source leaks | Parallel curl for .env, wp-config.php.bak, .git/config, debug.log, backup.sql | Real content (not SPA catch-all) |
Procedure
Phase 1 — Target Preparation
for sector in "landscaping" "roofing" "hvac" "pools" "plumbing"; do
curl -sk "https://crt.sh/?q=%25.${sector}%25&output=json" | jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u >> /root/output/discovered.txt
done
cat /root/output/discovered.txt | sed 's/^www\.//' | sort -u > /root/output/unique_domains.txt
Phase 2 — Live Host Discovery
httpx -silent -l /root/output/unique_domains.txt -threads 50 -tech-detect -status-code -title \
-o /root/output/alive.txt
awk '{print $1}' /root/output/alive.txt | grep -E '^https?://' > /root/output/urls.txt
Phase 3 — Parallel Vulnerability Scan
For each live target, run in parallel (20 workers):
while read -r url; do
domain=$(echo "$url" | sed 's|https\?://||')
(
echo "# $domain Findings" > "/root/output/findings/${domain}_findings.md"
wp_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 10 "$url/wp-login.php")
[[ "$wp_code" =~ ^(200|301|302|403)$ ]] && echo "- WordPress: YES (wp-login: $wp_code)" >> "/root/output/findings/${domain}_findings.md"
users=$(curl -sk --max-time 10 "$url/wp-json/wp/v2/users" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d) if isinstance(d,list) else 0)" 2>/dev/null)
[[ "$users" -gt 0 ]] && echo "- Users exposed: $users" >> "/root/output/findings/${domain}_findings.md"
cors=$(curl -skI --max-time 10 "$url/wp-json/wp/v2/users" -H "Origin: https://evil.com" 2>/dev/null | grep -i "access-control-allow-credentials: true")
[[ -n "$cors" ]] && echo "- CORS: CREDENTIAL REFLECTION CONFIRMED" >> "/root/output/findings/${domain}_findings.md"
xmlrpc=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 10 -X POST "$url/xmlrpc.php" \
-d '<?xml version="1.0"?><methodCall><methodName>demo.sayHello</methodName></methodCall>')
[[ "$xmlrpc" == "200" ]] && echo "- XMLRPC: OPEN" >> "/root/output/findings/${domain}_findings.md"
reg=$(curl -sk --max-time 10 "$url/wp-login.php?action=register" | grep -o 'user_login')
[[ -n "$reg" ]] && echo "- Open Registration: YES" >> "/root/output/findings/${domain}_findings.md"
for path in ".env" "wp-config.php.bak" ".git/config" "debug.log" "backup.sql" "info.php" "phpinfo.php" "wp-config.php~" ".env.backup" ".env.local" "docker-compose.yml" "Dockerfile"; do
leak_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 "$url/$path")
if [[ "$leak_code" == "200" ]]; then
content=$(curl -sk --max-time 5 "$url/$path" | head -c 500)
if echo "$content" | grep -qiE 'DB_|APP_|_KEY|_SECRET|password|mysql|\[core\]|PHP Version|CREATE TABLE'; then
echo "- Source leak: /$path (VERIFIED)" >> "/root/output/findings/${domain}_findings.md"
fi
fi
done
) &
while [[ $(jobs -r | wc -l) -ge 20 ]]; do sleep 0.5; done
done < /root/output/urls.txt
wait
Phase 4 — Consolidation
echo "## Mass Recon Summary" > /root/output/mass_summary.md
echo "" >> /root/output/mass_summary.md
for f in /root/output/findings/*_findings.md; do
domain=$(basename "$f" _findings.md)
criticals=$(grep -c "CRITICAL\|CREDENTIAL REFLECTION\|XMLRPC: OPEN\|Source leak: VERIFIED" "$f" || true)
[[ "$criticals" -gt 0 ]] && echo "- **$domain**: $criticals findings" >> /root/output/mass_summary.md
done
grep "^\- \*\*" /root/output/mass_summary.md | sort -t: -k2 -rn | head -20
Production Scanner (Python — parallel_batch.py pattern)
The production-proven approach uses concurrent.futures.ThreadPoolExecutor with 20 workers. Each worker calls curl via subprocess.run. This is 10x faster than bash while loops.
import concurrent.futures, subprocess, json
def curl_code(url, timeout=8):
cmd = ["curl", "-sk", "-m", str(timeout), "-o", "/dev/null", "-w", "%{http_code}", url]
r = subprocess.run(cmd, capture_output=True, timeout=timeout+5)
return r.stdout.decode().strip()
def test_target(domain):
proto = None
for p in ["https", "http"]:
code = curl_code(f"{p}://{domain}/")
if code not in ["000", ""]: proto = p; break
if not proto: return None
login_code = curl_code(f"{proto}://{domain}/wp-login.php")
json_code = curl_code(f"{proto}://{domain}/wp-json/")
is_wp = login_code not in ["000","404",""] or json_code not in ["000","404",""]
if not is_wp: return {"domain":domain, "is_wp":False}
score = 1
findings = ["wordpress"]
body, _ = curl_raw(f"{proto}://{domain}/wp-json/wp/v2/users")
try:
data = json.loads(body.decode())
if isinstance(data, list) and len(data) > 0:
findings.append(f"wp_users_{len(data)}")
score += 2
except: pass
cmd = ["curl","-sk","-m","8","-I","-H","Origin: https://evil.com",
f"{proto}://{domain}/wp-json/wp/v2/users"]
r = subprocess.run(cmd, capture_output=True, timeout=10)
hdrs = r.stdout.decode().lower()
acao = [l.split(":",1)[1].strip() for l in hdrs.split('\n') if 'access-control-allow-origin:' in l]
acac = [l.split(":",1)[1].strip() for l in hdrs.split('\n') if 'access-control-allow-credentials:' in l]
if acao and "evil.com" in acao[0] and acac and acac[0] == "true":
findings.append("cors_credentialed")
score += 3
xml = '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>'
body, _ = curl_raw(f"{proto}://{domain}/xmlrpc.php", method="POST", data=xml)
txt = body.decode()
if "system.multicall" in txt:
findings.append("xmlrpc_multicall")
score += 3
elif "methodName" in txt:
findings.append("xmlrpc_active")
body, _ = curl_raw(f"{proto}://{domain}/wp-login.php?action=register")
rt = body.decode().lower()
if "register" in rt and "user_login" in rt and "wp-submit" in rt:
findings.append("registration_open")
score += 2
if score >= 8: severity = "CRITICAL"
elif score >= 5: severity = "HIGH"
elif score >= 3: severity = "MEDIUM"
elif score >= 1: severity = "LOW"
else: severity = "NONE"
return {"domain":domain, "severity":severity, "score":score, "findings":findings}
targets = [(d.strip(), s.strip()) for line in open("targets.txt") if (p := line.split("|")) and (d:=p[0]) and (s:=p[-1] if len(p)>2 else "unknown")]
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as ex:
futures = {ex.submit(test_target, t[0]): t for t in targets}
for f in concurrent.futures.as_completed(futures):
r = f.result()
if r and r.get("score",0) > 0:
print(f"[{r['severity']:>8}] {r['domain']:40s} | {r['score']:2d} | {', '.join(r['findings'])}")
Pitfalls
- SPA catch-all false positives: Single-page apps return 200 for every path. Always verify
.env has DB_/APP_/_KEY/_SECRET patterns; .git/config has [core]; SQL files have CREATE TABLE/INSERT INTO. Skip bodies with <html or <script in first 100 chars.
- Cloudflare/WAF blocking: httpx may show tech as "Cloudflare" but WP is behind it. Try HTTP/1.0 for WP Engine-hosted sites:
curl -sk --http1.0 "https://TARGET/wp-json/..."
- Rate limiting: WP Engine and Hostinger throttle after ~50 requests. Use 2-4s jitter between requests. Chrome/125 UA has 0% block rate; curl/8.4 UA has 5% block rate; Python urllib has 15%.
- WordPress on subpaths: Check
/blog/, /magical/, /wp/ in addition to root. wines.com has /magical/ with separate, more vulnerable WP install.
- Non-standard XMLRPC paths: Some hosts rename xmlrpc.php. Verify with
system.listMethods (not just HTTP 200) — look for <string> tags in response XML.
- Registration form false positives: Many sites show login form on
?action=register without actually allowing registration. The v2 check requires ALL THREE strings: register + user_login + wp-submit.
Real-World Results (from 600+ US targets)
| Finding | Frequency | Best Sector |
|---|
| WP user enumeration | ~9% (55/600) | Landscaping, Law Firms |
| Sensitive files (3+) | ~7% (41 sites) | Auto Body, Window Cleaning |
| CORS credential reflection | ~3.3% (20+ sites) | Law Firms, Real Estate |
| XMLRPC system.multicall | ~1.7% (10+ sites) | HVAC, Landscaping |
| PHPInfo/info.php exposed | ~1.7% (~10 sites) | Dental, Gyms |
| MySQL 3306 exposed | 0.17% (1 site) | Healthcare SaaS |
WordPress = 36.5% of all US SMB targets. All CORS/XMLRPC vulns occur EXCLUSIVELY on WordPress.
Verification
- Every CORS finding must show
Access-Control-Allow-Credentials: true in curl -I response headers.
- Every source leak must pass content verification (not just HTTP 200). Skip HTML/SPA responses.
- Every XMLRPC finding must have
system.listMethods response containing <string> method names.
- Score targets with v2 thresholds: WP=+1, users (+2), CORS=+3, XMLRPC multicall=+3, open reg=+2. Score >=6 = deep-dive candidate (Phase 3).