Marta and the Thousand Hosts: Recon Flyovers Without Wreckage
Featuring aerial reconnaissance, polite rate limits, and a favicon that tattles like a teenager with a group chat.
TL;DR: do fast, polite “flyovers” of many hosts, collect useful metadata (headers, favicon hash, SPF/DMARC, CDN hints), and make decisions without melting anything.
assetfinder -subs-only example.com \
| webscout -c 40 -json \
| jq . \
| tee flyover.json
Use it: https://github.com/cybercdh/webscout
Marta Metadata didn’t start out angry. She started out optimistic, even cheerful, armed with a browser and the misguided belief that manually checking 1,000 subdomains was “totally doable before lunch.” By subdomain 47, her wrist hurt. By 112, she’d forgotten what happiness felt like. By 500, she was having philosophical arguments with her monitor about the nature of suffering.
The breaking point came at subdomain 623: dev-staging-test-backup-old.corp.example.com. The server header proudly announced “IIS/6.0” like it was 2003 and nobody had told it otherwise. The favicon was a default Apache feather on an IIS server, which shouldn’t be possible but somehow was. And the CSP? default-src *. Marta’s eye twitched. Then the other one. Then both, creating a concerning strobe effect that alarmed her cat.
“Never again,” she told her coffee mug (Gerald Jr., porcelain, supportive). “There has to be a better way.”
There was. She called it webscout—aerial reconnaissance for the civilized. Think of it as a drone that flies over your infrastructure, takes photos, and lands before anyone notices. No touching, no downloading, just observation and mild judgment.
The Wednesday that changed everything started with an innocent Slack message: “Hey Marta, can you check if any of our 10,000 subdomains have security issues? Thanks! 😊”
Ten. Thousand. Subdomains.
Marta looked at Gerald Jr. Gerald Jr., being ceramic, offered no solutions but excellent emotional stability.
Copy me:
# Minimal: enumerate -> scout -> keep only interesting bits
assetfinder -subs-only example.com \
| webscout -c 40 -json \
| jq -r '.[] | [.host, .http.status, (.headers.server // ""), (.headers.csp // ""), (.mail.dmarc // "")] | @tsv' \
| tee summary.tsv
Here’s the kind of signal Marta cares about and how she pulls it without waking the neighbors.
Favicon fingerprints (aka “that tiny image that outs your CDN/stack”):
// go: generate mmh3 hash of a favicon the way many scanners do
package main
import (
"encoding/base64"
"fmt"
"io"
"net/http"
mmh3 "github.com/spaolacci/murmur3"
)
func main() {
url := "https://example.com/favicon.ico"
resp, err := http.Get(url)
if err != nil { panic(err) }
defer resp.Body.Close()
b, _ := io.ReadAll(resp.Body)
s := base64.StdEncoding.EncodeToString(b)
h := mmh3.Sum32([]byte(s))
fmt.Println(h) // compare against known hashes (e.g., Shodan/Censys cheat-sheets)
}
Email policy sanity (DMARC/SPF), because phishing ruins everyone’s day and your CISO’s calendar:
# Quick look: DMARC published and posture
dom=example.com
rec=$(dig +short TXT _dmarc.$dom | tr -d '"')
echo "$rec" | sed 's/;/\n/g' | egrep '^(v=|p=|adkim=|aspf=)'
# SPF include chain length (depth is where configs go to die)
spf=$(dig +short TXT $dom | tr -d '"' | rg -i '^v=spf1')
echo "$spf"
echo "$spf" | rg -o 'include:[^ ]+' | wc -l
If you’d like a tiny alignment checker to drop into a pipeline:
# dmarc_check.py
import re, sys
rec = sys.stdin.read()
p = re.search(r'(^|;)p=([a-z]+)', rec)
adkim = re.search(r'(^|;)adkim=([rs])', rec)
aspf = re.search(r'(^|;)aspf=([rs])', rec)
posture = (p.group(2) if p else 'none')
print(f"policy={posture} adkim={(adkim.group(2) if adkim else 'r')} aspf={(aspf.group(2) if aspf else 'r')}")
dom=example.com
dig +short TXT _dmarc.$dom | tr -d '"' | python3 dmarc_check.py
Marta’s heuristics (worth their weight in pizza):
- Security headers: if CSP is missing (or just “default-src *”), note it; if HSTS is absent on apex HTTPS, note it louder.
- CDN hints: consistent CNAMEs to known edges,
serverheaders like “cloudflare,” and telltale favicon hashes; triangulate, don’t assume. - Disclosure: X-Powered-By banners and verbose error pages are the cargo shorts of the web—comfortable, revealing, easily fixed.
- Mail posture: DMARC p=reject/quarantine is civilized; p=none is a cry for help. SPF with a 14‑layer include-latte is a change control story waiting to happen.
Manners so you get invited back:
- Concurrency around 20–50 is the sweet spot for most programs; dial down on fragile infra.
- Timeouts, backoff, and a per‑host cap (“one look, move on”) keep you fast and friendly.
- Don’t screenshot logins or fetch authenticated paths;
webscoutis reconnaissance, not rummaging.
One more useful extraction: reading just enough to triage content policies at scale without becoming “that person” in the SOC channel:
# Pull just status + CSP/HSTS for a quick security header score
cat subs.txt \
| webscout -c 40 -json \
| jq -r '.[] | [.host, .http.status, (.headers.csp // ""), (.headers.strict_transport_security // "")] | @tsv'
By 4 PM, Marta had results that made her question reality:
The Hall of Shame:
- 2,847 hosts with no CSP (“Come on in, XSS, make yourself at home”)
- 1,293 with
Server: Apache/2.2.3(vintage, like finding a fax machine at a blockchain startup) - 743 favicon hashes matching default installations (the digital equivalent of leaving the dealership sticker on your car)
- One—just one—with
X-Powered-By: PHP/4.3.2(Marta screenshot this twice to make sure she wasn’t hallucinating)
The Mystery: Five hundred subdomains all had the same favicon hash: 873269302. Marta recognized it immediately—CloudFlare’s default. But the server headers said nginx. The CNAMEs pointed to AWS. Either someone was very confused, or Marta had discovered the world’s most ambitious catfishing operation.
The Smoking Gun:
A DMARC record that simply said v=DMARC1; p=none; pct=0. Not even pretending to care. It was like finding a “Steal Me” sign on a Ferrari.
She didn’t need to touch anything. The metadata told the whole story, like footprints in snow leading directly to the crime scene. And because we all love a neat handoff, here’s a report template your future self will actually reuse.
Report snippet (defender/eng-friendly):
Title: Recon flyover — header posture, email policy, and hosting hints
Scope: example.com (123 live hosts)
Summary:
- Security headers: 37 hosts missing CSP; 22 missing HSTS
- Email: DMARC p=none for apex; SPF include depth up to 6
- Hosting: 64% behind managed CDN (inferred via CNAME/server/favhash)
Evidence:
- Headers: see summary.tsv (host, status, server, csp, dmarc)
- DMARC: dig +short TXT _dmarc.example.com → v=DMARC1; p=none; aspf=r; adkim=r
- CDN hints: consistent CNAMEs (*.cdnvendor.net), server header = cloudflare, favicon mmh3= 873269302
Recommendations:
- Add CSP (start with default-src 'self'; frame-ancestors 'none') and enable HSTS on HTTPS apex
- Move DMARC to p=quarantine or reject after alignment review; reduce SPF include depth (<5)
- Track CDN config in change control to prevent “mystery headers”
Notes: Metadata-only collection; no authenticated paths or brute forcing performed.
The real victory wasn’t the vulnerabilities Marta found (though there were many). It wasn’t even the praise from the CISO (“How did you check 10,000 hosts in an afternoon?”). It was the look on Gerald Jr.’s face—well, the lack of look, being ceramic—when she closed her laptop at 5 PM instead of midnight.
“We did it,” she told the mug. “We achieved reconnaissance without rage.”
Gerald Jr. said nothing, which Marta took as approval.
webscout isn’t just a tool. It’s a philosophy: be fast, be polite, be gone before anyone notices. In the eternal war between thoroughness and sanity, sometimes you just need to fly over the battlefield, take notes, and live to scan another day.
The internet is vast. Your time is finite. Your coffee mug believes in you.