← back to Domain Sniper
step 8: registrar-fingerprints.json + cross-ref in honeypot check
7b38b4a1599c1f625687fbd3ce49bffa702e15e0 · 2026-05-12 14:36:40 -0700 · Steve Abrams
Public-registrar fingerprint database covering the big-three drop-catchers
(NameBright/DropCatch, Pool.com, NameJet+SnapNames) plus OVH, Sav, Above,
GoDaddy Auctions, Dynadot, NameSilo, Namecheap, Cloudflare, Tucows, Gandi.
Each entry: IANA ID, NS regex patterns, country, aggressive-flag, notes.
honeypot.js check phase now annotates every sniped name with its operator
and prints a loud KNOWN-AGGRESSIVE warning when the actor is in the
aggressive list. Lookup proven against the butlr.app evidence (ns112.ovh.net
correctly resolves to OVH SAS · AGGRESSIVE).
Pivot from scheduled Step 6 (watch-drops.js): the free public drop-list
sources all sit behind anti-bot walls. Step 8 was higher-leverage for this
tick because it has zero external dependencies and instantly hardens the
honeypot check we're already waiting on.
Files touched
M honeypot.jsA registrar-fingerprints.json
Diff
commit 7b38b4a1599c1f625687fbd3ce49bffa702e15e0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue May 12 14:36:40 2026 -0700
step 8: registrar-fingerprints.json + cross-ref in honeypot check
Public-registrar fingerprint database covering the big-three drop-catchers
(NameBright/DropCatch, Pool.com, NameJet+SnapNames) plus OVH, Sav, Above,
GoDaddy Auctions, Dynadot, NameSilo, Namecheap, Cloudflare, Tucows, Gandi.
Each entry: IANA ID, NS regex patterns, country, aggressive-flag, notes.
honeypot.js check phase now annotates every sniped name with its operator
and prints a loud KNOWN-AGGRESSIVE warning when the actor is in the
aggressive list. Lookup proven against the butlr.app evidence (ns112.ovh.net
correctly resolves to OVH SAS · AGGRESSIVE).
Pivot from scheduled Step 6 (watch-drops.js): the free public drop-list
sources all sit behind anti-bot walls. Step 8 was higher-leverage for this
tick because it has zero external dependencies and instantly hardens the
honeypot check we're already waiting on.
---
honeypot.js | 53 ++++++++++++++++++++-
registrar-fingerprints.json | 112 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 164 insertions(+), 1 deletion(-)
diff --git a/honeypot.js b/honeypot.js
index ad2ffcc..be2b9a7 100644
--- a/honeypot.js
+++ b/honeypot.js
@@ -19,9 +19,37 @@ const fs = require('fs');
const path = require('path');
const DATA_DIR = path.join(__dirname, 'data');
+const FINGERPRINTS_FILE = path.join(__dirname, 'registrar-fingerprints.json');
const args = process.argv.slice(2);
const subcommand = args[0] || 'help';
+// Load known-aggregator fingerprints (file is always present in repo)
+let FINGERPRINTS = { operators: {} };
+try {
+ FINGERPRINTS = JSON.parse(fs.readFileSync(FINGERPRINTS_FILE, 'utf8'));
+} catch (e) {
+ // Non-fatal: just means we can't annotate operators
+}
+
+function lookupOperator(fp) {
+ if (!fp) return null;
+ // First try by IANA ID (most reliable)
+ if (fp.registrarIanaId && FINGERPRINTS.operators[fp.registrarIanaId]) {
+ return { id: fp.registrarIanaId, ...FINGERPRINTS.operators[fp.registrarIanaId] };
+ }
+ // Fall back to NS pattern match
+ for (const ns of fp.nameservers || []) {
+ for (const [id, op] of Object.entries(FINGERPRINTS.operators)) {
+ for (const pat of op.nsPatterns || []) {
+ try {
+ if (new RegExp(pat).test(ns)) return { id, ...op };
+ } catch {}
+ }
+ }
+ }
+ return null;
+}
+
// ---------- deterministic RNG so we can reproduce the batch if needed ----------
function mulberry32(a) {
return function () {
@@ -298,9 +326,12 @@ async function check(batchPath) {
const snipedItems = results.items.filter((i) => i.sniped);
for (const item of snipedItems) {
item.attackerFingerprint = whoisFingerprint(item.domain);
+ item.knownOperator = lookupOperator(item.attackerFingerprint);
const f = item.attackerFingerprint;
+ const op = item.knownOperator;
+ const opTag = op ? (op.aggressive ? `\x1b[31m[KNOWN-AGGRESSIVE: ${op.name}]\x1b[0m` : `[${op.name}]`) : '[unknown operator]';
console.log(
- ` ${item.domain.padEnd(14)} registrar=${(f.registrar || '?').slice(0, 30).padEnd(30)} ns=${(f.nameservers[0] || '?').slice(0, 28)}`,
+ ` ${item.domain.padEnd(14)} registrar=${(f.registrar || '?').slice(0, 26).padEnd(26)} ns=${(f.nameservers[0] || '?').slice(0, 26).padEnd(26)} ${opTag}`,
);
await sleep(800);
}
@@ -340,6 +371,26 @@ async function check(batchPath) {
console.log(` abuse: ${snipedItems[0].attackerFingerprint.registrarAbuseEmail || '(none in WHOIS)'}`);
console.log(` countries: ${[...top[1].countries].join(', ') || '(privacy-protected)'}`);
}
+
+ // Cross-reference against known-aggressive operator database
+ const knownAggressive = snipedItems.filter((i) => i.knownOperator && i.knownOperator.aggressive);
+ if (knownAggressive.length) {
+ console.log(`\n \x1b[31m▲ KNOWN-AGGRESSIVE OPERATORS DETECTED:\x1b[0m`);
+ const byOp = {};
+ for (const item of knownAggressive) {
+ const k = item.knownOperator.name;
+ byOp[k] = byOp[k] || { count: 0, country: item.knownOperator.country, notes: item.knownOperator.notes, examples: [] };
+ byOp[k].count++;
+ byOp[k].examples.push(item.domain);
+ }
+ for (const [name, v] of Object.entries(byOp).sort((a, b) => b[1].count - a[1].count)) {
+ console.log(` ${name} (${v.country}) — ${v.count}/${snipedItems.length} sniped names`);
+ console.log(` example: ${v.examples[0]}`);
+ console.log(` notes: ${v.notes}`);
+ }
+ } else {
+ console.log(`\n no match against known-aggressive operator database — this attacker is new. Add their fingerprint to registrar-fingerprints.json.`);
+ }
} else {
console.log(`\n no snipes detected. either (a) we got lucky, (b) the aggregator paused, (c) different leak path. consider re-running with different channels or larger N.`);
}
diff --git a/registrar-fingerprints.json b/registrar-fingerprints.json
new file mode 100644
index 0000000..928358d
--- /dev/null
+++ b/registrar-fingerprints.json
@@ -0,0 +1,112 @@
+{
+ "_about": "Public-registrar fingerprint database used by honeypot.js check phase to cross-reference WHOIS evidence against known drop-catch / aggregator operators. 'aggressive' flags operators with documented snipe / drop-catch infrastructure. Not exhaustive; expand as new actors surface.",
+ "_updated": "2026-05-12",
+ "_schema": {
+ "ianaId": "registrar IANA ID (from WHOIS 'Registrar IANA ID' field)",
+ "name": "canonical registrar name",
+ "nsPatterns": "regex patterns matched against authoritative NS root domains",
+ "country": "registrar country (ISO 3166)",
+ "aggressive": "true if known to operate drop-catch / aggregator infrastructure",
+ "notes": "freeform"
+ },
+ "operators": {
+ "433": {
+ "name": "OVH SAS",
+ "nsPatterns": ["^ns\\d+\\.ovh\\.net$", "^dns\\d+\\.ovh\\.net$"],
+ "country": "FR",
+ "aggressive": true,
+ "notes": "Confirmed snipe on butlr.app 2026-05-12 with ns112.ovh.net/dns112.ovh.net. Common destination for European drop-catchers. Cheap registrations make sniping economic."
+ },
+ "1606": {
+ "name": "NameBright / DropCatch",
+ "nsPatterns": ["^ns\\d+\\.namebrightdns\\.com$", "^ns\\d+\\.aftermarket\\.com$"],
+ "country": "US",
+ "aggressive": true,
+ "notes": "DropCatch.com is one of the three major drop-catch services. Owns hundreds of credentialed connections to TLD registries to race-grab pending-delete domains."
+ },
+ "1330": {
+ "name": "Pool.com / TurnCommerce",
+ "nsPatterns": ["^ns\\d+\\.poolregistry\\.com$", "^ns\\d+\\.parkingcrew\\.net$"],
+ "country": "US",
+ "aggressive": true,
+ "notes": "Pool.com is the second of the big-three drop-catchers. Long-tail dictionary-based filtering, immediate auction listing."
+ },
+ "64": {
+ "name": "Web.com / NameJet / SnapNames",
+ "nsPatterns": ["^ns\\d+\\.snapnames\\.com$", "^ns\\d+\\.namejet\\.com$", "^ns\\d+\\.web\\.com$"],
+ "country": "US",
+ "aggressive": true,
+ "notes": "Third major drop-catcher. NameJet auctions, SnapNames retail. Aggressive on premium .com / .net."
+ },
+ "609": {
+ "name": "Sav.com",
+ "nsPatterns": ["^ns\\d+\\.sav\\.com$"],
+ "country": "US",
+ "aggressive": true,
+ "notes": "Newer entrant, aggressive on short-form .com and short-LL.io / .ai snipes. Public auctions follow."
+ },
+ "1170": {
+ "name": "Above.com / Trellian",
+ "nsPatterns": ["^ns\\d+\\.above\\.com$", "^ns\\d+\\.trellian\\.com$"],
+ "country": "AU",
+ "aggressive": true,
+ "notes": "Long-tail drop-catch + parking + PPC monetization. Common destination for sniped names that get parked rather than auctioned."
+ },
+ "146": {
+ "name": "GoDaddy / GoDaddy Auctions",
+ "nsPatterns": ["^ns\\d+\\.domaincontrol\\.com$"],
+ "country": "US",
+ "aggressive": true,
+ "notes": "GoDaddy operates its own auction marketplace and TDNAM (after-market). Snipes through GoDaddy Auctions land at domaincontrol.com NS."
+ },
+ "472": {
+ "name": "Dynadot",
+ "nsPatterns": ["^ns\\d+\\.dynadot\\.com$"],
+ "country": "US",
+ "aggressive": false,
+ "notes": "Mostly retail. Occasional drop-catch activity. Not a primary aggregator."
+ },
+ "1479": {
+ "name": "NameSilo",
+ "nsPatterns": ["^ns\\d+\\.namesilo\\.com$", "^ns\\d+\\.dnsowl\\.com$"],
+ "country": "US",
+ "aggressive": false,
+ "notes": "Retail registrar. Cheap renewals. Not a known aggregator."
+ },
+ "1861": {
+ "name": "Porkbun",
+ "nsPatterns": ["^curitiba\\.ns\\.porkbun\\.com$", "^salvador\\.ns\\.porkbun\\.com$", "^maceio\\.ns\\.porkbun\\.com$", "^fortaleza\\.ns\\.porkbun\\.com$", "^ns\\d*\\.porkbun\\.com$"],
+ "country": "US",
+ "aggressive": false,
+ "notes": "Retail-friendly registrar. Not an aggregator."
+ },
+ "1068": {
+ "name": "Namecheap",
+ "nsPatterns": ["^dns\\d+\\.registrar-servers\\.com$"],
+ "country": "US",
+ "aggressive": false,
+ "notes": "Retail registrar. Not a primary aggregator but resold to many."
+ },
+ "1910": {
+ "name": "Cloudflare Registrar",
+ "nsPatterns": ["^[a-z]+\\.ns\\.cloudflare\\.com$"],
+ "country": "US",
+ "aggressive": false,
+ "notes": "Wholesale-pricing retail registrar. No aftermarket activity."
+ },
+ "69": {
+ "name": "Tucows / OpenSRS / Hover",
+ "nsPatterns": ["^ns\\d+\\.tucows\\.com$", "^ns\\d+\\.hover\\.com$", "^ns\\d+\\.domainpeople\\.com$"],
+ "country": "CA",
+ "aggressive": false,
+ "notes": "Wholesale platform reselling through many small registrars. Identity-of-actual-actor is downstream."
+ },
+ "81": {
+ "name": "Gandi",
+ "nsPatterns": ["^ns\\d+\\.gandi\\.net$"],
+ "country": "FR",
+ "aggressive": false,
+ "notes": "Retail-focused European registrar. Not an aggregator."
+ }
+ }
+}
← 4aecb56 step 5: register.js — atomic registrar API with dry-run safe
·
back to Domain Sniper
·
step 9: README marks deny rules shipped df203a6 →