Skip to content

Commit e7035b6

Browse files
h0tak88rclaude
andcommitted
feat(scripts): chaos-takeover — drop nuclei verification, alert on raw grep matches
User wants a simple grep-match feed, not a verified-findings feed. Removed the httpx/nuclei verify stage entirely: download ZIPs → unzip -p → dnsx (CNAME) → grep fingerprints → notify Every CNAME match now fires a Discord alert live as dnsx resolves it — no buffering, no separate confirmed.txt. Header + inline comments make the tradeoff explicit: this is UNVERIFIED, most fingerprint matches are claimed (not exploitable), manual confirmation is required before treating a hit as reportable. Removed nuclei from deps and the --candidates-too flag (redundant — raw matches are now the only output). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e9e4dd2 commit e7035b6

1 file changed

Lines changed: 22 additions & 46 deletions

File tree

scripts/chaos-takeover.sh

Lines changed: 22 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
#!/usr/bin/env bash
22
#
3-
# chaos-takeover.sh — bulk subdomain-takeover hunt over the ProjectDiscovery
4-
# Chaos dataset, fully streamed (resolve → CNAME match → verify → Discord).
3+
# chaos-takeover.sh — bulk subdomain CNAME-fingerprint hunt over the
4+
# ProjectDiscovery Chaos dataset, fully streamed (resolve → match → Discord).
55
#
66
# Pipeline (every stage runs concurrently via pipes — no save-then-wait):
77
# download all program ZIPs → unzip -p → dnsx (CNAME) → grep fingerprints
8-
#nuclei takeover templates → notify (Discord, per confirmed hit)
8+
# → notify (Discord, per matched host)
99
#
10-
# A CNAME match is only a CANDIDATE. nuclei verifies fingerprint + unclaimed
11-
# target before it's alerted, so Discord only gets real, reportable takeovers.
10+
# NO VERIFICATION STEP. A CNAME match only means the host points at one of the
11+
# fingerprinted services — most are CLAIMED (not exploitable). This is a raw
12+
# candidate feed, not a confirmed-findings feed: expect false positives, and
13+
# manually confirm (unclaimed target / claimable) before treating anything here
14+
# as a reportable bug.
1215
#
1316
# Usage:
1417
# ./chaos-takeover.sh [options]
@@ -18,14 +21,13 @@
1821
# --par N parallel ZIP downloads (default: 4)
1922
# --threads N dnsx threads (default: 300)
2023
# --no-download skip download, reuse zips/ already on disk
21-
# --candidates-too also send raw CNAME candidates to Discord (batched; noisy)
2224
# -h | --help
2325
#
2426
# Note: the bulk dataset (index.json + program ZIPs) is a PUBLIC endpoint and is
2527
# fetched WITHOUT auth — sending CHAOS_API_KEY to it returns HTTP 400. The key is
2628
# only used by the per-domain DNS API (the AutoAR Chaos integration / chaos-client).
2729
#
28-
# Deps: curl jq unzip dnsx nuclei notify
30+
# Deps: curl jq unzip dnsx notify
2931
set -euo pipefail
3032

3133
# ── defaults ──────────────────────────────────────────────────────────────────
@@ -36,11 +38,11 @@ DNSX_T=300
3638
RESOLVERS=""
3739
WEBHOOK=""
3840
DO_DOWNLOAD=1
39-
CANDIDATES_TOO=0
4041
NOTIFY_ID="takeover"
4142

42-
# Takeover-able service fingerprints matched against the CNAME target. A match is
43-
# a candidate only; nuclei confirms. Reference: github.com/EdOverflow/can-i-take-over-xyz
43+
# Service fingerprints matched against the CNAME target. UNVERIFIED — a match
44+
# means "points at this service", not "takeover confirmed". Most are claimed.
45+
# Reference: github.com/EdOverflow/can-i-take-over-xyz
4446
FP='vercel-dns\.com|github\.io|netlify\.app|azurewebsites\.net|elasticbeanstalk\.com|webflow\.io|gitbook\.io|readme\.io|railway\.app|herokudns\.com|herokuapp\.com|fastly\.net|ghost\.io|helpscoutdocs\.com|surge\.sh|bitbucket\.io|wpengine\.com|pantheonsite\.io|zendesk\.com|statuspage\.io'
4547

4648
# ── args ──────────────────────────────────────────────────────────────────────
@@ -52,16 +54,14 @@ while [ $# -gt 0 ]; do
5254
--par) DL_PAR="$2"; shift 2 ;;
5355
--threads) DNSX_T="$2"; shift 2 ;;
5456
--no-download) DO_DOWNLOAD=0; shift ;;
55-
--candidates-too) CANDIDATES_TOO=1; shift ;;
5657
-h|--help) sed -n '2,40p' "$0"; exit 0 ;;
5758
*) echo "unknown option: $1" >&2; exit 2 ;;
5859
esac
5960
done
6061

