External Asset Discovery
#Purpose
This workflow combines passive sources, certificate transparency, controlled permutations, DNS resolution, and HTTP reachability checks. Discovery output is not automatically considered authorized: only names under an approved root domain are retained, and active probing should begin only after the resulting list is reviewed.
#Dependencies
Recommended tools: amass, subfinder, dnsx, alterx, httpx, nuclei, curl, and jq.
#Paste-Ready Discovery Script
Save as external-discovery.sh. It requires an initialized engagement workspace and the approved domains.txt.
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
[[ -n "${ENGAGEMENT_ROOT:-}" ]] || { echo "Run: source <workspace>/engagement.env"; exit 1; }
[[ -s "$SCOPE_DIR/domains.txt" ]] || { echo "No approved domains in $SCOPE_DIR/domains.txt"; exit 1; }
OUT="$RAW_DIR/external-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$OUT"/{passive,permutations,dns,http,takeover}
MISSING=()
have() { command -v "$1" >/dev/null 2>&1 || MISSING+=("$1"); }
for tool in amass subfinder dnsx httpx curl jq; do have "$tool"; done
if ((${#MISSING[@]})); then
echo "Missing required tools: ${MISSING[*]}"
exit 1
fi
normalize() {
tr '[:upper:]' '[:lower:]' \
| sed -E 's#^https?://##; s#/.*$##; s/\\.$//' \
| grep -E '^[a-z0-9][a-z0-9._-]*[a-z0-9]$' \
| sort -u
}
while IFS= read -r domain; do
[[ -z "$domain" ]] && continue
echo "[*] Passive discovery: $domain"
amass enum -passive -d "$domain" -o "$OUT/passive/amass-$domain.txt" || true
subfinder -silent -d "$domain" -o "$OUT/passive/subfinder-$domain.txt" || true
curl -fsS "https://crt.sh/?q=%25.$domain&output=json" \
| jq -r '.[].name_value | split("\n")[]' \
> "$OUT/passive/crtsh-$domain.txt" || true
done < "$SCOPE_DIR/domains.txt"
cat "$OUT"/passive/*.txt 2>/dev/null | normalize > "$OUT/passive/all.txt"
# Retain only exact approved roots and their descendants.
: > "$OUT/in-scope-candidates.txt"
while IFS= read -r host; do
while IFS= read -r domain; do
if [[ "$host" == "$domain" || "$host" == *".$domain" ]]; then
echo "$host" >> "$OUT/in-scope-candidates.txt"
break
fi
done < "$SCOPE_DIR/domains.txt"
done < "$OUT/passive/all.txt"
sort -u -o "$OUT/in-scope-candidates.txt" "$OUT/in-scope-candidates.txt"
if command -v alterx >/dev/null 2>&1; then
alterx -silent -l "$OUT/in-scope-candidates.txt" \
> "$OUT/permutations/generated.txt"
cat "$OUT/in-scope-candidates.txt" "$OUT/permutations/generated.txt" | sort -u \
> "$OUT/candidates-with-permutations.txt"
else
cp "$OUT/in-scope-candidates.txt" "$OUT/candidates-with-permutations.txt"
fi
dnsx -silent -l "$OUT/candidates-with-permutations.txt" -a -aaaa -cname -resp \
-o "$OUT/dns/resolved.txt"
awk '{print $1}' "$OUT/dns/resolved.txt" | sort -u > "$OUT/dns/resolved-hosts.txt"
httpx -silent -l "$OUT/dns/resolved-hosts.txt" -threads 20 -rate-limit "${HTTP_RATE:-25}" \
-status-code -title -tech-detect -server -ip -cname -follow-redirects \
-json -o "$OUT/http/services.jsonl"
# Detection templates only. Review every candidate before claiming a takeover.
if command -v nuclei >/dev/null 2>&1; then
nuclei -silent -l "$OUT/dns/resolved-hosts.txt" -tags takeover \
-rate-limit 10 -jsonl -o "$OUT/takeover/candidates.jsonl" || true
fi
echo "Review active targets before further testing:"
echo " $OUT/dns/resolved-hosts.txt"
echo "HTTP inventory:"
echo " $OUT/http/services.jsonl"
BASH
#Expected Output
passive/all.txt: normalized names from passive sources.in-scope-candidates.txt: names constrained to approved domain roots.dns/resolved.txt: DNS answers and CNAME evidence.http/services.jsonl: reachable web services with status, title, technology, and address data.takeover/candidates.jsonl: unverified takeover indicators requiring manual provider and ownership validation.
#Next Steps
- Review wildcard DNS before trusting permutation results.
- Confirm third-party hosted names are included in the rules of engagement.
- Feed approved web targets into Web Surface Discovery.
- Use DNS Enumeration for deeper record analysis.