Part 2 of the Open Season series from the THOR Collective. All free tools, no vendor gatekeeping
Part 1 left us holding fifa[.]house: twenty-one certificates, a first-to-last window from January to April, and about twenty sibling domains riding its SANs. Precise names, precise minutes, and not one address. The domain is NXDOMAIN today, so there is nothing left to resolve and nothing left to scan.
That is the shape of what Certificate Transparency hands you. A domain exists, its cert was issued at this minute, these names rode along on the SAN. A single point in time, the instant a piece of infrastructure announces itself.
Passive DNS gives you the rest of it.
It records the answers recursive resolvers actually returned, captured by sensors and stitched into a searchable history. Where CT shows the intent to stand something up, passive DNS shows the operational life: where a name pointed, when it moved, what it shared an address with, and what went dark the day the campaign got burned.
Passive DNS is also the source in this series where free access has narrowed the most. The two names everyone reaches for first, Farsight DNSDB and PassiveTotal, now sit inside larger platforms: DNSDB moved to DomainTools at enterprise pricing, and PassiveTotal became part of Microsoft Defender Threat Intelligence, where the free community tier did not survive the move. Both are strong products and the consolidation is ordinary. The practical problem is downstream of it: a generation of analysts learned pDNS through those tools, so a lot of intro writeups still assume a license you may not have. Everything below runs on data you can query today, for free, starting with infrastructure the community owns.
What the data tells you
A passive DNS record is boring on its own and devastating in aggregate. Each entry is roughly: a query name, a record type, the response data, a first-seen timestamp, a last-seen timestamp, and a count of how many times the sensor network observed it.
Those last two fields are what matter. A current DNS lookup answers where a name points right now. Passive DNS answers where it has pointed, and for how long, across years, which turns three otherwise impossible questions into routine ones:
What IPs did this domain use before takedown? Active DNS gives you nothing once a domain is parked or sinkholed; pDNS still has the body. What else resolved to this IP while the badness I already know about was live? That is reverse passive DNS, how you turn one indicator into a cluster. And when did the infrastructure turn on? First-seen timestamps date an operation and catch the staging nobody noticed.
You are reconstructing infrastructure that the operator thought was gone. They rotated the IP, they let the domain lapse, they moved to a new host. The resolver network already wrote it down.
Why adversaries leave traces in it
They cannot opt out. That is the short version.
DNS is not optional for an attacker; everything else they run rides on it. The moment a victim, a sandbox, a scanner, or the operator’s own implant resolves a domain near a sensor-instrumented resolver, that resolution gets recorded on infrastructure the adversary does not control. There is no setting to turn it off.
And operators are lazy in patterned ways. They reuse hosting, nameservers, a registrar, a mail setup, a TLS config, because standing up new ones is effort. Every reuse is a thread you can pull, and passive DNS keeps a lot of them visible over time. That is what lets you tell a coincidence, one shared IP ever, from a pattern, four domains on the same obscure VPS during the same two weeks.
The behaviors that show up loudest: fast flux and rapid IP rotation, a domain churning dozens of A records in a short window, which is not a normal CDN; DGA traffic, mostly-NXDOMAIN noise with occasional registered hits that cluster on shared infrastructure; and staging gaps, infrastructure that first-seen dates two weeks before an intrusion and goes dark right after, tempo you read off the timestamps.
How to query and pivot
Start with the data the community runs, because it is the most durable and the least likely to disappear behind a sales call.
CIRCL Passive DNS is run by the Luxembourg CERT for exactly this audience: incident handlers and researchers. Access is free but vetted: request an account, get credentials. It speaks the Passive DNS Common Output Format, which matters more than it sounds, and we come back to that. The REST query is about as simple as it gets:
# Forward lookup: where has this name lived
curl --user 'user:pass' \
'https://www.circl.lu/pdns/query/badexample.com'
# Returns newline-delimited JSON (COF), one record per line:
# {"rrname":"badexample.com","rrtype":"A","rdata":"203.0.113.40",
# "time_first":1708300000,"time_last":1709900000,"count":412}
Mnemonic Passive DNS is the other community workhorse, run by the Norwegian security firm mnemonic and exposed through an open API. Low-volume use needs no key, which makes it the fastest thing to script against when you are triaging:
# Forward lookup
curl 'https://api.mnemonic.no/pdns/v3/badexample.com'
# Reverse lookup: every name seen resolving to this IP
curl 'https://api.mnemonic.no/pdns/v3/203.0.113.40'
Notice the second call. That is the pivot that does the work: hand it an IP, get back every domain the sensor network saw pointing there, each with its own first-seen and last-seen. One known-bad IP becomes a candidate list in a single request.
One caveat before you conclude a source is broken. Sensor coverage is geographic, and the community networks are Europe-weighted. Running the Ghost Stadium origins below through Mnemonic returns almost nothing, while VirusTotal’s free tier returns the full cluster for the same addresses. That is not you querying wrong, it is a resolver network that never saw the traffic. Check a second source before you accept an empty answer, and expect community pDNS to be thinnest exactly where the campaign targets somewhere else.
Now the discipline that keeps you from drowning. Time-window everything. An IP that hosted fifty thousand domains over five years is shared hosting, a CDN edge, or parking, so pivoting on it will bury you. An IP that hosted four domains during the exact fourteen days your incident was live is a cluster. Treat first-seen and last-seen as your filter, not trivia: high count, long span, many unrelated names means shared infrastructure, so drop it; low count, tight window, and a name you already distrust means signal, so chase it.
The pivot fields, in rough order of how often they pay off:
A and AAAA records, forward and reverse, are your primary loop. Domain to historical IPs, each IP back to co-resident domains, filtered by overlapping time windows.
NS records cluster operations by shared nameserver. Self-hosted or bulletproof ones are especially telling: legitimate operators rarely run their own, and adversaries who do reuse them across campaigns.
MX records expose reused mail infrastructure, which is gold for phishing and business email compromise clusters.
TXT records leak verification tokens. A reused google-site-verification value or a distinctive SPF include string can tie together domains that share nothing else visible.
CNAME chains reveal shared redirectors and CDN fronting setups.
Once you have a candidate cluster from the community sources, widen the aperture with the free SaaS-style platforms, which give you a pivoting interface and deeper history than a raw API:
Validin Community Edition is the strongest free pivot environment right now: free account, web UI plus API, several years of history, and pivoting across DNS, certs, and host behaviors in one place. When you want to chase a cluster visually instead of by curl, go here.
Silent Push Community Edition is a free account with passive DNS lookups and infrastructure scanning, useful as a second opinion when your other sources come up thin.
VirusTotal gives passive DNS relations on the free API (/domains/{domain}/resolutions, /ip_addresses/{ip}/resolutions), rate-limited to about four requests a minute, so use it for confirmation and enrichment, not bulk sweeping.
SecurityTrails is worth knowing about and no longer worth planning around. It had a small free tier with historical DNS and subdomain data, and a lot of writeups still list it as a free option. Check what it costs before you build a workflow on it. This is the failure mode this installment opened with, and it happens to sources faster than the writeups get updated.
One more thing about the Common Output Format. COF is the shape CIRCL returns, one JSON record per line with rrname, rrtype, rdata, time_first, time_last, and count, and PyPDNS is a COF client you can point at any server that speaks it. Mnemonic is not one of them: it wraps its answers in a responseCode/data envelope, calls the fields query, answer, and times, and stamps its timestamps in epoch milliseconds. Normalize whatever you pull into COF at the edge of your tooling and the rest of your pipeline stops caring which source it came from.
# CIRCL, or any COF-speaking server, via pypdns
from pypdns import PyPDNS
pdns = PyPDNS(basic_auth=('user', 'pass'))
# rfc_query returns PDNSRecord objects; the older query() is deprecated
# and hands back plain dicts
for record in pdns.rfc_query('badexample.com'):
# record.rdata, record.time_first, record.time_last, record.count
if record.count and record.count < 50 and record.rrtype in ('A', 'AAAA'):
print(record.rdata, record.time_first, record.time_last)
Enrich and correlate
Passive DNS feeds everything else. Every pivot above produces indicators that get sharper when you cross them against the other sources in this series.
Run the resolved IPs and co-resident domains back through Certificate Transparency (Part 1). A shared cert across two domains you connected by IP is independent confirmation they are the same operator, and its SAN list often hands you names the pDNS sweep missed. That is not theoretical here: three Ghost Stadium seed domains expanded to twenty-five registrable domains through SAN expansion alone, and one certificate carried forty-five names.
Take the live IPs into internet scan data, the next installment. Open ports, banners, and fingerprints tell you what the infrastructure was for. A box that hosted three of your cluster domains and also exposed a known C2 panel is no longer a maybe.
Pull WHOIS and registration data on the clustered domains (Part 4). Even post-GDPR, registration timing, registrar choice, and nameserver assignment cluster actor infrastructure in ways the privacy proxy does not hide.
Add ASN and hosting context: which network owns the IP, its abuse history, whether it is a known bulletproof provider. A cluster sitting inside one obscure autonomous system is a far stronger signal than the same domains spread across AWS, Cloudflare, and a shared reseller.
And aggressively discard. Sinkholes pull thousands of dead malicious domains onto one IP, a massive-looking cluster that is really a researcher’s trap; parking and CDN edges do the same with benign domains. Learn the sinkhole and parking ranges, recognize the major CDNs on sight, and filter them before they waste an afternoon.
In practice: Ghost Stadium
The question going in: where does this operation actually host behind the CDN, and can we recover an origin to hunt from?
Forward-resolve the confirmed kit domains and the active core resists you. Nearly every record is Cloudflare (AS13335), and reversing any of those addresses returns the shared edge: tens of thousands of unrelated domains, first-seen dates scattered across years, the textbook high-count, long-span noise you time-window and throw away. For that slice, the origin this lens exists to recover is simply not in the record.
So go back to the one name Part 1 actually handed over. fifa[.]house is dead, and every active tool you own returns nothing on it. Its resolution history returns six addresses across five networks, every handoff inside ten weeks: NetLab Global (AS979) on 2026-01-03, a second NetLab address on 02-06, ArossCloud on 02-16, CogNetCloud on 02-24, then Zillion Network and IT7 Networks together on 03-10. The handoffs are contiguous. You can read the operator’s moving schedule straight off the first-seen column.
Then run the same filter on the last hop, because your own lead is where you are least likely to apply it. On 04-16 the domain answers from two Amazon addresses, and it is tempting to call that a seventh host and a move to legitimate cloud. Both reverse to the same name, a2aa9ff50de748dbe.awsglobalaccelerator[.]com, and each carries hundreds of unrelated tenants, dental practices and consultancies and half-built startups. That is parking. The domain was registered through GoDaddy and kept GoDaddy nameservers to the last day, so what the record shows on 04-16 is the registration lapsing, not the operator relocating. High count, many unrelated names, discard. The rule does not stop applying because the address is yours.
It also overturns the assumption we carried in with. This domain never sat behind Cloudflare. Its nameservers were GoDaddy’s domaincontrol[.]com from the first day to the last, matching the GoDaddy registration and the GoDaddy certificate that Part 1 documented, and it answered from origin hosts the entire time. The domain we assumed was the most hidden of the set turns out to be the least.
You will still find Cloudflare all over those captures, and it is worth knowing why before it misleads you. urlscan lists every ASN a scan touched, and AS13335 is in that list for all eleven fifa[.]house captures. None of it is the site. It is cdnjs.cloudflare[.]com serving the kit’s copied jQuery, plus a pair of Flourish embeds that happen to be Cloudflare-fronted themselves. The page’s own address, the page.ip field, is NetLab or CogNetCloud in every one of those captures. This is the shared-library trap from earlier wearing an ASN instead of a resource hash, and it is the single easiest way to talk yourself into a wrong answer here. A scan summary tells you who the page talked to. One field in it tells you where the page lived.
Better still, the two lenses explain each other. Twenty-one certificates between January and April read like reissuance for its own sake when CT was all we had. It was not. The domain sat on six addresses across five networks in that window, and the certificate churn is the shadow of the hosting churn. Neither source shows that behavior alone.
Two of those addresses reach past this domain. 65.49.223[.]138 is IT7 Networks (AS25820), the same network as the kit cluster below, and 148.178.16[.]5 sits in 148.178.16.0/23, the same Zillion Network /23 as the kit-cluster address 148.178.16[.]48. fifa[.]house is not merely adjacent to the directly-hosted slice. It is inside it.
Which is the wider correction. Part 1 closed on Validin’s framing, every active domain in the set sitting behind Cloudflare, and treated it as the wall this installment had to get over. The wall has holes. urlscan records the IP each capture actually resolved to, so bucket the kit-serving captures by address and a directly-hosted slice falls out, dozens of kit lookalikes answering straight from hosting IPs with no CDN in front. They cluster hard. 89.208.250[.]38 at IT7 Networks (AS25820) served eight of them (fifa[.]blue, fifa[.]cab, fifa[.]cash and more); a Zillion Network block (AS54801) carried another handful; a Hong Kong host (AS140227) carried the flfa typosquats. Reverse one of those IPs and the co-resident cluster comes back in a single pivot, which is exactly what reverse passive DNS is for.
Two disciplines make or break this. First, filter to the kit-unique bundle. Bucket on the kit’s shared library instead, a jQuery-style helper it copied wholesale, and you drag in unrelated GitHub Pages and Squarespace sites on Fastly, the same shared-infrastructure trap this installment keeps warning about, wearing a resource hash instead of an IP. Second, confirm before you attribute. One FIFA node in the favicon cluster, stadiumrushes[.]com, answered from a real origin, 198.54.115[.]155 on a LiteSpeed box, and looked like a find until checking the kit ticket path returned a 404. It serves a different FIFA clone, so it is a separate lead, not a Ghost Stadium origin.
Now the two names Part 1 surfaced that were absent from every published set. Mnemonic returns nothing for either one, which is precisely the empty answer this installment warned you not to accept. Check a second source and the history is sitting right there.
Validin has fifa-sg[.]shop resolving on 2026-05-17 and on no other day: two Cloudflare A records, the same two hosts again as AAAA, four Cloudflare nameservers, then NXDOMAIN from the eighteenth through today. VirusTotal returns the same two addresses on the same date, which is the corroboration you want before you build anything on one vendor’s history. That is also the date of Part 1’s four-certificate burst, which makes the domain’s entire operational life about one day wide. shop-26fifa[.]com never resolved at all. Cloudflare nameservers from 2026-05-20 to now, no A record anywhere in the history, and a registry record still marked active and paid through May 2027.
So passive DNS has plenty to say about both and still cannot hand you an origin, because every address either of them ever carried belonged to Cloudflare. That is the honest boundary of this lens. It recovers history, not hosting, and when an operator fronts a name from its first minute there is no origin in the record to recover. One of these ran for a day and was abandoned. The other was never switched on and is still being paid for. Write both down.
None of the origin-recovered domains are new; they were all in Validin’s set. What is new is the hosting. Validin’s view stopped at Cloudflare, and the per-capture IP fills that gap for the slice that never used it. 89.208.250[.]38 is a real host with ports and services you can point a scanner at, and eight of the domains it served are kit-confirmed. Reverse it and the FIFA footprint is wider than the kit filter alone shows: VirusTotal returns ten registrable fifa[.]* names there between 2026-01-10 and 03-18, and Validin adds fifa[.]work and wwww-fifa[.]com for twelve. Three counts, three filters, all of them honest as long as you say which one you used.
Then ask who else has lived there, because this is the discipline turned back on your own best lead. The same address answered for a Synology dynamic-DNS host in 2020 and an unrelated personal domain in 2019, and since 2026-05-08 it has resolved bwg-la9.ameredu[.]com, which is BandwagonHost naming. The FIFA tenancy is a ten-week window on a recycled reseller IP, not an operator-owned box. Time-windowing is not only how you filter a reverse lookup. It is how you decide what a recovered origin actually entitles you to say.
Assessment, high confidence: this operation is only partly Cloudflare-fronted. The active core hides its origin, but a substantial directly-hosted slice exposes it on a small set of reused IPs at IT7 Networks and Zillion Network. Recover origins from urlscan’s capture history rather than live resolution, filter to the kit-unique resource so shared-library noise does not fold in unrelated hosts, and treat every recovered IP as a reverse pivot into more of the cluster. Every address above is a capture-time observation from early to mid 2026. None of it is a claim about what is still listening today, which is the next installment’s job.
Auditing the list you were handed
Validin publishes their set as suspected, not confirmed, and that word earns its place. Two entries look nothing like the campaign: 808140[.]pro and 808150[.]pro, carrying forty-eight bbs* subdomains between them, with no FIFA string anywhere in sight. Worth asking what they are doing on a FIFA list. Passive DNS answers it, and the answer is not the one the list implies.
All fifty-one are NXDOMAIN today, so live DNS gives you nothing and pDNS is the only way in. Resolution history returns three addresses, and the first job is throwing one away. 91.195.240[.]94 reads as the most recent hosting until you check the owner: SEDO GmbH. That is parking, and the 2026 dates read as the domains expiring rather than the operator moving. The two real origins are 156.235.100[.]101 (USCLOUD, Seychelles) in February 2025 and 154.40.50[.]41 (NetLab Global) that April.
Reverse them and the sprawl comes back whole: the entire bbs*.808140[.]pro run on 2025-02-23, the 808150[.]pro half that April. Time-window as you go, because the Seychelles box also carried unrelated tenants that June, months after the operator left.
Now cross into Part 1’s lens. Let’s Encrypt issued four certificates for these names on 2025-02-23: a thirty-two name cert covering 808140[.]pro, its www. variant, and thirty bbs* hosts, reissued identically eight minutes later, plus two-name certs for 808140[.]com and 808150[.]com. Same day, same issuer, and that is the thread tying an unlisted .com side to the listed .pro side. Search those .com names in CT and a numbered series falls out, 808110 through 808190 on the tens plus a stray 808122, with m., admin., blog., and dev. subdomains. 808150[.]com has certificates going back to 2021 and a kefu. subdomain, Chinese for customer service, in 2023.
CT has one more thing to say, and it complicates the parking read rather than confirming it. Twenty-six single-name certificates land on these hosts between 2026-02-24 and 04-05, all from DigiCert’s Encryption Everywhere program, and they cover names Validin never listed: sitemap., sitemaps., and www. variants of individual bbs hosts. That window sits on top of the SEDO dates. Per-host DV issuance is also what a parking service does when it wants HTTPS on every name it is holding, so the parking read survives, but it is now a read rather than a fact. Note which one you took.
That is not World Cup infrastructure. It is a long-running numbered operation with a mobile site and a support desk, four years older than the tournament, and not one of its .com names made the published set at all. urlscan agrees: all fourteen captures of the bbs* hosts show the same control panel default page, 恭喜,站点创建成功!, and no capture anywhere shows kit content.
One thread keeps it open rather than closing it. 154.40.50[.]41 also served fifa[.]house and fifa[.]bio in February 2026, nine months after the 808150[.]pro sprawl. That shared address is the only bridge between the two, and it is a thin one. Nine months apart on a hosting provider’s IP is reuse as easily as tenancy, and that address was a ten-day stop in a six-address rotation rather than anybody’s home. It is probably why these names are on the list. It is not enough to keep them there.
Assessment, moderate confidence: 808140[.]pro and 808150[.]pro belong to a separate Chinese operation that predates the World Cup campaign and once shared an IP with it. Not one name in that numbered .com series is in the published set. Four have a tie to the listed pair beyond the numbering: 808140[.]com and 808150[.]com, certified the same day as the .pro names, and 808160[.]pro with its .com twin, which shared the Seychelles box in the same window. Those four are net-new to somebody else’s problem.
The transferable part is not the four names. A published indicator set is a starting point, not an answer, and the entries that look wrong to you are worth ten minutes before you block on them or build a detection from them. Every lens used above was free.
Hunt hypothesis template
HYPOTHESIS
An adversary operating against us reuses hosting and nameserver
infrastructure across domains. Starting from one known-bad
indicator, related infrastructure is discoverable in passive DNS
and was active during the incident window.
DATA SOURCES
Primary: CIRCL pDNS (COF), Mnemonic pDNS (own schema)
Pivot UI: Validin Community, Silent Push Community
Enrich: VirusTotal free, CT logs (Part 1)
Context: ASN / hosting reputation, self-run passivedns sensor
PIVOT STEPS
1. Forward-resolve the known-bad domain. Record A/AAAA history
with first/last seen.
2. Constrain to IPs active during the incident window. Drop
long-lived, high-count, shared-hosting addresses.
3. Reverse-resolve each surviving IP. List co-resident domains.
4. Keep only co-residents whose active window overlaps the
incident. This is the candidate cluster.
5. Pivot on shared NS, MX, and reused TXT tokens to extend the
cluster beyond IP co-residence.
6. Confirm cluster members against CT (shared certs) and scan
data (matching service fingerprints).
EXPECTED SIGNAL
A tight set of domains sharing IPs, nameservers, or verification
tokens, with first-seen dates clustered around the staging
period and last-seen dates clustered around takedown.
VALIDATION / TRIAGE
Discard sinkholes, parking, CDNs, and shared resellers. Require
at least two independent links (e.g. shared IP in-window AND
shared NS) before promoting a domain to confirmed cluster member.
ATT&CK MAPPING
T1583 Acquire Infrastructure (.001 Domains, .002 DNS Server)
T1584 Compromise Infrastructure (.001 Domains, .002 DNS Server)
T1568 Dynamic Resolution (.001 Fast Flux, .002 DGA)
T1071.004 Application Layer Protocol: DNS
Tooling quick reference
COMMUNITY-RUN (start here, free, durable)
CIRCL Passive DNS circl.lu/services/passive-dns
Free, vetted account. COF output. CSIRT-built.
Mnemonic pDNS api.mnemonic.no/pdns/v3/{query}
Open API, no key for low volume. Own JSON schema.
PyPDNS pypi.org/project/pypdns
Python COF client. Any COF server via url=.
FREE SAAS PIVOT INTERFACES
Validin Community app.validin.com
Best free pivoting. Web + API. Multi-year history.
Silent Push CE silentpush.com (Community Edition)
Free pDNS + infra scanning. Daily credits, and
the CE number is not published. Check first.
VirusTotal /domains/{d}/resolutions, /ip_addresses/{ip}/resolutions
Free API key. ~4 req/min, ~500/day.
RUN YOUR OWN
passivedns github.com/gamelinux/passivedns
Sniff and log resolutions off your own tap.
OPTIONAL COMMERCIAL UPGRADES (not required for any of the above)
DomainTools DNSDB (formerly Farsight), Microsoft Defender TI
(formerly PassiveTotal), SecurityTrails (Recorded Future),
Spamhaus pDNS, WhoisXML / DNSlytics.
The methodology does not change when you pay. Paid tools give you more coverage and faster queries, not a different technique. Run the loop above on the free sources and you already know how to use the expensive ones, and you are not stuck the day a trial expires.
Up Next
Open Season: Internet Scan Data. Take 89.208.250[.]38 with you, and take its dates with it. It is a real host at IT7 Networks that carried the kit from January to March 2026 and has belonged to somebody else since May, so a scan run today profiles the current tenant and tells you nothing about the campaign. Passive DNS got us the address and the window. It cannot tell us what was listening inside that window, and that is exactly the gap the next source closes. Censys and Shodan and what their free tiers actually give you, why a historical view beats a live probe on a recycled IP, the favicon hash everyone reaches for on a target like this and why it is the wrong primitive, and the one the kit cannot help serving.
Open Season is a recurring series from the THOR Collective exploring how practitioners can use open-source data to hunt adversary infrastructure. Each installment covers a single data source, soup to nuts. Want to contribute an installment or suggest a data source? Reach out.