6162
# ── deps ──────────────────────────────────────────────────────────────────────
62-
# Note: no httpx — nuclei probes hosts itself, and many systems have the Python
63-
# httpx CLI shadowing ProjectDiscovery's, which breaks the pipeline.
64-
for t in curl jq unzip dnsx nuclei notify; do
63+
# Note: no httpx/nuclei — this is a raw grep-match pipeline, unverified by design.
64+
for t in curl jq unzip dnsx notify; do
6565
command -v "$t" >/dev/null || { echo "[!] missing dependency: $t" >&2; exit 1; }
6666
done
6767

@@ -122,45 +122,21 @@ else
122122
echo "[*] --no-download: reusing $(ls zips/*.zip 2>/dev/null | wc -l | tr -d ' ') existing ZIPs"
123123
fi
124124

125-
# Ensure the takeover templates are actually installed. This is NOT best-effort —
126-
# `nuclei -update-templates` was previously swallowed by `|| true`, so a first-run
127-
# machine with an empty ~/nuclei-templates dir would silently proceed straight to
128-
# "no templates provided for scan" with zero hits ever alerted. Fail loud instead.
129-
echo "[*] Ensuring nuclei templates are installed…"
130-
nuclei -update-templates 2>&1 | tail -5
131-
TAKEOVER_TPL_COUNT=$(nuclei -tags takeover -tl 2>/dev/null | grep -c '\.yaml$' || true)
132-
if [ "${TAKEOVER_TPL_COUNT:-0}" -eq 0 ]; then
133-
echo "[!] 0 takeover templates found after update — nuclei can't verify anything." >&2
134-
echo " Check 'nuclei -update-templates' output above for the real error." >&2
135-
exit 1
136-
fi
137-
echo " ${TAKEOVER_TPL_COUNT} takeover templates available"
138-
139-
# ── 3. streaming resolve → match → verify → notify ──────────────────────────
125+
# ── 3. streaming resolve → match → notify (no verification step) ────────────
140126
DNSX_ARGS=(-cname -resp -silent -t "$DNSX_T")
141127
[ -n "$RESOLVERS" ] && DNSX_ARGS+=(-r "$RESOLVERS")
142128

143-
echo "[*] Streaming: unzip → dnsx → grep → nuclei(takeover) → notify"
144-
: > candidates.txt; : > confirmed.txt
129+
echo "[*] Streaming: unzip → dnsx → grep → notify (every match, unverified)"
130+
: > candidates.txt
145131

146-
# No httpx stage: nuclei probes each host itself, and -tags takeover selects the
147-
# takeover templates by tag (path-independent across nuclei versions).
148132
for z in zips/*.zip; do unzip -p "$z" 2>/dev/null || true; done \
149133
| dnsx "${DNSX_ARGS[@]}" \
150134
| stdbuf -oL grep -iE "$FP" \
151135
| tee -a candidates.txt \
152-
| awk '{print $1}' \
153-
| nuclei -tags takeover -silent \
154-
| tee -a confirmed.txt \
155-
| while IFS= read -r hit; do
156-
printf '🚨 Subdomain takeover confirmed: %s\n' "$hit" | notify -silent -id "$NOTIFY_ID"
136+
| while IFS= read -r line; do
137+
printf '🎯 CNAME match: %s\n' "$line" | notify -silent -id "$NOTIFY_ID"
157138
done
158139

159-
# ── 4. optional: batch the raw candidates to Discord too (noisy) ────────────
160-
if [ "$CANDIDATES_TOO" -eq 1 ] && [ -s candidates.txt ]; then
161-
echo "[*] Sending $(wc -l < candidates.txt | tr -d ' ') raw candidates (batched)…"
162-
notify -silent -id "$NOTIFY_ID" -bulk < candidates.txt || true
163-
fi
164-
165-
echo "[✓] Done — candidates=$(wc -l < candidates.txt | tr -d ' ') confirmed=$(wc -l < confirmed.txt | tr -d ' ')"
166-
echo " candidates.txt (all CNAME matches) · confirmed.txt (nuclei-verified)"
140+
echo "[✓] Done — candidates=$(wc -l < candidates.txt | tr -d ' ')"
141+
echo " candidates.txt has every CNAME match, sent to Discord live as found."
142+
echo " UNVERIFIED — confirm each is actually unclaimed before reporting."

0 commit comments

Comments
 (0)