This is an old revision of the document!
Table of Contents
Provenance: Programming:Internet scanning
Back to Internet scanning. Corpus-wide selection and extraction notes are on corpus. This is the page-specific query log: every query with its population, the fold and its residue, the probes and the hand verdicts behind them, the external sources and the ones rejected, and what could not be established.
No ~~DISCUSSION~~ block here. Comments belong on the content page; this is a working log.
Run record
- Run date: 2026-09-10 (UTC). Single sitting.
- Authoring agent: Claude Opus 5 (Claude Code), working the
wiki-measurethewebitem “scanning has no instrument page”, section A2 of the 2026-09-02 brainstorm pass. - Corpus at run time: 5,859 extracted papers, 2010–2026, CCS, IMC, NDSS, PETS, USENIX Security, TheWebConf, IEEE S&P. Read-only inputs under
/workspace/publications_dataset/data/. No write to the publication mount. - Creating, not extending. Before writing:
node scripts/sitemap.mjs(168 existing pages, 7 promised-but-missing, all 7 declared on roadmap), then exported and read automated_measurements, design, programming, ip_classification, dns, ethics, email_authentication, crawler and roadmap. - Wiki saves through
scripts/dw.mjs(JSON-RPC) with–if-revon every existing page. - Pages saved this sitting:
literature:bibliography(rev 1789063332 → 1789074254, 8 entries appended),programming:internet_scanning(new),roadmap(1789062297 →),programming(1788006004 →),design:automated_measurements(1787943845 →),security:email_authentication(1788954595 →), and this page.
A follow-up run on 2026-09-17 worked the three items the 2026-09-10 review left open. It changed the page's population from 245 to 241 and refreshed about forty figures. Everything below that carries a number is the state after that run; the run itself is written up in Follow-up run, 2026-09-17, which also says which of the three items is still open.
- Run date: 2026-09-17 (UTC). Single sitting. Authoring agent: Claude Opus 5 (Claude Code), item “internet_scanning: three follow-ups the review left open”.
- Corpus unchanged: 5,859 extracted papers. No re-extraction, no new venue-years.
- Pages saved:
programming:internet_scanning,design:dns,roadmap, this page andprovenance:design:dns. Each was saved twice: once at ACTIVE=242, then again at 241 after the review passes returned. No bibliography change — no new citekeys. - Scripts added:
scripts/reconcile_zmap.mjs,scripts/tool_use_overrides.mjs,scripts/audit_hand_verdicts.mjs. Scripts changed:scripts/scan_fold.mjs,scripts/dns_fold.mjs,scripts/report_internet_scanning.mjs,scripts/report_dns.mjs,scripts/verify_scanning_figures.mjs. All are published in full at the foot of this page or of dns.
Accidental exposure, caught in this run
A GH_TOKEN value was printed into the run transcript on 2026-09-10 and the token must be rotated. The expression was ${GH_TOKEN:+set}${GH_TOKEN:-unset}: the first half prints the literal set, but the second half prints the variable's value whenever it is non-empty, which is exactly the case being tested for. This is the same class of mistake already recorded in the workdir README (echo “GH_TOKEN: $GH_TOKEN”, 2026-08-14) and it was made again ten minutes after reading that note. The correct form is ${GH_TOKEN:+set} alone, and that is what scripts/external_checks_internet_scanning.sh uses ([ -n "${GH_TOKEN:+x}" ]).
Judgement calls
| Decision | Why, and what a reasonable person might have done instead |
|---|---|
id = programming:internet_scanning, not design:scanning | Already decided on roadmap on 2026-09-07 (see provenance:roadmap section 3) and already a red link that scripts/sitemap.mjs gates on. The decision holds on its merits too: this page is about the invocation, the flags, the rate and the output format — the same register as crawler. The whether to scan decision stays on automated_measurements. No change to the roadmap row was needed beyond retiring it. |
| Creating a page rather than broadening automated_measurements | That page is a three-branch router with a 20 KB budget; it already carries the crawl branch's routing without absorbing crawler. Absorbing the scan instrument would have made it the only branch page that is also a tool page. |
A separate fold (scan_fold.mjs) rather than extending am_fold.mjs | am_fold.mjs backs automated_measurements. Editing it would silently move that page's published 93/40/38/31/25 and its 205-string residue. scan_fold.mjs is a superset with different kinds, a negative-guard list, a generic/proper-name distinction and a 173-entry hand map. The two folds therefore give different counts for the same family — ZMap 95 here against 93 there, nmap 45 against 40, ZGrab 32 against 31, RIPE Atlas 25 there and excluded here. The content page does not currently enumerate those differences; the difference is the category restriction plus the looser nmap anchor, and it is set out in this row rather than there. |
| ACTIVE = 241, not SCAN = 930, as the page's population | 930 is a studyTypes tag and studyTypes is the least stable field in the schema (57% run-to-run). An instrument page should be denominated by the instrument. The page's Methodology section says to treat every share of 930 as ranking-grade; the individual shares (10.4%, 10.2%, 57.4%, 30.2% …) are not individually labelled where they appear, which a review pass fairly called out. |
ACTIVE membership is decided by the instrument, not by tools[].category | category is a denominator: restricting to network-scanner gives 227 and loses 14 papers whose ZMap/OONI/Geneva/Censored Planet/hitlist entry is filed under other, infrastructure, http-client or program-analysis. All 14 are listed in section 1 of the report. |
…but generic strings and hand verdicts stay inside category == “network-scanner” | Without that restriction /^custom\b/i alone put 464 papers in the population, almost all custom crawlers and custom scripts, and the hand entries Python script, nc and whois fired on unrelated papers (so did Unicorn, which the 2026-09-17 audit then removed from the map for a different reason). Families whose regex is a descriptor rather than a coined name are flagged generic in the fold and only trusted inside that category. |
| The four practice figures are published as “about a fifth”, not as percentages | They are regex probes with a hand-read precision sample of 10–12. Publishing “19.9%” would be a false precision claim. The probe count, the sample, the per-paper verdict and the deciding reading are all below. |
| The ethics norms are routed to ethics and not restated | That page already carries the SURF/Jisc/Internet2 policy text, Hantke et al.'s operator interviews and the opt-out norm, all with primary sources. This page carries only the mechanics: which file, which flag, which DNS record. |
| DNS scanning routed to dns | zdns and massdns fold to kind dns and are reported but excluded from ACTIVE. dns was written on 2026-08-27 for exactly this reason. |
| Web-application vulnerability scanners excluded (Nikto, ZAP, sqlmap, OpenVAS, Nessus, Acunetix, …) | 15 SCAN papers name one. They scan one host's application layer, not an address space, and one 2023 paper alone contributes eleven of the names. Routed to web_vulnerabilities. |
Follow-up run, 2026-09-17
Three items were left open by the 2026-09-10 review. Two are closed; the third is not.
| Item | State |
|---|---|
(1) design:dns publishes “ZMap and XMap (112 papers)” while this page has 95 + 16 = 111 over a different fold. Overlap artefact or real disagreement? | Closed. Neither: one compound tools[] string. Both pages now agree. |
| (2) Nobody hand-audited the 174 HAND verdicts for judgement | Closed. All read; five kinds changed, one entry removed, five tuples suppressed, ACTIVE 245 → 241. A review pass re-read every change and every borderline against the papers and found a sixth (see D. Review log, 2026-09-17). |
| (3) Replace the four practice estimates with per-paper verdicts over the whole hit list | Not done. ~351 reads. The sample was made survivable instead; see below. |
A. The ZMap/XMap disagreement with design:dns — resolved
The reviewer's hypothesis was an overlap artefact — a paper naming both ZMap and XMap, counted once on one page and twice on the other. It is not. scripts/reconcile_zmap.mjs computes both paper sets from the same extraction and prints the symmetric difference:
| Quantity | Value |
|---|---|
| papers naming both ZMap and XMap (the suspected double count) | 0 |
dns_fold.mjs family ZMap / XMap, as design:dns published it | 112 |
scan_fold.mjs ZMap ∪ XMap, as this page published it | 111 |
| papers in the symmetric difference | 1 |
The one paper is CCS 2016, A Surfeit of SSH Cipher Suites. Its tools[] entry is the single string ZGrab/ZMap, with evidence.quote “We ran two IPv4-address-space-wide scans for SSH servers using ZGrab/ZMap [13].” — matched verbatim in data/fulltext/2016/CCS/a-surfeit-of-ssh-cipher-suites/paper.cols.txt. The paper used both tools. Both folds are ordered first-match-wins and single-valued, so a compound string can only land in one family, and the two folds happen to test in opposite orders: dns_fold.mjs tests ZMap / XMap before ZGrab, scan_fold.mjs tests ZGrab before ZMap. Same string, two families, one paper of disagreement.
Neither page was right. Both folds now carry an identical, hand-read COMPOUND_SPLIT map that splits this one string into its two components, each folded through the normal path:
scan_fold.mjs: ZMap 95 → 96, ZGrab 32 (already had it).dns_fold.mjs: ZMap / XMap 112 (unchanged), ZGrab 31 → 32.
After that, and after the tuple suppression in item B below, the two pages agree exactly: ZMap ∪ XMap = 111 on both, ZGrab = 32 on both, symmetric difference 0.
The recall question — how many other compound names are there? A blanket split would be wrong (miekg/dns is a GitHub path, HTTP/OneProbe is a product name), so the sweep is a guard rather than a rule. It looks at every used-or-produced tools[] name containing /, '' + '', '' and '', '' & '' or a comma — 263 tuples — and reports each one whose parts fold to a family the whole string does not. There are four, and only the ZMap one touches a published figure:
| Compound name | Category | Part the fold loses | Disposition |
|---|---|---|---|
ZGrab/ZMap | network-scanner | ZMap/active-scan on this page, ZGrab/scanner-generic on design:dns | SPLIT — hand-read, both folds |
HTTP/OneProbe | network-scanner | OneProbe/topology | left whole: it is one product name, and the whole string already folds to topology |
tcpdump/libpcap | traffic-capture | tshark/tcpdump/passive | left whole: neither kind is counted on either page |
WireShark/tshark | traffic-capture | tshark/tcpdump/passive | left whole: neither kind is counted on either page |
reconcile_zmap.mjs exits 1 if a new compound name appears whose lost part is a membership kind (this page) or a DNS_KIND (design:dns). Mutation-tested: removing the zgrab/zmap entry from both maps produced three failures, including “unsplit compound \”ZGrab/ZMap\“ loses membership kind ZMap/active-scan on the scanning page”.
B. Hand audit of the 173 HAND verdicts
The HAND map in scan_fold.mjs is one verdict per distinct category == “network-scanner” string the FAMILIES list does not fold. The coverage and staleness guards prove every string has a verdict and every verdict matches a string; they prove nothing about whether the kind is right. A wrong kind on a one-off scanner name moves ACTIVE by one paper and nothing fires.
scripts/audit_hand_verdicts.mjs dumps every entry with the evidence needed to judge it — the tool's purpose, the extractor's evidence.quote, the category and the paper's own title — and its full output is published at the foot of this page. All 174 entries were read on 2026-09-17 (the map is 173 afterwards, because one entry was removed). Where the name and purpose did not settle it, the paper's paper.cols.txt was read as well.
Five verdicts changed. Each is annotated inline in the fold source with the sentence that decided it.
| Name | Was | Now | Deciding reading | Effect on ACTIVE |
|---|---|---|---|---|
Unicorn | active-scan | entry removed | IMC 2024 Have you SYN me? is a network-telescope study. It never ran Unicorn: “we find no evidence of Unicorn being used for Internet-wide scanning and instead record in total only 2 distinct IP addresses ever using the Unicorn scanning tool.” The tuple is suppressed rather than re-kinded (see below), so the HAND entry would have been stale. | −1 (with the three below it, one paper) |
Exitmap | active-scan | p2p-crawler | WWW 2018 CrimeBB is a forum-scraping paper whose studyTypes do not include network-scan-or-probe. It uses Exitmap only for the node list: “We first use Exitmap [45] to get current available exit nodes, from which we build Tor circuits and fetch the index page of the forums.” The lower-case exitmap entry, USENIX 2016 Sybils in the Tor Network, genuinely scans exits and stays active-scan — HAND is keyed by the exact string, so the two can and must differ. | −1 |
SNMPv3 | grabber | topology | IMC 2025 Replication: Characterizing MPLS Tunnels does not send the probes; it reads CAIDA's ITDK: “we mapped each IP address to an inferred router using the MIDAR, iffinder, and SNMPv3 alias resolution graph included in the March 2025 ITDK”. MIDAR and iffinder, named in that same sentence, were already topology. | −1 |
DTLS-MitM-Scanner (DMS) | active-scan | lab-protocol-test | USENIX 2026 Analyzing the WebRTC Ecosystem: “an automated MitM testing framework (DTLS-MitM-Scanner (DMS)) to test the DTLS channel of a DTLS-SRTP connection”, “requiring only MitM capabilities”, over 24 named service providers. No address set is enumerated. The same paper's other instrument, TLS-Attacker, was already lab-protocol-test. | −1 |
LFP | topology | grabber | IMC 2023 Illuminating Router Vendor Diversity: LFP is “a novel lightweight, accurate, and more complete remote vendor fingerprinting methodology and tool”, benchmarked as “at least as good as Nmap while sending orders of magnitude fewer packets”. It probes hosts to learn what they say; it infers no path and resolves no alias. | +1 |
Net from the five kind changes: 245 → 242, and one correction in each direction rather than all one way. A sixth change, found by the review pass and not by this audit, took it to 241; it is in the review log below and in the suppression section that follows.
The tuple suppression, and the rule behind it
Reading Unicorn exposed a defect the HAND map cannot fix, because it is not about the name. IMC 2024 Have you SYN me? is a telescope study of other people's scanning, and the extraction records ZMap, NMap, Masscan and Unicorn as used because the paper's sentences name them in the first person. The extraction's own purpose fields give it away — “Fingerprint ZMap-generated scanning traffic”, “Fingerprint NMap-generated scanning traffic”, “Test Unicorn packet fingerprints”. A name fold cannot see the difference: the string is ZMap either way.
Three of those four are families, not HAND entries, so the correction has to be per (paper, tool). scripts/tool_use_overrides.mjs holds it, and is imported by both report scripts and by the reconciler, because “the paper did not run this” is a fact about the tuple and not about either page.
The rule, and an honest account of how it changed mid-run:
- (a) Suppress when the paper did not run the tool — it fingerprinted it, or read someone else's output. Written before the first entry.
- (b) Suppress when the packets never left the host onto the public Internet — a closed local loop or a synthetic load generator. Added on 2026-09-17 after the review pass read Spoki; clause (a) alone would have kept it, and one published figure was wrong by one paper because the rule was incomplete.
- Do not suppress when the paper ran the tool and the packets did cross the Internet, even if aimed somewhere unusual — its own honeypot, its own hosts, a cloud VM it controls. “The target was ours” is a different claim from “no packet was sent”, and relitigating the former per paper has no stopping rule.
The recall probe for this defect. A title/slug sweep for /telescope|darknet|darkspace|network tele|scanning traffic|characteriz\w* .*scanning|honeypot|backscatter|unsolicited traffic|scan traffic/i returns 20 papers, of which 4 are in ACTIVE. All four were read:
| Paper | Read | Disposition |
|---|---|---|
| IMC 2024 Have you SYN me? | Pure telescope, three /16s, 2015–2024. Ran nothing. | Suppressed, all four tuples. Leaves ACTIVE. |
| IMC 2015 Detecting Malicious Activity with DNS Backscatter | “We scan several protocols (ICMP, TCP port 22, 23, 80, and UDP port 53, 123) using ZMap” | Kept. It ran the scan. |
| USENIX 2022 Spoki | Ran ZMap, but only inside its own machine: “A local pair of virtual Ethernet interfaces route packets between the packet source and Spoki. We use ZMap to generate TCP SYN packets, passing the MAC address of the destination interface along with a probing rate and a range of IP addresses to use as a source.” The address range is a synthetic source prefix; nothing is probed. | Kept on 2026-09-17 under clause (a), then SUPPRESSED the same day under clause (b) after the review pass read the veth sentence this run had not. It was Spoki's only membership tuple, so the paper leaves ACTIVE. |
| CCS 2020 HoneyPLC | Ran Nmap and PLCScan against its own honeypot on AWS, to test camouflage. | Kept, same reason. |
That is a bounded sweep over a title pattern, not over the population, so it is a floor. A paper that fingerprints scanners without saying so in its title would be missed.
Borderlines considered and kept
These are the judgement calls a second reader would most plausibly decide the other way. All five are load-bearing — each one alone puts its paper in ACTIVE — and all five were kept.
| Name | Paper | Why it is borderline | Why it was kept |
|---|---|---|---|
gateway checker | USENIX 2024 Guardians of the Galaxy (IPFS) | HTTP requests to a list of gateway URLs looks more like a crawl than a scan. | “Our software probed the gateways from 2023-12-27 to 2024-01-31 every three days to avoid rate limits” — the authors' own probing software, a chosen host list, the public Internet, rate-limited. That is the written boundary. |
nc | CCS 2019 Security Certification in PCI | nc is named as an implementation detail of PciCheckerLite, which is filed vuln-scanner. | The paper does remotely probe database and SSH ports across 1,203 chosen sites, and nc is the tool named for that step. |
CnCHunter | IMC 2022 MalNet | A dynamic malware-analysis harness, not an address-space scanner. | “The key idea in creating D-PC2 is to probe a target subnet and a set of ports” — it probes a subnet. |
active-geolocator | IMC 2018 How to Catch when Proxies Lie | RTT-based geolocation is not topology discovery. | topology is the nearest kind in the vocabulary and it routes to ip_classification, which is where geolocation belongs. Non-membership either way, so no figure depends on it. |
Python script | IMC 2019 ECS Behavior of DNS Resolvers | A generic name, and the probe is a DNS query, so dns is arguable. | “The scan was conducted from a machine on our campus network using a Python script that issued 25K DNS queries per second” — scanning the IPv4 space to find resolvers is exactly what the content page calls the subject. |
What the audit did not do
- It read 174 strings, not 174 papers. Where the name and purpose were unambiguous —
iperf→ perf,Airmon-ng→ local-link, the eleven web-application scanners from one 2023 paper → vuln-scanner — no paper was opened. Roughly 20 of the 174 needed the full text. - One reader, one pass. No second opinion was sought on any of the five changes or the five borderlines.
- It cannot reach the families.
ZMap,nmap,Masscanand the other 60-odd FAMILIES entries are matched by regex with no per-paper reading at all. The telescope sweep above is the only check that has ever been run on them, and it is a title pattern.
D. Review log, 2026-09-17
Two sonnet passes, both handed the page text, the report script, its unedited output, the reconciler output and the 173-entry evidence sheet, and both told explicitly that the run's context might not be exhaustive. No fable generic pass was run — the item said not to re-run it — and that remains a gap.
Pass 1: hand-audit judgement (independent re-read of the papers)
| Finding | Disposition |
|---|---|
All five kind changes (Unicorn, Exitmap, SNMPv3, DMS, LFP): AGREE, each confirmed against paper.cols.txt | Accepted. It found better evidence than this run had in two places: the PCI paper says “We check the Mysql port using nc” and “We use nc to connect with port 22” across “1,203 e-commerce website[s]”, which settles the nc borderline far more directly than the inference used here; and the MPLS paper says “CAIDA's August 2025 ITDK process sent SNMPv3 probes”, attributing the probing explicitly. Both quotes are now the ones this page relies on. |
The Exitmap/exitmap asymmetry is right | Accepted. USENIX 2016 “implemented modules that detect HTML and HTTP tampering by connecting to a decoy server under our control” and “using exitmap to send decoy traffic over Sybils” — it runs the detection modules. CrimeBB only pulls a node list. |
| All six borderlines: AGREE | Accepted, and the reasoning recorded above is unchanged. |
Spokis ZMap is not a scan at all — “A local pair of virtual Ethernet interfaces route packets between the packet source and Spoki. We use ZMap to generate TCP SYN packets, passing the MAC address of the destination interface along with a probing rate and a range of IP addresses to use as a source.” A closed loop on one host; the address range is a synthetic source prefix. It was Spoki's only route into ACTIVE. | Accepted. Verified independently against paper.cols.txt before acting. This is the finding that moved ACTIVE from 242 to 241. It also exposed that the suppression rule was incomplete — clause (a), “did not run it”, keeps Spoki — so clause (b), “the packets never left the host”, was added and its mid-run addition is recorded in tool_use_overrides.mjs rather than back-dated. HoneyPLC's Nmap stays: those packets cross the Internet to a real host. |
TorFlow is labelled p2p-crawler where perf fits better (it times file downloads through circuits) | Noted, not changed. Both kinds are non-membership, so no figure moves, and re-kinding on taxonomy taste rather than on a population effect is how a fold drifts. Recorded so the next run can decide. |
The blanket vuln-scanner exclusion sits awkwardly on USENIX 2025 Trust but Verify, which runs Nuclei against “104,930 endpoints (IP:port pairs)” | Noted, not changed. That paper is already in ACTIVE via ZMap, so nothing is hidden here — but the exclusion was written for single-test-app scanners and could be dropping an internet-scale vulnerability campaign that happens not to name a family-matched tool. A real recall question this run did not answer. |
Swept the other ~160 verdicts; read the least-obviously-supported sole-route entries (BreadCrumb, dMAP, RAT-Scan, WhatWeb) | Accepted as confirmation. All four hold on direct quotes. No false positive among the load-bearing memberships. |
Pass 2: figures vs script
| Finding | Disposition |
|---|---|
verify_scanning_figures.mjs carried ['174 / 56 = 3.1', …] in EXTERNAL_FIGURES, and none of EXTERNAL_FIGURES, CODE_CONSTANTS or NOT_FIGURES is verified against anything — they are printed, never asserted. The real operands had moved to 172 and 55; the printed ratio still looked right only because of rounding. | Accepted, and the class fixed rather than the instance. The entry is gone; a new section E, DERIVED ARITHMETIC, recomputes the ratio from the two PUBLISHED_ tokens in the report and fails on disagreement. The script's header now says in terms that B, C and D are documentation and that anything which can go stale belongs in E. |
check_page_numbers.mjs run against this page exits 1 on 174 — contradicting this page's own sentence that the 2026-09-17 run “added this page to that check” | Accepted. That sentence was false when written, and it is the worst kind of error a provenance page can carry: a claim of rigour that had not been performed. The check is now genuinely run against this page, and its result is in the guards table below. |
page_scan.txt contradicted itself: “174 hand verdicts” in one section, “173” in another | Accepted and fixed to 173, which is Object.keys(HAND).length. |
page_scan.txt: “4 of the 46 papers in the provisional window” against its own table's ACTIVE=44 two lines above | Accepted and fixed to 44. Neither number guard can see this — 46 occurs legitimately elsewhere in the report — so only reading the prose against its own table catches it. |
namesNS in report_internet_scanning.mjs does not call notRun(), unlike its two siblings. Harmless today because the suppressed paper keeps an unsuppressed massDNS tuple. | Accepted and fixed anyway. A latent inconsistency that would have silently over-counted the moment NOT_RUN suppressed a paper's only network-scanner tuple, with no test to catch it. |
reconcile_zmap.mjs line 16 comment said ACTIVE=245 — published verbatim as a <file> block | Accepted and fixed. No functional effect; a wrong number inside citable published code is still a wrong number. |
report_dns.mjs line 86 comment said “the 143” where the published figure is 136 | Accepted and fixed. Internal comment, not published anywhere; pre-dates this run. |
| Section 8's three new guards: tried to construct a passing-but-wrong state, found none | Accepted as confirmation. |
kindsOfName / foldDnsToolAll dedup, and the three RESIDUE rows correctly not treated as defects | Accepted as confirmation. |
| Grepped all nine scripts for hardcoded 245/174/746/231/235 — only the comment-only instances above | Accepted as confirmation. |
What the second pass cost to ignore would have been high, and one of its findings is about this page lying about itself. Both passes are recorded in full because the rejections matter as much as the fixes: two findings (TorFlow, the Nuclei/vuln-scanner tension) were deliberately not acted on, and the reasons are above.
Not re-reviewed. The figures pass ran against the 242-state snapshot. Accepting its findings and the Spoki finding together moved ACTIVE to 241 and changed roughly forty cells again, and that final state has not been through a figures pass. It has been through check_page_numbers.mjs –code on all four pages, every report and verify script, and the reconciler — but a fresh adversarial read of the 241 state is the obvious next thing to do.
E. Guards run on 2026-09-17
| Guard | Result |
|---|---|
node scripts/reconcile_zmap.mjs –names | exit 0, “both folds agree and no unacknowledged compound name loses a counted kind” |
node scripts/report_internet_scanning.mjs | exit 0 |
node scripts/report_dns.mjs | exit 0 |
node scripts/verify_scanning_figures.mjs | exit 0, RESULT: OK |
node scripts/verify_dns_figures.mjs | exit 0 |
node scripts/check_page_numbers.mjs <page> <report+verify+reconcile> --code on programming:internet_scanning | 1 unaccounted, the 26 of the year range 2010–2026 — a pre-existing false positive of the checker, present before this run |
the same on design:dns | no unaccounted figure |
node scripts/check_wrap.mjs on both content pages and both provenance pages | OK |
node scripts/check_page_numbers.mjs <this page> <report+verify+reconcile> | run for the first time on 2026-09-17, after the review pass showed the earlier claim to be false. It does not reach zero on this page and cannot: 36 figures are unaccounted, and they are wiki revision ids, source line numbers, mutation-test constants, regex quantifiers like {0,120}, numbers quoted from papers (“104,930 endpoints”), and historical values this page deliberately records (the 2026-09-10 figures, the mutation-test constants). Every one of the 57 was read by hand on 2026-09-17 and the stale ones fixed; the tool is a prompt to re-read, not a pass/fail gate, on a page whose job is to record superseded numbers. Running it with --code is worse still — 101 unaccounted, almost all from the five published script sources. |
node scripts/verify_scanning_figures.mjs section E | new: recomputes the page's one derived ratio from the report's own PUBLISHED_ tokens instead of describing it |
mutation test: remove zgrab/zmap from both COMPOUND_SPLIT maps | 3 failures, exit 1 |
mutation test: misspell a NOT_RUN key | “NOT_RUN key names no tools[] entry”, exit 1 |
| mutation tests: the three pinned-sample guards | all three fire; commands and messages in Probes |
rendered DOM after save, programming:internet_scanning | 74 bibtex_citekey spans, 1 bibtex_references list, 18 headings, 6 tables, 0 red links |
rendered DOM after save, design:dns | 96 bibtex_citekey spans, 1 bibtex_references list, 28 headings, 11 tables, 0 red links |
No review sub-agents were run on 2026-09-17. The item said explicitly not to re-run the generic pass, and the run changed figures rather than prose. That is a gap: the figure-vs-script check that a sonnet reviewer would do was done by check_page_numbers.mjs and by re-reading the diff, both by the same agent that made the change.
Populations and queries
Counts are papers, never tuples. Sentinels are never answers. An absent ethics or artifacts object is counted as neither a stated value nor a sentinel and is printed as its own row.
| Query | Denominator | Result |
|---|---|---|
| corpus | — | 5,859 |
SCAN: studyTypes contains network-scan-or-probe | 5,859 | 930 (15.9%) — matches OVERVIEW.md; the report exits 1 if it does not |
| ACTIVE: names an active-scan / grabber / reachability / censorship-probe / IPv6-targeting instrument, used or produced | 5,859 | 241 (4.1%) |
| ACTIVE ∩ SCAN | 930 | 232 (24.9%) |
| ACTIVE outside SCAN | 241 | 9 (3.7%) |
ACTIVE if membership were restricted to category == “network-scanner” | 5,859 | 227 — the any-category rule adds 14 |
SCAN papers naming any category == “network-scanner” tool | 930 | 396 (42.6%) |
| SCAN papers naming no such tool | 930 | 534 (57.4%) |
| SCAN papers naming no instrument the fold recognises at all | 930 | 416 (44.7%) |
| ZMap ∪ ZGrab ∪ Masscan ∪ nmap, used or produced | 5,859 / 930 | 143 corpus-wide, 139 inside SCAN (the roadmap row said 143) |
| home-grown scanner (generic “custom” string, or a one-off name the paper produced) | 241 | 45 (18.7%) |
| families named by exactly one paper | 241 | 37 |
classification[].target == “ip-address” | 241 / 930 / 5,859 | 75 (31.1%) / 213 (22.9%) / 295 (5.0%) |
ACTIVE posters (poster- slug or Poster: title) | 241 | 4 (1.7%) |
| ACTIVE records of ≤4 pages | 241 | 6 (2.5%) |
ACTIVE papers with ≥1 detection[].prevalence | 241 | 240 (99.6%) |
Poster sensitivity. Dropping the 6 short records moves “vantage location stated” from 55.6% to 56.2% — 0.6 points. The corpus-wide poster caveat (posters have methods compressed out and bias every silence figure upward) applies, but ACTIVE is 1.7% posters against SCAN's 3.7%, so it bites less here than on most pages.
The fold
scripts/scan_fold.mjs. The written boundary is at the top of the file and was fixed before the first count.
- Coverage is a contract, not an aspiration. Every
category == “network-scanner”used-or-produced string must resolve to a family or to a HAND verdict. 741 tuples, 0 unmapped, 0 stale HAND entries. The report exits 1 in either direction — an unmapped string and a hand verdict that nothing matches, because a shadowed verdict is a lie about what was read. - Kinds, not one ranking. Twenty kinds. Five are membership (
active-scan,grabber,reachability,censorship-probe,ipv6-targeting); the rest are reported beside ACTIVE and routed elsewhere (dataset,topology,dns,vantage-platform,routing,vuln-scanner,passive,local-link,perf,simulation,p2p-crawler,name-enumeration,packet-crafting,registry,lab-protocol-test,host-tool). - One negative guard.
/z-?map/imatches WizMap, an embedding-visualisation tool that appears in one paper undercategory: other. Without the guard it would have entered ACTIVE as a ZMap use. This is the\b-does-not-protect-an-acronym trap in a different costume, and it was found only by printing the strings that matched outsidenetwork-scanner. - Generic families.
custom scannerandpingare flaggedgenericand only fire insidecategory == “network-scanner”. Measured cost of not doing this: ACTIVE went from 245 to 708, withcustom scanneralone at 464 papers. (Measured on 2026-09-10, when ACTIVE was 245; not re-measured.) - ZMap appears under 9 spellings in ACTIVE:
TCP Options module for ZMap,ZMap,ZMap-based script,ZMap6,ZMapv6,Zmap,zmap,zmap2,zmap6. nmap has 10, ZGrab 7, TLS banner grabbers 8, and the generic “custom” strings 21. - Self-tests: 74 cases plus 6 structural assertions, including both directions of the generic/category rule and the WizMap guard. The module throws at import if any fails, so every script that imports it inherits the check.
The full residue — which is empty — and the complete 173-entry hand map with its kinds are in the fold source at the foot of this page. Every one of the 173 was read against its tool's purpose text, its paper's title and, where those did not settle it, the paper's full text on 2026-09-17; see B. Hand audit of the 173 HAND verdicts.
Probes
All probes read paper.cols.txt with whitespace collapsed (a PDF line break inside a phrase otherwise silently undercounts). Three of the 930 SCAN papers have no paper.cols.txt; the report prints the missing count for every probe.
Each practice gets two widths. The loose regex is the recall claim; the first-person regex requires “we”/“our” near the term. Neither is a population, and the width decides the claim — publishing either alone would have been wrong in a different direction.
| Practice | loose | first-person | hand-read precision | published estimate |
|---|---|---|---|---|
| own probe rate or rate limit | 124 (51.5%) | 81 (33.6%) | 5/12 | ~34, “about one in seven” |
| honours a blocklist / exclusion list | 103 (42.7%) | 88 (36.5%) | 6/11 | ~48, “about one in five” |
| offers an opt-out | 74 (30.7%) | 70 (29.0%) | 9/10 | ~63, “about one in four” |
| source-address hygiene | 167 (69.3%) | 112 (46.5%) | 6/10 | ~67, “a little over a quarter” |
| abuse complaints received or handled | 58 (24.1%) | 45 (18.7%) | not sampled | published as an upper bound only |
| names an IRB or ethics board | 55 (22.8%) (loose; there is no first-person version) | — | not sampled | upper bound, and not to be read against ethics.reviewOutcome (92, 38.2%) |
| cites Durumeric / Partridge-Allman / Menlo / “good Internet citizen” | 172 (71.4%) (loose) | — | not sampled | upper bound |
Two of those cells were wrong on this page until 2026-09-17, and nothing caught them. The rate row said precision 6/12 and estimate ~42; the script has said 5/12 and ~35 since the generic review pass of 2026-09-10 flipped the PCspoof verdict from own to other. The per-paper table below still carried the pre-flip own as well. The lesson is the one already on Guards run in a different form: check_page_numbers.mjs was run against the content page and not against this one, so a provenance page restating a figure in prose had no guard at all.
And the first draft of this very paragraph then claimed the fix had been made when it had not. It said “the 2026-09-17 run added this page to that check”; the review pass ran the check against this page and it exited 1 on a stale 174. A provenance page asserting a rigour it has not performed is worse than one that admits the gap, and this is the second instance of the same class on the same page in one sitting. The check is now genuinely run against this page and its result is in the guards table.
Samples were every k-th paper of the sorted hit list. That made the sample a function of ACTIVE, and on 2026-09-17 a three-paper change to ACTIVE reshuffled all four strides and failed 46 of these checks on papers that were still perfectly good hits — the reads were invalidated by arithmetic, not by evidence. The sample is now the verdict map itself: a pinned list of papers, each with the sentence that decided it, drawn originally as every k-th of a hit list of the length recorded in the script. Three guards keep that honest, and all three were mutation-tested on 2026-09-17:
| Guard | What it catches | Mutation used to prove it fires |
|---|---|---|
| every pinned paper is still a hit | the probe changed, or the paper left ACTIVE | renamed one pinned key to CCS/2018/NOT-A-REAL-PAPER → “pinned sample paper is no longer a hit”. It then fired for real the same day: suppressing Spoki's ZMap tuple took that paper out of ACTIVE, and the blocklist sample had it pinned. Its verdict was dropped with the reason recorded inline, so that sample is 11, not 12, and its precision is 6/11. |
| hit-list length within 15% of the length at draw time | a frozen sample quietly stops being representative as the list grows | set the source-address pin to 250 against a real 113 → “hit list moved 54.8% since the sample was drawn” |
| pinned papers span at least half the sorted hit list | a sample clustered in one corner is not a sample | cut the opt-out map to its first three keys → “pinned sample spans only 12 of 70 sorted hits” |
Every hand verdict, with its deciding reading
own = the matched sentence is about the authors' own scan. other = the term is in the paper for a different reason.
| Probe | Paper | Verdict | Deciding reading |
|---|---|---|---|
| rate | CCS/2015 Search engine backed by Internet-wide scanning | other | rate-limits its own web interface, not its scan |
| rate | CCS/2025 Poster: ERIS | other | ICMPv6 rate limiting is the side channel under study |
| rate | IEEE S&P/2023 PCspoof | other | “sending ARP requests at 100 Mbps” is an attack rate on a TTEthernet lab testbed, not a scan rate. Read as own on the first pass and flipped by the generic review on 2026-09-10; this table was not updated then, and said own until 2026-09-17 |
| rate | IMC/2016 Free bridge across the digital divide | other | Free Basics traffic is throttled |
| rate | IMC/2022 Saving Brian's privacy | own | “We rate-limit requests to authoritative name servers” |
| rate | IMC/2025 Chaos in the chain | own | “we limited the scanning rate to below 500KB/s” |
| rate | NDSS/2020 Measuring the deployment of network censorship filters | own | “we abide by best practices and limit the rate of sending requests” |
| rate | NDSS/2024 Proof of Backhaul | other | tc-shaped test rig, not a scan rate |
| rate | USENIX/2011 Toward secure embedded web interfaces | other | the embedded device throttles requests |
| rate | USENIX/2021 AmpMap | other | “block or rate-limit specific queries” is the defence it recommends |
| rate | USENIX/2024 6Sense | own | “a low scanning rate of 10,000 packets per second” |
| rate | USENIX/2025 Exposing and circumventing SNI-based QUIC censorship | own | “at a rate of 100 packets per second” |
| blocklist | CCS/2018 Practical state recovery attacks | own | “respecting hosts who wished to be blacklisted” |
| blocklist | IEEE S&P/2017 Augur | other | block lists of censored sites, used as validation data |
| blocklist | IMC/2011 Detecting, validating and characterizing computer infections | other | blacklists are an input dataset |
| blocklist | IMC/2016 Security harm of TLS crypto shortcuts | own | “followed the institutional blacklist” |
| blocklist | IMC/2018 Clusters in the Expanse | own | “maintaining a blacklist and using dedicated servers” |
| blocklist | IMC/2022 Deep dive into the IoT backend ecosystem | other | the FireHOL blocklist is the object of measurement |
| blocklist | IMC/2023 ECN with QUIC | own | “how to be added to our blocklist to be excluded from further measurements” |
| blocklist | NDSS/2020 Decentralized control: Russia | other | Roskomnadzor blocklists are the subject |
| blocklist | NDSS/2025 Revealing the black box of device search engine | other | operators blocklisting the scanners it studies |
| blocklist | USENIX/2014 An Internet-wide view of Internet-wide scanning | own | “only 208 organizations have requested that we exclude their networks from our scans” |
| blocklist | USENIX/2018 Quack | own | “respecting an IP blacklist shared with other scanning research” |
| blocklist | USENIX/2022 Spoki | other | analysis-time exclusion of known scan projects |
| opt-out | CCS/2015 Search engine backed by Internet-wide scanning | own | “we treat opt-out requests for scanning as a request to be removed from the search index” |
| opt-out | IEEE S&P/2020 Meddling middlemen | other | browser users opting out of data saving |
| opt-out | IMC/2010 Demystifying service discovery | own | TXT records pointing at a page with opt-out instructions |
| opt-out | IMC/2018 In the IP of the beholder | own | “we received two opt-out requests with which we immediately complied” |
| opt-out | IMC/2022 Deep dive into the IoT backend ecosystem | own | “We run a Web server with experiment and opt-out information” |
| opt-out | IMC/2024 Destination reachable | own | “We received a single request for opt-out and acted accordingly” |
| opt-out | NDSS/2024 Protective DNS services | own | PTR records giving contacts for opt-out |
| opt-out | PETS/2023 CERTainty | own | “offer administrators the option to opt out of our scanning” |
| opt-out | USENIX/2018 Quack | own | “simple ways for packet recipients to opt out of future probes” |
| opt-out | USENIX/2024 LanDscAPe | own | “a website gives project details and contact information for opt-out requests” |
| source | CCS/2015 Search engine backed by Internet-wide scanning | own | “All of the scanning hosts have WHOIS records and reverse DNS entries” |
| source | IEEE S&P/2010 Investigation of triangular spamming | other | source-IP rewriting is the attack under study |
| source | IMC/2010 Demystifying service discovery | own | describes its own source IPs and how many it used |
| source | IMC/2016 HoBBIT | other | rDNS patterns of the targets |
| source | IMC/2019 End-to-end measurement of DNS over encryption | own | “setting a reverse DNS record for our scanning system and building a website” |
| source | IMC/2023 Does it spin? | own | rDNS entries, project name in every request, dedicated website |
| source | NDSS/2020 Peeking behind NAT gateways | own | “The reverse DNS record of the scanning host was set to indicate its use for research purposes” |
| source | NDSS/2026 OSAVRoute | other | source-address validation is the subject |
| source | USENIX/2021 LZR | other | discusses scanners' source IPs as a defence property |
| source | USENIX/2024 6Sense | own | “The webpage hosted on each scanning IP/domain includes a contact email” |
Probes run and rejected
| Probe | Result | Why rejected |
|---|---|---|
/Gasser/ over SCAN | 93 papers (10.0%) | Reads as “cites the IPv6 hitlist authors”. It is not: the surname is not unique (Urs Gasser publishes on Internet governance) and the hits are reference-list entries, so it counts citations rather than use. Kept in the report as a labelled rejected probe. |
/(IPv6[^.]{0,80}hit ?list\|hit ?list[^.]{0,80}IPv6\|Gasser\|IPv6 Hitlist)/i | 97 (10.4%) | Same defect: with Gasser in the alternation the “IPv6 hitlist” figure came out larger than the loose /hit ?list/i figure (62), which is arithmetically impossible for a narrowing and is what exposed it. Narrowed to /(IPv6[^.]{0,80}hit ?list\|hit ?list[^.]{0,80}IPv6\|IPv6 Hitlist)/i = 39. |
/black ?list\|block ?list\|…\|exclude/i near “we/our” | 151 (61.6%) | An early version admitted a bare exclude after “we/our”, which matches “we exclude domains from our dataset” — it returned more hits than the loose probe it was supposed to narrow. Tightened to require a blocklist term or an address-shaped exclusion. |
/(we\|our)[^.]{0,120}(…\|reverse DNS\|rDNS\|PTR record\|WHOIS)/i | 141 (57.6%) | Fired on every paper that uses WHOIS or rDNS as analysis data. Tightened so rDNS/WHOIS/PTR must be near “our”/“we”/“explain”/“scan”. |
| the queued claim “only 5 papers in the corpus mention a hitlist” | — | Rejected as the page's IPv6-thinness figure. It is a tools[] fold, and an extraction fold is not a full-text count: 62 SCAN papers say “hitlist”, 39 say it near “IPv6”, and 11 name an IPv6-targeting instrument. The page publishes all three with their populations rather than one of them. |
Quotes and per-paper figures
scripts/verify_scanning_figures.mjs looks up every quoted sentence and per-paper figure on the content page in the cited paper's paper.cols.txt with whitespace collapsed, falling back to a pypdf re-extraction of paper.pdf. 30 needles, 0 not located, 0 weak needles under 20 characters. 26 are in-corpus and all 26 resolved in .cols, so the PDF fallback never had to fire for those. The other 4 are from [1Durumeric, Zakir; Wustrow, Eric; Halderman, J. Alex (2013): "ZMap: Fast Internet-wide Scanning and Its Security Applications", in: Proceedings of the USENIX Security Symposium. (Link)], which is not in the extraction: they are checked against out/scan/zmap2013_paper.txt (72,192 bytes), a pypdf extraction of the authors' own copy at zmap.io/paper.pdf. That check needs its own de-hyphenation step — pypdf keeps the printed hyphen at a line break, so accu- racy and theoreti- cal are two words in the extraction and one in the PDF, and two of the four quotes failed until it was added.
Two things that went wrong and were fixed:
- A misread digit. A first pass read the Durumeric et al. USENIX Security 2014 sentence off a 180-character context window as “908 organizations have requested that we exclude their networks”. The paper says 208, in both
paper.cols.txtand an independentpypdfextraction ofpaper.pdf. The wrong number never reached the page, but it did reach a hand-verdict comment in the report script and had to be corrected there. A narrow context window is not a source. - Two quotes failed on capitalisation. “Consider whether existing open source datasets…” and “In early 2024, ZMap changed…” are sentence-initial in the paper, and the draft had lower-cased them to fit mid-sentence. The page now quotes them with the source's capitalisation. A quote check that had been case-insensitive would have passed both silently.
The published script
pages/scan_budget.py is embedded in the page as a <file python> block. Verified after saving:
- The embedded block is byte-identical to
pages/scan_budget.py. - Downloading it back off the rendered page (
/_export/code/programming:internet_scanning?codeblock=3) and diffing gives one difference, a stripped trailing newline. The downloaded copy runs its own–self-test: 9 checks, OK. - The
<code>block beside it is the whole output of a real run, compared line-by-line for exact equality — not a containment check, which passes on a truncated block. - The self-test has a mutation guard: replacing
collapse_addresses()with a baresummakes the overlapping-CIDR case return 33,554,432 instead of 16,777,216 and check 3 fails. Check 4 exists so that “collapse does nothing at all” cannot also pass — a disjoint pair must still add up. - The collapse is doing real work on the real file:
255.255.255.255/32is inside240.0.0.0/4, so a naive sum reports 592,708,865 removed against the correct 592,708,864.
External sources
scripts/external_checks_internet_scanning.sh re-fetches every non-corpus fact and prints FAILED explicitly per check. Its unedited output is at the foot of this page. Honours GH_TOKEN via ${GH_TOKEN:+x} — see the exposure note above for the form that leaks.
| Source | How verified | Verdict |
|---|---|---|
| ZMap flags and defaults | src/zmap.1.ronn at tag v4.4.0, and separately at main, with the defaults diffed between them (identical) | Accepted. Tip-of-tree is not the shipping build; both were read. |
--blocklist-file-extra | Does not exist. A draft of the example command invented it. The check now greps for it and reports if it ever appears. | Rejected and corrected: ZMap has one --blocklist-file. |
--target-port | Also wrong in the same draft: the flag is -p, --target-ports (plural). | Corrected. |
conf/blocklist.conf | Fetched at v4.4.0; 16 CIDR entries, header dated 2013, and grepped for opt-out language (none) | Accepted. The “it is not an opt-out list” claim is a scripted check, not an assertion. |
ziterate / zblocklist / ztee | GitHub contents API on src/ at v4.4.0, plus their own ronn man pages | Accepted, quoted from the man pages. |
| ZGrab2 input format and the ZMap/ZGrab division of labour | README.md at tag v1.0.0 | Accepted, quoted verbatim. |
| Repository state and releases | GitHub REST API. /releases/latest and /tags, because a tag-only repo 404s on the former and /tags is unsorted | Accepted. masscan's newest Release is 1.3.2 (2021-01-31) while pushed_at is 2026-04-23 — both are printed, and the page says “newest GitHub Release”, not “last activity”. |
| nmap version | newest nmap-*.tar.bz2 on nmap.org/dist, sorted with sort -V | Accepted (7.991). |
| IPv6 Hitlist Service | The three open files downloaded and decompressed; line counts, not advertised figures, with the server's own Last-Modified | Accepted. Snapshot 2026-09-05. |
| Rapid7 Project Sonar | opendata.rapid7.com followed (302 → sonardata.rapid7.com), then /about with a browser User-Agent, tags stripped before matching | Accepted. The free Open Data programme is gone; access is commercial. A curl without -L would have recorded a redirect as content. |
| Censys | search.censys.io 403 to automated clients, censys.com 200 | Accepted, and stated as a bot wall rather than an outage — the same finding automated_measurements already records. |
| The two USENIX bibliography entries | PETS and USENIX index records carry no authors. Both author lists were taken from citation_author meta tags on the landing page and checked against the paper's own PDF header text | Accepted. Both match exactly, 7 and 9 authors. “Gurur Öndarö” is what the PDF says. |
| SEO listicles, “top 10 scanner” pages, vendor blogs | Not consulted | Rejected by policy. Every tool claim on the page traces to a repository, a man page, a README or a paper. |
| Cloud-provider scanning policies | Not re-derived here | Deliberately deferred: ethics already carries AWS and Azure with primary sources and read dates. Duplicating them would have created two copies to keep current. |
What could not be established
- How many scan papers actually rate-limited, honoured an exclusion list, or offered an opt-out. Still open after 2026-09-17. Only estimates, from a regex probe with a hand-read precision sample of 10–12. Closing this needs the same treatment
harm_fold.mjsgot on ethics: a loose candidate sweep plus a per-paper verdict over the whole hit list. The current hit lists are 81 / 88 / 70 / 112 papers — 351 reads, not the ~90 per practice the original note estimated — and the report now says so in its own output. What the 2026-09-17 run did do is make the existing sample survive a population change instead of dissolving into it; the four estimates are still estimates. - Whether the 534 SCAN papers that name no
network-scannertool scanned without saying with what, or queried a dataset. The schema cannot separate those two, and neither can a name fold. A full-text pass over a sample would. - The recall of ACTIVE outside
category == “network-scanner”. One-off scanner names filed underotherare not hand-read — that would be the whole 16,737-name tool vocabulary. ACTIVE is a floor and the page says so. - Venue coverage. PAM, TMA, ANRW, CoNEXT and ACSAC are not in this corpus, and for network scanning that is a larger gap than for the web-measurement pages: Yarrp, MIDAR, scamper and several IPv6 target generators have their tool papers at those venues. Any “the literature does X” sentence on this page should be read as “these seven venues do X”. Nothing here can fix that; it needs the corpus extended.
- Whether ZMap's 33% traffic-share figure still holds. It is [2Durumeric, Zakir; Adrian, David; Stephens, Phillip; Wustrow, Eric; Halderman, J. Alex (2024): "Ten Years of ZMap", in: Proceedings of the ACM Internet Measurement Conference. (DOI)]'s Q1 2024 measurement of scan traffic seen at one telescope, not a corpus figure and not a 2026 figure. No newer public measurement was found.
- Whether the roadmap's original “143” was ever right. It re-derives to 141 in SCAN / 145 corpus-wide under this fold and 136/137 under
am_fold.mjs. The difference is entirely in how loosely thenmapregex is anchored. No reconstruction of the original query exists.
Guards run
| Guard | Result |
|---|---|
node scripts/report_internet_scanning.mjs | exit 0, “OK — all contracts hold” |
node scripts/verify_scanning_figures.mjs | exit 0, 26/26 needles located |
bash scripts/external_checks_internet_scanning.sh | 0 FAILED lines |
node scripts/check_page_numbers.mjs <page> <report+verify> --code | whole-page, including <file> and <code> blocks: “OK — every figure in the page traces to” |
node scripts/check_wrap.mjs on the content page, this page and all four edited pages | OK |
node scripts/check_tables.mjs on the same | “every table has one width” |
python3 scripts/bib_dedup_scan.py –bib <fresh export + 8 new> | 0 definite duplicate pairs; the one candidate touching a new key is durumeric2013_zmap/williams2024_6sense, different papers |
| rendered DOM after save | 37 source {[key]} markers → 74 bibtex_citekey spans (the plugin emits two per marker) → 15 <dt> references for 15 distinct keys; 0 “could not be found” warnings; 0 red links |
| red links in the rendered DOM | 3, all of them to this page, before it existed |
published <file> round-trip | byte-identical apart from a stripped trailing newline; runs its self-test |
Review log
Four passes, all told explicitly that the author's context might not be
exhaustive, and all handed the page text, the report script and its unedited
output, the fold, the published code and the provenance notes. The three focused
passes were spawned in parallel against the first published revision
(programming:internet_scanning rev 1789074265); the generic pass was spawned
afterwards, against the corrected page, so it saw a different snapshot. That
matters for reading their findings and is recorded here rather than left to be
inferred.
Two of the three focused passes had to be prompted to return partial findings — they were still fetching after a long wall-clock stretch. All three returned in full in the end.
Applied before the passes returned (author-found)
| # | Finding | Disposition |
|---|---|---|
| A1 | The Masscan comparison was quoted as “Masscan finds notably fewer hosts than ZMap…”; the paper reads “Masscan [48] finds…”, and the result is Adrian et al.'s, relayed by [2Durumeric, Zakir; Adrian, David; Stephens, Phillip; Wustrow, Eric; Halderman, J. Alex (2024): "Ten Years of ZMap", in: Proceedings of the ACM Internet Measurement Conference. (DOI)]. | Fixed. Ellipsis added and Adrian et al. named. The citations pass independently reached the same place and called the original phrasing “borderline but not dishonest”. |
| A2 | Family table wrote “Entropy-IP”; the tool and the report say Entropy/IP. | Fixed. |
| A3 | “orders of magnitude slower” for nmap was an unsourced assertion. | Fixed with the ZMap paper's own measurement — “over 1300 times faster than the most aggressive Nmap default settings, with equivalent accuracy” — and the framing changed to “a different instrument”, not an obsolete one. That paper is not in the extraction; the authors' copy at zmap.io/paper.pdf was fetched, extracted with pypdf and added to the verifier as four off-corpus needles. |
| A4 | The rate table had no independent check. | Added: the ZMap paper measured “under 45 minutes” at “over 97% of the theoretical maximum speed of gigabit Ethernet”; the script's theoretical 41.5 min ÷ 0.97 = 42.8. Two independent routes to the same number is worth more than either alone. |
Pass 1 — figures vs script (''sonnet'')
Re-ran all four scripts, confirmed they reproduce the committed outputs
byte-for-byte and that the provenance page's embedded copies are byte-identical
to fresh runs. Mutation-tested excluded_addresses, line_rate_pps,
scan_plan, both directions of the section-8 sample check and the section-0
fold-coverage check, and confirmed each fires. Found no figure that mixes the
SCAN/ACTIVE denominators. Four findings:
| # | Finding | Disposition |
|---|---|---|
| P1.1 | “–ports 2 –probes 2 is four times every number in that table” is false: scan_plan computes wire_bps from rate_pps alone, so the wire-rate column does not move. Verified by calling the function both ways — seconds ratio 4.0, wire_mbps ratio 1.0. | Accepted and fixed. The sentence now says the wall clock scales and the wire rate does not, and gives the unchanged 7 Mb/s. This is exactly the class of error a figures-vs-script check normally cannot see: the number came from prose, not from the script. |
| P1.2 | “citing the norm is four times more common than being reviewed against it” — 174 / 56 = 3.1 (the operands as they stood on 2026-09-10; they are 172 / 55 = 3.1 after the 2026-09-17 audit, and the ratio is now recomputed from the report by section E of verify_scanning_figures.mjs rather than described). | Accepted and fixed to “about three times”, with the division shown. |
| P1.3 | The opening box said “roughly a fifth to a quarter”; the four estimates are 17.1 / 18.4 / 25.3 / 27.8%, so the band is wrong at both ends. | Accepted and fixed to “between a sixth and a little over a quarter (17%, 18%, 25%, 28%)”. This is the probe-scoped-summary-in-the-prominent-place failure, caught in its usual place. |
| P1.4 | pages/scan_budget.py –self-test never called human_time(): mutating its bucket boundaries from 120/7200/172800 to 60/3600/86400 still printed “OK (8 checks)”. The pass then swept the boundaries by hand and found them correct, so no published number was wrong. | Accepted and fixed. Check 9 added: six ±0.01 s boundary cases plus three exact conversions. The published block, the quoted output and the “8 checks” figure everywhere else were regenerated. A self-test that claims coverage it does not have is worse than none. |
Pass 2 — citations and quotes (''sonnet'')
All 15 citekeys resolve exactly once in a fresh export;
bib_dedup_scan.py finds 0 definite duplicates; check_attributions.mjs
passes 10/10; every quoted sentence checked matched verbatim; both hand-fetched
USENIX author lists match the PDF header in order, including the last author and
the umlauts; the published code contains no names at all. One finding:
| # | Finding | Disposition |
|---|---|---|
| P2.1 | “Beverly et al. …, on a six-month IPv6 topology campaign”. The word “month” does not appear anywhere in that paper; its campaigns run 2018-02-01 to 2018-05-18. The six months belonged to Gasser et al., a different paper quoted three paragraphs earlier, and had been carried across. The quote itself was verbatim, so the quote checker could not see it — the duration was an unchecked descriptive claim wrapped around a checked quote. | Accepted and fixed to “between February and May 2018”. |
Pass 3 — external currency (''sonnet'')
Independently re-fetched and confirmed: both ZMap man pages (tag and main,
byte-identical, 332 lines each), every published flag and default, the absence of
--blocklist-file-extra, the three helper binaries, conf/blocklist.conf
(16 entries, 2013 header, no opt-out language) with the CIDR arithmetic
recomputed by hand including the 255.255.255.255/32-inside-240.0.0.0/4
overlap, all ZGrab2 README quotes, every repository state and release date, nmap
7.991 against nmap.org's own changelog, all three IPv6 Hitlist line counts
downloaded and counted independently, the Rapid7 Sonar redirect and the 8 / 47,457
/ 60.7 TB figures (summed by hand from the eight per-dataset counts), and both
USENIX author lists. Two findings:
| # | Finding | Disposition |
|---|---|---|
| P3.1 | The ziterate man page says “psuedorandom” — a transposition in the source. The page quoted it as “pseudorandom”, silently correcting it inside quotation marks. | Accepted and fixed to the source's own spelling with [sic]. Small, and exactly the bar the page sets for every other man-page quote. |
| P3.2 | external_checks_internet_scanning.sh printed release tag names but not release dates, so the three dates the page publishes were not actually verified by the script — the pass had to hit /releases/tags/<tag> itself. | Accepted and fixed: the script now fetches published_at per release and prints it beside the tag. A date is precisely the fact that goes stale without anyone noticing. |
| P3.3 | www.zoomeye.org answers HTTP 521 with an X-Via-JSL header (Knownsec's own bot challenge), while zoomeye.ai answers 200. | Noted, no change. The page names ZoomEye in a list of query targets and makes no uptime or URL claim about it. Recorded here so a later tightening of the “still operating” check does not read the 521 as a shutdown. |
Pass 4 — generic (''fable'')
It returned after the “did not return” note above had already been written and published, in response to a second prompt for partial findings. Fifteen findings on the already-corrected page, and it earned its slot: five of them are things the three focused passes structurally could not see, and two are wrong numbers the number guard passed because the digits occur elsewhere in the report. This is the fourth page in a row where the generic pass found the worst item.
| # | Finding | Disposition |
|---|---|---|
| P4.1 | A contradiction at a seam. “56 (22.9%) name an IRB or ethics board at all” — with a ratio built on it — sits two paragraphs above the extraction's own ethics.reviewOutcome, stated on 93 (38.0%) of the same 245 papers. The 56 is a loose regex with no measured precision, and “at all” is the probe-scoped qualifier dropped in the prominent place. | Accepted and fixed. Both numbers now appear together, the ratio is gone, and the sentence says the 93 is the number to quote. Neither a figures pass (both numbers match the script) nor a citations pass (no citation involved) could see this. |
| P4.2 | The opening box republished the precision the page's own limitations forbid — it listed “(17%, 18%, 25%, 28%)” while §Methodology says “read 'about a fifth', not '18.4%'”. That box had been rewritten by finding P1.3 of the figures pass two hours earlier: fixing a wrong band introduced a false precision. | Accepted and fixed to “somewhere between a seventh and a quarter … read them as 'a minority, and a small one', not as percentages”. A fix is a new edit and needs the same scrutiny as the thing it replaced. |
| P4.3 | “Forty more families are named by exactly one paper each … 45 of 245 rolled their own” does not add up, and the reviewer worked out why from the report source: only 20 of the 40 one-paper families are a tool the paper produced; the rest are third-party tools (nc, ike-scan, smbmap, JARM, WhatWeb) that appear once. Re-derived: 20 produced + 25 “custom scanner” − 5 overlap = 45. | Accepted and fixed, and the report now prints the decomposition. The follow-on flourish “the scanner is a hundred lines around a raw socket” was cut as unbacked and, for an Internet-wide scanner, untrue. |
| P4.4 | “several instruments above (Yarrp, MIDAR, scamper) have their tool papers at those venues [PAM, TMA, ANRW, CoNEXT, ACSAC]” — flagged as probably wrong. Checked: Yarrp's tool paper is IMC 2016 and is in this corpus. | Accepted and fixed. The venue-gap caveat stands; the examples were removed and replaced with the Yarrp counter-example. A confident parenthetical inside a correct caveat is a good hiding place. |
| P4.5 | This page claimed “All 26 [needles] resolved in .cols; the PDF route did not have to fire”. By then it was 30 needles, four of them off-corpus against a pypdf extraction that needs a de-hyphenation step two quotes failed without. | Accepted and fixed above. The claim had gone stale inside the section whose purpose is auditability. |
| P4.6 | The scan_budget.py docstring still said “eight checks” after check 9 was added. | Accepted and fixed. A byte-identity guard proves the page matches the file, not that the file is right. |
| P4.7 | “The convention this literature has converged on” is contradicted eight lines later by the page's own ~28% estimate. | Accepted and fixed to “the convention the ZMap-era methods papers set — and about a quarter of scanning papers follow”. |
| P4.8 | The aliasing figure had the wrong denominator. 10 of 930 (1.1%) counts papers that never touch IPv6. Against the 42 papers that name an IPv6-targeting instrument or say “hitlist” near “IPv6” it is 8 (19.0%). “Almost nobody writes it” was a “nobody” from a probe. | Accepted and fixed, both denominators now published side by side, and the report computes the subset. The rewritten sentence is also a stronger finding: four in five of the papers doing IPv6 targeting do not discuss de-aliasing. |
| P4.9 | Two overclaims on this page: that the content page says where the two folds disagree (it does not), and that every share of 930 is labelled ranking-grade (only the Methodology section says it). | Accepted and fixed, by writing down what is actually true — including the four family counts where this fold and am_fold.mjs disagree. |
| P4.10 | The “Review in flight” placeholder was still on the published page. | Already fixed before the pass returned; it saw an older snapshot. |
| P4.11 | OONI (7) and Censored Planet (4) contradict the ACTIVE definition: a paper re-analysing OONI data did not send probes to addresses it chose. And an IPv6 target generator “sends nothing”. | Accepted, definition rewritten rather than the population changed. Both edges are now stated on the content page as judgement calls: a censorship platform counts either way because the platform is the instrument and the corpus rarely says which; a target generator counts because in IPv6 it is the step that decides what gets probed. |
| P4.12 | Four technical errors in the instrument table and one in the published docstring: ZGrab does not “complete the handshake ZMap only started” (ZMap RSTs; ZGrab opens its own connection); nmap is not “connection-oriented”; ZMap was called “IPv4/IPv6” while a later section says its design does not extend to 128 bits; masscan has had IPv6 since 1.3; and ZMap's default synscan sends no TCP options, so the docstring's “SYN with an MSS option” was wrong (the 84-byte constant is unchanged — the frame is padded to the 60-byte minimum either way). | All accepted and fixed. None of these has a number in it, which is why nothing caught them. |
| P4.13 | Four unbacked framing sentences, including “the fourth party most papers forget” and a per-year IPv6 series quoted only at its two most favourable endpoints (2010 12.5% → 2025 44.3%, skipping 2013's 0% and 2024's 29.2%). | Accepted and fixed, the series now given at five points including the non-monotone ones. |
| P4.14 | A hand verdict disputed. IEEE S&P 2023 PCspoof was marked own on “sending ARP requests at 100 Mbps” — an attack rate on a TTEthernet lab testbed, not a scan rate. | Accepted and flipped, which moved the rate precision from 6/12 to 5/12 and the estimate from ~42 to ~35 (one in seven). Two figures on the page were stale afterwards and check_page_numbers.mjs passed anyway, because 42, 6 and 17 all occur elsewhere in the report — the documented presence-not-binding weakness, hit for real. Found by grep, not by the guard. |
| P4.15 | A structure gap: the invocation only ever scans the whole space on TCP/443, when most first scans are a target list. | Accepted, and -w/--allowlist-file, -I/--list-of-ips-file and --probe-module added with the note that the blocklist still applies on top. |
It also read the provenance probe table and found the IRB row filed under the
“first-person” column although the report calls it loose. That is correct and
the row is now labelled. Its own NOT READ list is long — the embedded fold
source, the external-checks output, `practices:ethics` and `design_dns.txt` for
duplication, and a possible disagreement between this page's ZMap 95 + XMap 16
and dns' “ZMap and XMap (112 papers)” that it could not resolve.
That last one is unresolved. A follow-up item
(internet_scanning: three follow-ups the review left open) is filed for it,
together with the two gaps below.
What the passes did not cover
Each pass named its own gaps and they are worth recording, because they are the parts of this page nothing checked:
- The 112-vs-111 question above. dns reports “ZMap and XMap (112 papers)” against this page's ZMap 95 + XMap 16 = 111. That may be an overlap artefact (a paper naming both) or a real disagreement between two folds. Not resolved.
- Nobody hand-audited the 174
HANDverdicts inscan_fold.mjsfor judgement (only the coverage and staleness guards were mutation-tested). A wrong kind on a one-off scanner name moves ACTIVE by one paper and nothing would fire. - Nobody re-read the 44 probe papers to confirm the
own/otherverdicts in section 8 independently of the author's reading. Those verdicts carry the four practice estimates. - The figures pass did not re-fetch the external facts; the currency pass did not re-derive the corpus figures. That split is deliberate, but it means no single pass read an external figure and a corpus figure in the same sentence.
- The citations pass flagged that nobody had verified [1Durumeric, Zakir; Wustrow, Eric; Halderman, J. Alex (2013): "ZMap: Fast Internet-wide Scanning and Its Security Applications", in: Proceedings of the USENIX Security Symposium. (Link)]'s claimed absence from the corpus. Closed by the author on 2026-09-10: 0 extraction records, 0
data/fulltext/2013/USENIXdirectories and 0 of the 28corpus2/.meta/USENIX-2013.jsonrecords match/zmap|internet-wide-scanning/i. Twenty-eight records is itself thin for that venue-year — a corpus-level observation, recorded on corpus' territory rather than here.
The report script and its unedited output
- report_internet_scanning-output.txt
============================================================================== 0. FOLD COVERAGE — every category=network-scanner name must be decided ============================================================================== category='network-scanner' used/produced tuples: 741 unmapped strings: 0 HAND entries: 173, matched: 173, stale: 0 ============================================================================== 0b. IS THE ZMAP TOOL PAPER IN THE CORPUS? (the page says no, twice) ============================================================================== extraction records matching /zmap|internet-wide-scanning/i in USENIX 2013: 0 data/fulltext/2013/USENIX directories matching /zmap|scann/i: 0 corpus2/.meta/USENIX-2013.json records matching /zmap/i: 0 of 28 PUBLISHED_ZMAP2013_ABSENT yes PUBLISHED_USENIX2013_INDEX 28 ============================================================================== 1. POPULATIONS ============================================================================== Population Papers Share ---------------------------------------------------------- ------ -------------- corpus (7 venues, 2010–2026) 5859 100% SCAN — studyTypes 'network-scan-or-probe' 930 15.9% ACTIVE — names an active-scan instrument (page population) 241 4.1% of corpus … of those, inside SCAN 232 24.9% of SCAN … of those, outside SCAN 9 3.7% of ACTIVE SCAN papers naming ANY category='network-scanner' tool 396 42.6% SCAN papers naming NO category='network-scanner' tool 534 57.4% PUBLISHED_CORPUS 5859 PUBLISHED_SCAN 930 PUBLISHED_ACTIVE 241 PUBLISHED_ACTIVE_IN_SCAN 232 PUBLISHED_ACTIVE_IN_SCAN_PCT 24.9% PUBLISHED_ACTIVE_OUTSIDE 9 PUBLISHED_NAMES_NS 396 PUBLISHED_NO_NS 534 57.4% category-restricted membership would give 227; the any-category rule adds 14 PUBLISHED_NS_ONLY 227 +CCS/2015/iris-vetting-private-api-abuse-in-ios-applications Iris[program-analysis] +IMC/2016/entropy-ip-uncovering-structure-in-ipv6-addresses Entropy/IP[other] +IMC/2018/digging-into-browser-based-crypto-mining ZGrab[http-client] +IMC/2018/where-the-light-gets-in-analyzing-web-censorship-mechanisms-in-india OONI[other] +CCS/2019/geneva-evolving-censorship-evasion-strategies Geneva[other] +IMC/2020/flashroute-efficient-traceroute-on-a-massive-scale IPv6 Hitlist Service[other] +USENIX/2022/get-out-automated-discovery-of-application-layer-censorship-evasion-strategies Geneva[other] +IMC/2023/replication-towards-a-publicly-available-internet-scale-ip-geolocation-dataset IPv6 Hitlist Service[other] +PETS/2023/certainty-detecting-dns-manipulation-at-scale-using-tls-certificates Censored Planet[infrastructure] +IMC/2023/inferring-changes-in-daily-human-activity-from-internet-response Trinocular[infrastructure] +USENIX/2023/deresistor-toward-detection-resistant-probing-for-evasion-of-internet-censorship Geneva[other] +NDSS/2024/modeling-and-detecting-internet-censorship-events Censored Planet[other], OONI[other] +IMC/2024/poster-traffic-engineering-security-implications IPv6 Hitlist Service[other] +USENIX/2024/with-great-power-come-great-side-channels-statistical-timing-side-channel-analys TLS banner grabbers[other] ============================================================================== 2. KIND UNION over SCAN (a paper can name several kinds) ============================================================================== kind membership? Papers Share of 930 routed to ----------------- ----------- ------ ------------ ---------------------------- active-scan YES 177 19.0% dataset 97 10.4% design:existing_datasets topology 94 10.1% design:ip_classification passive 85 9.1% vantage-platform 74 8.0% design:crawling_location routing 72 7.7% design:ip_classification dns 72 7.7% design:dns grabber YES 53 5.7% censorship-probe YES 23 2.5% reachability YES 19 2.0% vuln-scanner 15 1.6% security:web_vulnerabilities ipv6-targeting YES 11 1.2% local-link 9 1.0% perf 7 0.8% p2p-crawler 6 0.6% name-enumeration 3 0.3% packet-crafting 3 0.3% simulation 3 0.3% lab-protocol-test 3 0.3% registry 1 0.1% SCAN papers whose tools[] name no instrument this fold recognises: 416 (44.7%) PUBLISHED_SCAN_NO_INSTRUMENT 416 44.7% ============================================================================== 3. THE INSTRUMENTS — membership families over ACTIVE ============================================================================== Family kind Papers Share of ACTIVE distinct spellings -------------------- ---------------- ------ --------------- ------------------ ZMap active-scan 94 39.0% 10 nmap active-scan 45 18.7% 10 ZGrab grabber 32 13.3% 7 custom scanner active-scan 25 10.4% 21 ping reachability 18 7.5% 7 XMap active-scan 16 6.6% 3 TLS banner grabbers grabber 11 4.6% 8 OONI censorship-probe 7 2.9% 3 Geneva censorship-probe 6 2.5% 1 Censored Planet censorship-probe 4 1.7% 1 IPv6 Hitlist Service ipv6-targeting 4 1.7% 5 Masscan active-scan 4 1.7% 1 Quack censorship-probe 4 1.7% 1 6Gen ipv6-targeting 3 1.2% 1 Hyperquack censorship-probe 3 1.2% 1 Trinocular reachability 3 1.2% 1 addr6 ipv6-targeting 2 0.8% 1 Augur censorship-probe 2 0.8% 1 Entropy/IP ipv6-targeting 2 0.8% 1 GeoInspector censorship-probe 2 0.8% 1 Goscanner grabber 2 0.8% 1 Iris censorship-probe 2 0.8% 2 Karma active-scan 2 0.8% 1 LZR grabber 2 0.8% 1 QScanner grabber 2 0.8% 1 Satellite censorship-probe 2 0.8% 1 families named by exactly one paper: 37 — 6Graph, 6Sense, 6Tree, AmpMap, BreadCrumb, CnCHunter, dMAP (DPI Mapper), EAST, ERIS, exitmap, forwarders-based scanner, gateway checker, gQUIC-module, ICMP_Sonar, ike-scan, IRBlock, IRLscanner, JARM, LanDscAPe, LFP, Lumscan, Mint, modified ntpdedup, Monocle, nc, Netcat, node probing program, onesixtyone, onion-grab, OSAVRoute, PLCScan, Python script, quicgrabber, RAT-Scan, smbmap, TMC, WhatWeb PUBLISHED_ONEOFF_FAMILIES 37 PUBLISHED_FAM_ZMAP 94 PUBLISHED_FAM_NMAP 45 PUBLISHED_FAM_ZGRAB 32 PUBLISHED_FAM_CUSTOM_SCANNER 25 PUBLISHED_FAM_PING 18 PUBLISHED_FAM_XMAP 16 PUBLISHED_FAM_TLS_BANNER_GRABBERS 11 PUBLISHED_FAM_OONI 7 ZMap|ZGrab|Masscan|nmap, used or produced: 143 corpus-wide, 139 inside SCAN (the roadmap row said 143) PUBLISHED_CORE_FOUR_ALL 143 PUBLISHED_CORE_FOUR_SCAN 139 ZMap spellings: TCP Options module for ZMap | ZGrab/ZMap | ZMap | ZMap-based script | ZMap6 | ZMapv6 | Zmap | zmap | zmap2 | zmap6 PUBLISHED_ZMAP_SPELLINGS 10 ACTIVE papers whose scanner is home-grown: 45 (18.7%) = generic 'custom scanner' string: 25 + a one-paper name the paper itself PRODUCED: 20 - overlap: 0 (the other 17 one-paper families are third-party tools only this corpus's one paper used) PUBLISHED_HOMEGROWN 45 18.7% PUBLISHED_HOMEGROWN_CUSTOM 25 PUBLISHED_HOMEGROWN_PRODUCED 20 PUBLISHED_ONEOFF_THIRDPARTY 17 ============================================================================== 4. CURRENCY — membership families by year bucket, share of that bucket's ACTIVE papers ============================================================================== Window ACTIVE papers ZMap ZGrab nmap XMap Masscan custom scanner ping ---------- ------------- ---------- ---------- ---------- ---------- -------- -------------- --------- 2010–2013 12 1 (8.3%) 0 (0.0%) 5 (41.7%) 0 (0.0%) 0 (0.0%) 3 (25.0%) 3 (25.0%) 2014–2017 45 27 (60.0%) 4 (8.9%) 8 (17.8%) 0 (0.0%) 1 (2.2%) 7 (15.6%) 2 (4.4%) 2018–2021 55 21 (38.2%) 9 (16.4%) 15 (27.3%) 0 (0.0%) 0 (0.0%) 5 (9.1%) 3 (5.5%) 2022–2024 85 24 (28.2%) 9 (10.6%) 13 (15.3%) 10 (11.8%) 3 (3.5%) 6 (7.1%) 9 (10.6%) 2025–2026* 44 21 (47.7%) 10 (22.7%) 4 (9.1%) 6 (13.6%) 0 (0.0%) 4 (9.1%) 1 (2.3%) Per year, ZMap and the whole ACTIVE population (2026 is provisional): Year corpus SCAN ACTIVE ZMap ---- ------ ---- ------ ---- 2010 119 32 6 0 2011 116 23 5 0 2012 151 19 0 0 2013 125 15 1 1 2014 166 33 9 6 2015 190 40 13 6 2016 182 46 12 6 2017 231 54 11 9 2018 254 54 17 6 2019 402 65 12 3 2020 404 71 12 7 2021 379 65 14 5 2022 546 70 24 9 2023 719 79 30 7 2024 690 96 31 8 2025 770 122 33 17 2026 415 46 11 4 first ZMap year in ACTIVE: 2013 PUBLISHED_ZMAP_FIRST_YEAR 2013 ============================================================================== 5. WHERE SCANNING PUBLISHES — venue ============================================================================== Venue Papers SCAN SCAN share of venue ACTIVE ACTIVE share of venue ------- ------ ---- ------------------- ------ --------------------- CCS 990 132 13.3% 23 2.3% IEEE-SP 767 87 11.3% 18 2.3% IMC 638 296 46.4% 79 12.4% NDSS 701 117 16.7% 34 4.9% PETS 510 49 9.6% 8 1.6% USENIX 1410 199 14.1% 68 4.8% WWW 843 50 5.9% 11 1.3% ============================================================================== 6. WHAT A SCAN PAPER REPORTS — schema fields, denominator ACTIVE and SCAN ============================================================================== Field ACTIVE (241) share SCAN (930) share ------------------------------ ------------ ----- ---------- ----- vantage location 134 55.6% 487 52.4% vantage infrastructure 164 68.0% 612 65.8% any used/produced tool version 115 47.7% 401 43.1% version of the SCANNER itself 20 8.3% 20 2.2% own artifact URL 144 59.8% 506 54.4% ethics review outcome 92 38.2% 290 31.2% notified affected parties 184 76.3% 560 60.2% harm mitigation described 208 86.3% 700 75.3% robots.txt 11 4.6% 22 2.4% temporal span start 192 79.7% 656 70.5% ACTIVE papers with NO ethics record at all: 7 (2.9%) — not counted as stated, not counted as a sentinel ACTIVE papers with NO artifacts record at all: 3 (1.2%) PUBLISHED_NO_ETHICS_RECORD 7 2.9% PUBLISHED_NO_ARTIFACT_RECORD 3 1.2% PUBLISHED_REPORT_VANTAGE_LOCATION 134 55.6% | SCAN 487 52.4% PUBLISHED_REPORT_VANTAGE_INFRASTRUCTURE 164 68.0% | SCAN 612 65.8% PUBLISHED_REPORT_ANY_USED_PRODUCED_TOOL_VERSION 115 47.7% | SCAN 401 43.1% PUBLISHED_REPORT_VERSION_OF_THE_SCANNER_ITSELF 20 8.3% | SCAN 20 2.2% PUBLISHED_REPORT_OWN_ARTIFACT_URL 144 59.8% | SCAN 506 54.4% PUBLISHED_REPORT_ETHICS_REVIEW_OUTCOME 92 38.2% | SCAN 290 31.2% PUBLISHED_REPORT_NOTIFIED_AFFECTED_PARTIES 184 76.3% | SCAN 560 60.2% PUBLISHED_REPORT_HARM_MITIGATION_DESCRIBED 208 86.3% | SCAN 700 75.3% PUBLISHED_REPORT_ROBOTS_TXT 11 4.6% | SCAN 22 2.4% PUBLISHED_REPORT_TEMPORAL_SPAN_START 192 79.7% | SCAN 656 70.5% ============================================================================== 7. FULL-TEXT PROBES over ACTIVE — a loose upper bound and a tight estimate ============================================================================== PUBLISHED_PROBE_STATES_ITS_OWN_PROBE_RATE_OR_RATE_LIMIT_LOOSE 124 51.5% PUBLISHED_PROBE_STATES_ITS_OWN_PROBE_RATE_OR_RATE_LIMIT_TIGHT 81 33.6% PUBLISHED_PROBE_HONOURS_A_BLOCKLIST_EXCLUSION_LIST_LOOSE 103 42.7% PUBLISHED_PROBE_HONOURS_A_BLOCKLIST_EXCLUSION_LIST_TIGHT 88 36.5% PUBLISHED_PROBE_OFFERS_AN_OPT_OUT_LOOSE 74 30.7% PUBLISHED_PROBE_OFFERS_AN_OPT_OUT_TIGHT 70 29.0% PUBLISHED_PROBE_SOURCE_ADDRESS_HYGIENE_DEDICATED_IPS_RDNS_PTR_WHOIS_WEB_PAGE__LOOSE 167 69.3% PUBLISHED_PROBE_SOURCE_ADDRESS_HYGIENE_DEDICATED_IPS_RDNS_PTR_WHOIS_WEB_PAGE__TIGHT 112 46.5% PUBLISHED_PROBE_ABUSE_COMPLAINTS_RECEIVED_OR_HANDLED_LOOSE 58 24.1% PUBLISHED_PROBE_ABUSE_COMPLAINTS_RECEIVED_OR_HANDLED_TIGHT 45 18.7% Practice loose probe (upper bound) first-person probe ----------------------------------------------------------------- ------------------------- ------------------ states its own probe rate or rate limit 124 (51.5%) 81 (33.6%) honours a blocklist / exclusion list 103 (42.7%) 88 (36.5%) offers an opt-out 74 (30.7%) 70 (29.0%) source-address hygiene (dedicated IPs, rDNS/PTR, WHOIS, web page) 167 (69.3%) 112 (46.5%) abuse complaints received or handled 58 (24.1%) 45 (18.7%) Neither column is a population. The loose regex fires on any sentence in the paper, including one about somebody else's rate limit or somebody else's blocklist; the first-person regex requires "we"/"our" near the term and still admits sentences about the authors' own *subject*. Section 8 hand-reads a deterministic sample of each first-person hit list and gives the measured precision. ACTIVE papers naming an IRB / ethics board: 55 (22.8%) [loose, no precision measured] ACTIVE papers citing Durumeric / Partridge-Allman / Menlo / "good Internet citizen": 172 (71.4%) [loose] PUBLISHED_IRB 55 22.8% PUBLISHED_GOODPRACTICE 172 71.4% ============================================================================== 8. HAND-READ PRECISION — a PINNED sample of each first-person hit list ============================================================================== --- own probe rate: 81 first-person hits of 241 ACTIVE; pinned sample of 12 sample drawn on 2026-09-10 as every-k-th of a hit list that was then 84 long other CCS/2015/a-search-engine-backed-by-internet-wide-scanning other CCS/2025/poster-eris-evaluating-rov-via-icmpv6-rate-limiting-side-channels other IEEE-SP/2023/pcspoof-compromising-the-safety-of-time-triggered-ethernet other IMC/2016/on-the-free-bridge-across-the-digital-divide-assessing-the-quality-of-facebooks own IMC/2022/saving-brians-privacy-the-perils-of-privacy-exposure-through-reverse-dns own IMC/2025/chaos-in-the-chain-evaluate-deployment-and-construction-compliance-of-web-pki-ce own NDSS/2020/measuring-the-deployment-of-network-censorship-filters-at-global-scale other NDSS/2024/proof-of-backhaul-trustfree-measurement-of-broadband-bandwidth other USENIX/2011/toward-secure-embedded-web-interfaces other USENIX/2021/accurately-measuring-global-risk-of-amplification-attacks-using-ampmap own USENIX/2024/6sense-internet-wide-ipv6-scanning-and-its-security-applications own USENIX/2025/exposing-and-circumventing-sni-based-quic-censorship-of-the-great-firewall-of-ch hit list 81 now vs 84 at draw time — drift 3.6% sample positions in the sorted hit list: 0, 7, 14, 21, 28, 34, 41, 48, 55, 62, 68, 75 (spread 75 of 81) precision 5/12; 81 x 5/12 = about 34 papers (14.1% of ACTIVE) PUBLISHED_PRECISION_OWN_PROBE_RATE 5/12 -> ~34 14.1% --- own blocklist / exclusion: 88 first-person hits of 241 ACTIVE; pinned sample of 11 sample drawn on 2026-09-10 as every-k-th of a hit list that was then 90 long own CCS/2018/practical-state-recovery-attacks-against-legacy-rng-implementations other IEEE-SP/2017/augur-internet-wide-detection-of-connectivity-disruptions other IMC/2011/detecting-validating-and-characterizing-computer-infections-in-the-wild own IMC/2016/measuring-the-security-harm-of-tls-crypto-shortcuts own IMC/2018/clusters-in-the-expanse-understanding-and-unbiasing-ipv6-hitlists other IMC/2022/deep-dive-into-the-iot-backend-ecosystem own IMC/2023/ecn-with-quic-challenges-in-the-wild other NDSS/2020/decentralized-control-a-case-study-of-russia other NDSS/2025/revealing-the-black-box-of-device-search-engine-scanning-assets-strategies-and-ethical-consideration own USENIX/2014/an-internet-wide-view-of-internet-wide-scanning own USENIX/2018/quack-scalable-remote-measurement-of-application-layer-censorship hit list 88 now vs 90 at draw time — drift 2.2% sample positions in the sorted hit list: 0, 7, 14, 21, 28, 35, 42, 48, 55, 62, 69 (spread 69 of 88) precision 6/11; 88 x 6/11 = about 48 papers (19.9% of ACTIVE) PUBLISHED_PRECISION_OWN_BLOCKLIST_EXCLUSION 6/11 -> ~48 19.9% --- own opt-out offer: 70 first-person hits of 241 ACTIVE; pinned sample of 10 sample drawn on 2026-09-10 as every-k-th of a hit list that was then 69 long own CCS/2015/a-search-engine-backed-by-internet-wide-scanning other IEEE-SP/2020/meddling-middlemen-empirical-analysis-of-the-risks-of-data-saving-mobile-browser own IMC/2010/demystifying-service-discovery-implementing-an-internet-wide-scanner own IMC/2018/in-the-ip-of-the-beholder-strategies-for-active-ipv6-topology-discovery own IMC/2022/deep-dive-into-the-iot-backend-ecosystem own IMC/2024/destination-reachable-what-icmpv6-error-messages-reveal-about-their-sources own NDSS/2024/understanding-the-implementation-and-security-implications-of-protective-dns-services own PETS/2023/certainty-detecting-dns-manipulation-at-scale-using-tls-certificates own USENIX/2018/quack-scalable-remote-measurement-of-application-layer-censorship own USENIX/2024/landscape-exploring-ldap-weaknesses-and-data-leaks-at-internet-scale hit list 70 now vs 69 at draw time — drift 1.4% sample positions in the sorted hit list: 0, 6, 12, 18, 24, 31, 37, 43, 49, 55 (spread 55 of 70) precision 9/10; 70 x 9/10 = about 63 papers (26.1% of ACTIVE) PUBLISHED_PRECISION_OWN_OPT_OUT_OFFER 9/10 -> ~63 26.1% --- own source-address hygiene: 112 first-person hits of 241 ACTIVE; pinned sample of 10 sample drawn on 2026-09-10 as every-k-th of a hit list that was then 114 long own CCS/2015/a-search-engine-backed-by-internet-wide-scanning other IEEE-SP/2010/investigation-of-triangular-spamming-a-stealthy-and-efficient-spamming-technique own IMC/2010/demystifying-service-discovery-implementing-an-internet-wide-scanner other IMC/2016/identifying-and-aggregating-homogeneous-ipv4-24-blocks-with-hobbit own IMC/2019/an-end-to-end-large-scale-measurement-of-dns-over-encryption-how-far-have-we-com own IMC/2023/does-it-spin-on-the-adoption-and-use-of-quics-spin-bit own NDSS/2020/on-using-application-layer-middlebox-protocols-for-peeking-behind-nat-gateways other NDSS/2026/osavroute-advancing-outbound-source-address-validation-deployment-detection-with-non-cooperative-measurement other USENIX/2021/lzr-identifying-unexpected-internet-services own USENIX/2024/6sense-internet-wide-ipv6-scanning-and-its-security-applications hit list 112 now vs 114 at draw time — drift 1.8% sample positions in the sorted hit list: 0, 11, 22, 33, 44, 55, 66, 77, 88, 98 (spread 98 of 112) precision 6/10; 112 x 6/10 = about 67 papers (27.8% of ACTIVE) PUBLISHED_PRECISION_OWN_SOURCE_ADDRESS_HYGIENE 6/10 -> ~67 27.8% Practice first-person probe hand-read precision point estimate -------------------------- ------------------ ------------------- -------------- own probe rate 81 (33.6%) 5/12 ~34 (14.1%) own blocklist / exclusion 88 (36.5%) 6/11 ~48 (19.9%) own opt-out offer 70 (29.0%) 9/10 ~63 (26.1%) own source-address hygiene 112 (46.5%) 6/10 ~67 (27.8%) The point estimate is probe count x sample precision. It is an estimate on a sample of 10-12, so read it as "about a fifth" rather than as a percentage. Replacing these four estimates with four numbers needs a per-paper verdict over the whole hit list — about 400 reads. That work is NOT done. ============================================================================== 9. IPv6 — the address space is not enumerable ============================================================================== Query Population Papers Share --------------------------------------------- ------------ ------ ----- mentions IPv6 anywhere SCAN 930 281 30.2% mentions IPv6 anywhere ACTIVE 241 107 44.4% names an IPv6-targeting instrument in tools[] SCAN 930 11 1.2% names an IPv6-targeting instrument in tools[] corpus 5,859 11 0.2% full text says "hitlist"/"hit list" (loose) SCAN 930 62 6.7% "hitlist" within 80 chars of "IPv6" SCAN 930 39 4.2% names a target-generation algorithm SCAN 930 20 2.2% discusses aliased prefixes / dealiasing SCAN 930 10 1.1% PUBLISHED_V6_SCAN 281 30.2% PUBLISHED_V6_ACTIVE 107 44.4% PUBLISHED_V6_INSTR_SCAN 11 PUBLISHED_V6_INSTR_ALL 11 PUBLISHED_HITLIST_LOOSE 62 6.7% PUBLISHED_HITLIST_V6 39 4.2% REJECTED_PROBE /Gasser/ over SCAN: 93 (10.0%) — surname is not unique, hits are reference-list entries; not published PUBLISHED_TARGETGEN 20 2.2% PUBLISHED_ALIASED 10 1.1% IPv6-SCANNING SUBSET (hitlist-near-IPv6 UNION names an IPv6-targeting instrument): 42 papers of those, discuss aliased prefixes / dealiasing: 8 (19.0%) the same 10 papers against all of SCAN would be 1.1% — the wrong denominator, most of SCAN never touches IPv6 PUBLISHED_V6_SUBSET 42 PUBLISHED_ALIAS_IN_SUBSET 8 19.0% IPv6-targeting instruments named, corpus-wide: 4 IPv6 Hitlist Service (IMC/2020/flashroute-efficient-traceroute-on-a-massive-scale, IMC/2023/replication-towards-a-publicly-available-internet-scale-ip-geolocation-dataset, IMC/2024/poster-traffic-engineering-security-implications, PETS/2025/path-to-encrypted-dns-with-ddr-adoption-configuration-patterns-and-privacy-impli) 3 6Gen (IMC/2017/target-generation-for-internet-wide-ipv6-scanning, IMC/2018/clusters-in-the-expanse-understanding-and-unbiasing-ipv6-hitlists, IMC/2018/in-the-ip-of-the-beholder-strategies-for-active-ipv6-topology-discovery) 2 Entropy/IP (IMC/2016/entropy-ip-uncovering-structure-in-ipv6-addresses, IMC/2018/clusters-in-the-expanse-understanding-and-unbiasing-ipv6-hitlists) 2 addr6 (IMC/2018/in-the-ip-of-the-beholder-strategies-for-active-ipv6-topology-discovery, NDSS/2026/on-borrowed-time-measurement-informed-understanding-of-the-ntp-pools-robustness-to-monopoly-attacks) 1 6Graph (IMC/2022/rusty-clusters-dusting-an-ipv6-research-foundation) 1 6Tree (IMC/2022/rusty-clusters-dusting-an-ipv6-research-foundation) 1 6Sense (USENIX/2024/6sense-internet-wide-ipv6-scanning-and-its-security-applications) Per-year IPv6 mention rate inside SCAN (2026 provisional): Year SCAN mentions IPv6 share ---- ---- ------------- ----- 2010 32 4 12.5% 2011 23 2 8.7% 2012 19 1 5.3% 2013 15 0 0.0% 2014 33 6 18.2% 2015 40 11 27.5% 2016 46 12 26.1% 2017 54 13 24.1% 2018 54 19 35.2% 2019 65 21 32.3% 2020 71 21 29.6% 2021 65 22 33.8% 2022 70 24 34.3% 2023 79 31 39.2% 2024 96 28 29.2% 2025 122 54 44.3% 2026 46 12 26.1% ============================================================================== 10. THE JOIN TO design:ip_classification ============================================================================== Population N classification[].target == 'ip-address' share ---------- ---- --------------------------------------- ----- ACTIVE 241 75 31.1% SCAN 930 213 22.9% corpus 5859 295 5.0% PUBLISHED_IPCLS_ACTIVE 75 31.1% PUBLISHED_IPCLS_SCAN 213 22.9% PUBLISHED_IPCLS_CORPUS 295 5.0% distinct raw resourceName strings on ACTIVE ip-address classifications: 110 (free text; folded on design:ip_classification, not here) 5 MaxMind 4 MaxMind GeoLite2 3 IPinfo 3 VirusTotal 3 Censys 2 MaxMind GeoIP 2 regular expressions (custom) 2 ipinfo.io 2 ASdb 1 custom response categorization 1 Snacktime (custom implementation) 1 five public and one commercial blacklist 1 12 IP reputation blacklists 1 MaxMind GeoLiteCity 1 WHOIS ============================================================================== 11. POSTERS AND SHORT RECORDS ============================================================================== ACTIVE posters: 4 (1.7%); SCAN posters: 34 (3.7%) ACTIVE records of <=4 pages: 6 (2.5%) vantage location stated: all ACTIVE 55.6%, excluding <=4-page records 56.2% PUBLISHED_ACTIVE_POSTERS 4 PUBLISHED_VANTAGE_FULLONLY 56.2% ============================================================================== 12. MEASURED RESULTS available for the page (detection[].prevalence on ACTIVE) ============================================================================== ACTIVE papers with at least one detection[].prevalence: 240 (99.6%) PUBLISHED_ACTIVE_PREVALENCE 240 99.6% ============================================================================== Z. NON-CORPUS FIGURES ON THE PAGE (checked by external_checks_internet_scanning.sh) ============================================================================== ZMap v4.4.0, released 2026-05-29; repo zmap/zmap not archived, pushed 2026-08-28 ZGrab2 v1.0.0, released 2025-12-04; repo zmap/zgrab2 pushed 2026-09-07 ZDNS v2.1.1, released 2026-05-28; repo zmap/zdns pushed 2026-09-09 masscan latest GitHub Release 1.3.2, 2021-01-31; repo pushed 2026-04-23, not archived XMap 2.0.6, 2026-03-10; repo idealeer/xmap pushed 2026-03-10 LZR repo stanford-esrg/lzr pushed 2026-01-06, not archived nmap 7.991 is the newest tarball on nmap.org/dist ZMap v4.4.0 has ONE --blocklist-file flag (-b); there is no --blocklist-file-extra. The port flag is -p/--target-ports. ziterate, zblocklist and ztee ship in src/ alongside zmap ZGrab2 input is CSV "IP, DOMAIN, TAG, PORT"; the multiple module takes a .ini ZMap changed to random per-probe IP IDs in early 2024 (durumeric2024_years section 3) ZMap defaults (v4.4.0 and main agree): --rate 10000 pps, --probes 1, --cooldown-time 8, --retries 10, --batch 64 ZMap conf/blocklist.conf ships 16 CIDR entries, all IANA special-purpose and multicast ranges, header dated 2013 IPv6 Hitlist Service open files, snapshot 2026-09-05: responsive-addresses 29,050,771 lines; aliased-prefixes 24,476,266; non-aliased-prefixes 222,836,153 Rapid7 Project Sonar: opendata.rapid7.com now redirects to sonardata.rapid7.com, which is commercial access only (8 datasets, 47,457 files, 60.7 TB) search.censys.io returns HTTP 403 to automated clients; censys.com returns 200 2^32 = 4,294,967,296 IPv4 addresses; the shipped blocklist removes 592,708,864 (13.8%), leaving 3,702,258,432 — computed by pages/scan_budget.py from the live file, collapsing overlaps (255.255.255.255/32 is inside 240.0.0.0/4, so a naive sum gives one address more) pages/scan_budget.py --self-test: 9 checks (check 9 covers human_time() boundaries). Default run: 4.29 days at 10,000 pps, 10.3 h at 100,000 pps, 41.5 min at 1 GbE line rate (1,488,095 pps), 4.1 min at 10 GbE (14,880,952 pps), on 84 bytes of wire time per probe ============================================================================== RESULT ============================================================================== OK — all contracts hold
- verify_scanning_figures-output.txt
============================================================================== A. QUOTES AND PER-PAPER FIGURES ============================================================================== OK [.cols] IMC/2024/ten-years-of-zmap over 33% of all Internet-wide IPv4 scan traffic can be fingerprinted as coming from ZMap OK [.cols] IMC/2024/ten-years-of-zmap 35.4% of all Internet- OK [.cols] IMC/2024/ten-years-of-zmap TCP scan packets originated from ZMap OK [.cols] IMC/2024/ten-years-of-zmap the best way to mitigate transient drop is to scan from 2-3 geographically and topologically div… OK [.cols] IMC/2024/ten-years-of-zmap Provide a simple mechanism for operators to request exclusion from future scans. Indicate the IP… OK [.cols] IMC/2024/ten-years-of-zmap Consider whether existing open source datasets provide the data you need OK [.cols] IMC/2024/ten-years-of-zmap In early 2024, ZMap changed its default behavior to use random per-probe IP IDs OK [.cols] IMC/2024/ten-years-of-zmap is typically driven by a handful of small service and cloud providers OK [.cols] IMC/2024/ten-years-of-zmap Masscan [48] finds notably fewer hosts than ZMap, likely due to biases in its randomization algo… OK [.cols] IMC/2024/ten-years-of-zmap None of the top 100 ASes that emit the most ZMap OK [.cols] USENIX/2014/an-internet-wide-view-of-internet-wide-scanning only 208 organizations have requested that we exclude their networks from our scans, reducing th… OK [.cols] IMC/2018/in-the-ip-of-the-beholder-strategies-for-active-ipv6-topology-discovery we received two opt-out requests with OK [.cols] IMC/2018/in-the-ip-of-the-beholder-strategies-for-active-ipv6-topology-discovery which we immediately complied OK [.cols] USENIX/2021/lzr-identifying-unexpected-internet-services only 3% of HTTP and 6% of TLS services run on ports 80 and 443, respectively OK [.cols] USENIX/2021/lzr-identifying-unexpected-internet-services Services on non-standard ports are more likely to be insecure OK [.cols] USENIX/2024/6sense-internet-wide-ipv6-scanning-and-its-security-applications a low scanning rate of 10,000 packets per second, total, across all networks OK [.cols] IMC/2025/tracking-internet-disruptions-in-ukraine-insights-from-three-years-of-active-ful a low probing rate of 8,000 packets per second, i.e., around 500KB/s from a single vantage point OK [.cols] CCS/2015/a-search-engine-backed-by-internet-wide-scanning All of the scanning hosts have WHOIS records and reverse DNS entries that describe the intent of… OK [.cols] CCS/2015/a-search-engine-backed-by-internet-wide-scanning each scanning host runs a simple website on port 80 that describes the goals of the research OK [.cols] USENIX/2024/landscape-exploring-ldap-weaknesses-and-data-leaks-at-internet-scale On the scanning IPs, a website gives project details and contact information for opt-out request… OK [.cols] IMC/2018/clusters-in-the-expanse-understanding-and-unbiasing-ipv6-hitlists identifies 1.5 % of our prefixes as aliased, pertaining to about half of our target addresses OK [.cols] IMC/2022/rusty-clusters-dusting-an-ipv6-research-foundation More than 98 % of all IPv6 addresses announced by Fastly were labeled as aliased and Cloudflare … OK [.cols] IMC/2024/have-you-syn-me-characterizing-ten-years-of-internet-scanning Internet scanning has increased 30-fold over the last ten years OK [.cols] IMC/2024/have-you-syn-me-characterizing-ten-years-of-internet-scanning the ecosystem is extremely volatile, where targeted ports and geographical scanner locations dra… OK [.cols] IMC/2024/have-you-syn-me-characterizing-ten-years-of-internet-scanning this number has increased to 345 million in 2024 OK [.cols] IMC/2024/have-you-syn-me-characterizing-ten-years-of-internet-scanning in 2015 we observed 11 million scanning A2. OFF-CORPUS PAPER (fetched copy, not in the extraction) OK [out/scan/zmap2013_paper.txt] capable of scanning the IPv4 public address space over 1300 times faster than the most agg… OK [out/scan/zmap2013_paper.txt] surveying the entire IPv4 address space in under 45 minutes from user space OK [out/scan/zmap2013_paper.txt] over 97% of the theoretical maximum speed of gigabit Ethernet OK [out/scan/zmap2013_paper.txt] an estimated 98% coverage of publicly available hosts 30 needles, 0 not located ============================================================================== B. EXTERNAL FIGURES (re-fetched by external_checks_internet_scanning.sh) ============================================================================== ZMap v4.4.0 / 2026-05-29 / pushed 2026-08-28 / 6,374 stars GitHub REST API repos/zmap/zmap and /releases/latest, 2026-09-10 ZGrab2 v1.0.0 / 2025-12-04 / pushed 2026-09-07 GitHub REST API repos/zmap/zgrab2, 2026-09-10 masscan 1.3.2 / 2021-01-31 / pushed 2026-04-23 GitHub REST API repos/robertdavidgraham/masscan, 2026-09-10 XMap 2.0.6 / 2026-03-10 GitHub REST API repos/idealeer/xmap, 2026-09-10 nmap 7.991 newest nmap-*.tar.bz2 on https://nmap.org/dist/, 2026-09-10 ZMap defaults: --rate 10000 pps, --probes 1, --cooldown-time 8 src/zmap.1.ronn at tag v4.4.0 (byte-identical defaults to main), 2026-09-10 1300x faster than the most aggressive Nmap default settings; under 45 minutes; 97% of theoretical maximum; 98% coverage Durumeric, Wustrow and Halderman, USENIX Security 2013 — the paper is NOT in this extraction; read from the authors' copy at zmap.io/paper.pdf (Last-Modified 2025-06-22) on 2026-09-10, extracted with pypdf. Needles A27-A30. 41.5 / 0.97 = 42.8 the page's own arithmetic reconciling scan_budget.py's theoretical 1 GbE figure with the ZMap paper's measured 45-minute ceiling 16 CIDR entries; 592,708,864 removed (13.8%); 3,702,258,432 in scope conf/blocklist.conf at tag v4.4.0, computed by pages/scan_budget.py 4.29 days / 10.3 h / 41.5 min / 4.1 min; 7, 67, 1,000 and 10,000 Mb/s; 1,488,095 and 14,880,952 pps; 84 bytes pages/scan_budget.py run 2026-09-10, whole output quoted verbatim on the page 17.14 days (--ports 2 --probes 2) and 34.28 days (--ports 8) pages/scan_budget.py with those flags, 2026-09-10 0.15% of the IPv4 address space inside the durumeric2014_view quote, checked in needle A11 IPv6 Hitlist open snapshot 2026-09-05: 29,050,771 / 24,476,266 / 222,836,153 lines alcatraz.net.in.tum.de/ipv6-hitlist-service/open/*.txt.xz, decompressed line counts, 2026-09-10 Rapid7 Sonar: 8 datasets, 47,457 files, 60.7 TB, commercial access only sonardata.rapid7.com and /about with a browser User-Agent, 2026-09-10 search.censys.io HTTP 403; censys.com HTTP 200 curl with a browser User-Agent, 2026-09-10 ============================================================================== C. CODE CONSTANTS in pages/scan_budget.py and the example invocations ============================================================================== 84 WIRE_OVERHEAD_BYTES in scan_budget.py: 60-byte minimum frame + 4 FCS + 8 preamble/SFD + 12 IFG 32 / 128 IPv4 and IPv6 address widths in bits 16777216 / 65536 / 33554432 hand-computed /8, /16 and the mutation-guard double count in scan_budget.py self-test 120 / 120.0 / 7199.0 / 7200 / 7200.0 / 171999.0 / 172800 / 172800.0 / 3600 / 3600.0 / 86400 / 86400.0 / 60 / 60.0 / 119.9 / 24.0 / 2.00 second thresholds, divisors and the human_time() boundary cases in scan_budget.py --self-test check 9 1e9 / 1e6 bits per gigabit and per megabit in line_rate_pps() and scan_plan() 8 bits per byte in scan_plan(); also ZMap --cooldown-time default 443 / 80 / 22 / 8080 / 53 ports in the example invocations and in the ZGrab2 .ini example 20260910 the example --seed value: today's date, chosen so it is obviously arbitrary 198.51.100.10 / 198.51.100.13 / 10.0.0.0 / 10.1.0.0 / 192.168.0.0 / 0.0.0.0 RFC 5737 and RFC 1918 literals in the example command and the self-test 4 example --sender-threads; also the "four times" arithmetic for 2 ports x 2 probes 1 example --probes; ZMap default 10000 example --rate; ZMap default 9 checks pages/scan_budget.py --self-test count ============================================================================== D. NOT FIGURES ============================================================================== 1918 / 5737 / 1122 / 6598 / 3927 RFC numbers named in blocklist.conf and in the page text 2013 the year in the blocklist.conf header comment, and ZMap's release year 1.0.0 / 4.4.0 / 2.0.6 / 1.3.2 / 7.991 / 2.1.1 version strings 30-fold / 2-3 ratios inside quoted sentences ============================================================================== E. DERIVED ARITHMETIC (recomputed from out/scan/rep.txt, not described) ============================================================================== OK cites-the-norm over named-an-IRB: PUBLISHED_GOODPRACTICE=172 / PUBLISHED_IRB=55 = 3.127, page states 3.1 RESULT: OK
- external_checks_internet_scanning-output.txt
=== GitHub repository state === zmap/zmap archived=False pushed=2026-08-28T04:15:23Z stars=6374 zmap/zgrab2 archived=False pushed=2026-09-07T09:29:47Z stars=2173 zmap/zdns archived=False pushed=2026-09-09T17:12:25Z stars=1135 robertdavidgraham/masscan archived=False pushed=2026-04-23T20:34:38Z stars=25996 idealeer/xmap archived=False pushed=2026-03-10T14:15:08Z stars=491 stanford-esrg/lzr archived=False pushed=2026-01-06T17:27:02Z stars=196 === Latest release AND newest tag (a tag-only repo 404s on /releases/latest; /tags is unsorted) === zmap/zmap release=v4.4.0 published=2026-05-29T06:37:41Z tags[0:6]=v4.4.0,v4.3.4,v4.3.3,v4.3.2,v4.3.1,v4.3.0 zmap/zgrab2 release=v1.0.0 published=2025-12-04T23:04:38Z tags[0:6]=v1.0.0,v1.0.0-RC3,v1.0.0-RC2,v1.0.0-RC1,v0.2.0,v0.1.8 zmap/zdns release=v2.1.1 published=2026-05-28T23:11:26Z tags[0:6]=v2.1.1,v2.1.0,v2.0.5,v2.0.4,v2.0.3,v2.0.2 robertdavidgraham/masscan release=1.3.2 published=2021-01-31T09:19:32Z tags[0:6]=v2,v1,kali_1_0,1.3.2,1.3.1,1.3.0 idealeer/xmap release=2.0.6 published=2026-03-10T14:15:08Z tags[0:6]=2.0.6,2.0.5,2.0.4,2.0.3,2.0.2,2.0.1 === ZMap v4.4.0 man page: the flags and defaults the page publishes === * `-p`, `--target-ports=port(s)`: * `-b`, `--blocklist-file=path`: * `-w`, `--allowlist-file=path`: * `-r`, `--rate=pps`: * `-B`, `--bandwidth=bps`: * `-c`, `--cooldown-time=secs`: * `-e`, `--seed=n`: * `-P`, `--probes=n`: sequentially for a given IP/Port, so if --probes=4, we'll send 4 probe packets * `--shards=N`: * `-S`, `--source-ip=ip|range`: * `-O`, `--output-module=name`: * `-f`, `--output-fields=fields`: * `-m`, `--metadata-file=filename`: * `-T`, `--sender-threads=n`: DEFAULT at full line rate. Default: 10000 pps. DEFAULT How long to continue receiving after sending has completed (default=8) DEFAULT Number of probes to send to each IP/Port pair (default=1). Probes are sent DEFAULT zmap (default=1). When sharding, **--seed** is required. OK: no --blocklist-file-extra (the page's claim) === ZMap ships ziterate / zblocklist / ztee === ziterate.c present zblocklist.c present ztee.c present zmap.c present === conf/blocklist.conf at v4.4.0 and the arithmetic the page publishes === lines mentioning "Updated 2013": 2 OK: no opt-out entries in the shipped file (the page's claim) blocklist /tmp/bl.conf CIDR entries 16 addresses removed 592,708,864 (13.8% of IPv4) addresses in scope 3,702,258,432 plan: 1 port(s) x 1 probe(s) per target = 3,702,258,432 packets rate (pps) wall clock wire rate -------------- ------------ ------------ 10,000 4.29 days 7 Mb/s 100,000 10.3 h 67 Mb/s 1,488,095 41.5 min 1,000 Mb/s 14,880,952 4.1 min 10,000 Mb/s Reference rates: ZMap's default --rate is 10,000 pps; 1 GbE line rate is 1,488,095 pps and 10 GbE is 14,880,952 pps at 84 bytes of wire time per probe. What this file does NOT tell you: whether anyone asked to be excluded. The shipped blocklist is IANA special-purpose space. Opt-out entries are yours to add, and to keep across runs. self-test OK (9 checks) plan: 2 port(s) x 2 probe(s) per target = 14,809,033,728 packets rate (pps) wall clock wire rate -------------- ------------ ------------ 10,000 17.14 days 7 Mb/s plan: 8 port(s) x 1 probe(s) per target = 29,618,067,456 packets rate (pps) wall clock wire rate -------------- ------------ ------------ 10,000 34.28 days 7 Mb/s === ZGrab2 README at v1.0.0: the input format the page quotes === 115:IP, DOMAIN, TAG, PORT 118:Each line must specify `IP`, `DOMAIN`, or both. If only `DOMAIN` is provided, scanners perform a DNS hostname lookup to determine the IP address. If both `IP` and `DOMAIN` are provided, scanners connect to `IP` but use `DOMAIN` in protocol-specific contexts, such as the HTTP HOST header and TLS SNI extension. 4:ZGrab is a fast, modular application-layer network scanner designed for completing large Internet-wide surveys. ZGrab is built to work with ZMap (ZMap identifies L4 responsive hosts, ZGrab performs in-depth, follow-up L7 handshakes). Unlike many other network scanners, ZGrab outputs detailed transcripts of network handshakes (e.g., all messages exchanged in a TLS handshake) for offline analysis. === nmap: newest tarball on nmap.org/dist === nmap-7.991.tar.bz2 === IPv6 Hitlist Service (TUM) — open tier === responsive-addresses lines=29050771 Last-Modified: Sat, 05 Sep 2026 11:37:21 GMT aliased-prefixes lines=24476266 Last-Modified: Sat, 05 Sep 2026 11:43:49 GMT non-aliased-prefixes lines=222836153 Last-Modified: Sat, 05 Sep 2026 11:49:08 GMT ipv6hitlist.github.io http=200 === Scan datasets: access terms === opendata.rapid7.com http=200 final=https://sonardata.rapid7.com/ sonardata about: provides commercial access to data from Project Sonar Sign In (existing accounts only) | Datasets: 8 | Files: 47,457 | Total size: 60.7 TB search.censys.io http=403 censys.com http=200 shodan.io http=200 === The two USENIX author lists in the bibliography (venue records carry no authors) === usenixsecurity24/presentation/williams Grant Williams Mert Erdemir Amanda Hsu Shraddha Bhat Abhishek Bhaskar Frank Li Paul Pearce usenixsecurity24/presentation/kaspereit Jonas Kaspereit Gurur Öndarö Gustavo Luvizotto Cesar Simon Ebbers Fabian Ising Christoph Saatjohann Mattijs Jonker Ralph Holz Sebastian Schinzel DONE
- scan_fold.mjs
// Instrument fold and written population boundary for programming:internet_scanning. // // WRITTEN BOUNDARY (fixed 2026-09-10, before the first figure was counted). // The page is about ACTIVE INTERNET SCANNING: sending probe packets to an // address set the researcher chose, over the public Internet, to learn which // hosts answer and what they say. // // SCAN = the 930 papers tagged studyTypes 'network-scan-or-probe'. This is // the branch population design:automated_measurements routes with. // Every reporting-rate denominator on the page is a subset of it. // ACTIVE = papers naming, as a tool they USED or PRODUCED, an instrument // whose folded kind is in MEMBERSHIP_KINDS below. This is the // page's own population. // // Membership is decided by the INSTRUMENT, not by tools[].category: a scanner // filed under 'other' or 'infrastructure' is still a scanner (see // tool-category-is-a-denominator). Concretely: // (a) any tools[] entry in ANY category whose name folds to a membership // family, plus // (b) any tools[] entry with category === 'network-scanner' whose name is // unmapped by (a) and whose HAND verdict below is a membership kind. // Residue outside category 'network-scanner' is not hand-read — it would be // the whole 16,737-name tool vocabulary — so ACTIVE is a floor and the report // prints how many papers (a) adds beyond the category restriction. // // Reported beside ACTIVE but deliberately NOT in it, each with the page it is // routed to: // dataset someone else's scan (Censys, Shodan, FOFA, Rapid7 ...) // topology traceroute / alias resolution / anycast census // dns DNS-specific instruments -> design:dns // vantage-platform RIPE Atlas, looking glasses -> design:crawling_location // routing BGP and AS-relationship inference // vuln-scanner one host's application layer -> security:web_vulnerabilities // passive telescopes, sniffers, IDS // local-link Wi-Fi / Bluetooth / cellular / LAN probes // perf, simulation, p2p-crawler, name-enumeration, packet-crafting, registry // // Every unmapped category==='network-scanner' string is a FAILURE, not a drop. const F = (family, kind, ...patterns) => ({ family, kind, patterns, generic: false }); // A GENERIC family's regex is a *descriptor* ("a custom scanner", "ping"), not a // coined product name, so it is only trusted inside tools[].category === // 'network-scanner'. Without that restriction /^custom\b/i alone puts 464 // papers in the population, almost all of them custom crawlers and scripts. const G = (family, kind, ...patterns) => ({ family, kind, patterns, generic: true }); // Checked before the families. A name here is never folded, with its reason. export const NEGATIVE = [ [/^wizmap$/i, 'WizMap is an embedding-visualisation tool; /zmap/i matches it'], ]; // Ordered: first match wins. Specific products before the generic term they // contain (ZGrab / ZDNS / ZMapv6 before ZMap; Paris traceroute before // traceroute; Yarrp6 before Yarrp). export const FAMILIES = [ // --- address-space scanners and banner grabbers: the page's subject -------- F('ZGrab', 'grabber', /zgrab/i), F('ZDNS', 'dns', /^zdns$/i), F('ZMap', 'active-scan', /z-?map/i), F('XMap', 'active-scan', /^x-?map\b/i), F('Masscan', 'active-scan', /masscan/i), F('nmap', 'active-scan', /n-?map/i), F('LZR', 'grabber', /^lzr$/i), F('Goscanner', 'grabber', /^goscanner$/i), F('TLS banner grabbers', 'grabber', /sslscan|sslyze|testssl|cipherscan|tls-?scanner|tls scanner|tls-crawler/i), F('JARM', 'grabber', /^jarm$/i), G('custom scanner', 'active-scan', /^custom\b|^(efficient|systematic) scanning (tool|software)$|^stateless scanner|^scanner application$|^scanning tool$/i), // --- IPv6 targeting: the only way a v6 "scan" has a target list ------------ F('IPv6 Hitlist Service', 'ipv6-targeting', /hitlist|hit-list/i), F('6Gen', 'ipv6-targeting', /^6gen$/i), F('6Sense', 'ipv6-targeting', /^6sense$/i), F('6Graph', 'ipv6-targeting', /^6graph$/i), F('6Tree', 'ipv6-targeting', /^6tree$/i), F('Entropy/IP', 'ipv6-targeting', /entropy\/ip/i), F('addr6', 'ipv6-targeting', /^addr6$/i), // --- reachability sweeps --------------------------------------------------- G('ping', 'reachability', /^[hf]?ping\d?$/i, /^icmp( ping)?$/i, /^tcp pings?$/i, /^pingtools$/i), F('Trinocular', 'reachability', /^trinocular$/i), // --- remote censorship / reachability probing ------------------------------ F('Censored Planet', 'censorship-probe', /censored planet/i), F('OONI', 'censorship-probe', /^ooni/i), F('Quack', 'censorship-probe', /^quack$/i), F('Hyperquack', 'censorship-probe', /^hyperquack$/i), F('Satellite', 'censorship-probe', /^satellite$/i), F('Augur', 'censorship-probe', /^augur$/i), F('Iris', 'censorship-probe', /^iris$/i), F('GeoInspector', 'censorship-probe', /^geoinspector$/i), F('Geneva', 'censorship-probe', /^geneva$/i), // --- scan datasets: someone else's scan ------------------------------------ F('Censys', 'dataset', /censys/i), F('Shodan', 'dataset', /shodan/i), F('FOFA', 'dataset', /^fofa$/i), F('ZoomEye', 'dataset', /^zoomeye$/i), F('Rapid7 Open Data', 'dataset', /rapid7 (labs open data|sonar)/i), F('GreyNoise', 'dataset', /^greynoise$/i), F('LeakIX', 'dataset', /^leakix$/i), F('Open Resolver Project', 'dataset', /open resolver project/i), // --- topology -------------------------------------------------------------- F('Yarrp', 'topology', /^(extended )?yarrp(6|box)?$/i), F('scamper', 'topology', /^scamper( python module)?$/i), F('Paris traceroute', 'topology', /paris traceroute/i), F('traIXroute', 'topology', /^traixroute$/i), F('mtr', 'topology', /^mtr$/i), F('MIDAR', 'topology', /^midar$/i), F('bdrmap', 'topology', /bdrmap/i), F('TNT', 'topology', /^p?y?tnt$/i), F('traceroute', 'topology', /traceroute/i), // --- routing --------------------------------------------------------------- F('RIPE RIS', 'routing', /ripe ris/i), F('RouteViews', 'routing', /^route ?views\b/i), F('BGPStream', 'routing', /bgpstream/i), // --- distributed vantage platforms ---------------------------------------- F('RIPE Atlas', 'vantage-platform', /ripe atlas/i), F('perfSONAR', 'vantage-platform', /^perfsonar$/i), F('looking glass', 'vantage-platform', /looking glass|looking glasses|^periscope$/i), // --- DNS instruments ------------------------------------------------------- F('massdns', 'dns', /^massdns$/i), F('fpdns', 'dns', /^fpdns$/i), F('OpenINTEL', 'dns', /openintel/i), F('dig/drill', 'dns', /^(dig|drill|nsupdate|unbound|dnspython|dnsviz)$/i), // --- passive --------------------------------------------------------------- F('Zeek/Bro', 'passive', /^(zeek|bro|bro ?-?ids?)$/i), F('Suricata', 'passive', /^suricata$/i), F('Snort', 'passive', /^snort$/i), F('p0f', 'passive', /^p0f\d?$/i), F('tshark/tcpdump', 'passive', /^(tshark|tcpdump|argus)$/i), // --- one host's application layer ----------------------------------------- F('Nessus', 'vuln-scanner', /^nessus$/i), F('Acunetix', 'vuln-scanner', /acunetix/i), F('OpenVAS', 'vuln-scanner', /^openvas$/i), F('OWASP ZAP', 'vuln-scanner', /zed attack proxy|owasp zap|^zap$/i), F('Nikto', 'vuln-scanner', /^nikto$/i), F('sqlmap', 'vuln-scanner', /^sqlmap$/i), ]; export const KIND_OF = new Map(FAMILIES.map((f) => [f.family, f.kind])); export const GENERIC_FAMILIES = new Set(FAMILIES.filter((f) => f.generic).map((f) => f.family)); // Kinds that put a paper in ACTIVE. export const MEMBERSHIP_KINDS = new Set([ 'active-scan', 'grabber', 'reachability', 'censorship-probe', 'ipv6-targeting', ]); export function foldScannerName(raw) { const v = String(raw).trim(); for (const [re] of NEGATIVE) if (re.test(v)) return null; for (const { family, patterns } of FAMILIES) { for (const re of patterns) { if (re instanceof RegExp && re.test(v)) return family; } } return null; } // --------------------------------------------------------------------------- // HAND verdicts for every category==='network-scanner' name the families above // do not fold. One entry per distinct string, read off the tool's own // `purpose` field and, where the name alone was ambiguous, the paper's title. // The report throws on an unmapped string and on an entry nothing matches. export const HAND = { '0trace': 'topology', '5GSniffer': 'local-link', 'active DNS crawler': 'dns', 'active scanning tool': 'dns', // "Query DKIM, SPF, DMARC records" 'active-geolocator': 'topology', 'AdaptOver': 'local-link', 'Airmon-ng': 'local-link', 'Ally': 'topology', 'amass': 'name-enumeration', 'AmpMap': 'active-scan', 'Anaximander': 'topology', 'Aquantone': 'name-enumeration', 'Arachni': 'vuln-scanner', 'arpscan': 'local-link', 'Bitnodes': 'p2p-crawler', 'BitTorrent crawler': 'p2p-crawler', 'Black Widow': 'vuln-scanner', 'Bluesniff': 'local-link', 'BreadCrumb': 'censorship-probe', 'Btsniffer': 'local-link', 'BurpSuite Scanner': 'vuln-scanner', 'CAIDA AS Relationships Dataset': 'routing', 'Cenzic HailStorm Pro': 'vuln-scanner', 'Chaos': 'simulation', 'CheckHost': 'vantage-platform', 'cloud_enum': 'name-enumeration', 'CnCHunter': 'active-scan', 'Commix': 'vuln-scanner', 'COVScan': 'vuln-scanner', 'Critical.IO': 'dataset', 'ct-sans': 'dataset', 'danglingcname': 'dns', 'Diamond Miner': 'topology', 'DMiner': 'dns', 'dMAP (DPI Mapper)': 'active-scan', 'tcp_probe': 'passive', 'TMC': 'censorship-probe', 'ThunderPing': 'vantage-platform', 'DNS crawler': 'dns', 'DNS Scanner': 'dns', 'DNS X-Ray Tool': 'dns', 'DoE-Hunter': 'dns', 'DTLS-Fuzzer': 'lab-protocol-test', // HAND-AUDIT 2026-09-17: was 'active-scan'. USENIX 2026 "Analyzing the WebRTC // Ecosystem...": "an automated MitM testing framework (DTLS-MitM-Scanner (DMS)) // to test the DTLS channel of a DTLS-SRTP connection", "requiring only MitM // capabilities", over 24 named service providers. No address set is enumerated; // the same paper's other instrument, TLS-Attacker, is already lab-protocol-test. 'DTLS-MitM-Scanner (DMS)': 'lab-protocol-test', 'EAST': 'active-scan', // STARTTLS test cases against live MTAs 'Enemy of the State': 'vuln-scanner', 'ERIS': 'active-scan', // Two spellings, two papers, two uses — and HAND is keyed by the exact string, // so they can and must differ. USENIX 2016 "Identifying and Characterizing // Sybils in the Tor Network" runs the scan: "We exposed malicious exit relays // using Winter et al.'s exitmap tool". 'exitmap': 'active-scan', // HAND-AUDIT 2026-09-17: was 'active-scan'. WWW 2018 CrimeBB is a forum-scraping // paper; its studyTypes do not include network-scan-or-probe. It uses Exitmap // only for the node list: "We first use Exitmap [45] to get current available // exit nodes, from which we build Tor circuits and fetch the index page of the // forums." Enumerating an overlay's nodes is what p2p-crawler is for. 'Exitmap': 'p2p-crawler', 'FastMDA': 'topology', 'FingerprinTLS': 'passive', 'FlashRoute': 'topology', 'forwarders-based scanner': 'active-scan', 'gateway checker': 'active-scan', 'Google Tsunami': 'vuln-scanner', 'gQUIC-module': 'grabber', 'h2spec': 'lab-protocol-test', 'HP WebInspect': 'vuln-scanner', 'HTTP/OneProbe': 'topology', 'IBM Rational AppScan': 'vuln-scanner', 'ibportstate': 'local-link', 'ICMP Paris': 'topology', 'ICMP_Sonar': 'active-scan', 'iffinder': 'topology', 'iGreedy': 'topology', 'ike-scan': 'grabber', 'Internet Census': 'dataset', 'iperf': 'perf', 'iPerf3': 'perf', 'IPFS Crawler': 'p2p-crawler', 'IRBlock': 'censorship-probe', 'IRLscanner': 'active-scan', 'IRTT': 'perf', 'Iterative Network Tracing (INT)': 'topology', 'James': 'topology', 'kapar': 'topology', 'Karma': 'active-scan', 'l2ping': 'local-link', 'LACeS': 'topology', 'LanDscAPe': 'active-scan', // HAND-AUDIT 2026-09-17: was 'topology'. IMC 2023 "Illuminating Router Vendor // Diversity": LFP is "a novel lightweight, accurate, and more complete remote // vendor fingerprinting methodology and tool", benchmarked as "at least as good // as Nmap while sending orders of magnitude fewer packets". It probes hosts to // learn what they say; it infers no path and resolves no alias. That is a // grabber, and this correction puts the paper INTO ACTIVE. 'LFP': 'grabber', 'LTE PROBE': 'local-link', 'Lumscan': 'censorship-probe', 'MacStealer': 'local-link', 'MAnycast anycast census': 'topology', 'MAnycast2': 'topology', 'Mao et al. AS-path inference algorithm': 'routing', 'MAP-IT': 'routing', 'MarcoPolo': 'routing', 'McAfee SECURE': 'vuln-scanner', 'Mercator': 'topology', 'Mint': 'censorship-probe', 'modified ntpdedup': 'grabber', 'Monocle': 'censorship-probe', 'mrinfo': 'topology', 'mrinfo-rec': 'topology', 'N-Stalker QA Edition': 'vuln-scanner', 'Namehelp': 'vantage-platform', 'nc': 'active-scan', 'Netcat': 'active-scan', 'ndisc6': 'local-link', 'NetfilterQueue': 'packet-crafting', 'netperf': 'perf', 'netstat': 'host-tool', 'Netstat': 'host-tool', 'Network Analyzer': 'topology', 'NextTrace': 'topology', 'node probing program': 'active-scan', 'NodeFinder': 'p2p-crawler', 'NR-Scope': 'local-link', 'ns-3': 'simulation', 'Nuclei': 'vuln-scanner', 'nuttcp': 'perf', 'OneProbe': 'topology', 'onesixtyone': 'grabber', 'onion-grab': 'grabber', 'OSAVRoute': 'active-scan', 'PciCheckerLite': 'vuln-scanner', 'Perf': 'host-tool', 'PLCScan': 'grabber', 'Prefixscan': 'topology', 'Python script': 'active-scan', // "Scan IPv4 space with DNS queries" 'QScanner': 'grabber', 'QualysGuard PCI': 'vuln-scanner', 'quicgrabber': 'grabber', 'RAINBOW': 'p2p-crawler', 'Rapid7 NeXpose': 'vuln-scanner', 'RAT-Scan': 'active-scan', 'RDScan': 'dns', 'revtr 2.0': 'topology', 'RMap': 'dns', 'SAD DNS': 'dns', 'SadDNS': 'dns', 'Scapy': 'packet-crafting', 'SecSpider': 'dataset', 'Shadow': 'simulation', 'ShaperProbe': 'perf', 'Skipfish': 'vuln-scanner', 'smbmap': 'grabber', // HAND-AUDIT 2026-09-17: was 'grabber'. IMC 2025 "Replication: Characterizing // MPLS Tunnels" does not send the SNMPv3 probes; it reads CAIDA's ITDK: "we // mapped each IP address to an inferred router using the MIDAR, iffinder, and // SNMPv3 alias resolution graph included in the March 2025 ITDK". MIDAR and // iffinder, named in that same sentence, are already topology. ('dataset' is // the arguable alternative; the sentence's own company decided it.) This // correction takes the paper OUT of ACTIVE. 'SNMPv3': 'topology', 'Speedtrap': 'topology', 'Spoki': 'passive', 'Spoofer project': 'vantage-platform', 'SSFNet': 'simulation', 'Swordqiu': 'routing', 'Time Series Latency Probing (TSLP)': 'topology', 'TLS-Attacker': 'lab-protocol-test', 'Tor DNSEL': 'dataset', 'TorFlow': 'p2p-crawler', 'tracebox': 'topology', 'tracefilter': 'topology', 'tracenet': 'topology', 'Trufflehunter': 'dns', 'Tsunami': 'vuln-scanner', 'Ubertooth One': 'local-link', // HAND-AUDIT 2026-09-17: 'Unicorn' was here as 'active-scan'. Its one tuple is // now suppressed in scripts/tool_use_overrides.mjs — IMC 2024 "Have you SYN // me?" never ran it, it fingerprinted its traffic at a telescope — so the name // no longer reaches this map and an entry here would fail the staleness guard. 'University of Michigan Daily Full IPv4 HTTPS Handshakes': 'dataset', 'URHunter': 'dns', 'v6disc.sh': 'local-link', 'Verfploeter': 'topology', 'Verloc': 'topology', 'VerLoc': 'topology', 'W3af': 'vuln-scanner', 'Wapiti': 'vuln-scanner', 'WeHeY': 'perf', 'WhatWeb': 'grabber', 'whois': 'registry', 'WiFi Inspector': 'local-link', 'WMap': 'vuln-scanner', 'WPScan': 'vuln-scanner', 'wrk': 'perf', 'Xray': 'vuln-scanner', 'YoDNS': 'dns', 'ZreFinder': 'dns', }; // A hand verdict is a family of one: its display name is the string itself. // // `category` is the tools[] category the name was found under. Hand verdicts // and GENERIC families are only trusted under 'network-scanner', because those // strings ("Python script", "Unicorn", "custom scanner", "nc") are ordinary // words elsewhere in the tool vocabulary. Pass category === null to fold a bare // string, which is what the self-test does. // --------------------------------------------------------------------------- // COMPOUND TUPLE STRINGS. One tools[] entry that names more than one // instrument. FAMILIES is ordered and first-match-wins, so a compound string // lands in exactly one family and the other instrument silently disappears — // which is how this page and design:dns came to disagree by one paper about // the size of the ZMap family (the two folds test ZGrab and ZMap in opposite // order, so the same string went to different families). Each key is // hand-read against the paper's own evidence quote. The sweep in // scripts/reconcile_zmap.mjs fails if any NEW compound name appears whose // parts fold to a family the whole string does not. // Key is the lower-cased, trimmed tools[] name; value is the component names, // each folded through the normal path. export const COMPOUND_SPLIT = new Map([ // CCS 2016 "A Surfeit of SSH Cipher Suites". evidence.quote: "We ran two // IPv4-address-space-wide scans for SSH servers using ZGrab/ZMap [13]." — // matched verbatim in data/fulltext/2016/CCS/a-surfeit-of-ssh-cipher-suites/ // paper.cols.txt on 2026-09-17. Both instruments were used. ['zgrab/zmap', ['ZGrab', 'ZMap']], ]); // Every verdict a tools[] name yields, compound names included. Deduplicated // by family, because a paper naming one instrument twice is still one paper. export function kindsOfName(raw, category = null) { const k = String(raw).trim().toLowerCase(); const parts = COMPOUND_SPLIT.has(k) ? COMPOUND_SPLIT.get(k) : [raw]; const seen = new Map(); for (const part of parts) { const v = kindOfName(part, category); if (v && !seen.has(v.family)) seen.set(v.family, v); } return [...seen.values()]; } export function kindOfName(raw, category = null) { const fam = foldScannerName(raw); if (fam) { if (GENERIC_FAMILIES.has(fam) && category !== null && category !== 'network-scanner') return null; return { family: fam, kind: KIND_OF.get(fam), via: 'family' }; } const v = String(raw).trim(); if (Object.prototype.hasOwnProperty.call(HAND, v)) { if (category !== null && category !== 'network-scanner') return null; return { family: v, kind: HAND[v], via: 'hand' }; } return null; } export const ROUTED_TO = { 'dns': 'design:dns', 'vantage-platform': 'design:crawling_location', 'vuln-scanner': 'security:web_vulnerabilities', 'dataset': 'design:existing_datasets', 'topology': 'design:ip_classification', 'routing': 'design:ip_classification', }; function selfTest() { const cases = [ ['ZMap', 'ZMap', 'active-scan'], ['zmap', 'ZMap', 'active-scan'], ['ZMapv6', 'ZMap', 'active-scan'], ['zmap6', 'ZMap', 'active-scan'], ['ZMap-based script', 'ZMap', 'active-scan'], ['TCP Options module for ZMap', 'ZMap', 'active-scan'], ['ZGrab/ZMap', 'ZGrab', 'grabber'], ['zgrab2', 'ZGrab', 'grabber'], ['ZGrab 2.0', 'ZGrab', 'grabber'], ['XMap', 'XMap', 'active-scan'], ['Xmap (extended)', 'XMap', 'active-scan'], ['Masscan', 'Masscan', 'active-scan'], ['Nmap Security Scanner', 'nmap', 'active-scan'], ['nmap plugin for Special DROWN', 'nmap', 'active-scan'], ['LZR', 'LZR', 'grabber'], ['Goscanner', 'Goscanner', 'grabber'], ['TLS-Crawler', 'TLS banner grabbers', 'grabber'], ['custom scanner', 'custom scanner', 'active-scan'], ['efficient scanning software', 'custom scanner', 'active-scan'], ['stateless scanner (custom)', 'custom scanner', 'active-scan'], ['systematic scanning tool', 'custom scanner', 'active-scan'], ['scanner application', 'custom scanner', 'active-scan'], ['ARGUS', 'tshark/tcpdump', 'passive'], ['Routeviews BGP table', 'RouteViews', 'routing'], ['Tnt', 'TNT', 'topology'], ['active scanning tool', 'active scanning tool', 'dns'], ['IPv6 Hitlist Service', 'IPv6 Hitlist Service', 'ipv6-targeting'], ['Hitlist-Downloader', 'IPv6 Hitlist Service', 'ipv6-targeting'], ['6Gen', '6Gen', 'ipv6-targeting'], ['6Sense', '6Sense', 'ipv6-targeting'], ['addr6', 'addr6', 'ipv6-targeting'], ['ping', 'ping', 'reachability'], ['hping3', 'ping', 'reachability'], ['fping', 'ping', 'reachability'], ['ICMP ping', 'ping', 'reachability'], ['TCP pings', 'ping', 'reachability'], ['Censys', 'Censys', 'dataset'], ['Censys Search API', 'Censys', 'dataset'], ['ZoomEye', 'ZoomEye', 'dataset'], ['Zoomeye', 'ZoomEye', 'dataset'], ['FOFA', 'FOFA', 'dataset'], ['Fofa', 'FOFA', 'dataset'], ['Rapid7 Sonar', 'Rapid7 Open Data', 'dataset'], ['Yarrp6', 'Yarrp', 'topology'], ['Yarrpbox', 'Yarrp', 'topology'], ['extended Yarrpbox', 'Yarrp', 'topology'], ['scamper Python module', 'scamper', 'topology'], ['Paris traceroute', 'Paris traceroute', 'topology'], ['tcptraceroute', 'traceroute', 'topology'], ['nmap traceroute', 'nmap', 'active-scan'], // nmap wins: it is the instrument ['PyTNT', 'TNT', 'topology'], ['TNT', 'TNT', 'topology'], ['Route Views', 'RouteViews', 'routing'], ['RIPE Atlas', 'RIPE Atlas', 'vantage-platform'], ['public looking glasses', 'looking glass', 'vantage-platform'], ['Periscope', 'looking glass', 'vantage-platform'], ['dig', 'dig/drill', 'dns'], ['drill', 'dig/drill', 'dns'], ['DNSViz', 'dig/drill', 'dns'], ['Bro IDS', 'Zeek/Bro', 'passive'], ['p0f3', 'p0f', 'passive'], ['Tshark', 'tshark/tcpdump', 'passive'], ['Zed Attack Proxy (ZAP)', 'OWASP ZAP', 'vuln-scanner'], ['OWASP ZAP', 'OWASP ZAP', 'vuln-scanner'], ['Nikto', 'Nikto', 'vuln-scanner'], ['Quack', 'Quack', 'censorship-probe'], ['Satellite', 'Satellite', 'censorship-probe'], ['OONI Probe', 'OONI', 'censorship-probe'], // hand verdicts ['LanDscAPe', 'LanDscAPe', 'active-scan'], ['Scapy', 'Scapy', 'packet-crafting'], ['iperf', 'iperf', 'perf'], ['Ubertooth One', 'Ubertooth One', 'local-link'], ['whois', 'whois', 'registry'], ]; for (const [raw, wantFam, wantKind] of cases) { const got = kindOfName(raw); if (!got || got.family !== wantFam || got.kind !== wantKind) { throw new Error( `scan_fold self-test: ${JSON.stringify(raw)} -> ${JSON.stringify(got)}, want ${wantFam}/${wantKind}` ); } } // NEGATIVE guard: /z-?map/i matches an unrelated visualisation tool. if (foldScannerName('WizMap') !== null) throw new Error('scan_fold self-test: WizMap must not fold'); // GENERIC and HAND names are category-restricted; distinctive names are not. if (kindOfName('custom scanner', 'other') !== null) throw new Error('scan_fold self-test: generic family must not fire outside network-scanner'); if (kindOfName('custom scanner', 'network-scanner') === null) throw new Error('scan_fold self-test: generic family must fire inside network-scanner'); if (kindOfName('Python script', 'other') !== null) throw new Error('scan_fold self-test: hand verdict must not fire outside network-scanner'); if (kindOfName('ZMap', 'infrastructure') === null) throw new Error('scan_fold self-test: distinctive name must fire in any category'); if (kindOfName('LEGO Mindstorms NXT') !== null) throw new Error('scan_fold self-test: unknown name must stay residue'); for (const k of Object.values(HAND)) { if (typeof k !== 'string' || !k.length) throw new Error('scan_fold self-test: empty HAND kind'); } if (MEMBERSHIP_KINDS.has('dataset')) throw new Error('scan_fold self-test: dataset must not be membership'); } selfTest();
- report_internet_scanning.mjs
#!/usr/bin/env node // Every figure on programming:internet_scanning, with its denominator. // // Population boundary is written down in scripts/scan_fold.mjs and was fixed // before the first count. Two denominators, never mixed: // SCAN 930 papers tagged studyTypes 'network-scan-or-probe' // ACTIVE 242 papers naming an active-scan / grabber / reachability / // censorship-probe / IPv6-targeting instrument they used or produced // (245 before the 2026-09-17 hand audit of the HAND verdicts and the // tuple suppression in tool_use_overrides.mjs) // // Flags: --wiki (DokuWiki tables) --list <kind> --quotes <regex> --hits <regex> // // Exits 1 if a corpus contract, the fold coverage check, or a hand-map // consistency check fails. import fs from 'node:fs'; import path from 'node:path'; import { loadExtractions, pct, table, wikiTable, isSentinel, dataRoot, YEAR_BUCKETS } from './lib.mjs'; import { kindsOfName, MEMBERSHIP_KINDS, HAND, KIND_OF, ROUTED_TO, GENERIC_FAMILIES } from './scan_fold.mjs'; import { notRun, NOT_RUN, checkNotRun } from './tool_use_overrides.mjs'; const argv = process.argv.slice(2); const WIKI = argv.includes('--wiki'); const opt = (n) => { const i = argv.indexOf(n); return i >= 0 ? argv[i + 1] : null; }; const LIST = opt('--list'); const QUOTES = opt('--quotes'); const HITS = opt('--hits'); const out = []; const say = (s = '') => out.push(s); const h = (s) => { say(''); say('='.repeat(78)); say(s); say('='.repeat(78)); }; const t = (headers, rows) => say(WIKI ? wikiTable(headers, rows) : table(headers, rows)); const fails = []; const fail = (m) => { fails.push(m); say(`FAILURE: ${m}`); }; // ---------------------------------------------------------------- corpus ---- const rows = loadExtractions(); const DROVE = new Set(['used', 'produced']); const key = (p) => `${p.venue}/${p.year}/${p.slug}`; if (rows.length !== 5859) fail(`corpus ${rows.length} != 5859`); // A NOT_RUN entry that matches nothing is a lie about what was read. for (const m of checkNotRun(rows)) fail(m); const SCAN = rows.filter((p) => p.studyTypes.includes('network-scan-or-probe')); if (SCAN.length !== 930) fail(`network-scan-or-probe ${SCAN.length} != 930 (OVERVIEW.md)`); // Every instrument a paper names, folded. Category is passed through so the // generic families and the hand map stay inside category 'network-scanner'. function instruments(p) { const seen = new Map(); // family -> {family, kind, via, names:Set, versions:Set} for (const tool of p.tools) { if (!DROVE.has(tool.usedOrMentioned)) continue; if (notRun(p, tool.name)) continue; for (const v of kindsOfName(tool.name, tool.category)) { if (!seen.has(v.family)) seen.set(v.family, { ...v, names: new Set(), versions: new Set(), cats: new Set() }); const e = seen.get(v.family); e.names.add(String(tool.name).trim()); e.cats.add(tool.category); if (!isSentinel(tool.version)) e.versions.add(String(tool.version)); } } return [...seen.values()]; } const memberOf = (p) => instruments(p).filter((v) => MEMBERSHIP_KINDS.has(v.kind)); const ACTIVE = rows.filter((p) => memberOf(p).length > 0); const ACTIVE_KEYS = new Set(ACTIVE.map(key)); const SCAN_KEYS = new Set(SCAN.map(key)); // --------------------------------------------------------------- probes ---- // Two widths per practice. The loose one is the recall claim; the tight one // requires the authors to be the subject of the sentence. Both are upper // bounds, and section 8 measures the precision of the tight one by hand. const PROBE = { rateLoose: /(rate[- ]?limit|packets? per second|\bpps\b|probing rate|scan(ning)? rate|bandwidth (of|cap|limit|is set)|--bandwidth|-B \d)/i, rateTight: /((we|our (scan|scanner|probe|measurement|experiment)s?)[^.]{0,120}(rate[- ]?limit|limit(ed)? (the |our )?(scan|probe|probing|sending|bandwidth|rate)|throttl)|(we|our)[^.]{0,80}(scan|prob|sent|send)[^.]{0,80}at (a )?(rate of |about |approximately )?\d[\d,.]*\s?(k|m|g)?(bps|b\/s|pps|packets)|(probing|scan(ning)?|sending) rate of \d|limit(ed)? (the |our )?(scan|probe|probing|bandwidth|rate)[^.]{0,60}\d)/i, blockLoose: /(black ?list|block ?list|exclusion (list|file)|excluded? (address|IP|prefix|network|range)|--blacklist-file|--blocklist-file)/i, blockTight: /((we|our)[^.]{0,140}(black ?list|block ?list|exclusion (list|file))|(black ?list|block ?list|exclusion list)[^.]{0,100}(we |our |scan)|--black(list)?-file|--block(list)?-file|honou?r(ed|ing)?[^.]{0,40}(black ?list|block ?list|exclusion|opt-?out|removal)|exclud(e|ed|ing)[^.]{0,80}(IP|address|prefix|network|range|subnet)[^.]{0,80}(scan|prob|request|our))/i, optLoose: /(opt(ed)?[- ]out|removal request|asked (us )?to be excluded)/i, optTight: /((we|our)[^.]{0,140}opt(ed)?[- ]out|opt[- ]out (request|list|form|page|mechanism)|(request(ed|s)?|asked)[^.]{0,60}(be )?(excluded|removed) (from|our)|removal request)/i, srcLoose: /(source (IP|address)|dedicated (IP|machine|server|host)|scanning (host|machine|server|IP)|reverse DNS|rDNS|PTR record|WHOIS)/i, srcTight: /((we|our)[^.]{0,120}(dedicated (IP|machine|server|host|address)|scanning (host|machine|server|IP|address)|(source|scanner) (IP|address)(es)?|our (scanner|scanning) (IP|host|machine))|(reverse DNS|rDNS|PTR record|WHOIS (record|entry|entries))[^.]{0,120}(our |we |explain|scan))/i, abuseLoose: /(abuse (complaint|report|email|e-mail|contact|team)|abuse@|complaints?)/i, abuseTight: /((we|our)[^.]{0,140}(abuse (complaint|report|email|e-mail|contact)|complaint)|abuse@|(received|got) [^.]{0,40}(complaint|abuse))/i, irb: /(\bIRB\b|institutional review board|ethics (board|committee|review|approval)|REB approval)/i, goodpractice: /(Durumeric|Partridge and Allman|Menlo Report|scanning best practice|good (Internet|internet) citizen)/i, }; // Hand verdicts. 'own' = the matched sentence is about the authors' own scan. // 'other' = the term is in the paper for a different reason (a target's rate // limiting, a censor's blocklist, a defence recommendation, an analysis-time // exclusion). Read on 2026-09-10 from paper.cols.txt with whitespace collapsed, // first two matches per paper. Every entry names the deciding reading. const RATE_VERDICT = { 'CCS/2015/a-search-engine-backed-by-internet-wide-scanning': 'other', // rate-limits its own *web interface*, not its scan 'CCS/2025/poster-eris-evaluating-rov-via-icmpv6-rate-limiting-side-channels': 'other', // ICMPv6 rate limiting is the side channel under study 'IEEE-SP/2023/pcspoof-compromising-the-safety-of-time-triggered-ethernet': 'other', // "sending ARP requests at 100 Mbps" is an attack rate on a TTEthernet lab testbed, not a scan rate. Read as 'own' on the first pass and flipped after the generic review pass disputed it on 2026-09-10 'IMC/2016/on-the-free-bridge-across-the-digital-divide-assessing-the-quality-of-facebooks': 'other', // Free Basics traffic is throttled 'IMC/2022/saving-brians-privacy-the-perils-of-privacy-exposure-through-reverse-dns': 'own', // "We rate-limit requests to authoritative name servers" 'IMC/2025/chaos-in-the-chain-evaluate-deployment-and-construction-compliance-of-web-pki-ce': 'own', // "we limited the scanning rate to below 500KB/s" 'NDSS/2020/measuring-the-deployment-of-network-censorship-filters-at-global-scale': 'own', // "we abide by best practices and limit the rate of sending requests" 'NDSS/2024/proof-of-backhaul-trustfree-measurement-of-broadband-bandwidth': 'other', // tc-shaped test rig, not a scan rate 'USENIX/2011/toward-secure-embedded-web-interfaces': 'other', // the embedded device throttles requests 'USENIX/2021/accurately-measuring-global-risk-of-amplification-attacks-using-ampmap': 'other', // "block or rate-limit specific queries" is the defence it recommends 'USENIX/2024/6sense-internet-wide-ipv6-scanning-and-its-security-applications': 'own', // "a low scanning rate of 10,000 packets per second" 'USENIX/2025/exposing-and-circumventing-sni-based-quic-censorship-of-the-great-firewall-of-ch': 'own', // "at a rate of 100 packets per second" }; const BLOCK_VERDICT = { 'CCS/2018/practical-state-recovery-attacks-against-legacy-rng-implementations': 'own', // "respecting hosts who wished to be blacklisted" 'IEEE-SP/2017/augur-internet-wide-detection-of-connectivity-disruptions': 'other', // block lists of *censored sites*, used as validation data 'IMC/2011/detecting-validating-and-characterizing-computer-infections-in-the-wild': 'other', // blacklists are an input dataset 'IMC/2016/measuring-the-security-harm-of-tls-crypto-shortcuts': 'own', // "followed the institutional blacklist" 'IMC/2018/clusters-in-the-expanse-understanding-and-unbiasing-ipv6-hitlists': 'own', // "maintaining a blacklist and using dedicated servers" 'IMC/2022/deep-dive-into-the-iot-backend-ecosystem': 'other', // FireHOL blocklist is the object of measurement 'IMC/2023/ecn-with-quic-challenges-in-the-wild': 'own', // "how to be added to our blocklist to be excluded from further measurements" 'NDSS/2020/decentralized-control-a-case-study-of-russia': 'other', // Roskomnadzor blocklists are the subject 'NDSS/2025/revealing-the-black-box-of-device-search-engine-scanning-assets-strategies-and-ethical-consideration': 'other', // operators blocklisting the scanners it studies 'USENIX/2014/an-internet-wide-view-of-internet-wide-scanning': 'own', // "only 208 organizations have requested that we exclude their networks from our scans" — 208, checked against paper.pdf with pypdf; a first read of a narrow context window misread it as 908 'USENIX/2018/quack-scalable-remote-measurement-of-application-layer-censorship': 'own', // "respecting an IP blacklist shared with other scanning research" // DROPPED 2026-09-17, was 'other' ("analysis-time exclusion of known scan // projects"). The reading still stands, but the paper left ACTIVE when its // only membership tuple — a ZMap run over a closed veth loop — was // suppressed (see scripts/tool_use_overrides.mjs), so it is no longer a hit // and the pinned-sample guard rejected it. The sample is 11, not 12. }; const OPT_VERDICT = { 'CCS/2015/a-search-engine-backed-by-internet-wide-scanning': 'own', // "we treat opt-out requests for scanning as a request to be removed from the search index" 'IEEE-SP/2020/meddling-middlemen-empirical-analysis-of-the-risks-of-data-saving-mobile-browser': 'other', // browser users opting out of data saving 'IMC/2010/demystifying-service-discovery-implementing-an-internet-wide-scanner': 'own', // TXT records pointing at a page with opt-out instructions 'IMC/2018/in-the-ip-of-the-beholder-strategies-for-active-ipv6-topology-discovery': 'own', // "we received two opt-out requests with which we immediately complied" 'IMC/2022/deep-dive-into-the-iot-backend-ecosystem': 'own', // "We run a Web server with experiment and opt-out information" 'IMC/2024/destination-reachable-what-icmpv6-error-messages-reveal-about-their-sources': 'own', // "We received a single request for opt-out and acted accordingly" 'NDSS/2024/understanding-the-implementation-and-security-implications-of-protective-dns-services': 'own', // PTR records giving contacts for opt-out 'PETS/2023/certainty-detecting-dns-manipulation-at-scale-using-tls-certificates': 'own', // "offer administrators the option to opt out of our scanning" 'USENIX/2018/quack-scalable-remote-measurement-of-application-layer-censorship': 'own', // "simple ways for packet recipients to opt out of future probes" 'USENIX/2024/landscape-exploring-ldap-weaknesses-and-data-leaks-at-internet-scale': 'own', // "a website gives project details and contact information for opt-out requests" }; const SRC_VERDICT = { 'CCS/2015/a-search-engine-backed-by-internet-wide-scanning': 'own', // "All of the scanning hosts have WHOIS records and reverse DNS entries" 'IEEE-SP/2010/investigation-of-triangular-spamming-a-stealthy-and-efficient-spamming-technique': 'other', // source-IP rewriting is the attack under study 'IMC/2010/demystifying-service-discovery-implementing-an-internet-wide-scanner': 'own', // describes its own source IPs and how many it used 'IMC/2016/identifying-and-aggregating-homogeneous-ipv4-24-blocks-with-hobbit': 'other', // rDNS patterns of the *targets* 'IMC/2019/an-end-to-end-large-scale-measurement-of-dns-over-encryption-how-far-have-we-com': 'own', // "setting a reverse DNS record for our scanning system and building a website" 'IMC/2023/does-it-spin-on-the-adoption-and-use-of-quics-spin-bit': 'own', // rDNS entries, project name in every request, dedicated website 'NDSS/2020/on-using-application-layer-middlebox-protocols-for-peeking-behind-nat-gateways': 'own', // "The reverse DNS record of the scanning host was set to indicate its use for research purposes" 'NDSS/2026/osavroute-advancing-outbound-source-address-validation-deployment-detection-with-non-cooperative-measurement': 'other', // source-address validation is the subject 'USENIX/2021/lzr-identifying-unexpected-internet-services': 'other', // discusses scanners' source IPs as a defence property 'USENIX/2024/6sense-internet-wide-ipv6-scanning-and-its-security-applications': 'own', // "The webpage hosted on each scanning IP/domain includes a contact email" }; // Every non-corpus number that appears on the page, so check_page_numbers.mjs // can run whole-page. Re-checked by scripts/external_checks_internet_scanning.sh. const EXTERNAL = [ 'ZMap v4.4.0, released 2026-05-29; repo zmap/zmap not archived, pushed 2026-08-28', 'ZGrab2 v1.0.0, released 2025-12-04; repo zmap/zgrab2 pushed 2026-09-07', 'ZDNS v2.1.1, released 2026-05-28; repo zmap/zdns pushed 2026-09-09', 'masscan latest GitHub Release 1.3.2, 2021-01-31; repo pushed 2026-04-23, not archived', 'XMap 2.0.6, 2026-03-10; repo idealeer/xmap pushed 2026-03-10', 'LZR repo stanford-esrg/lzr pushed 2026-01-06, not archived', 'nmap 7.991 is the newest tarball on nmap.org/dist', 'ZMap v4.4.0 has ONE --blocklist-file flag (-b); there is no --blocklist-file-extra. The port flag is -p/--target-ports. ziterate, zblocklist and ztee ship in src/ alongside zmap', 'ZGrab2 input is CSV "IP, DOMAIN, TAG, PORT"; the multiple module takes a .ini', 'ZMap changed to random per-probe IP IDs in early 2024 (durumeric2024_years section 3)', 'ZMap defaults (v4.4.0 and main agree): --rate 10000 pps, --probes 1, --cooldown-time 8, --retries 10, --batch 64', 'ZMap conf/blocklist.conf ships 16 CIDR entries, all IANA special-purpose and multicast ranges, header dated 2013', 'IPv6 Hitlist Service open files, snapshot 2026-09-05: responsive-addresses 29,050,771 lines; aliased-prefixes 24,476,266; non-aliased-prefixes 222,836,153', 'Rapid7 Project Sonar: opendata.rapid7.com now redirects to sonardata.rapid7.com, which is commercial access only (8 datasets, 47,457 files, 60.7 TB)', 'search.censys.io returns HTTP 403 to automated clients; censys.com returns 200', '2^32 = 4,294,967,296 IPv4 addresses; the shipped blocklist removes 592,708,864 (13.8%), leaving 3,702,258,432 — computed by pages/scan_budget.py from the live file, collapsing overlaps (255.255.255.255/32 is inside 240.0.0.0/4, so a naive sum gives one address more)', 'pages/scan_budget.py --self-test: 9 checks (check 9 covers human_time() boundaries). Default run: 4.29 days at 10,000 pps, 10.3 h at 100,000 pps, 41.5 min at 1 GbE line rate (1,488,095 pps), 4.1 min at 10 GbE (14,880,952 pps), on 84 bytes of wire time per probe', ]; h('0. FOLD COVERAGE — every category=network-scanner name must be decided'); { const un = new Map(); const usedHand = new Set(); const usedFam = new Set(); let tuples = 0; for (const p of rows) { for (const tool of p.tools) { if (!DROVE.has(tool.usedOrMentioned) || tool.category !== 'network-scanner') continue; if (notRun(p, tool.name)) continue; tuples += 1; const vs = kindsOfName(tool.name, tool.category); if (!vs.length) { un.set(String(tool.name).trim(), (un.get(String(tool.name).trim()) || 0) + 1); continue; } for (const v of vs) { if (v.via === 'hand') usedHand.add(String(tool.name).trim()); else usedFam.add(v.family); } } } say(`category='network-scanner' used/produced tuples: ${tuples}`); say(`unmapped strings: ${un.size}`); for (const [k, v] of [...un].sort()) say(` UNMAPPED ${v} ${JSON.stringify(k)}`); if (un.size > 0) fail(`${un.size} unmapped network-scanner strings — every one must get a family or a HAND verdict`); const stale = Object.keys(HAND).filter((k) => !usedHand.has(k)); say(`HAND entries: ${Object.keys(HAND).length}, matched: ${usedHand.size}, stale: ${stale.length}`); for (const s of stale) say(` STALE HAND ${JSON.stringify(s)}`); if (stale.length > 0) fail(`${stale.length} HAND entries match nothing — a shadowed verdict is a lie about what was read`); } h('0b. IS THE ZMAP TOOL PAPER IN THE CORPUS? (the page says no, twice)'); { const inExtract = rows.filter((p) => p.year === 2013 && p.venue === 'USENIX' && /zmap|internet-wide-scanning/i.test(p.slug)); const ftDir = path.join(dataRoot(), 'fulltext/2013/USENIX'); const ftHits = fs.existsSync(ftDir) ? fs.readdirSync(ftDir).filter((x) => /zmap|scann/i.test(x)) : []; const metaFile = path.join(dataRoot(), 'corpus2/.meta/USENIX-2013.json'); const meta = JSON.parse(fs.readFileSync(metaFile, 'utf8')); const recs = Array.isArray(meta) ? meta : (meta.papers ?? meta.records ?? []); const metaHits = recs.filter((r) => /zmap/i.test(JSON.stringify(r))); say(`extraction records matching /zmap|internet-wide-scanning/i in USENIX 2013: ${inExtract.length}`); say(`data/fulltext/2013/USENIX directories matching /zmap|scann/i: ${ftHits.length}`); say(`corpus2/.meta/USENIX-2013.json records matching /zmap/i: ${metaHits.length} of ${recs.length}`); say(`PUBLISHED_ZMAP2013_ABSENT ${inExtract.length === 0 && ftHits.length === 0 && metaHits.length === 0 ? 'yes' : 'NO — the page says it is absent'}`); say(`PUBLISHED_USENIX2013_INDEX ${recs.length}`); if (inExtract.length || ftHits.length || metaHits.length) fail('the ZMap tool paper is in the corpus after all; the page says it is not'); } h('1. POPULATIONS'); { const activeInScan = ACTIVE.filter((p) => SCAN_KEYS.has(key(p))); const activeOutside = ACTIVE.filter((p) => !SCAN_KEYS.has(key(p))); // What the tools[].category restriction alone would give. const nsOnly = rows.filter((p) => p.tools.some((tool) => { if (!DROVE.has(tool.usedOrMentioned) || tool.category !== 'network-scanner') return false; if (notRun(p, tool.name)) return false; return kindsOfName(tool.name, tool.category).some((v) => MEMBERSHIP_KINDS.has(v.kind)); }) ); const namesNS = SCAN.filter((p) => p.tools.some((tool) => DROVE.has(tool.usedOrMentioned) && tool.category === 'network-scanner' && !notRun(p, tool.name)) ); t(['Population', 'Papers', 'Share'], [ ['corpus (7 venues, 2010–2026)', rows.length, '100%'], ["SCAN — studyTypes 'network-scan-or-probe'", SCAN.length, pct(SCAN.length, rows.length)], ['ACTIVE — names an active-scan instrument (page population)', ACTIVE.length, `${pct(ACTIVE.length, rows.length)} of corpus`], [' … of those, inside SCAN', activeInScan.length, pct(activeInScan.length, SCAN.length) + ' of SCAN'], [' … of those, outside SCAN', activeOutside.length, pct(activeOutside.length, ACTIVE.length) + ' of ACTIVE'], ["SCAN papers naming ANY category='network-scanner' tool", namesNS.length, pct(namesNS.length, SCAN.length)], ["SCAN papers naming NO category='network-scanner' tool", SCAN.length - namesNS.length, pct(SCAN.length - namesNS.length, SCAN.length)], ]); say(`PUBLISHED_CORPUS ${rows.length}`); say(`PUBLISHED_SCAN ${SCAN.length}`); say(`PUBLISHED_ACTIVE ${ACTIVE.length}`); say(`PUBLISHED_ACTIVE_IN_SCAN ${activeInScan.length}`); say(`PUBLISHED_ACTIVE_IN_SCAN_PCT ${pct(activeInScan.length, SCAN.length)}`); say(`PUBLISHED_ACTIVE_OUTSIDE ${activeOutside.length}`); say(`PUBLISHED_NAMES_NS ${namesNS.length}`); say(`PUBLISHED_NO_NS ${SCAN.length - namesNS.length} ${pct(SCAN.length - namesNS.length, SCAN.length)}`); say(''); say(`category-restricted membership would give ${nsOnly.length}; the any-category rule adds ${ACTIVE.length - nsOnly.length}`); say(`PUBLISHED_NS_ONLY ${nsOnly.length}`); const added = ACTIVE.filter((p) => !nsOnly.some((q) => key(q) === key(p))); for (const p of added) { const fam = memberOf(p).map((v) => `${v.family}[${[...v.cats].join('/')}]`).join(', '); say(` +${key(p)} ${fam}`); } } h('2. KIND UNION over SCAN (a paper can name several kinds)'); { const kinds = new Map(); for (const p of SCAN) for (const k of new Set(instruments(p).map((v) => v.kind))) kinds.set(k, (kinds.get(k) || 0) + 1); t(['kind', 'membership?', 'Papers', 'Share of 930', 'routed to'], [...kinds.entries()] .sort((a, b) => b[1] - a[1]) .map(([k, v]) => [k, MEMBERSHIP_KINDS.has(k) ? 'YES' : '', v, pct(v, SCAN.length), ROUTED_TO[k] ?? ''])); const none = SCAN.filter((p) => instruments(p).length === 0); say(''); say(`SCAN papers whose tools[] name no instrument this fold recognises: ${none.length} (${pct(none.length, SCAN.length)})`); say(`PUBLISHED_SCAN_NO_INSTRUMENT ${none.length} ${pct(none.length, SCAN.length)}`); } h('3. THE INSTRUMENTS — membership families over ACTIVE'); { const fam = new Map(); const spell = new Map(); for (const p of ACTIVE) { for (const v of memberOf(p)) { if (!fam.has(v.family)) { fam.set(v.family, new Set()); spell.set(v.family, new Set()); } fam.get(v.family).add(key(p)); for (const n of v.names) spell.get(v.family).add(n); } } const ordered = [...fam.entries()].sort((a, b) => b[1].size - a[1].size || a[0].localeCompare(b[0])); t(['Family', 'kind', 'Papers', 'Share of ACTIVE', 'distinct spellings'], ordered .filter(([, s]) => s.size >= 2) .map(([f, s]) => [f, KIND_OF.get(f) ?? HAND[f], s.size, pct(s.size, ACTIVE.length), spell.get(f).size])); const ones = ordered.filter(([, s]) => s.size === 1); say(''); say(`families named by exactly one paper: ${ones.length} — ${ones.map(([f]) => f).join(', ')}`); say(`PUBLISHED_ONEOFF_FAMILIES ${ones.length}`); for (const [f, s] of ordered.slice(0, 8)) say(`PUBLISHED_FAM_${f.replace(/\W+/g, '_').toUpperCase()} ${s.size}`); say(''); // The roadmap queued this page on "143 name ZMap/ZGrab/masscan/nmap". Re-derive it. { const core = new Set(['ZMap', 'ZGrab', 'Masscan', 'nmap']); const hasCore = (p) => instruments(p).some((v) => core.has(v.family)); const all = rows.filter(hasCore); const inScan = all.filter((p) => SCAN_KEYS.has(key(p))); say(''); say(`ZMap|ZGrab|Masscan|nmap, used or produced: ${all.length} corpus-wide, ${inScan.length} inside SCAN (the roadmap row said 143)`); say(`PUBLISHED_CORE_FOUR_ALL ${all.length}`); say(`PUBLISHED_CORE_FOUR_SCAN ${inScan.length}`); } say('ZMap spellings: ' + [...spell.get('ZMap')].sort().join(' | ')); say(`PUBLISHED_ZMAP_SPELLINGS ${spell.get('ZMap').size}`); // Home-grown: a membership instrument whose family is 'custom scanner' or a // one-paper coined name found only in this paper. const homegrown = ACTIVE.filter((p) => memberOf(p).some((v) => v.family === 'custom scanner' || (v.via === 'hand' && fam.get(v.family).size === 1 && [...p.tools].some((tool) => tool.usedOrMentioned === 'produced' && String(tool.name).trim() === v.family)))); const customOnly = ACTIVE.filter((p) => memberOf(p).some((v) => v.family === 'custom scanner')); const oneoffProduced = ACTIVE.filter((p) => memberOf(p).some((v) => v.via === 'hand' && fam.get(v.family).size === 1 && p.tools.some((tool) => tool.usedOrMentioned === 'produced' && String(tool.name).trim() === v.family))); say(`ACTIVE papers whose scanner is home-grown: ${homegrown.length} (${pct(homegrown.length, ACTIVE.length)})`); say(` = generic 'custom scanner' string: ${customOnly.length}`); say(` + a one-paper name the paper itself PRODUCED: ${oneoffProduced.length}`); say(` - overlap: ${customOnly.length + oneoffProduced.length - homegrown.length}`); say(` (the other ${ones.length - oneoffProduced.length} one-paper families are third-party tools only this corpus's one paper used)`); say(`PUBLISHED_HOMEGROWN ${homegrown.length} ${pct(homegrown.length, ACTIVE.length)}`); say(`PUBLISHED_HOMEGROWN_CUSTOM ${customOnly.length}`); say(`PUBLISHED_HOMEGROWN_PRODUCED ${oneoffProduced.length}`); say(`PUBLISHED_ONEOFF_THIRDPARTY ${ones.length - oneoffProduced.length}`); if (LIST === 'homegrown') for (const p of homegrown) say(` ${key(p)}`); } h('4. CURRENCY — membership families by year bucket, share of that bucket\'s ACTIVE papers'); { const FAMS = ['ZMap', 'ZGrab', 'nmap', 'XMap', 'Masscan', 'custom scanner', 'ping']; const hdr = ['Window', 'ACTIVE papers', ...FAMS]; const body = YEAR_BUCKETS.map(([label, inb]) => { const b = ACTIVE.filter((p) => inb(p.year)); return [label, b.length, ...FAMS.map((f) => { const n = b.filter((p) => memberOf(p).some((v) => v.family === f)).length; return `${n} (${pct(n, b.length)})`; })]; }); t(hdr, body); say(''); say('Per year, ZMap and the whole ACTIVE population (2026 is provisional):'); const years = [...new Set(rows.map((p) => p.year))].sort(); t(['Year', 'corpus', 'SCAN', 'ACTIVE', 'ZMap'], years.map((y) => [ y, rows.filter((p) => p.year === y).length, SCAN.filter((p) => p.year === y).length, ACTIVE.filter((p) => p.year === y).length, ACTIVE.filter((p) => p.year === y && memberOf(p).some((v) => v.family === 'ZMap')).length, ])); const zFirst = Math.min(...ACTIVE.filter((p) => memberOf(p).some((v) => v.family === 'ZMap')).map((p) => p.year)); say(`first ZMap year in ACTIVE: ${zFirst}`); say(`PUBLISHED_ZMAP_FIRST_YEAR ${zFirst}`); } h('5. WHERE SCANNING PUBLISHES — venue'); { const venues = [...new Set(rows.map((p) => p.venue))].sort(); t(['Venue', 'Papers', 'SCAN', 'SCAN share of venue', 'ACTIVE', 'ACTIVE share of venue'], venues.map((v) => { const all = rows.filter((p) => p.venue === v); const s = all.filter((p) => SCAN_KEYS.has(key(p))); const a = all.filter((p) => ACTIVE_KEYS.has(key(p))); return [v, all.length, s.length, pct(s.length, all.length), a.length, pct(a.length, all.length)]; })); } // ---------------------------------------------------- full-text machinery --- const ftCache = new Map(); function fulltextFlat(p) { const k = key(p); if (!ftCache.has(k)) { const f = path.join(dataRoot(), 'fulltext', String(p.year), p.venue, p.slug, 'paper.cols.txt'); ftCache.set(k, fs.existsSync(f) ? fs.readFileSync(f, 'latin1').replace(/\s+/g, ' ') : null); } return ftCache.get(k); } function probe(pop, re) { const hits = []; let missing = 0; for (const p of pop) { const txt = fulltextFlat(p); if (txt === null) { missing += 1; continue; } if (re.test(txt)) hits.push(p); } return { hits, missing }; } function ctx(p, re, width = 300) { const txt = fulltextFlat(p); if (txt === null) return []; const g = new RegExp(re.source, re.flags.includes('g') ? re.flags : re.flags + 'g'); const o = []; let m; while ((m = g.exec(txt)) !== null) { o.push(txt.slice(Math.max(0, m.index - width / 2), m.index + width / 2)); if (o.length >= 6) break; } return o; } h('6. WHAT A SCAN PAPER REPORTS — schema fields, denominator ACTIVE and SCAN'); { const stated = { 'vantage location': (p) => p.vantage.some((v) => (v.locations ?? []).some((l) => !isSentinel(l))), 'vantage infrastructure': (p) => p.vantage.some((v) => !isSentinel(v.infrastructure)), 'any used/produced tool version': (p) => p.tools.some((tool) => DROVE.has(tool.usedOrMentioned) && !isSentinel(tool.version)), 'version of the SCANNER itself': (p) => memberOf(p).some((v) => v.versions.size > 0), // `artifacts` and `ethics` are objects that can legitimately be absent // (320 and 894 papers corpus-wide). An absent record is NOT a stated value // and NOT a sentinel: it is its own row, printed below the table. 'own artifact URL': (p) => p.artifacts !== null && !isSentinel(p.artifacts.availability) && (p.artifacts.codeUrl !== null || p.artifacts.dataUrl !== null || p.artifacts.links.length > 0), 'ethics review outcome': (p) => p.ethics !== null && !isSentinel(p.ethics.reviewOutcome), 'notified affected parties': (p) => p.ethics !== null && !isSentinel(p.ethics.notifiedAffectedParties), 'harm mitigation described': (p) => p.ethics !== null && !isSentinel(p.ethics.harmMitigation), "robots.txt": (p) => p.ethics !== null && !isSentinel(p.ethics.robotsTxt), 'temporal span start': (p) => p.temporal.some((x) => !isSentinel(x.spanStart)), }; t(['Field', `ACTIVE (${ACTIVE.length})`, 'share', `SCAN (${SCAN.length})`, 'share'], Object.entries(stated).map(([label, f]) => { const a = ACTIVE.filter(f).length; const s = SCAN.filter(f).length; return [label, a, pct(a, ACTIVE.length), s, pct(s, SCAN.length)]; })); const noEthics = ACTIVE.filter((p) => p.ethics === null).length; const noArt = ACTIVE.filter((p) => p.artifacts === null).length; say(''); say(`ACTIVE papers with NO ethics record at all: ${noEthics} (${pct(noEthics, ACTIVE.length)}) — not counted as stated, not counted as a sentinel`); say(`ACTIVE papers with NO artifacts record at all: ${noArt} (${pct(noArt, ACTIVE.length)})`); say(`PUBLISHED_NO_ETHICS_RECORD ${noEthics} ${pct(noEthics, ACTIVE.length)}`); say(`PUBLISHED_NO_ARTIFACT_RECORD ${noArt} ${pct(noArt, ACTIVE.length)}`); for (const [label, f] of Object.entries(stated)) { say(`PUBLISHED_REPORT_${label.replace(/\W+/g, '_').toUpperCase()} ${ACTIVE.filter(f).length} ${pct(ACTIVE.filter(f).length, ACTIVE.length)} | SCAN ${SCAN.filter(f).length} ${pct(SCAN.filter(f).length, SCAN.length)}`); } } h('7. FULL-TEXT PROBES over ACTIVE — a loose upper bound and a tight estimate'); { const PAIRS = [ ['states its own probe rate or rate limit', PROBE.rateLoose, PROBE.rateTight], ['honours a blocklist / exclusion list', PROBE.blockLoose, PROBE.blockTight], ['offers an opt-out', PROBE.optLoose, PROBE.optTight], ['source-address hygiene (dedicated IPs, rDNS/PTR, WHOIS, web page)', PROBE.srcLoose, PROBE.srcTight], ['abuse complaints received or handled', PROBE.abuseLoose, PROBE.abuseTight], ]; const body = []; for (const [label, loose, tight] of PAIRS) { const l = probe(ACTIVE, loose); const tt = probe(ACTIVE, tight); body.push([label, `${l.hits.length} (${pct(l.hits.length, ACTIVE.length)})`, `${tt.hits.length} (${pct(tt.hits.length, ACTIVE.length)})`]); say(`PUBLISHED_PROBE_${label.replace(/\W+/g, '_').toUpperCase()}_LOOSE ${l.hits.length} ${pct(l.hits.length, ACTIVE.length)}`); say(`PUBLISHED_PROBE_${label.replace(/\W+/g, '_').toUpperCase()}_TIGHT ${tt.hits.length} ${pct(tt.hits.length, ACTIVE.length)}`); } t(['Practice', 'loose probe (upper bound)', 'first-person probe'], body); say(''); say('Neither column is a population. The loose regex fires on any sentence in'); say('the paper, including one about somebody else\'s rate limit or somebody'); say('else\'s blocklist; the first-person regex requires "we"/"our" near the term'); say('and still admits sentences about the authors\' own *subject*. Section 8'); say('hand-reads a deterministic sample of each first-person hit list and gives'); say('the measured precision.'); const also = probe(ACTIVE, PROBE.irb).hits; const cite = probe(ACTIVE, PROBE.goodpractice).hits; say(''); say(`ACTIVE papers naming an IRB / ethics board: ${also.length} (${pct(also.length, ACTIVE.length)}) [loose, no precision measured]`); say(`ACTIVE papers citing Durumeric / Partridge-Allman / Menlo / "good Internet citizen": ${cite.length} (${pct(cite.length, ACTIVE.length)}) [loose]`); say(`PUBLISHED_IRB ${also.length} ${pct(also.length, ACTIVE.length)}`); say(`PUBLISHED_GOODPRACTICE ${cite.length} ${pct(cite.length, ACTIVE.length)}`); } h('8. HAND-READ PRECISION — a PINNED sample of each first-person hit list'); { // The sample used to be recomputed as every-k-th of the sorted hit list. That // made the sample a function of ACTIVE: the 2026-09-17 hand audit moved ACTIVE // by three papers and the stride reshuffled all four samples, failing 46 of // these checks on papers that were still perfectly good hits — the read was // invalidated by arithmetic rather than by evidence. The sample is now the // verdict map itself: a frozen list of papers, each with the sentence that // decided it. The guards below are what keeps that honest. const CHECKS = [ ['own probe rate', PROBE.rateTight, RATE_VERDICT, 84], ['own blocklist / exclusion', PROBE.blockTight, BLOCK_VERDICT, 90], ['own opt-out offer', PROBE.optTight, OPT_VERDICT, 69], ['own source-address hygiene', PROBE.srcTight, SRC_VERDICT, 114], ]; const summary = []; for (const [label, re, verdicts, drawnFrom] of CHECKS) { const hits = probe(ACTIVE, re).hits.slice().sort((a, b) => key(a).localeCompare(key(b))); const hitKeys = new Set(hits.map(key)); const sampleKeys = Object.keys(verdicts).sort(); say(''); say(`--- ${label}: ${hits.length} first-person hits of ${ACTIVE.length} ACTIVE; pinned sample of ${sampleKeys.length}`); say(` sample drawn on 2026-09-10 as every-k-th of a hit list that was then ${drawnFrom} long`); // GUARD 1. Every pinned paper must still be a hit. If it is not, either the // probe changed or the paper left ACTIVE, and the verdict is about a paper // this figure no longer counts. for (const k of sampleKeys) { const inHits = hitKeys.has(k); say(` ${verdicts[k].padEnd(6)} ${inHits ? ' ' : '!'} ${k}`); if (!inHits) fail(`${label}: pinned sample paper is no longer a hit: ${k} — re-read it or drop it with a reason`); } // GUARD 2. A frozen sample silently stops being representative as the hit // list grows. Pin the length it was drawn from and fail on real drift. const drift = Math.abs(hits.length - drawnFrom) / drawnFrom; say(` hit list ${hits.length} now vs ${drawnFrom} at draw time — drift ${(100 * drift).toFixed(1)}%`); if (drift > 0.15) fail(`${label}: hit list moved ${(100 * drift).toFixed(1)}% since the sample was drawn (${drawnFrom} -> ${hits.length}); re-draw and re-read`); // GUARD 3. A sample clustered in one corner of the sorted hit list is not a // sample. Report where the pinned papers sit, so clustering is visible. const posns = sampleKeys.filter((k) => hitKeys.has(k)).map((k) => hits.findIndex((p) => key(p) === k)); const spread = posns.length > 1 ? Math.max(...posns) - Math.min(...posns) : 0; say(` sample positions in the sorted hit list: ${posns.join(', ')} (spread ${spread} of ${hits.length})`); if (posns.length > 1 && spread < 0.5 * hits.length) fail(`${label}: pinned sample spans only ${spread} of ${hits.length} sorted hits — it is clustered, not a sample`); const own = Object.values(verdicts).filter((v) => v === 'own').length; const tot = sampleKeys.length; const est = Math.round((hits.length * own) / tot); say(` precision ${own}/${tot}; ${hits.length} x ${own}/${tot} = about ${est} papers (${pct(est, ACTIVE.length)} of ACTIVE)`); summary.push([label, `${hits.length} (${pct(hits.length, ACTIVE.length)})`, `${own}/${tot}`, `~${est} (${pct(est, ACTIVE.length)})`]); say(`PUBLISHED_PRECISION_${label.replace(/\W+/g, '_').toUpperCase()} ${own}/${tot} -> ~${est} ${pct(est, ACTIVE.length)}`); } say(''); t(['Practice', 'first-person probe', 'hand-read precision', 'point estimate'], summary); say(''); say('The point estimate is probe count x sample precision. It is an estimate on a'); say('sample of 10-12, so read it as "about a fifth" rather than as a percentage.'); say('Replacing these four estimates with four numbers needs a per-paper verdict'); say('over the whole hit list — about 400 reads. That work is NOT done.'); } h('9. IPv6 — the address space is not enumerable'); { const v6 = probe(ACTIVE, /IPv6/).hits; const v6scan = probe(SCAN, /IPv6/).hits; const hitlistLoose = probe(SCAN, /hit ?list/i).hits; const hitlistV6 = probe(SCAN, /(IPv6[^.]{0,80}hit ?list|hit ?list[^.]{0,80}IPv6|IPv6 Hitlist)/i).hits; // A /Gasser/ probe returned 93 SCAN papers (10.0%) and was REJECTED: the // surname is not unique and the hits are reference-list entries, so it // counts citations, not use. Kept here as a rejected probe, not published. const gasser = probe(SCAN, /Gasser/).hits; const targetGen = probe(SCAN, /(target generation|address generation algorithm|\b6Gen\b|Entropy\/IP|\b6Graph\b|\b6Sense\b|\b6Tree\b|\b6Hit\b|\b6Forest\b)/i).hits; const aliased = probe(SCAN, /(aliased (prefix|region)|alias(ed)? detection|dealias)/i).hits; const v6instr = SCAN.filter((p) => instruments(p).some((v) => v.kind === 'ipv6-targeting')); const v6instrAll = rows.filter((p) => instruments(p).some((v) => v.kind === 'ipv6-targeting')); t(['Query', 'Population', 'Papers', 'Share'], [ ['mentions IPv6 anywhere', `SCAN ${SCAN.length}`, v6scan.length, pct(v6scan.length, SCAN.length)], ['mentions IPv6 anywhere', `ACTIVE ${ACTIVE.length}`, v6.length, pct(v6.length, ACTIVE.length)], ["names an IPv6-targeting instrument in tools[]", 'SCAN 930', v6instr.length, pct(v6instr.length, SCAN.length)], ["names an IPv6-targeting instrument in tools[]", 'corpus 5,859', v6instrAll.length, pct(v6instrAll.length, rows.length)], ['full text says "hitlist"/"hit list" (loose)', 'SCAN 930', hitlistLoose.length, pct(hitlistLoose.length, SCAN.length)], ['"hitlist" within 80 chars of "IPv6"', 'SCAN 930', hitlistV6.length, pct(hitlistV6.length, SCAN.length)], ['names a target-generation algorithm', 'SCAN 930', targetGen.length, pct(targetGen.length, SCAN.length)], ['discusses aliased prefixes / dealiasing', 'SCAN 930', aliased.length, pct(aliased.length, SCAN.length)], ]); say(`PUBLISHED_V6_SCAN ${v6scan.length} ${pct(v6scan.length, SCAN.length)}`); say(`PUBLISHED_V6_ACTIVE ${v6.length} ${pct(v6.length, ACTIVE.length)}`); say(`PUBLISHED_V6_INSTR_SCAN ${v6instr.length}`); say(`PUBLISHED_V6_INSTR_ALL ${v6instrAll.length}`); say(`PUBLISHED_HITLIST_LOOSE ${hitlistLoose.length} ${pct(hitlistLoose.length, SCAN.length)}`); say(`PUBLISHED_HITLIST_V6 ${hitlistV6.length} ${pct(hitlistV6.length, SCAN.length)}`); say(`REJECTED_PROBE /Gasser/ over SCAN: ${gasser.length} (${pct(gasser.length, SCAN.length)}) — surname is not unique, hits are reference-list entries; not published`); say(`PUBLISHED_TARGETGEN ${targetGen.length} ${pct(targetGen.length, SCAN.length)}`); say(`PUBLISHED_ALIASED ${aliased.length} ${pct(aliased.length, SCAN.length)}`); const v6subset = new Set([...hitlistV6, ...v6instr].map(key)); const aliasIn = aliased.filter((p) => v6subset.has(key(p))); say(''); say(`IPv6-SCANNING SUBSET (hitlist-near-IPv6 UNION names an IPv6-targeting instrument): ${v6subset.size} papers`); say(` of those, discuss aliased prefixes / dealiasing: ${aliasIn.length} (${pct(aliasIn.length, v6subset.size)})`); say(` the same 10 papers against all of SCAN would be ${pct(aliased.length, SCAN.length)} — the wrong denominator, most of SCAN never touches IPv6`); say(`PUBLISHED_V6_SUBSET ${v6subset.size}`); say(`PUBLISHED_ALIAS_IN_SUBSET ${aliasIn.length} ${pct(aliasIn.length, v6subset.size)}`); say(''); say('IPv6-targeting instruments named, corpus-wide:'); const m = new Map(); for (const p of v6instrAll) for (const v of instruments(p).filter((x) => x.kind === 'ipv6-targeting')) { if (!m.has(v.family)) m.set(v.family, new Set()); m.get(v.family).add(key(p)); } for (const [f, s] of [...m].sort((a, b) => b[1].size - a[1].size)) say(` ${String(s.size).padStart(3)} ${f} (${[...s].join(', ')})`); say(''); say('Per-year IPv6 mention rate inside SCAN (2026 provisional):'); const years = [...new Set(SCAN.map((p) => p.year))].sort(); t(['Year', 'SCAN', 'mentions IPv6', 'share'], years.map((y) => { const b = SCAN.filter((p) => p.year === y); const n = probe(b, /IPv6/).hits.length; return [y, b.length, n, pct(n, b.length)]; })); } h('10. THE JOIN TO design:ip_classification'); { const ipCls = (p) => p.classification.some((c) => c.target === 'ip-address'); const a = ACTIVE.filter(ipCls); const s = SCAN.filter(ipCls); const c = rows.filter(ipCls); t(['Population', 'N', "classification[].target == 'ip-address'", 'share'], [ ['ACTIVE', ACTIVE.length, a.length, pct(a.length, ACTIVE.length)], ['SCAN', SCAN.length, s.length, pct(s.length, SCAN.length)], ['corpus', rows.length, c.length, pct(c.length, rows.length)], ]); say(`PUBLISHED_IPCLS_ACTIVE ${a.length} ${pct(a.length, ACTIVE.length)}`); say(`PUBLISHED_IPCLS_SCAN ${s.length} ${pct(s.length, SCAN.length)}`); say(`PUBLISHED_IPCLS_CORPUS ${c.length} ${pct(c.length, rows.length)}`); // Which resources those papers reach for. const res = new Map(); for (const p of a) for (const cl of p.classification) { if (cl.target !== 'ip-address') continue; for (const rn of cl.resourceName ? [cl.resourceName] : (cl.resources ?? [])) { if (isSentinel(rn)) continue; const kk = String(rn).trim(); if (!res.has(kk)) res.set(kk, new Set()); res.get(kk).add(key(p)); } } say(''); say(`distinct raw resourceName strings on ACTIVE ip-address classifications: ${res.size} (free text; folded on design:ip_classification, not here)`); for (const [k, v] of [...res].sort((x, y) => y[1].size - x[1].size).slice(0, 15)) say(` ${String(v.size).padStart(3)} ${k}`); } h('11. POSTERS AND SHORT RECORDS'); { const poster = (p) => /^poster-/.test(p.slug) || /^Poster:/.test(p.title); const ap = ACTIVE.filter(poster); const sp = SCAN.filter(poster); const short = ACTIVE.filter((p) => p.pages !== null && p.pages <= 4); say(`ACTIVE posters: ${ap.length} (${pct(ap.length, ACTIVE.length)}); SCAN posters: ${sp.length} (${pct(sp.length, SCAN.length)})`); say(`ACTIVE records of <=4 pages: ${short.length} (${pct(short.length, ACTIVE.length)})`); const full = ACTIVE.filter((p) => !(p.pages !== null && p.pages <= 4)); const vantageStated = (p) => p.vantage.some((v) => (v.locations ?? []).some((l) => !isSentinel(l))); say(`vantage location stated: all ACTIVE ${pct(ACTIVE.filter(vantageStated).length, ACTIVE.length)}, excluding <=4-page records ${pct(full.filter(vantageStated).length, full.length)}`); say(`PUBLISHED_ACTIVE_POSTERS ${ap.length}`); say(`PUBLISHED_VANTAGE_FULLONLY ${pct(full.filter(vantageStated).length, full.length)}`); } h('12. MEASURED RESULTS available for the page (detection[].prevalence on ACTIVE)'); { let n = 0; const withPrev = ACTIVE.filter((p) => p.detection.some((d) => !isSentinel(d.prevalence))); say(`ACTIVE papers with at least one detection[].prevalence: ${withPrev.length} (${pct(withPrev.length, ACTIVE.length)})`); say(`PUBLISHED_ACTIVE_PREVALENCE ${withPrev.length} ${pct(withPrev.length, ACTIVE.length)}`); if (QUOTES) { const re = new RegExp(QUOTES, 'i'); for (const p of ACTIVE) for (const d of p.detection) { if (isSentinel(d.prevalence)) continue; const blob = `${d.phenomenon} ${d.technique} ${d.metric} ${d.prevalence}`; if (!re.test(blob)) continue; say(`--- ${key(p)}`); say(` phenomenon: ${d.phenomenon}`); say(` technique : ${d.technique}`); say(` metric : ${d.metric}`); say(` prevalence: ${d.prevalence}`); say(` quote : ${(d.evidence?.quote ?? '').replace(/\s+/g, ' ')}`); if (++n > 40) break; } } } if (HITS) { h(`SENTENCES matching ${HITS} in ACTIVE`); const re = new RegExp(HITS, 'i'); let n = 0; for (const p of ACTIVE) { const cs = ctx(p, re); if (!cs.length) continue; say(`--- ${key(p)}`); for (const c of cs.slice(0, 2)) say(` …${c}…`); if (++n > 40) break; } } if (LIST === 'active') { h('ACTIVE paper list'); for (const p of ACTIVE) say(`${key(p)} [${memberOf(p).map((v) => v.family).join(', ')}]`); } if (LIST === 'scan') { h('SCAN paper list'); for (const p of SCAN) say(key(p)); } h('Z. NON-CORPUS FIGURES ON THE PAGE (checked by external_checks_internet_scanning.sh)'); for (const line of EXTERNAL) say(line); h('RESULT'); say(fails.length === 0 ? 'OK — all contracts hold' : `${fails.length} FAILURE(S)`); console.log(out.join('\n')); if (fails.length) process.exit(1);
- reconcile_zmap.mjs
#!/usr/bin/env node // Why design:dns said "ZMap and XMap (112 papers)" while // programming:internet_scanning said "ZMap 95" + "XMap 16", and what closed it. // // The two pages count the same thing over the same corpus: // // design:dns dns_fold.mjs family 'ZMap / XMap', one family // for both scanners, counted corpus-wide in the // scanner-generic block of report_dns.mjs §E // ("reported only, NOT in the population rule"). // programming:internet_scanning scan_fold.mjs families 'ZMap' and 'XMap', // kept apart. Both kinds are 'active-scan', // which is a MEMBERSHIP_KIND, so every paper // naming either is in ACTIVE by construction // and the two counts are ALSO corpus-wide. Only // only the percentage column on that page uses // ACTIVE as a denominator; this script prints none. // // So the populations are identical and the 1-paper gap was never a population // difference. It was also not an overlap artefact: no paper names both ZMap and // XMap. It was ONE compound tools[] string, "ZGrab/ZMap", resolved differently // by two ordered first-match-wins folds (this fold tests ZGrab before ZMap; // dns_fold tests ZMap/XMap before ZGrab). // // Sections: // 1 the two sides as published before 2026-09-17 (single-valued folds) // 2 the compound-name sweep: every used/produced tools[] name containing a // separator whose PARTS fold to a family the WHOLE string does not // 3 the two sides after COMPOUND_SPLIT, which must now agree exactly // // Exit 1 if an identity fails or if a compound name appears that is not in // COMPOUND_SPLIT. A new compound name is a FAILURE, not a drop. // // Usage: node scripts/reconcile_zmap.mjs [--names] import { loadExtractions, isSentinel } from './lib.mjs'; import { foldDnsTool, foldDnsToolAll, COMPOUND_SPLIT as DNS_COMPOUND } from './dns_fold.mjs'; import { kindOfName, kindsOfName, COMPOUND_SPLIT as SCAN_COMPOUND } from './scan_fold.mjs'; import { notRun } from './tool_use_overrides.mjs'; const SHOW_NAMES = process.argv.includes('--names'); const rows = loadExtractions(); const key = (p) => `${p.venue}/${p.year}/${p.slug}`; const out = []; const say = (s = '') => out.push(s); const fails = []; const fail = (m) => { fails.push(m); say(`FAILURE: ${m}`); }; const h = (s) => { say(''); say('='.repeat(78)); say(s); say('='.repeat(78)); }; if (rows.length !== 5859) fail(`corpus ${rows.length} != 5859`); // Both report scripts use the same tuple filter: used or produced, non-sentinel. const DROVE = new Set(['used', 'produced']); // Section 1 reproduces the state the two pages were published in, so it uses // the raw tuples. Everything after it applies the 2026-09-17 tuple suppression // too: a tool the paper fingerprinted rather than ran is not an instrument on // either page (scripts/tool_use_overrides.mjs). const tuplesRaw = (p) => p.tools.filter((t) => DROVE.has(t.usedOrMentioned) && !isSentinel(t.name)); const tuples = (p) => tuplesRaw(p).filter((t) => !notRun(p, t.name)); // Collect one side. `fold` returns a list of labels for a tuple. function collect(fold, pick) { const m = new Map(); // label -> Map(paperKey -> [names]) for (const p of rows) { const k = key(p); for (const t of pick(p)) { for (const label of fold(t)) { if (!m.has(label)) m.set(label, new Map()); const inner = m.get(label); if (!inner.has(k)) inner.set(k, []); inner.get(k).push(t.name); } } } return m; } const emptyMap = new Map(); const papersOf = (m, label) => new Set((m.has(label) ? m.get(label) : emptyMap).keys()); // --- 1. as published before the fix ----------------------------------------- const oldDns = collect((t) => { const f = foldDnsTool(t.name, t.category, t.purpose); return f ? [f.label] : []; }, tuplesRaw); const oldScan = collect((t) => { const v = kindOfName(t.name, t.category); return v ? [v.family] : []; }, tuplesRaw); const oD = papersOf(oldDns, 'ZMap / XMap'); const oZ = papersOf(oldScan, 'ZMap'); const oX = papersOf(oldScan, 'XMap'); const oS = new Set([...oZ, ...oX]); const oBoth = [...oZ].filter((k) => oX.has(k)); const oInter = new Set([...oD].filter((k) => oS.has(k))); const oDOnly = [...oD].filter((k) => !oS.has(k)).sort(); const oSOnly = [...oS].filter((k) => !oD.has(k)).sort(); h('1. THE TWO SIDES AS PUBLISHED (single-valued folds, before 2026-09-17)'); say(''); say(`corpus ${rows.length}`); say(`dns_fold 'ZMap / XMap' — design:dns said ${oD.size}`); say(`scan_fold 'ZMap' — internet_scanning said ${oZ.size}`); say(`scan_fold 'XMap' — internet_scanning said ${oX.size}`); say(` naive sum ${oZ.size + oX.size}`); say(` papers naming BOTH ZMap and XMap (would be double-counted) ${oBoth.length}`); say(` scan_fold ZMap U XMap, papers ${oS.size}`); say(''); say(`shared by both folds ${oInter.size}`); say(`dns_fold only ${oDOnly.length}`); say(`scan_fold only ${oSOnly.length}`); say(''); say('NOT AN OVERLAP ARTEFACT: the double-count the review suspected is zero.'); if (oBoth.length !== 0) fail(`${oBoth.length} papers name both ZMap and XMap — the overlap story is back`); say(''); say('The whole gap, paper by paper, with the deciding tools[] string:'); for (const k of oDOnly) { const p = rows.find((r) => key(r) === k); say(` dns_fold ONLY ${k}`); for (const n of oldDns.get('ZMap / XMap').get(k)) { const t = tuplesRaw(p).find((tt) => tt.name === n); const v = kindOfName(n, t.category); say(` ${JSON.stringify(n)} [cat=${t.category}] scan_fold -> ${v ? v.family + ' / ' + v.kind : 'UNMAPPED'}`); say(` purpose: ${t.purpose}`); say(` evidence: ${t.evidence ? t.evidence.quote : '(none)'}`); } } for (const k of oSOnly) { const p = rows.find((r) => key(r) === k); say(` scan_fold ONLY ${k}`); const names = [...(oZ.has(k) ? oldScan.get('ZMap').get(k) : []), ...(oX.has(k) ? oldScan.get('XMap').get(k) : [])]; for (const n of names) { const t = tuplesRaw(p).find((tt) => tt.name === n); const f = foldDnsTool(n, t.category, t.purpose); say(` ${JSON.stringify(n)} [cat=${t.category}] dns_fold -> ${f ? f.kind + ' / ' + f.label : 'UNMAPPED'}`); } } if (oInter.size + oDOnly.length !== oD.size) fail('dns side does not decompose'); if (oInter.size + oSOnly.length !== oS.size) fail('scan side does not decompose'); // --- 2. the compound-name sweep --------------------------------------------- // A separator-bearing name whose PARTS fold to a family the WHOLE string does // not is a name one of the two folds is silently losing. Wide on purpose: the // point is recall, and every hit is printed whether or not it is acknowledged. h('2. COMPOUND-NAME SWEEP (recall check over every used/produced tools[] name)'); const SEP = /\s*(?:\/|\s\+\s|\sand\s|\s&\s|\s?,\s)\s*/; const compounds = new Map(); let sepNames = 0; for (const p of rows) { for (const t of tuples(p)) { const n = String(t.name).trim(); if (!SEP.test(n)) continue; sepNames += 1; const parts = n.split(SEP).filter(Boolean); if (parts.length < 2) continue; const wS = kindOfName(n, t.category); const wD = foldDnsTool(n, t.category, t.purpose); const extraS = new Set(); const extraD = new Set(); for (const part of parts) { const vs = kindOfName(part, t.category); if (vs && (!wS || vs.family !== wS.family)) extraS.add(`${vs.family}/${vs.kind}`); const vd = foldDnsTool(part, t.category, t.purpose); if (vd && (!wD || vd.label !== wD.label)) extraD.add(`${vd.label}/${vd.kind}`); } if (!extraS.size && !extraD.size) continue; const kk = `${n}|${t.category}`; if (!compounds.has(kk)) { compounds.set(kk, { n, cat: t.category, papers: new Set(), wS: wS ? wS.family : null, wD: wD ? wD.label : null, extraS: [...extraS], extraD: [...extraD], }); } compounds.get(kk).papers.add(key(p)); } } say(''); say(`used/produced tools[] names containing a separator: ${sepNames} tuples`); say(`…of those, names a fold loses part of: ${compounds.size} distinct (name, category) pairs`); say(''); const ACK = new Set([...DNS_COMPOUND.keys(), ...SCAN_COMPOUND.keys()]); for (const v of [...compounds.values()].sort((a, b) => b.papers.size - a.papers.size)) { const ack = ACK.has(v.n.toLowerCase()); say(` ${ack ? 'SPLIT ' : 'RESIDUE'} ${v.papers.size} paper(s) ${JSON.stringify(v.n)} [cat=${v.cat}]`); say(` scan_fold whole=${v.wS} parts add ${JSON.stringify(v.extraS)}`); say(` dns_fold whole=${v.wD} parts add ${JSON.stringify(v.extraD)}`); for (const pk of [...v.papers].sort()) say(` ${pk}`); } say(''); say('RESIDUE rows are names where a fold loses a part and no hand verdict says'); say('that is correct. They are left unsplit ON PURPOSE only when the lost part'); say('changes no published figure; the judgement is on provenance:programming:'); say('internet_scanning. A row whose lost part is a MEMBERSHIP kind (scan page)'); say('or a DNS_KIND (dns page) is a real defect and fails this script.'); { const MEMBER = new Set(['active-scan', 'grabber', 'reachability', 'censorship-probe', 'ipv6-targeting']); const DNSK = new Set(['query-tool', 'resolver-software', 'public-resolver', 'passive-dns', 'dnssec-tool', 'open-resolver-census', 'custom']); for (const v of compounds.values()) { if (ACK.has(v.n.toLowerCase())) continue; for (const e of v.extraS) if (MEMBER.has(e.split('/')[1])) fail(`unsplit compound ${JSON.stringify(v.n)} loses membership kind ${e} on the scanning page`); for (const e of v.extraD) if (DNSK.has(e.split('/')[1])) fail(`unsplit compound ${JSON.stringify(v.n)} loses DNS kind ${e} on design:dns`); } } // --- 3. after the fix -------------------------------------------------------- const newDns = collect((t) => foldDnsToolAll(t.name, t.category, t.purpose).map((f) => f.label), tuples); const newScan = collect((t) => kindsOfName(t.name, t.category).map((v) => v.family), tuples); const nD = papersOf(newDns, 'ZMap / XMap'); const nZ = papersOf(newScan, 'ZMap'); const nX = papersOf(newScan, 'XMap'); const nS = new Set([...nZ, ...nX]); const nBoth = [...nZ].filter((k) => nX.has(k)); const nDG = papersOf(newDns, 'ZGrab'); const nSG = papersOf(newScan, 'ZGrab'); h('3. AFTER COMPOUND_SPLIT AND THE TUPLE SUPPRESSION — they must agree exactly'); say(''); say(`dns_fold 'ZMap / XMap' ${nD.size}`); say(`scan_fold 'ZMap' ${nZ.size}`); say(`scan_fold 'XMap' ${nX.size}`); say(` papers naming both ${nBoth.length}`); say(`scan_fold ZMap U XMap ${nS.size}`); say(`symmetric difference ${[...nD].filter((k) => !nS.has(k)).length + [...nS].filter((k) => !nD.has(k)).length}`); say(''); say(`dns_fold 'ZGrab' ${nDG.size}`); say(`scan_fold 'ZGrab' ${nSG.size}`); say(`symmetric difference ${[...nDG].filter((k) => !nSG.has(k)).length + [...nSG].filter((k) => !nDG.has(k)).length}`); say(''); say(`PUBLISHED_ZMAP_FAMILY_UNION ${nS.size}`); say(`PUBLISHED_ZMAP ${nZ.size}`); say(`PUBLISHED_XMAP ${nX.size}`); say(`PUBLISHED_ZGRAB ${nSG.size}`); if (nD.size !== nS.size) fail(`after the split the two folds still disagree: ${nD.size} vs ${nS.size}`); if (nDG.size !== nSG.size) fail(`ZGrab still disagrees: dns ${nDG.size} vs scan ${nSG.size}`); if (nZ.size + nX.size - nBoth.length !== nS.size) fail('ZMap+XMap-overlap != union'); if (SHOW_NAMES) { h('4. EVERY DISTINCT SPELLING ON EACH SIDE, AFTER THE SPLIT'); const spell = (m, label) => [...new Set([...(m.has(label) ? m.get(label) : emptyMap).values()].flat())].sort().join(' | '); say(''); say(`dns_fold 'ZMap / XMap': ${spell(newDns, 'ZMap / XMap')}`); say(`scan_fold 'ZMap': ${spell(newScan, 'ZMap')}`); say(`scan_fold 'XMap': ${spell(newScan, 'XMap')}`); say(`scan_fold 'ZGrab': ${spell(newScan, 'ZGrab')}`); } say(''); say(fails.length ? `FAILURES: ${fails.length}` : 'OK — both folds agree and no unacknowledged compound name loses a counted kind'); console.log(out.join('\n')); process.exit(fails.length ? 1 : 0);
- reconcile_zmap-output.txt
============================================================================== 1. THE TWO SIDES AS PUBLISHED (single-valued folds, before 2026-09-17) ============================================================================== corpus 5859 dns_fold 'ZMap / XMap' — design:dns said 112 scan_fold 'ZMap' — internet_scanning said 95 scan_fold 'XMap' — internet_scanning said 16 naive sum 111 papers naming BOTH ZMap and XMap (would be double-counted) 0 scan_fold ZMap U XMap, papers 111 shared by both folds 111 dns_fold only 1 scan_fold only 0 NOT AN OVERLAP ARTEFACT: the double-count the review suspected is zero. The whole gap, paper by paper, with the deciding tools[] string: dns_fold ONLY CCS/2016/a-surfeit-of-ssh-cipher-suites "ZGrab/ZMap" [cat=network-scanner] scan_fold -> ZGrab / grabber purpose: Performed IPv4-wide SSH server scans. evidence: We ran two IPv4-address-space-wide scans for SSH servers using ZGrab/ZMap [13]. ============================================================================== 2. COMPOUND-NAME SWEEP (recall check over every used/produced tools[] name) ============================================================================== used/produced tools[] names containing a separator: 263 tuples …of those, names a fold loses part of: 4 distinct (name, category) pairs RESIDUE 1 paper(s) "HTTP/OneProbe" [cat=network-scanner] scan_fold whole=HTTP/OneProbe parts add ["OneProbe/topology"] dns_fold whole=null parts add [] IMC/2010/measurement-of-loss-pairs-in-network-paths RESIDUE 1 paper(s) "tcpdump/libpcap" [cat=traffic-capture] scan_fold whole=null parts add ["tshark/tcpdump/passive"] dns_fold whole=null parts add [] PETS/2015/an-automated-approach-for-complementing-ad-blockers-blacklists SPLIT 1 paper(s) "ZGrab/ZMap" [cat=network-scanner] scan_fold whole=ZGrab parts add ["ZMap/active-scan"] dns_fold whole=ZMap / XMap parts add ["ZGrab/scanner-generic"] CCS/2016/a-surfeit-of-ssh-cipher-suites RESIDUE 1 paper(s) "WireShark/tshark" [cat=traffic-capture] scan_fold whole=null parts add ["tshark/tcpdump/passive"] dns_fold whole=null parts add [] IMC/2023/in-the-room-where-it-happens-characterizing-local-communication-and-threats-in-s RESIDUE rows are names where a fold loses a part and no hand verdict says that is correct. They are left unsplit ON PURPOSE only when the lost part changes no published figure; the judgement is on provenance:programming: internet_scanning. A row whose lost part is a MEMBERSHIP kind (scan page) or a DNS_KIND (dns page) is a real defect and fails this script. ============================================================================== 3. AFTER COMPOUND_SPLIT AND THE TUPLE SUPPRESSION — they must agree exactly ============================================================================== dns_fold 'ZMap / XMap' 110 scan_fold 'ZMap' 94 scan_fold 'XMap' 16 papers naming both 0 scan_fold ZMap U XMap 110 symmetric difference 0 dns_fold 'ZGrab' 32 scan_fold 'ZGrab' 32 symmetric difference 0 PUBLISHED_ZMAP_FAMILY_UNION 110 PUBLISHED_ZMAP 94 PUBLISHED_XMAP 16 PUBLISHED_ZGRAB 32 ============================================================================== 4. EVERY DISTINCT SPELLING ON EACH SIDE, AFTER THE SPLIT ============================================================================== dns_fold 'ZMap / XMap': TCP Options module for ZMap | XMAP | XMap | Xmap (extended) | ZGrab/ZMap | ZMap | ZMap-based script | ZMap6 | ZMapv6 | Zmap | zmap | zmap2 | zmap6 scan_fold 'ZMap': TCP Options module for ZMap | ZGrab/ZMap | ZMap | ZMap-based script | ZMap6 | ZMapv6 | Zmap | zmap | zmap2 | zmap6 scan_fold 'XMap': XMAP | XMap | Xmap (extended) scan_fold 'ZGrab': ZGrab | ZGrab 2.0 | ZGrab/ZMap | ZGrab2 | Zgrab2 | zgrab | zgrab2 OK — both folds agree and no unacknowledged compound name loses a counted kind
- tool_use_overrides.mjs
// Tuple-level corrections to the extraction's `usedOrMentioned` field, shared // by report_internet_scanning.mjs and report_dns.mjs. // // WHY THIS FILE EXISTS. The extraction records a tools[] entry as 'used' when // the paper's own sentence names the tool in the first person. That reading is // wrong for one paper shape: a study of *other people's* scanning, where the // scanner name appears because the authors FINGERPRINTED its traffic, not // because they ran it. A name fold cannot see the difference — the string is // "ZMap" either way — so the correction has to be per (paper, tool) and // hand-read. Found on 2026-09-17 while hand-auditing scan_fold.mjs's HAND // verdicts; the recall probe that bounded it is in the provenance page. // // THE RULE APPLIED, written before the entries below: // (a) suppress when the paper DID NOT RUN the tool — it fingerprinted it, or // read someone else's output; and // (b) suppress when the packets NEVER LEFT THE HOST onto the public Internet: // a closed local loop or a synthetic load generator. // Do NOT suppress when the paper ran the tool and the packets did cross the // Internet, even if they were aimed somewhere unusual — its own honeypot, its // own hosts, a cloud VM it controls. "The target was ours" is a different // claim from "no packet was sent", and relitigating the former per paper has // no stopping rule. // // Clause (b) was NOT in the rule when the first entries were written. It was // added on 2026-09-17 after a review pass read USENIX 2022 Spoki and found a // ZMap run that clause (a) alone would have kept. Recording that here rather // than back-dating the rule: the rule was incomplete, and one published // figure was wrong by one paper because of it. // // Key is `venue/year/slug|toolName`, exactly as the extraction spells both. export const NOT_RUN = new Map([ // IMC 2024, "Have you SYN me? Characterizing Ten Years of Internet // Scanning" — a pure network-telescope study of three /16s. All four scanner // names are fingerprint references. The extraction's purpose fields say so // themselves ("Fingerprint ZMap-generated scanning traffic"), and the paper // says of one of them: "we find no evidence of Unicorn being used for // Internet-wide scanning and instead record in total only 2 distinct IP // addresses ever using the Unicorn scanning tool." ['IMC/2024/have-you-syn-me-characterizing-ten-years-of-internet-scanning|ZMap', 'telescope fingerprint reference, not a scan the authors ran'], ['IMC/2024/have-you-syn-me-characterizing-ten-years-of-internet-scanning|NMap', 'telescope fingerprint reference, not a scan the authors ran'], ['IMC/2024/have-you-syn-me-characterizing-ten-years-of-internet-scanning|Masscan', 'telescope fingerprint reference, not a scan the authors ran'], ['IMC/2024/have-you-syn-me-characterizing-ten-years-of-internet-scanning|Unicorn', 'telescope fingerprint reference, not a scan the authors ran'], // USENIX 2022, "Spoki: Unveiling a New Wave of Scanners through a Reactive // Network Telescope". Clause (b). The authors DID run ZMap, but only as a // packet generator inside their own machine: "A local pair of virtual // Ethernet interfaces route packets between the packet source and Spoki. We // use ZMap to generate TCP SYN packets, passing the MAC address of the // destination interface along with a probing rate and a range of IP // addresses to use as a source." The address range is a synthetic SOURCE // prefix for load generation; nothing is probed. Verified in // data/fulltext/2022/USENIX/spoki-.../paper.cols.txt on 2026-09-17. // This tuple was Spoki's only route into ACTIVE, so the paper leaves it. ['USENIX/2022/spoki-unveiling-a-new-wave-of-scanners-through-a-reactive-network-telescope|ZMap', 'closed veth loop on one host for a throughput benchmark; no packet reaches the Internet'], ]); // The canonical key. Each report script spells its own paper key differently // (report_dns.mjs uses year/venue/slug, report_internet_scanning.mjs uses // venue/year/slug), so notRun() takes the extraction RECORD, not a key string — // passing the wrong spelling would silently match nothing. export const paperKey = (p) => `${p.venue}/${p.year}/${p.slug}`; // True if this (paper, tool) tuple must not count as a used/produced instrument. export function notRun(p, toolName) { return NOT_RUN.has(`${paperKey(p)}|${String(toolName).trim()}`); } // Self-check: every NOT_RUN key must name a paper and a tool tuple that exist, // or the entry is a lie about what was read. Call it from a report script. export function checkNotRun(rows) { const fails = []; for (const k of NOT_RUN.keys()) { const [pk, tool] = k.split('|'); const p = rows.find((r) => paperKey(r) === pk); if (!p) { fails.push(`NOT_RUN key names no paper: ${pk}`); continue; } if (!p.tools.some((t) => String(t.name).trim() === tool)) fails.push(`NOT_RUN key names no tools[] entry: ${k}`); } return fails; }
- audit_hand_verdicts.mjs
#!/usr/bin/env node // Dump every HAND verdict in scripts/scan_fold.mjs with the evidence a reader // needs to judge it: the tools[] purpose the verdict was supposedly read off, // the extractor's evidence quote, the tool category, and the paper's own title. // // scan_fold.mjs's HAND map is 174 hand verdicts over category='network-scanner' // names the FAMILIES list does not fold. The report's coverage and staleness // guards prove every name has a verdict and every verdict matches a name; they // prove nothing about whether the KIND is right. A wrong kind on a one-off // scanner name moves ACTIVE by one paper and nothing fires. // // Usage: node scripts/audit_hand_verdicts.mjs [--membership-only] [--tsv] import { loadExtractions, isSentinel } from './lib.mjs'; import { HAND, MEMBERSHIP_KINDS } from './scan_fold.mjs'; const MEMBER_ONLY = process.argv.includes('--membership-only'); const TSV = process.argv.includes('--tsv'); const rows = loadExtractions(); const DROVE = new Set(['used', 'produced']); const key = (p) => `${p.venue}/${p.year}/${p.slug}`; // name -> list of occurrences const occ = new Map(); for (const p of rows) { for (const t of p.tools) { if (!DROVE.has(t.usedOrMentioned) || t.category !== 'network-scanner') continue; const n = String(t.name).trim(); if (!Object.prototype.hasOwnProperty.call(HAND, n)) continue; if (!occ.has(n)) occ.set(n, []); occ.get(n).push({ paper: key(p), title: p.title, uom: t.usedOrMentioned, purpose: isSentinel(t.purpose) ? '(sentinel)' : t.purpose, quote: t.evidence ? t.evidence.quote : '(no evidence quote)', section: t.evidence ? t.evidence.section : '', }); } } const names = Object.keys(HAND).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); const shown = names.filter((n) => !MEMBER_ONLY || MEMBERSHIP_KINDS.has(HAND[n])); if (TSV) { console.log(['name', 'verdict', 'membership', 'paper', 'title', 'purpose', 'quote'].join('\t')); for (const n of shown) { for (const o of occ.get(n)) { console.log([n, HAND[n], MEMBERSHIP_KINDS.has(HAND[n]) ? 'IN-ACTIVE' : 'out', o.paper, o.title, o.purpose, String(o.quote).replace(/\s+/g, ' ')].join('\t')); } } process.exit(0); } console.log(`HAND verdicts: ${names.length}; shown: ${shown.length}${MEMBER_ONLY ? ' (membership kinds only)' : ''}`); console.log(`of the ${names.length}, ${names.filter((n) => MEMBERSHIP_KINDS.has(HAND[n])).length} put their paper in ACTIVE`); console.log(''); for (const n of shown) { const list = occ.get(n); console.log(`### ${JSON.stringify(n)} -> ${HAND[n]}${MEMBERSHIP_KINDS.has(HAND[n]) ? ' [IN ACTIVE]' : ''}`); for (const o of list) { console.log(` paper ${o.paper}`); console.log(` title ${o.title}`); console.log(` purpose ${o.purpose}`); console.log(` quote ${String(o.quote).replace(/\s+/g, ' ').slice(0, 400)}`); } console.log(''); }
- audit_hand_verdicts-output.txt
HAND verdicts: 173; shown: 173 of the 173, 35 put their paper in ACTIVE ### "0trace" -> topology paper USENIX/2024/calculatency-leveraging-cross-layer-network-latency-measurements-to-detect-proxy title CalcuLatency: Leveraging Cross-Layer Network Latency Measurements to Detect Proxy-Enabled Abuse purpose Measures RTT using probes piggybacked on established TCP connections. quote We developed a Go package that implements 0trace. ### "5GSniffer" -> local-link paper IEEE-SP/2023/from-5g-sniffing-to-harvesting-leakages-of-privacy-preserving-messengers title From 5G Sniffing to Harvesting Leakages of Privacy-Preserving Messengers. purpose Decode live 5G control channels and recover RNTI scheduling quote We introduce our tool 5GSniffer, which is, to the best of our knowledge, the first open-source implementation of a promiscuous 5G control channel sniffer ### "active DNS crawler" -> dns paper IMC/2015/from-academy-to-zone-an-analysis-of-the-new-tld-land-rush title From .academy to .zone: An Analysis of the New TLD Land Rush. purpose Followed DNS chains and collected A, AAAA, CNAME, and NS records quote Every time we Web crawl a domain, we also perform a DNS query using a DNS crawler developed for [15]. ### "active scanning tool" -> dns paper USENIX/2022/a-large-scale-and-longitudinal-measurement-study-of-dkim-deployment title A Large-scale and Longitudinal Measurement Study of DKIM Deployment purpose Query DKIM, SPF, and DMARC records. quote We select the top 40 selectors to actively scan the Alexa top 1 million domains. ### "active-geolocator" -> topology paper IMC/2018/how-to-catch-when-proxies-lie-verifying-the-physical-locations-of-network-proxie title How to Catch when Proxies Lie: Verifying the Physical Locations of Network Proxies with Active Geolocation. purpose Reimplemented and ran active-geolocation algorithms. quote We reimplemented four active geolocation algorithms from earlier papers: CBG [20], Octant [45], Spotter [30], and an Octant/Spotter hybrid of our own invention. ### "AdaptOver" -> local-link paper USENIX/2022/ltrack-stealthy-tracking-of-mobile-phones-in-lte title LTrack: Stealthy Tracking of Mobile Phones in LTE purpose Perform stealthy LTE message overshadowing quote For message overshadowing, we use AdaptOver [13], a recently proposed LTE overshadowing attack. ### "Airmon-ng" -> local-link paper IEEE-SP/2019/drones-cryptanalysis-smashing-cryptography-with-a-flicker title Drones' Cryptanalysis - Smashing Cryptography with a Flicker. purpose Enabled Wi-Fi receiver monitor mode. quote We enabled "monitor mode" on the Wi-Fi receiver (used by the interceptor) using Airmon-ng [58]. ### "Ally" -> topology paper IMC/2016/bdrmap-inference-of-borders-between-ip-networks title bdrmap: Inference of Borders Between IP Networks. purpose Resolved IP aliases using IP-ID measurements. quote bdrmap ... probes candidate pairs of IP addresses using alias resolution methods Ally and Mercator. paper IMC/2019/learning-regexes-to-extract-router-names-from-hostnames title Learning Regexes to Extract Router Names from Hostnames. purpose Performed follow-up ICMP, UDP, and TCP alias probing. quote we used Ally [27] with ICMP, UDP, and TCP probes, and Mercator [8]. ### "amass" -> name-enumeration paper USENIX/2021/can-i-take-your-subdomain-exploring-same-site-attacks-in-the-modern-web title Can I Take Your Subdomain? Exploring Same-Site Attacks in the Modern Web purpose Enumerated subdomains using public sources and DNS techniques. quote The enumeration phase was based on amass [41], a state of the art information gathering tool backed by the OWASP project. ### "AmpMap" -> active-scan [IN ACTIVE] paper USENIX/2021/accurately-measuring-global-risk-of-amplification-attacks-using-ampmap title Accurately Measuring Global Risk of Amplification Attacks using AmpMap purpose Discover amplification-inducing query patterns quote We implemented AmpMap, validated our parameter settings in lab settings, and ran real-world measurements. ### "Anaximander" -> topology paper IMC/2025/autonomous-systems-under-arest-advanced-revelation-of-segment-routing-tunnels title Autonomous Systems under AReST: Advanced Revelation of Segment Routing Tunnels. purpose Generate ordered target IP lists for each targeted AS. quote Then, for each AS, we built a list of target IP addresses, using Anaximander, an AS mapping framework. ### "Aquantone" -> name-enumeration paper IMC/2018/dissecting-apples-meta-cdn-during-an-ios-update title Dissecting Apple's Meta-CDN during an iOS Update. purpose Enumerating Apple CDN DNS names quote By scanning Apple's IP range (17.0.0.0/8) for the availability of iOS image downloads and by enumerating the DNS names using the Aquantone tool [21], we reconstructed the Apple's naming scheme ### "Arachni" -> vuln-scanner paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Scanned test web applications quote Table 1 lists the 12 scanners that we analyze in this study along with their corresponding version information. ### "arpscan" -> local-link paper CCS/2025/exploring-and-analyzing-cross-layer-dos-attack-against-udp-based-services-on-lin title Exploring and Analyzing Cross Layer DoS Attack Against UDP-based Services on Linux. purpose Probe IPv4 address-resolution behavior in cloud VPS networks. quote conducted ARP and NDP test scans on IPv4 and IPv6 networks using arpscan and ndisc6 tools, respectively. ### "Bitnodes" -> p2p-crawler paper CCS/2014/deanonymisation-of-clients-in-bitcoin-p2p-network title Deanonymisation of Clients in Bitcoin P2P Network. purpose Collected running Bitcoin server lists. quote In order to periodically get the list of all running Bitcoin servers we used an open source crawler [4]. ### "BitTorrent crawler" -> p2p-crawler paper IMC/2011/i-know-where-you-are-and-what-you-are-sharing-exploiting-p2p-communications-to-i title I know where you are and what you are sharing: exploiting P2P communications to invade users' privacy. purpose Collects IP addresses from BitTorrent swarms. quote The BitTorrent crawler collects the IP addresses participating in the 50,000 most popular swarms ### "Black Widow" -> vuln-scanner paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Scanned test web applications quote We augment this list with two academic scanners: Black Widow and Enemy of the State, resulting in 12 scanners. ### "Bluesniff" -> local-link paper USENIX/2011/comprehensive-experimental-analyses-of-automotive-attack-surfaces title Comprehensive Experimental Analyses of Automotive Attack Surfaces purpose Sniffed Bluetooth MAC addresses quote we can use the open source Bluesniff [23] package and a USRP-based software radio to sniff our car's Bluetooth MAC address ### "BreadCrumb" -> censorship-probe [IN ACTIVE] paper USENIX/2022/many-roads-lead-to-rome-how-packet-headers-influence-dns-censorship-measurement title Many Roads Lead To Rome: How Packet Headers Influence DNS Censorship Measurement purpose Generate, capture, and analyze DNS censorship probes quote Developing BreadCrumb, a measurement methodology and associated tool to quantitatively understand the prevalence of censorship changes due to router load balancing ### "Btsniffer" -> local-link paper IEEE-SP/2023/blues-clues-practical-discovery-of-non-discoverable-bluetooth-devices title Blue's Clues: Practical Discovery of Non-Discoverable Bluetooth Devices. purpose Recover Bluetooth LAP and UAP address fragments. quote We employ the current state-of-the-art in passive Bluetooth monitoring [6] as a method for recovering addresses from local traffic. This program is open-source [34] ### "BurpSuite Scanner" -> vuln-scanner paper USENIX/2019/less-is-more-quantifying-the-security-benefits-of-debloating-web-applications title Less is More: Quantifying the Security Benefits of Debloating Web Applications purpose Scanned headers, URLs, and forms for vulnerabilities. quote We use BurpSuite Scanner v2.0.14beta [2] based on the URLs extracted by the spider to look for vulnerabilities in headers, URLs and forms. ### "CAIDA AS Relationships Dataset" -> routing paper USENIX/2021/the-hijackers-guide-to-the-galaxy-off-path-taking-over-internet-resources title The Hijackers Guide To The Galaxy: Off-Path Taking Over Internet Resources purpose Construct AS-level topology maps. quote For creating the topological map of the AS-relationship dataset of the customer domains and the providers in our dataset we use CAIDA [3]. ### "Cenzic HailStorm Pro" -> vuln-scanner paper IEEE-SP/2010/state-of-the-art-automated-black-box-web-application-vulnerability-testing title State of the Art: Automated Black-Box Web Application Vulnerability Testing. purpose Scan test applications for web vulnerabilities. quote The eight scanners are listed in Table I. ### "Chaos" -> simulation paper IEEE-SP/2019/on-the-feasibility-of-rerouting-based-ddos-defenses title On the Feasibility of Rerouting-Based DDoS Defenses. purpose Simulated BGP propagation over inferred AS topology. quote We use the Chaos simulator [4], an open-source BGP simulator that has been used to evaluate the RAC proposal [51], to simulate the network topology and BGP propagation among ASes in the network. ### "CheckHost" -> vantage-platform paper NDSS/2023/hope-of-delivery-extracting-user-locations-from-mobile-instant-messengers title Hope of Delivery: Extracting User Locations From Mobile Instant Messengers purpose Runs ping and traceroute measurements from multiple locations. quote We continue with a series of systematic Ping and Traceroute experiments from different geographical locations using a public API provided by CheckHost [7]. ### "cloud_enum" -> name-enumeration paper PETS/2022/we-may-share-the-number-of-diaper-changes-a-privacy-and-security-analysis-of-mob title “We may share the number of diaper changes”: A Privacy and Security Analysis of Mobile Child Care Applications purpose Find publicly accessible cloud storage resources quote This tool tries to find any publicly accessible cloud storage resources at Amazon Web Services (AWS), Microsoft Azure, and Google Cloud Platform based on given keywords. ### "CnCHunter" -> active-scan [IN ACTIVE] paper IMC/2022/malnet-a-binary-centric-network-level-profiling-of-iot-malware title MalNet: a binary-centric network-level profiling of IoT malware. purpose Activate binaries and identify or probe live C2 servers. quote Among the various tools, we selected CnCHunter, a powerful open-source tool for analyzing IoT malware binaries. ### "Commix" -> vuln-scanner paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Scanned test web applications quote Table 1 lists the 12 scanners that we analyze in this study along with their corresponding version information. ### "COVScan" -> vuln-scanner paper USENIX/2025/universal-cross-app-attacks-exploiting-and-securing-oauth-2-0-in-integration-pla title Universal Cross-app Attacks: Exploiting and Securing OAuth 2.0 in Integration Platforms purpose Detect cross-app OAuth vulnerabilities through modified traffic quote To facilitate systematic discovery of vulnerabilities, we develop COVScan, a semi-automated black-box testing tool that profiles varied OAuth designs ### "Critical.IO" -> dataset paper USENIX/2014/when-governments-hack-opponents-a-look-at-actors-and-technology title When Governments Hack Opponents: A Look at Actors and Technology purpose Searched historical Internet scanning data for FinSpy indicators. quote Previous work by Guarnieri on scanning for FinSpy servers found that in response to a request such as GET /, the Bahraini FinSpy C&C server returns a response with the string "Hallo Steffi" ### "ct-sans" -> dataset paper PETS/2025/onion-location-measurements-and-fingerprinting title Onion-Location Measurements and Fingerprinting purpose Downloaded CT logs and assembled unique SAN datasets. quote we created a free and open-source tool named ct-sans that assembles a reproducible dataset of Subject Alternative Names (SANs) found via CT. ### "danglingcname" -> dns paper PETS/2026/dead-domains-living-data-a-privacy-risk-analysis-of-domain-lifecycle-in-android title Dead Domains, Living Data: A Privacy Risk Analysis of Domain Lifecycle in Android Apps purpose Detect dangling CNAME chains and unclaimed resources quote We used danglingcname [44] to identify CNAME chains that point to deallocated or unclaimed third-party resources ### "Diamond Miner" -> topology paper IMC/2020/reduce-reuse-recycle-repurposing-existing-measurements-to-identify-stale-tracero title Reduce, Reuse, Recycle: Repurposing Existing Measurements to Identify Stale Traceroutes. purpose Provide interdomain load-balancing diamonds. quote The authors of Diamond Miner provided us with 81, 581 interdomain diamonds collected in August 2019. ### "dMAP (DPI Mapper)" -> active-scan [IN ACTIVE] paper CCS/2025/fingerprinting-deep-packet-inspection-devices-by-their-ambiguities title Fingerprinting Deep Packet Inspection Devices by their Ambiguities. purpose Generate, send, analyze, and aggregate DPI fingerprinting probes. quote we design and implement dMAP (DPI Mapper), a framework that systematically discovers, selects, and deploys specialized network probes ### "DMiner" -> dns paper CCS/2023/silence-is-not-golden-disrupting-the-load-balancing-of-authoritative-dns-servers title Silence is not Golden: Disrupting the Load Balancing of Authoritative DNS Servers. purpose Measured vulnerable DNS services and load-balancing impact. quote We designed DMiner, which is a semiautomatic framework that finds affected services, and it further estimates the effect that an adversary can cause. ### "DNS crawler" -> dns paper IEEE-SP/2011/click-trajectories-end-to-end-analysis-of-the-spam-value-chain title Click Trajectories: End-to-End Analysis of the Spam Value Chain. purpose Enumerated DNS records and name-server infrastructure. quote We developed a DNS crawler to identify the name server infrastructure used to support spam-advertised domains, and the address records they specify for hosting those names. ### "DNS Scanner" -> dns paper WWW/2026/unveiling-the-resilience-of-llm-enhanced-search-engines-against-black-hat-seo-ma title Unveiling the Resilience of LLM-Enhanced Search Engines against Black-Hat SEO Manipulation. purpose Detected wildcard-DNS link farms. quote Link Farm (SSP) [6, 11, 27, 64] Hot-words DNS Scanner [11] 200 ### "DNS X-Ray Tool" -> dns paper NDSS/2019/dns-cache-based-user-tracking title dns-cache-based-user-tracking purpose Implemented and customized the authoritative DNS server quote written in Perl, based on the DNS X-Ray Tool [22]. and modified to include the specific tests needed for this research. ### "DoE-Hunter" -> dns paper PETS/2025/path-to-encrypted-dns-with-ddr-adoption-configuration-patterns-and-privacy-impli title Path to Encrypted DNS with DDR: Adoption, Configuration Patterns, and Privacy Implications purpose Conducted scalable DDR and encrypted-DNS measurements. quote the measurement tool DoE-Hunter [63], along with the corresponding research study dataset, has been made available to the public online [64]. ### "DTLS-Fuzzer" -> lab-protocol-test paper NDSS/2023/automata-based-automated-detection-of-state-machine-bugs-in-protocol-implementations title Automata-Based Automated Detection of State Machine Bugs in Protocol Implementations purpose Learn models and test DTLS implementations. quote DTLS-Fuzzer [16], the tool we use for learning models of DTLS implementations ### "DTLS-MitM-Scanner (DMS)" -> lab-protocol-test paper USENIX/2026/analyzing-the-webrtc-ecosystem-and-breaking-authentication-in-dtls-srtp title Analyzing the WebRTC Ecosystem and Breaking Authentication in DTLS-SRTP purpose Intercept and actively test DTLS-SRTP connections quote We developed an automated DTLS-SRTP testing platform, called DMS, to probe the DTLS component of any party willing to establish DTLS-SRTP/WebRTC connections. ### "EAST" -> active-scan [IN ACTIVE] paper USENIX/2021/why-tls-is-better-without-starttls-a-security-analysis-of-starttls-in-the-email title Why TLS is better without STARTTLS: A Security Analysis of STARTTLS in the Email Context purpose Generated and executed semi-automatic STARTTLS test cases. quote We introduce EAST, a semi-automatic toolkit for analyzing SMTP, POP3, and IMAP implementations, including a server for client testing, server testing scripts, and ZMap modules for Internet scanning. ### "Enemy of the State" -> vuln-scanner paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Scanned test web applications quote We augment this list with two academic scanners: Black Widow and Enemy of the State, resulting in 12 scanners. ### "ERIS" -> active-scan [IN ACTIVE] paper CCS/2025/poster-eris-evaluating-rov-via-icmpv6-rate-limiting-side-channels title Poster: ERIS: Evaluating ROV via ICMPv6 Rate Limiting Side Channels. purpose Probe IPv6 reachability and infer ROV deployment quote We implemented a prototype framework (about 4.5K lines of code) and conducted a preliminary experiment that covers 5,440 ASes. ### "exitmap" -> active-scan [IN ACTIVE] paper USENIX/2016/identifying-and-characterizing-sybils-in-the-tor-network title Identifying and Characterizing Sybils in the Tor Network purpose Scan Tor exit relays for traffic tampering quote We exposed malicious exit relays using Winter et al.'s exitmap tool [37, § 3.1]. Exitmap is a Python-based scanning framework for Tor exit relays. ### "Exitmap" -> p2p-crawler paper WWW/2018/crimebb-enabling-cybercrime-research-on-underground-forums-at-scale title CrimeBB: Enabling Cybercrime Research on Underground Forums at Scale. purpose Identified currently available Tor exit nodes. quote We first use Exitmap [45] to get current available exit nodes, from which we build Tor circuits ### "FastMDA" -> topology paper IMC/2025/remaproute-local-remapping-of-internet-path-changes title RemapRoute: Local Remapping of Internet Path Changes. purpose Perform complete-remapping comparison measurements. quote We execute both FastMDA and Scamper [29], popular traceroute tools, to perform complete remapping in order to compare across different implementations. ### "FingerprinTLS" -> passive paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Collected passive TLS fingerprints quote We incorporate the FingerprinTLS library in our system to passively collect TLS fingerprints. ### "FlashRoute" -> topology paper IMC/2020/flashroute-efficient-traceroute-on-a-massive-scale title FlashRoute: Efficient Traceroute on a Massive Scale. purpose Conduct high-speed Internet-scale traceroute scans. quote FlashRoute is written in C++ using 2.4k lines of code. It is compatible with a variety of Unix-based operating systems. ### "forwarders-based scanner" -> active-scan [IN ACTIVE] paper IEEE-SP/2022/deployment-of-source-address-validation-by-network-operators-a-randomized-contro title Deployment of Source Address Validation by Network Operators: A Randomized Control Trial. purpose Detect SAV failures via misbehaving DNS forwarders quote We performed Internet-wide forwarders-based scans of IPv4 space weekly between September 2020 and February 2021 ### "gateway checker" -> active-scan [IN ACTIVE] paper USENIX/2024/guardians-of-the-galaxy-content-moderation-in-the-interplanetary-file-system title Guardians of the Galaxy: Content Moderation in the InterPlanetary File System purpose Probed gateway accessibility for denylisted CIDs. quote We then implement a gateway checker that verifies the accessibility of all the denylist CIDs from every gateway on the gateway list. ### "Google Tsunami" -> vuln-scanner paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Scanned test web applications quote Table 1 lists the 12 scanners that we analyze in this study along with their corresponding version information. ### "gQUIC-module" -> grabber [IN ACTIVE] paper PETS/2019/a-quic-look-at-web-tracking title A QUIC Look at Web Tracking purpose Sent QUIC client hello probes during the Internet scan. quote Thus, we used the tool Zmap [8] with the gQUIC-module [29] to send a client hello message to UDP port 443 of each Alexa-listed host. ### "h2spec" -> lab-protocol-test paper CCS/2024/poster-kill-krill-or-proxy-rpki title Poster: Kill Krill or Proxy RPKI. purpose Testing HTTP/2 continuation-frame flooding quote We used the open-source tool h2spec to test Krill and Routinator and verify the presence of this vulnerability. ### "HP WebInspect" -> vuln-scanner paper IEEE-SP/2010/state-of-the-art-automated-black-box-web-application-vulnerability-testing title State of the Art: Automated Black-Box Web Application Vulnerability Testing. purpose Scan test applications for web vulnerabilities. quote The eight scanners are listed in Table I. ### "HTTP/OneProbe" -> topology paper IMC/2010/measurement-of-loss-pairs-in-network-paths title Measurement of loss pairs in network paths. purpose Probe Internet paths and identify loss-pair events. quote We conducted loss-pair measurement using HTTP/OneProbe (an OneProbe implementation based on HTTP/1.1 [19]) for 88 round-trip paths ### "IBM Rational AppScan" -> vuln-scanner paper IEEE-SP/2010/state-of-the-art-automated-black-box-web-application-vulnerability-testing title State of the Art: Automated Black-Box Web Application Vulnerability Testing. purpose Scan test applications for web vulnerabilities. quote The eight scanners are listed in Table I. ### "ibportstate" -> local-link paper CCS/2022/nevermore-exploiting-rdma-mistakes-in-nvme-of-storage-applications title NeVerMore: Exploiting RDMA Mistakes in NVMe-oF Storage Applications. purpose Change an InfiniBand port local identifier quote we found out that the ibportstate tool from the infinibanddiags package allows privileged users to change the configuration of the RNIC ### "ICMP Paris" -> topology paper IMC/2011/on-the-prevalence-and-characteristics-of-mpls-deployments-in-the-open-internet title On the prevalence and characteristics of MPLS deployments in the open internet. purpose Specified the traceroute probe method. quote The probing tool used in Ark is Scamper [26], and the specific traceroute method used is ICMP Paris [28]. ### "ICMP_Sonar" -> active-scan [IN ACTIVE] paper IMC/2024/rumors-stop-with-the-wise-unveiling-inbound-sav-deployment-through-spoofed-icmp title Rumors Stop with the Wise: Unveiling Inbound SAV Deployment through Spoofed ICMP Messages. purpose Probe hosts using spoofed ICMP messages quote This study introduces two novel approaches implemented in ICMP_Sonar, a tool using meticulously crafted spoofed ICMP messages to detect the presence and efficacy of ISAV. ### "iffinder" -> topology paper IMC/2018/pushing-the-boundaries-with-bdrmapit-mapping-router-ownership-at-internet-scale title Pushing the Boundaries with bdrmapIT: Mapping Router Ownership at Internet Scale. purpose Provide IP alias-resolution data for inferred routers. quote All the traceroutes used ICMP Paris traceroute [15,39], which controls the IP and transport headers to reduce load balancing, and a combination of iffinder [21] and MIDAR [22] for alias resolution. paper IMC/2025/replication-characterizing-mpls-tunnels-over-internet-paths title Replication: Characterizing MPLS Tunnels over Internet Paths. purpose Resolving aliases in the ITDK topology graph quote we mapped each IP address to an inferred router using the MIDAR, iffinder, and SNMPv3 alias resolution graph included in the March 2025 ITDK ### "iGreedy" -> topology paper IMC/2020/manycast2-using-anycast-to-measure-anycast title MAnycast2: Using Anycast to Measure Anycast. purpose Compared MAnycast2 classifications using latency-based measurements. quote To compare our results with those obtained by iGreedy (Table 3), we ran iGreedy against a set of target addresses. ### "ike-scan" -> grabber [IN ACTIVE] paper USENIX/2023/back-to-school-on-the-in-security-of-academic-vpns title Back to School: On the (In)Security of Academic VPNs purpose Probe IKE back-ends for aggressive mode. quote we probed the IKE back-ends discovered in Section 3.2 using the open source tool ike-scan ### "Internet Census" -> dataset paper USENIX/2014/when-governments-hack-opponents-a-look-at-actors-and-technology title When Governments Hack Opponents: A Look at Actors and Technology purpose Searched Internet-wide service-probe and certificate-scan data. quote We searched sources including Shodan, Internet Census service probes, and Critical.IO scanning data for the observed distinctive HTTP behavior. ### "iperf" -> perf paper IMC/2010/cloudcmp-comparing-public-cloud-providers title CloudCmp: comparing public cloud providers. purpose Measure network throughput quote We use standard tools such as iperf [8] and ping to measure the network throughput and path latency of a provider. ### "iPerf3" -> perf paper IEEE-SP/2024/dnsbomb-a-new-practical-and-powerful-pulsing-dos-attack-exploiting-dns-queries-a title DNSBomb: A New Practical-and-Powerful Pulsing DoS Attack Exploiting DNS Queries-and-Responses. purpose Measure maximum achievable network bandwidth. quote To test network bandwidth occupation, we employ the iPerf3 tool [45] that is designed for active measurements of the maximum achievable bandwidth. ### "IPFS Crawler" -> p2p-crawler paper IMC/2023/the-cloud-strikes-back-investigating-the-decentralization-of-ipfs title The Cloud Strikes Back: Investigating the Decentralization of IPFS. purpose Enumerated outgoing DHT connections and network topology. quote Using the DHT crawler presented in [32, 79], we can enumerate all outgoing DHT connections of functional DHT server nodes. ### "IRBlock" -> censorship-probe [IN ACTIVE] paper USENIX/2025/irblock-a-large-scale-measurement-study-of-the-great-firewall-of-iran title IRBlock: A Large-Scale Measurement Study of the Great Firewall of Iran purpose Scan Iranian IPs and domains for censorship quote we developed IRBlock, a large-scale, multi-protocol measurement system designed to detect DNS poisoning, HTTP blockpage injection, and UDP-based filtering in Iran. ### "IRLscanner" -> active-scan [IN ACTIVE] paper IMC/2010/demystifying-service-discovery-implementing-an-internet-wide-scanner title Demystifying service discovery: implementing an internet-wide scanner. purpose Conduct Internet-wide service-discovery scans quote Using IRLscanner and 24-hour scans, we perform 21 Internet-wide experiments using 6 different protocols ### "IRTT" -> perf paper IMC/2025/from-geo-to-leo-first-look-into-starlink-in-flight-connectivity title From GEO to LEO: First Look Into Starlink In-Flight Connectivity. purpose Performed 10 ms-granularity UDP latency measurements. quote This allows MEs to conduct high-frequency (10 ms granularity) UDP ping measurements via IRTT [19] to the closest AWS server ### "Iterative Network Tracing (INT)" -> topology paper IMC/2018/where-the-light-gets-in-analyzing-web-censorship-mechanisms-in-india title Where The Light Gets In: Analyzing Web Censorship Mechanisms in India. purpose Located censorship middleboxes along router-level paths. quote We devised a technique which we collectively call Iterative Network Tracing (INT), that works on the principle used by traceroute. ### "James" -> topology paper IMC/2022/measuring-ipv6-extension-headers-survivability-with-james title Measuring IPv6 extension headers survivability with James. purpose Probe IPv6 extension-header survivability across Internet paths. quote This extended abstract introduces James, a new tool for measuring how IPv6 Extension Headers (IPv6 EHs) are processed in the network. ### "kapar" -> topology paper IMC/2018/pushing-the-boundaries-with-bdrmapit-mapping-router-ownership-at-internet-scale title Pushing the Boundaries with bdrmapIT: Mapping Router Ownership at Internet Scale. purpose Compare alternative alias-resolution groupings. quote Specifically, we use kapar [20] along with midar and iffinder, while the results presented in §7.2 use only the midar and iffinder techniques ### "Karma" -> active-scan [IN ACTIVE] paper USENIX/2025/am-i-infected-lessons-from-operating-a-large-scale-iot-security-diagnostic-servi title Am I Infected? Lessons from Operating a Large-Scale IoT Security Diagnostic Service purpose Scanning IoT IP addresses for vulnerabilities and device models. quote Our service utilizes the IoT scan engine Karma [9], developed by 00One, Inc. The Karma's scan engine was deployed on our university's network. paper IEEE-SP/2024/who-left-the-door-open-investigating-the-causes-of-exposed-iot-devices-in-an-aca title Who Left the Door Open? Investigating the Causes of Exposed IoT Devices in an Academic Network. purpose Identify hosts exposing Telnet, FTP, and IoT risks. quote We used a service named Karma [10] to identify hosts running Telnet (23/TCP) and/or FTP (21/TCP). ### "l2ping" -> local-link paper CCS/2024/blueswat-a-lightweight-state-aware-security-framework-for-bluetooth-low-energy title BlueSWAT: A Lightweight State-Aware Security Framework for Bluetooth Low Energy. purpose Sent random L2CAP packets for RTT evaluation. quote We further use l2ping [58] on Linux to perform end-to-end evaluation on BlueSWAT. ### "LACeS" -> topology paper IMC/2025/laces-an-open-fast-responsible-and-efficient-longitudinal-anycast-census-system title LACeS: An Open, Fast, Responsible and Efficient Longitudinal Anycast Census System. purpose Run distributed anycast and latency-based Internet censuses. quote We built LACeS, a new system that combines the MAnycast2 anycast measurement approach ... and the Great Circle Distance (GCD) approach ### "LanDscAPe" -> active-scan [IN ACTIVE] paper USENIX/2024/landscape-exploring-ldap-weaknesses-and-data-leaks-at-internet-scale title LanDscAPe: Exploring LDAP Weaknesses and Data Leaks at Internet Scale purpose Scan, sample, and analyze Internet-facing LDAP servers. quote For our study, we built a custom tool, LanDscAPe, that identifies public LDAP servers on the Internet, assesses their configuration, including TLS setup, and samples the hosted directories. ### "LFP" -> grabber [IN ACTIVE] paper IMC/2023/illuminating-router-vendor-diversity-within-providers-and-along-network-paths title Illuminating Router Vendor Diversity Within Providers and Along Network Paths. purpose Fingerprint router vendors using lightweight active probes. quote Lightweight FingerPrinting (LFP), a novel lightweight, accurate, and more complete remote vendor fingerprinting methodology and tool ### "LTE PROBE" -> local-link paper USENIX/2022/ltrack-stealthy-tracking-of-mobile-phones-in-lte title LTrack: Stealthy Tracking of Mobile Phones in LTE purpose Passively sniff uplink and downlink LTE traffic quote We implement the first white-box uplink and downlink LTE sniffer, called LTE PROBE. ### "Lumscan" -> censorship-probe [IN ACTIVE] paper IMC/2018/403-forbidden-a-global-view-of-cdn-geoblocking title 403 Forbidden: A Global View of CDN Geoblocking. purpose Probe websites through residential proxy exits. quote We implemented a new probing tool, Lumscan, that greatly improves the reliability of the data. ### "MacStealer" -> local-link paper NDSS/2024/exploiting-sequence-number-leakage-tcp-hijacking-in-nat-enabled-wi-fi-networks title Exploiting Sequence Number Leakage: TCP Hijacking in NAT-Enabled Wi-Fi Networks purpose Tested Wi-Fi client isolation. quote the attacker detects whether AP isolation is enabled via network scanning tools (e.g., Nmap [36], MacStealer [32]). ### "MAnycast anycast census" -> topology paper NDSS/2026/on-borrowed-time-measurement-informed-understanding-of-the-ntp-pools-robustness-to-monopoly-attacks title On Borrowed Time: Measurement-Informed Understanding of the NTP Pool’s Robustness to Monopoly Attacks purpose Identify registered IP anycast servers. quote the remaining six servers are all IP anycast, as determined by looking up their addresses in the MAnycast anycast census [40]. ### "MAnycast2" -> topology paper IMC/2024/of-choices-and-control-a-comparative-analysis-of-government-hosting title Of Choices and Control - A Comparative Analysis of Government Hosting. purpose Identified anycast server addresses. quote To determine if a server address is anycast, we rely on a recent data snapshot from MAnycast2 ### "Mao et al. AS-path inference algorithm" -> routing paper PETS/2018/tempest-temporal-dynamics-in-anonymity-systems title Tempest: Temporal Dynamics in Anonymity Systems purpose Inferred shortest valley-free AS paths. quote We infer AS paths using the algorithm proposed by Mao et al. [43], which searches for shortest valley-free paths ### "MAP-IT" -> routing paper IMC/2020/reduce-reuse-recycle-repurposing-existing-measurements-to-identify-stale-tracero title Reduce, Reuse, Recycle: Repurposing Existing Measurements to Identify Stale Traceroutes. purpose Infer AS interconnections when bdrmapIT cannot apply. quote Otherwise we use MAP-IT [52]. ### "MarcoPolo" -> routing paper IMC/2025/a-framework-to-evaluate-mpic-security-using-real-world-bgp-announcements title A Framework to Evaluate MPIC Security using Real-World BGP Announcements. purpose Launch BGP hijacks and collect MPIC perspective outcomes. quote We design and deploy a system that evaluates the effectiveness of MPIC in the real world using ethically-launched real-world BGP attacks. ### "McAfee SECURE" -> vuln-scanner paper IEEE-SP/2010/state-of-the-art-automated-black-box-web-application-vulnerability-testing title State of the Art: Automated Black-Box Web Application Vulnerability Testing. purpose Remotely scan test applications for vulnerabilities. quote We ran two of the tested scanners, McAfee and Qualys, as remote services whereby the user configures the scanner via a web-interface. ### "Mercator" -> topology paper IMC/2016/bdrmap-inference-of-borders-between-ip-networks title bdrmap: Inference of Borders Between IP Networks. purpose Resolved aliases from ICMP port-unreachable responses. quote Mercator [15] infers two IP addresses are aliases if the source address of ICMP port unreachable responses is the same. paper IMC/2019/learning-regexes-to-extract-router-names-from-hostnames title Learning Regexes to Extract Router Names from Hostnames. purpose Provided aliases and performed follow-up probing. quote For the interfaces in the good class that were responsive to alias resolution at the time of our probing in May 2019, we used Ally [27] with ICMP, UDP, and TCP probes, and Mercator [8]. ### "Mint" -> censorship-probe [IN ACTIVE] paper IEEE-SP/2025/is-nobody-there-good-globally-measuring-connection-tampering-without-responsive title Is Nobody There? Good! Globally Measuring Connection Tampering Without Responsive Endhosts. purpose Automated packet-sequence interference measurement quote We present a system, Mint (Measuring Interference with Nonresponsive Targets), that generalizes this approach to the global IPv4 and IPv6 Internet. ### "modified ntpdedup" -> grabber [IN ACTIVE] paper NDSS/2026/on-borrowed-time-measurement-informed-understanding-of-the-ntp-pools-robustness-to-monopoly-attacks title On Borrowed Time: Measurement-Informed Understanding of the NTP Pool’s Robustness to Monopoly Attacks purpose Fingerprint NTP responses and identify aliased servers. quote we implement active server fingerprinting starting with the open-source "ntpdedup" code. (We forked this codebase...) ### "Monocle" -> censorship-probe [IN ACTIVE] paper CCS/2024/understanding-routing-induced-censorship-changes-globally title Understanding Routing-Induced Censorship Changes Globally. purpose Route-stable censorship measurements and application-level traceroutes quote We develop Monocle (Figure 1) to explore 3 commonly censored protocols: DNS, HTTP, and HTTPS. ### "mrinfo" -> topology paper IMC/2010/on-the-impact-of-layer-2-on-node-degree-distribution title On the impact of layer-2 on node degree distribution. purpose Discover multicast-enabled router interfaces and neighbors. quote mrinfo is a tool that silently discovers all multicast enabled links belonging to an IPv4 router. paper IMC/2010/resolving-ip-aliases-with-prespecified-timestamps title Resolving IP aliases with prespecified timestamps. purpose Generated known-alias ground truth. quote we use a dataset of known aliases generated by mrinfo measurements on December 30th, 2009 ### "mrinfo-rec" -> topology paper IMC/2010/on-the-impact-of-layer-2-on-node-degree-distribution title On the impact of layer-2 on node degree distribution. purpose Recursively probe discovered router addresses each day. quote Our approach in probing the network with mrinfo is recursive and we call such a probing scheme mrinfo-rec. ### "N-Stalker QA Edition" -> vuln-scanner paper IEEE-SP/2010/state-of-the-art-automated-black-box-web-application-vulnerability-testing title State of the Art: Automated Black-Box Web Application Vulnerability Testing. purpose Scan test applications for web vulnerabilities. quote The eight scanners are listed in Table I. ### "Namehelp" -> vantage-platform paper IMC/2015/in-and-out-of-cuba-characterizing-cubas-connectivity title In and Out of Cuba: Characterizing Cuba's Connectivity. purpose Run hourly traceroutes from globally distributed endhosts. quote For this dataset, Namehelp clients ran hourly traceroute measurements to prefixes located in Cuba. ### "nc" -> active-scan [IN ACTIVE] paper CCS/2019/security-certification-in-payment-card-industry-testbeds-measurements-and-recomm title Security Certification in Payment Card Industry: Testbeds, Measurements, and Recommendations. purpose Probed database and SSH ports. quote We use basic Linux tools (e.g., nc, openssl) and Java net URL APIs to implement the system. ### "ndisc6" -> local-link paper CCS/2025/exploring-and-analyzing-cross-layer-dos-attack-against-udp-based-services-on-lin title Exploring and Analyzing Cross Layer DoS Attack Against UDP-based Services on Linux. purpose Probe IPv6 neighbor-discovery behavior in cloud VPS networks. quote conducted ARP and NDP test scans on IPv4 and IPv6 networks using arpscan and ndisc6 tools, respectively. ### "Netcat" -> active-scan [IN ACTIVE] paper USENIX/2025/beyond-exploit-scanning-a-functional-change-driven-approach-to-remote-software-v title Beyond Exploit Scanning: A Functional Change-Driven Approach to Remote Software Version Identification purpose Checked active servers and open ports. quote Then we used the Netcat [28] to scan open ports and filter out inaccessible or offline servers. ### "NetfilterQueue" -> packet-crafting paper CCS/2019/geneva-evolving-censorship-evasion-strategies title Geneva: Evolving Censorship Evasion Strategies. purpose Interpose on and alter client packets. quote Geneva runs strictly at the client, and uses NetfilterQueue [30] to interpose on (and possibly alter) all of the client's outbound and inbound packets. ### "netperf" -> perf paper IMC/2017/tcp-congestion-signatures title TCP congestion signatures. purpose Run controlled and baseline throughput tests. quote We use netperf to run 10-second downstream throughput tests from Server 1 to Pi 1. ### "netstat" -> host-tool paper CCS/2021/android-on-pc-on-the-security-of-end-user-android-emulators title Android on PC: On the Security of End-user Android Emulators. purpose Monitored Android network usage and channels. quote We associated the channels (TCP/UDP and virtual devices) with the emulator components through dynamically monitoring the network and resource usage (e.g., through "netstat", "lsof", "dumpsys" commands on Android). paper IEEE-SP/2025/housefuzz-service-aware-grey-box-fuzzing-for-vulnerability-detection-in-linux-ba title HouseFuzz: Service-Aware Grey-Box Fuzzing for Vulnerability Detection in Linux-Based Firmware. purpose Identified network-facing processes for FirmAE comparison. quote To compare with FirmAE, we ran the netstat [39] tool to identify network-facing processes during emulation. ### "Netstat" -> host-tool paper PETS/2025/privacy-settings-of-third-party-libraries-in-android-apps-a-study-of-facebook-sd title Privacy Settings of Third-Party Libraries in Android Apps: A Study of Facebook SDKs purpose Identify open device ports during traffic monitoring. quote We use Netstat to identify open ports on the mobile device, ensuring that connections are exclusively made by the app under analysis. ### "Network Analyzer" -> topology paper USENIX/2025/esimplicity-or-esimplification-privacy-and-security-risks-in-the-esim-ecosystem title eSIMplicity or eSIMplification? Privacy and Security Risks in the eSIM Ecosystem purpose Ran traceroute measurements on iOS devices. quote In the case of iOS, we use the applications Net Analyzer and Ping Traceroute. ### "NextTrace" -> topology paper CCS/2025/time-tells-all-deanonymization-of-blockchain-rpc-users-with-zero-transaction-fee title Time Tells All: Deanonymization of Blockchain RPC Users with Zero Transaction Fee. purpose Identify realistic geographic routing paths for relay placement. quote We artificially deploy software routers on cloud servers at various geographic locations along real-world routes identified using traceroute-like tools (e.g., NextTrace). ### "node probing program" -> active-scan [IN ACTIVE] paper NDSS/2025/eclipse-attacks-on-moneros-peer-to-peer-network title Eclipse Attacks on Monero’s Peer-to-Peer Network purpose Identified Monero mainnet nodes with open RPC ports. quote We utilized a node probing program to identify Monero mainnet nodes with open RPC ports. ### "NodeFinder" -> p2p-crawler paper IMC/2018/measuring-ethereum-network-peers title Measuring Ethereum Network Peers. purpose Discover, monitor, and characterize Ethereum DEVp2p nodes. quote We then built NodeFinder, an open-source and ethical measurement tool for characterizing the three protocols that constitute Ethereum's P2P network ### "NR-Scope" -> local-link paper IMC/2025/automated-cross-layer-root-cause-analysis-of-5g-video-conferencing-quality-degra title Automated, Cross-Layer Root Cause Analysis of 5G Video-Conferencing Quality Degradation. purpose Collected sub-millisecond 5G PHY/MAC telemetry and retransmissions. quote We use NR-Scope [33] to gather measurements from the 5G PHY/MAC layers in sub-millisecond resolution, including traffic-scheduling information and retransmission events. ### "ns-3" -> simulation paper IMC/2023/localizing-traffic-differentiation title Localizing Traffic Differentiation. purpose Simulate rate-limiter and network scenarios. quote We use our prototype to answer the former and ns-3 simulation [32] for the latter. ### "Nuclei" -> vuln-scanner paper USENIX/2025/trust-but-verify-an-assessment-of-vulnerability-tagging-services title Trust but Verify: An Assessment of Vulnerability Tagging Services purpose Independently probed endpoints using CVE templates. quote We use the Nuclei toolchain [31] to conduct payload-based detections. ### "nuttcp" -> perf paper IMC/2023/performance-of-cellular-networks-on-the-wheels title Performance of Cellular Networks on the Wheels. purpose Generated single-connection TCP throughput traffic. quote To measure throughput, we used nuttcp [10] with the default TCP congestion control algorithm, CUBIC, to generate downlink and uplink backlogged traffic ### "OneProbe" -> topology paper IMC/2010/measurement-of-loss-pairs-in-network-paths title Measurement of loss pairs in network paths. purpose Send TCP probe pairs and infer delivery statuses. quote OneProbe sends a sequence of probe pairs, each consisting of two TCP data packets, to a remote server. ### "onesixtyone" -> grabber [IN ACTIVE] paper CCS/2025/forward-to-hell-on-the-potentials-of-misusing-transparent-dns-forwarders-in-refl title Forward to Hell? On the Potentials of Misusing Transparent DNS Forwarders in Reflective Amplification Attacks. purpose Retrieve device information through SNMP scanning. quote We use ZGrab [6] and a fast SNMP scanner [31] to retrieve systems information about these transparent forwarders. ### "onion-grab" -> grabber [IN ACTIVE] paper PETS/2025/onion-location-measurements-and-fingerprinting title Onion-Location Measurements and Fingerprinting purpose Scanned HTTPS domains for Onion-Location configuration. quote We created a free and open-source tool named onion-grab to determine which HTTPS sites configure O-L. ### "OSAVRoute" -> active-scan [IN ACTIVE] paper NDSS/2026/osavroute-advancing-outbound-source-address-validation-deployment-detection-with-non-cooperative-measurement title OSAVRoute: Advancing Outbound Source Address Validation Deployment Detection with Non-Cooperative Measurement purpose Discover transparent forwarders and measure OSAV deployment characteristics. quote We present OSAVRoute, the first non-cooperative measurement system capable of capturing fine-grained characteristics of OSAV deployment. ### "PciCheckerLite" -> vuln-scanner paper CCS/2019/security-certification-in-payment-card-industry-testbeds-measurements-and-recomm title Security Certification in Payment Card Industry: Testbeds, Measurements, and Recommendations. purpose Performed low-interaction PCI vulnerability scans. quote Using PciCheckerLite, we evaluated 1,203 e-commerce website across various business categories. ### "Perf" -> host-tool paper IEEE-SP/2019/sok-the-challenges-pitfalls-and-perils-of-using-hardware-performance-counters-fo title SoK: The Challenges, Pitfalls, and Perils of Using Hardware Performance Counters for Security. purpose Obtain hardware performance-counter measurements. quote We used the Perf tool v4.4.90 to obtain performance counter measurements. ### "PLCScan" -> grabber [IN ACTIVE] paper CCS/2020/honeyplc-a-next-generation-honeypot-for-industrial-control-systems title HoneyPLC: A Next-Generation Honeypot for Industrial Control Systems. purpose Scanned simulated PLCs for device information. quote Next, we used PLCScan to scan each PLC Profile in similar fashion as the Nmap methodology. ### "Prefixscan" -> topology paper IMC/2016/bdrmap-inference-of-borders-between-ip-networks title bdrmap: Inference of Borders Between IP Networks. purpose Confirmed inbound interfaces and point-to-point links. quote We use the prefixscan algorithm [26] to try to confirm that we observe the inbound interface of a router in traceroute, rather than a third-party address. ### "Python script" -> active-scan [IN ACTIVE] paper IMC/2019/a-look-at-the-ecs-behavior-of-dns-resolvers title A Look at the ECS Behavior of DNS Resolvers. purpose Scan IPv4 space with DNS queries quote The scan was conducted from a machine on our campus network using a Python script that issued 25K DNS queries per second ### "QScanner" -> grabber [IN ACTIVE] paper IMC/2022/towards-a-tectonic-traffic-shift-investigating-apples-new-relay-network title Towards a tectonic traffic shift?: investigating Apple's new relay network. purpose Test standard QUIC handshakes quote testing standard QUIC handshakes using the QScanner published by Zirngibl et al. [39] or a current curl version does not even trigger a response by ingress nodes paper IMC/2024/reacked-quicer-measuring-the-performance-of-instant-acknowledgments-in-quic-hand title ReACKed QUICer: Measuring the Performance of Instant Acknowledgments in QUIC Handshakes. purpose Performed QUIC handshakes and HTTP/3 HEAD requests. quote We perform QUIC handshakes and HTTP/3 HEAD requests using QScanner [30]. ### "QualysGuard PCI" -> vuln-scanner paper IEEE-SP/2010/state-of-the-art-automated-black-box-web-application-vulnerability-testing title State of the Art: Automated Black-Box Web Application Vulnerability Testing. purpose Remotely scan test applications for vulnerabilities. quote We ran two of the tested scanners, McAfee and Qualys, as remote services whereby the user configures the scanner via a web-interface. ### "quicgrabber" -> grabber [IN ACTIVE] paper PETS/2019/a-quic-look-at-web-tracking title A QUIC Look at Web Tracking purpose Established QUIC connections to identified websites. quote To analyze the default configuration of the identified 186 QUIC-enabled websites, we used the tool quicgrabber [29] to establish QUIC connections to them. ### "RAINBOW" -> p2p-crawler paper USENIX/2025/deanonymizing-ethereum-validators-the-p2p-network-has-a-privacy-issue title Deanonymizing Ethereum Validators: The P2P Network Has a Privacy Issue purpose Logs attestations, subscriptions, peers, and connection data. quote We call our modified consensus node RAINBOW throughout, as RAINBOW acts as a Prysm, taking peer input and breaking it down into the “colors” of the validators. ### "Rapid7 NeXpose" -> vuln-scanner paper IEEE-SP/2010/state-of-the-art-automated-black-box-web-application-vulnerability-testing title State of the Art: Automated Black-Box Web Application Vulnerability Testing. purpose Scan test applications for web vulnerabilities. quote The eight scanners are listed in Table I. ### "RAT-Scan" -> active-scan [IN ACTIVE] paper USENIX/2018/schrodinger-s-rat-profiling-the-stakeholders-in-the-remote-access-trojan-ecosyst title Schrödinger’s RAT: Profiling the Stakeholders in the Remote Access Trojan Ecosystem purpose Scanned IPv4 addresses for RAT controllers. quote we also created RAT-Scan, which is able to differentiate some sinkholes, including our high fidelity RAT-Hole, from actual RAT controllers ### "RDScan" -> dns paper USENIX/2021/can-i-take-your-subdomain-exploring-same-site-attacks-in-the-modern-web title Can I Take Your Subdomain? Exploring Same-Site Attacks in the Modern Web purpose Detected dangling DNS records and takeover preconditions. quote This component, that we call RDScan, has three different modules that test for the presence of the vulnerable scenarios described in §3.2.1. ### "revtr 2.0" -> topology paper IMC/2022/internet-scale-reverse-traceroute title Internet scale reverse traceroute. purpose Measure reverse Internet paths at scale. quote In this paper we design, implement and evaluate revtr 2.0, an Internet-scale Reverse Traceroute system ### "RMap" -> dns paper IMC/2025/poster-rmap-uncovering-risky-dns-resolution-chains-and-misconfigurations title Poster: RMap: Uncovering Risky DNS Resolution Chains and Misconfigurations. purpose Probe DNS resolution chains and identify configuration risks. quote We present RMap, a tool that rapidly probes all potential resolution chains of a domain, reveals its resolution dependency topology, and detects security risks. ### "SAD DNS" -> dns paper USENIX/2023/the-maginot-line-attacking-the-boundary-of-dns-caching-protection title The Maginot Line: Attacking the Boundary of DNS Caching Protection purpose Scanned ephemeral UDP ports during off-path attacks. quote We solve this challenge by adjusting SAD DNS to attack BIND. ### "SadDNS" -> dns paper USENIX/2021/the-hijackers-guide-to-the-galaxy-off-path-taking-over-internet-resources title The Hijackers Guide To The Galaxy: Off-Path Taking Over Internet Resources purpose Infer resolver source ports through ICMP side channels. quote We use our own implementation of the SadDNS port scanning application with binary search and attempt to poison the resolver with a malicious record. ### "Scapy" -> packet-crafting paper USENIX/2018/the-dangers-of-key-reuse-practical-attacks-on-ipsec-ike title The Dangers of Key Reuse: Practical Attacks on IPsec IKE purpose Construct and control IKE network packets. quote Our test initiator is based on Scapy [8], a Python library for network packet manipulation. paper NDSS/2019/nearby-threats-reversing-analyzing-and-attacking-googles-nearby-connections-on-android title nearby-threats-reversing-analyzing-and-attacking-googles-nearby-connections-on-android purpose Decoded, crafted, and manipulated protocol packets quote We analyze the packets using scapy [7], a network analysis tool. paper NDSS/2025/redan-an-empirical-study-on-remote-dos-attacks-against-nat-networks title ReDAN: An Empirical Study on Remote DoS Attacks against NAT Networks purpose Crafted and transmitted attack and probing packets. quote an attack machine equipped with Linux 5.19.0 and Scapy, with the capability of IP spoofing ### "SecSpider" -> dataset paper WWW/2021/security-of-alerting-authorities-in-the-www-measuring-namespaces-dnssec-and-web title Security of Alerting Authorities in the WWW: Measuring Namespaces, DNSSEC, and Web PKI. purpose Provide historical DNSSEC penetration statistics. quote our partial historical DNSSEC penetration statistics (collected through SecSpider4 [67]) ### "Shadow" -> simulation paper NDSS/2024/mirageflow-a-new-bandwidth-inflation-attack-on-tor title MirageFlow: A New Bandwidth Inflation Attack on Tor purpose Simulated Tor circuits using injected consensus files. quote we simulated Tor with Shadow [15] based on the injected consensus files and server descriptors. ### "ShaperProbe" -> perf paper IMC/2011/shaperprobe-end-to-end-detection-of-isp-traffic-shaping-using-active-methods title ShaperProbe: end-to-end detection of ISP traffic shaping using active methods. purpose Detect shaping and estimate capacity, rate, and burst size. quote We develop an active end-to-end detection mechanism, referred to as ShaperProbe, that can infer whether a particular path is subject to traffic shaping. ### "Skipfish" -> vuln-scanner paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Scanned test web applications quote Table 1 lists the 12 scanners that we analyze in this study along with their corresponding version information. ### "smbmap" -> grabber [IN ACTIVE] paper IEEE-SP/2025/beyond-the-horizon-uncovering-hosts-and-services-behind-misconfigured-firewalls title Beyond the Horizon: Uncovering Hosts and Services Behind Misconfigured Firewalls. purpose Enumerated anonymous SMB sessions and unprotected shares. quote We customize it to add support for specifying source ports and enhance its performance and stability. ### "SNMPv3" -> topology paper IMC/2025/replication-characterizing-mpls-tunnels-over-internet-paths title Replication: Characterizing MPLS Tunnels over Internet Paths. purpose Obtaining self-reported router vendors quote We were able to identify the router vendor for 95,013 of these addresses. We identified vendors for 49,390 routers by the routers disclosing their own manufacturer in the SNMPv3 probe response. ### "Speedtrap" -> topology paper IMC/2019/learning-regexes-to-extract-router-names-from-hostnames title Learning Regexes to Extract Router Names from Hostnames. purpose Supplied IPv6 topology datasets for evaluation. quote Two ITDK datasets (August 2017 and January 2019) contain router-level graphs inferred using Speedtrap [16]. ### "Spoki" -> passive paper USENIX/2022/spoki-unveiling-a-new-wave-of-scanners-through-a-reactive-network-telescope title Spoki: Unveiling a New Wave of Scanners through a Reactive Network Telescope purpose Reactive telescope probing and scanner-event collection quote In this work, we examine Internet-wide scan traffic through Spoki, a reactive network telescope operating in real-time that we design and implement. ### "Spoofer project" -> vantage-platform paper CCS/2019/network-hygiene-incentives-and-regulation-deployment-of-source-address-validatio title Network Hygiene, Incentives, and Regulation: Deployment of Source Address Validation in the Internet. purpose Conducted automated source-address-validation tests from volunteer networks. quote The data from the Spoofer project comes from volunteers who run the Spoofer client, which sends and receives a variety of spoofed packets. ### "SSFNet" -> simulation paper IEEE-SP/2019/on-the-feasibility-of-rerouting-based-ddos-defenses title On the Feasibility of Rerouting-Based DDoS Defenses. purpose Simulated fine-grained BGP convergence and path hunting. quote we use SSFNet [13] and add BGP poisoning and hole punching capabilities to it, instead of using the Chaos simulator ### "Swordqiu" -> routing paper PETS/2016/denasa-destination-naive-as-awareness-in-anonymous-communications title DeNASA: Destination-Naive AS-Awareness in Anonymous Communications purpose Inferred AS-level paths for simulated Tor streams. quote After all streams are generated, we use them as input to Swordqiu, the Qiu and Gao AS-level path inference tool. ### "tcp_probe" -> passive paper IMC/2012/confused-timid-and-unstable-picking-a-video-streaming-rate-is-hard title Confused, timid, and unstable: picking a video streaming rate is hard. purpose Logged TCP congestion-window values. quote we instead use our local proxy to serve both the video traffic and the competing flow, and use the tcp_probe kernel module to log the cwnd values. paper IMC/2017/measurement-based-practical-techniques-to-improve-802-11ac-performance title Measurement-based, practical techniques to improve 802.11ac performance. purpose Observe the sender TCP congestion window. quote We use tcp_probe [5] to observe the sender's TCP congestion window cwnd. ### "ThunderPing" -> vantage-platform paper IMC/2011/pingin-in-the-rain title Pingin' in the rain. purpose Collected distributed ICMP reachability samples during weather alerts. quote We developed ThunderPing to test our hypothesis that weather affects the performance of residential Internet connections, measured by connectivity, loss rate, and latency. ### "Time Series Latency Probing (TSLP)" -> topology paper IMC/2017/tcp-congestion-signatures title TCP congestion signatures. purpose Identify periodically congested interdomain links. quote we use the dataset resulting from our deployment of the TSLP [35] tool on the Archipelago (Ark) [19] infrastructure to identify interdomain links ### "TLS-Attacker" -> lab-protocol-test paper IEEE-SP/2019/postcards-from-the-post-http-world-amplification-of-https-vulnerabilities-in-the title Postcards from the Post-HTTP World: Amplification of HTTPS Vulnerabilities in the Web Ecosystem. purpose Tested TLS vulnerabilities and padding oracles. quote The analysis tools include testssl.sh, TLS-Attacker and the nmap plugin for Special DROWN ### "TMC" -> censorship-probe [IN ACTIVE] paper WWW/2023/measuring-and-evading-turkmenistans-internet-censorship-a-case-study-in-large-sc title Measuring and Evading Turkmenistan's Internet Censorship: A Case Study in Large-Scale Measurements of a Low-Penetration Country. purpose Probe Turkmenistan’s censorship across protocols quote We present the design and implementation of TMC, a large-scale measurement system capable of testing millions of domains from outside a censoring nation-state ### "Tor DNSEL" -> dataset paper USENIX/2017/characterizing-the-nature-and-dynamics-of-tor-exit-blocking title Characterizing the Nature and Dynamics of Tor Exit Blocking purpose Obtain relay IPs used for exiting traffic. quote Tor DNSEL gathers the IP address used by a relay for exiting traffic based on active testing. ### "TorFlow" -> p2p-crawler paper USENIX/2019/point-break-a-study-of-bandwidth-denial-of-service-attacks-against-tor title Point Break: A Study of Bandwidth Denial-of-Service Attacks against Tor purpose Scanned relays and measured bandwidth-authority performance. quote We ran a TorFlow instance, analyzed its output, and computed the time to measure the fastest slice over 20 scans. ### "tracebox" -> topology paper IMC/2023/ecn-with-quic-challenges-in-the-wild title ECN with QUIC: Challenges in the Wild. purpose Traced paths and detected ECN codepoint changes. quote we use a traceboxbased [27] approach sending QUIC Initial packets with active ECT codepoints and increasing TTLs to trigger ICMP time exceeded messages from the devices on path. ### "tracefilter" -> topology paper CCS/2019/network-hygiene-incentives-and-regulation-deployment-of-source-address-validatio title Network Hygiene, Incentives, and Regulation: Deployment of Source Address Validation in the Internet. purpose Inferred the network hop where SAV filtering occurred. quote These 16,998 tests come from implementing tracefilter [6] on the Spoofer platform and deploying it during the period October 2017 to August 2019. ### "tracenet" -> topology paper IMC/2010/tracenet-an-internet-topology-data-collector title TraceNET: an internet topology data collector. purpose Collect subnet-level Internet topology quote In this study, we have devised a set of heuristics to develop a router level Internet topology collector called tracenet. ### "Trufflehunter" -> dns paper IMC/2020/trufflehunter-cache-snooping-rare-domains-at-large-public-dns-resolvers title Trufflehunter: Cache Snooping Rare Domains at Large Public DNS Resolvers. purpose Snoop public DNS caches and estimate cached-domain counts. quote We present Trufflehunter, a tool to snoop the caches of public DNS resolvers. ### "Tsunami" -> vuln-scanner paper IMC/2022/no-keys-to-the-kingdom-required-a-comprehensive-investigation-of-missing-authent title No keys to the kingdom required: a comprehensive investigation of missing authentication vulnerabilities in the wild. purpose Detected missing-authentication vulnerabilities with application plugins. quote For this, we created and open sourced a generic network security scanner called Tsunami [33]. ### "Ubertooth One" -> local-link paper IEEE-SP/2023/blues-clues-practical-discovery-of-non-discoverable-bluetooth-devices title Blue's Clues: Practical Discovery of Non-Discoverable Bluetooth Devices. purpose Page devices and exchange Bluetooth LMP messages. quote Our chosen Bluetooth transceiver is an Ubertooth One [38], a popular and inexpensive embedded Bluetooth prototyping platform that connects to a host through USB. ### "University of Michigan Daily Full IPv4 HTTPS Handshakes" -> dataset paper IMC/2015/an-end-to-end-measurement-of-certificate-revocation-in-the-webs-pki title An End-to-End Measurement of Certificate Revocation in the Web's PKI. purpose Measured server support for OCSP Stapling. quote we use the IPv4 TLS Handshake scans conducted by the University of Michigan ### "URHunter" -> dns paper IMC/2023/wolf-in-sheeps-clothing-evaluating-security-risks-of-the-undelegated-record-on-d title Wolf in Sheep's Clothing: Evaluating Security Risks of the Undelegated Record on DNS Hosting Services. purpose Collect and classify undelegated DNS records quote we designed a framework to collect URs and identify their malicious behaviors ### "v6disc.sh" -> local-link paper IEEE-SP/2023/man-in-the-middle-attacks-without-rogue-ap-when-wpas-meet-icmp-redirects title Man-in-the-Middle Attacks without Rogue AP: When WPAs Meet ICMP Redirects. purpose Probe active IPv6 victims quote In our experiments, we validate that an attacker can identify an active IPv6 victim in the same /64 prefix within 5 seconds by leveraging existing IPv6 scan tools (e.g., v6disc.sh). ### "Verfploeter" -> topology paper IMC/2017/broad-and-load-aware-anycast-mapping-with-verfploeter title Broad and load-aware anycast mapping with verfploeter. purpose Probe anycast catchments using ICMP Echo Requests. quote In Verfploeter, our queries are ICMP Echo Requests (pings), sent using a custom program, soliciting ICMP Echo Replies. paper IMC/2020/manycast2-using-anycast-to-measure-anycast title MAnycast2: Using Anycast to Measure Anycast. purpose Sequentially probed targets from each anycast vantage point. quote For probing we use Verfploeter [12], a global probing system running on the Tangled testbed ### "Verloc" -> topology paper NDSS/2025/lamp-lightweight-approaches-for-latency-minimization-in-mixnets-with-practical-deployment-considerations title LAMP: Lightweight Approaches for Latency Minimization in Mixnets with Practical Deployment Considerations purpose Measure and obtain mixnode latency data. quote We build the mixnode latency dataset for evaluation using the Verloc [13] measurement framework as deployed on the Nym network. ### "VerLoc" -> topology paper NDSS/2026/optimix-scalable-and-distributed-approaches-for-latency-optimization-in-modern-mixnets title OptiMix: Scalable and Distributed Approaches for Latency Optimization in Modern Mixnets purpose Measure peer-to-peer latency between mixnodes. quote To ensure statistical reliability, each experiment is repeated over at least 400 independent mixnet instantiations ### "W3af" -> vuln-scanner paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Scanned test web applications quote Table 1 lists the 12 scanners that we analyze in this study along with their corresponding version information. ### "Wapiti" -> vuln-scanner paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Scanned test web applications quote Table 1 lists the 12 scanners that we analyze in this study along with their corresponding version information. ### "WeHeY" -> perf paper IMC/2023/localizing-traffic-differentiation title Localizing Traffic Differentiation. purpose Localize traffic differentiation within the client ISP. quote We present WeHeY: a system for localizing traffic differentiation within a client's ISP. ### "WhatWeb" -> grabber [IN ACTIVE] paper IMC/2018/characterizing-the-deployment-and-performance-of-multi-cdns title Characterizing the Deployment and Performance of Multi-CDNs. purpose Fingerprinted CDN instances from IP addresses and domain names. quote we use the WhatWeb [8] web scanner to find details about IP addresses. ### "whois" -> registry paper USENIX/2021/can-i-take-your-subdomain-exploring-same-site-attacks-in-the-modern-web title Can I Take Your Subdomain? Exploring Same-Site Attacks in the Modern Web purpose Checked whether expired canonical domains were registered. quote if the whois command on the apex domain does not return any match, RDScan queries GoDaddy to detect if the domain can be purchased. ### "WiFi Inspector" -> local-link paper USENIX/2019/all-things-considered-an-analysis-of-iot-devices-on-home-networks title All Things Considered: An Analysis of IoT Devices on Home Networks purpose Scanned home subnets and tested IoT security quote WiFi Inspector runs locally on the user's personal computer and performs network scans of the local subnet to check for devices that accept weak credentials or have remotely exploitable vulnerabilities. ### "WMap" -> vuln-scanner paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Scanned test web applications quote Table 1 lists the 12 scanners that we analyze in this study along with their corresponding version information. ### "WPScan" -> vuln-scanner paper WWW/2023/scan-me-if-you-can-understanding-and-detecting-unwanted-vulnerability-scanning title Scan Me If You Can: Understanding and Detecting Unwanted Vulnerability Scanning. purpose Scanned WordPress test applications quote For each tool, we used the latest version available at the time of our analysis. ### "wrk" -> perf paper IEEE-SP/2023/rulekeeper-gdpr-aware-personal-data-compliance-for-web-frameworks title RuleKeeper: GDPR-Aware Personal Data Compliance for Web Frameworks. purpose Generated HTTP benchmark traffic quote We used wrk [51] to simulate traffic by generating HTTP requests for each one of the user tasks for 10 minutes each. ### "Xray" -> vuln-scanner paper NDSS/2024/reqsminer-automated-discovery-of-cdn-forwarding-request-inconsistencies-and-dos-attacks-with-grammar-based-fuzzing title ReqsMiner: Automated Discovery of CDN Forwarding Request Inconsistencies and DoS Attacks with Grammar-based Fuzzing purpose Provide HTTP logs from vulnerability scans. quote We also collected the HTTP logs of the network security scanner Xray during vulnerability scans [10]. ### "YoDNS" -> dns paper IMC/2025/measuring-the-deployment-of-dnssec-bootstrapping-using-authenticated-signals title Measuring the deployment of DNSSEC Bootstrapping Using Authenticated Signals. purpose Large-scale DNS resolution and DNSSEC record scanning quote Therefore, we use the YoDNS scanning tool [40] designed for large-scale DNS scans. ### "ZreFinder" -> dns paper CCS/2020/zombie-awakening-stealthy-hijacking-of-active-domains-through-dns-hosting-referr title Zombie Awakening: Stealthy Hijacking of Active Domains through DNS Hosting Referral. purpose Discover stale NS records and hijackable domains quote For this purpose, we designed a semi-automated approach, called ZreFinder, to automatically discover Zrefs.
- [1]
- Durumeric, Zakir; Wustrow, Eric; Halderman, J. Alex (2013): "ZMap: Fast Internet-wide Scanning and Its Security Applications", in: Proceedings of the USENIX Security Symposium. (Link)
- [2]
- Durumeric, Zakir; Adrian, David; Stephens, Phillip; Wustrow, Eric; Halderman, J. Alex (2024): "Ten Years of ZMap", in: Proceedings of the ACM Internet Measurement Conference. (DOI)
