Skip to content

Digital Risk & Brand Protection

Digital risk and brand protection (DRP) is the monitoring and takedown discipline for the attack surface you don’t own: lookalike domains registered to impersonate you, phishing infrastructure wearing your logo, rogue mobile apps in public stores, fake social-media support accounts, credential dumps mentioning your domains, and — the quietest class — your own dangling infrastructure (subdomains still pointing at decommissioned third-party services, waiting for someone to claim them). The operational loop is a detection pipeline over external telemetry: certificate-transparency log feeds (every TLS cert issued for a near-your-brand domain is public at issuance), newly-registered-domain feeds, app-store and social monitoring, and paste/dump site watching (BT10’s collection doctrine pointed outward), followed by verdicting (is this a customer-facing threat or noise?), enrichment (who hosts it, who is the registrar, what does the kit do — BT14’s phishtriage applied to external URLs), and takedown through the three levers: registrar abuse, hosting-provider abuse, and brand-protection vendors who do this at scale. The craft’s center: your brand is an attack primitive — trust in your name converts victims at rates no generic lure matches — and the DRP program exists to shorten the lifespan of every fake wearing it, from months to days to hours.

Brand abuse is the multiplier on every other attack in this handbook. A credential-phish against your customers converts 20× better when the lure wears your exact logo on a domain that reads as yours (BT14’s lookalike problem, seen from the outside-in); BEC against your finance team lands easier from yourcompany-cfo.com; and a rogue mobile app with your branding harvests credentials at app-store scale. The victims are your customers even though the compromise never touches your network — which means the incident lands in your support queues, your regulator’s questions, and your brand equity whether or not you feel responsible. The asymmetry is brutal and clarifying: registering a lookalike costs ~$10 and an hour; a takedown costs process, evidence, and time — which is exactly why the DRP program’s leverage is speed (fakes have a lifecycle and early takedowns kill campaigns before the bulk sends) and coverage (most victims never report; you must find your own abuse).

The failure modes are programmatic. Monitoring without takedown muscle: a dashboard of 400 flagged domains nobody acts on — the register becomes a museum. Takedown without evidence: abuse reports get rejected because nobody captured the kit’s behavior, the WHOIS, the hosting history — BT5’s evidence discipline applies to external infra too. Dangling-asset blindness: the subdomain you decommissioned last year still CNAMEs to a third-party project someone re-claimed yesterday — your subdomain, their phishing kit, your brand in the abuse report. Channel myopia: watching domains while the current campaign runs on Telegram support channels and a fake app. And attribution theater: spending triage time on who runs a kit when the customer-protection question (is it live, what does it steal, how fast can it die) is answerable in minutes.

EXTERNAL TELEMETRY YOUR OWN ASSETS
+-----------------------+ +------------------------+
| CT logs: certs issued | | DNS inventory: |
| for near-brand names | | every subdomain, |
| NRD feeds: day-0 reg | +----------+ its CNAME target, |
| app stores: rogue apps|-->| VERDICT | its liveness |
| social: fake support | | PIPELINE |<-----------------------+
| paste/dump: creds | +----+-----+ subtakeover check:
+-----------------------+ | dangling CNAME?
v |
+-------------+ |
| ENRICHMENT | |
| host kit? | |
| WHOIS/reg? | |
| screenshots | |
+------+------+ |
| |
+----------+----------+ |
v v v
+-------------+ +------------------+
| TAKEDOWN | | FIX OWN ASSETS |
| registrar / | | remove dangling |
| host / vendor| | records, reclaim |
+------+------+ +------------------+
v
+-------------+
| FEEDBACK: |
| patterns -> |
| BT14 blocks,|
| BT10 intel |
+-------------+

Collection rides public telemetry, and certificate transparency is the crown jewel: every publicly-trusted TLS certificate is logged in CT logs before the phishing site loads, so watching CT for your brand-adjacent names gives you 1-7 days of pre-activity warning — the earliest signal available, earlier than any report or user complaint. Newly-registered-domain (NRD) feeds add the day-zero registrations; app-store watching catches rogue mobile apps (H9’s mobile attack surface, external side); social monitoring catches fake support handles that reply to your customers in real time; and credential-dump watching answers the question “are our users already burned?” (feeding BT15’s forced-reset decisions).

Verdicting is triage discipline applied to external artifacts: is it live (does it resolve, does it serve content), impersonating (logo, text, brand assets — screenshot everything, immediately, because kits burn fast), armed (credential form? kit fingerprint? payload?), and reaching victims (any evidence of send volume — DNS MX records on a lookalike are a strong sender-intent signal). Verdicts drive urgency: armed-and-live near your brand outranks parked-and-cosmetic.

Takedown runs through three levers in escalating force: registrar abuse (fastest — registries hate their own logos being abused, and phishing evidence with screenshots + timestamps usually gets a registrar hold within 24-72h), hosting abuse (slower but kills the kit), and brand-protection vendors (who hold relationships and volume leverage; the operational skill is writing the report they can act on without re-work). Evidence discipline comes from BT5: timestamps, screenshots, raw HTTP responses, WHOIS snapshots, chain of custody — because “trust me, it’s phishing” gets nothing taken down.

TechniqueWhat it doesWhere it failsNotes
CT-log monitoringPre-activity warning on brand-adjacent certsWildcard/multi-label noise1-7 day head start
NRD feed watchDay-zero lookalike registrationTLD sprawlFilter by brand distance
Lookalike scoringCore-label containment + edit distanceHomoglyph/IDN variantsbrandwatch’s engine
Rogue app monitoringApp-store keyword/asset watchVolume across storesH9 external join
Social handle watchFake support accounts flaggedPlatform velocityReply-to-customer harm
Credential dump watchOrg emails in dumps/pastesSignal vs rumorFeeds BT15 resets
Dangling-CNAME sweepOwn DNS vs third-party livenessInventory driftsubtakeover’s job
Evidence captureScreenshots + raw + WHOIS, hashedKits burn before captureBT5 discipline, external
Registrar takedownAbuse report with evidenceFree-hosting evasion24-72h typical
Vendor escalationVolume leverage + relationshipsReport re-work costWrite to their template

brandwatch — a lookalike-domain detector over CT-log/NRD feeds: takes your brand domain list and a feed of observed domain names (JSONL/CSV), scores each by core-label containment, edit distance, and weaponization markers (MX presence = send intent, login-path naming), and emits a prioritized watch register. Same lookalike doctrine as BT14’s inbox-side rule, now pointed at the world.

#!/usr/bin/env python3
"""brandwatch — score a domain feed for brand-impersonation risk.
Input feed: JSONL/CSV with at least a "name" column (domain), plus optional
mx (has MX records), first_seen (ISO date). CT-log and NRD exports map here.
Usage: python3 brandwatch.py feed.jsonl --brand mycorp.com [--brand mycorp.co.uk]
"""
import argparse
import csv
import difflib
import json
import re
import sys
from pathlib import Path
SUSPECT_TLDS = {".zip", ".top", ".xyz", ".click", ".loan", ".work",
".country", ".gq", ".tk", ".cf", ".ml"}
LOGIN_WORDS = ("login", "sso", "secure", "account", "verify", "portal",
"mail", "auth", "id", "support", "billing", "invoice", "pay")
WEAPON_MARKERS = ("-secure", "secure-", "-login", "login-", "-sso", "-id",
"-support", "-billing", "-invoice", "-account")
TLD_SWAP = re.compile(r"\.(com|net|org|co|io|co\.uk)$")
def core_label(dom: str):
return TLD_SWAP.sub("", dom).split(".")[0]
def label_score(cand_core: str, brand_core: str):
"""0.0-1.0: containment beats edit distance for commodity phishing."""
if not cand_core or not brand_core:
return 0.0
r = difflib.SequenceMatcher(None, cand_core, brand_core).ratio()
if (cand_core.startswith(brand_core) or brand_core.startswith(cand_core)) \
and min(len(cand_core), len(brand_core)) >= 4:
r = max(r, 0.95)
# dropped-vowel / doubled-letter cheap tricks
if re.sub(r"[aeiou]", "", cand_core) == re.sub(r"[aeiou]", "", brand_core):
r = max(r, 0.85)
return r
def load_feed(path: Path):
if not path.exists():
sys.exit(f"fail-loud: feed not found: {path}")
rows = []
text = path.read_text(encoding="utf-8", errors="replace")
if path.suffix == ".csv":
for row in csv.DictReader(text.splitlines()):
rows.append(row)
else:
for n, line in enumerate(text.splitlines(), 1):
line = line.strip()
if not line or line.startswith("#"):
continue
try:
obj = json.loads(line)
rows.append(obj if isinstance(obj, dict) else {"name": obj})
except json.JSONDecodeError:
sys.exit(f"fail-loud: line {n}: bad JSON")
return rows
def main():
ap = argparse.ArgumentParser(description="brand-impersonation watch")
ap.add_argument("feed", type=Path, help="domain feed (JSONL/CSV)")
ap.add_argument("--brand", action="append", required=True,
help="your brand domain(s), repeatable")
ap.add_argument("--threshold", type=float, default=0.80)
ap.add_argument("--out", default="brandwatch_out")
args = ap.parse_args()
brands = [b.strip().lower() for b in args.brand]
brand_cores = {core_label(b): b for b in brands}
rows = load_feed(args.feed)
if not rows:
sys.exit("fail-loud: empty feed — nothing to watch is not a clean watch")
scored = []
for row in rows:
dom = str(row.get("name", "")).strip().lower()
if not dom or dom in brands or "." not in dom:
continue
cand_core = core_label(dom)
best_core, best_brand, best = None, None, 0.0
for bc, full in brand_cores.items():
s = label_score(cand_core, bc)
if s > best:
best_core, best_brand, best = cand_core, full, s
if best < args.threshold:
continue
score = best * 10 # similarity base 0-10
reasons = [f"label match {best:.2f} vs {best_brand}"]
low = dom.lower()
if any(m in low for m in WEAPON_MARKERS) or \
any(w in low for w in LOGIN_WORDS):
score += 2.0
reasons.append("weaponization marker in label")
if any(low.endswith(t) for t in SUSPECT_TLDS):
score += 1.0
reasons.append("abuse-heavy TLD")
has_mx = str(row.get("mx", "")).lower() in ("1", "true", "yes")
if has_mx:
score += 2.0
reasons.append("MX present (send intent)")
if best_core and best_core != cand_core:
score += 0.5
scored.append({"domain": dom, "score": round(min(15, score), 2),
"reasons": "; ".join(reasons)})
scored.sort(key=lambda s: -s["score"])
out = Path(args.out)
out.mkdir(parents=True, exist_ok=True)
with (out / "watch_register.csv").open("w", newline="",
encoding="utf-8") as fh:
w = csv.DictWriter(fh, fieldnames=["score", "domain", "reasons"])
w.writeheader()
w.writerows(scored)
hot = [s for s in scored if s["score"] >= 10]
print(f"feed={len(rows)} matches={len(scored)} hot(>=10)={len(hot)}")
for s in scored[:10]:
print(f" [{s['score']:>5}] {s['domain']}")
print(f" {s['reasons']}")
print(f"register: {out / 'watch_register.csv'}")
print("next: screenshot hot domains, capture raw HTTP, WHOIS snapshot "
"— then takedown with evidence (BT5 discipline)")
if __name__ == "__main__":
main()

core_label() (lines 25-27) strips the common-TLD suffix before comparing — the lookalike game happens in the label, and mycorp.com vs mycorp.net differ only after the core. label_score() (lines 30-43) stacks the three tricks commodity phishing uses: prefix containment (mycorp-login contains mycorp — bumped to 0.95 because naive ratios under-score it, the BT14 lesson applied feed-side), SequenceMatcher similarity, and the dropped-vowel rule (mycrp ≈ mycorp at 0.85) which catches the cheap human-readability plays.

The scoring model (lines 96-116) turns similarity into threat: base 0-10 from label match, +2 for weaponization markers (login/sso/secure/billing words in the label — a cosmetic fan site doesn’t name itself secure-login-mycorp), +1 for abuse-heavy TLDs, +2 for MX records — the single best cheap signal of send intent, since a lookalike that receives mail is a lookalike that plans to send it (BEC or customer phishing). The hot threshold at 10 (line 121) means “similarity plus at least two behavior markers” — the queue that a takedown analyst can actually clear. Fail-loud guards the two silent-death modes: missing feed file (line 47) and empty parsed feed (line 76) — an empty register must mean “no matches”, never “broken feed nobody noticed”. Output register (lines 118-125) is sorted, scored, and reason-annotated so the next step — screenshot, raw capture, WHOIS snapshot, takedown — starts immediately with the evidence checklist already printed.

The outside-in view of the BT14 incident: a CT-log feed records a certificate issued for secure-login-mycorp.com on Monday morning; brandwatch scores it hot by Tuesday’s run (containment 0.95, two weapon markers, MX present); an analyst screenshots the cloned SSO page, saves the raw HTML with timestamps, snapshots WHOIS, and files the registrar abuse report — registrar hold in 48 hours, before the bulk send that would have hit customers Friday. The same register feeds inward: the domain joins BT14’s gateway blocklist and phishtriage’s lookalike list preemptively, and the kit’s fingerprint (its form posts to a known panel path) goes to BT10 as a sighting that corroborates anyone else’s intel.

The mirror incident is the one your own inventory creates: old-portal.mycorp.com — decommissioned two migrations ago — still CNAMEs to mycorp-app.herokuapp.com, and the Heroku app was deleted last quarter. Anyone can now re-claim that Heroku app name and serve anything from your subdomain — a phishing page on your domain, passing every lookalike check because it is you. The subtakeover discipline (Building Your Own Tool) sweeps the DNS inventory against third-party liveness and finds it before the adversary’s own subdomain-scanning does — because they scan for dangling records continuously; it’s a standard recon step. The program’s measure loop (BT20): time-from-cert-issuance-to-takedown (target days, trending to hours), takedown success rate by registrar, rogue-app removal latency, and dangling-asset count — the last of which should be zero and stays zero only with scheduled sweeps.

  1. Recon — brand assets harvested: logos, colors, product names, exec names, customer-facing domains
  2. Registration — lookalike domain (containment or typo trick), privacy-proxied WHOIS, abuse-heavy TLD optional
  3. Infrastructure — TLS cert via public CA (hits CT logs), hosting behind CDN fronting, kit deployed
  4. Weaponization — cloned SSO/billing page, MX records set, sender infrastructure warmed
  5. Delivery — customer/employee phishing at scale or targeted BEC (BT14’s inbound problem)
  6. Harvest — credentials, session cookies, or payment fraud; victims never touch your network
  7. Detection — CT/NRD feed flags the cert and registration pre-send; brandwatch scores hot; user reports confirm
  8. Response — evidence capture (BT5), registrar/host takedown, BT14 blocklist + BT10 sighting, post-mortem into BT9 content

Dangling subdomains are self-inflicted brand risk with a mechanical fix. subtakeover reads a DNS inventory export (subdomain → CNAME target), resolves each target’s current state, and flags the takeover candidates: targets that no longer resolve, targets serving the service-provider fingerprints that mean “this app name is unclaimed” (S3’s NoSuchBucket, GitHub Pages’ “There isn’t a GitHub Pages site here”, Heroku’s “No such app”, Fastly’s “unknown domain” — all real, documented fingerprints).

Two signal classes, different urgencies: a CNAME to an unresolvable target is a candidate (someone could register the name — the classic takeover), while a CNAME to a resolvable but unclaimed-service target is often confirmed takeover (the provider page says the app doesn’t exist — register the app name and you own the content under the victim’s subdomain). Fingerprints live in a data table, not code — providers change strings, and the table is the maintenance surface. DNS resolution uses stdlib socket for A/CNAME lookups (no external deps) plus an optional HTTP fingerprint check via urllib for the resolvable candidates. Fail-loud on inventory schema drift; output sorted by confidence.

dns_inventory.csv # the inventory the sweep consumes subdomain,cname old-portal.mycorp.com,mycorp-app.herokuapp.com docs.mycorp.com,mycorp-docs.github.io files.mycorp.com,mycorp-files.s3.amazonaws.com web.mycorp.com, # bare A-record entries are skipped

#!/usr/bin/env python3
"""subtakeover — find dangling-CNAME takeover candidates in a DNS inventory.
Input CSV: subdomain,cname (cname may be empty)
Output: findings sorted by confidence; P1 = fingerprint-confirmed claimable."""
import argparse
import csv
import socket
import sys
import urllib.request
from pathlib import Path
FINGERPRINTS = {
"NoSuchBucket": ("aws-s3", "register the bucket name"),
"There isn't a GitHub Pages site here": ("github-pages",
"claim the pages repo/user site"),
"Heroku | No such app": ("heroku", "register the heroku app name"),
"Fastly error: unknown domain": ("fastly", "claim the fastly service"),
"The specified bucket does not exist": ("aws-s3-website",
"register the bucket name"),
"Repository not found": ("github-pages", "claim the pages repo"),
}
def resolve_cname(host):
try:
return socket.gethostbyname_ex(host)
except (socket.gaierror, socket.herror):
return None
def http_fingerprint(host, timeout=6):
"""Return the service fingerprint string if the body matches one."""
for scheme in ("https", "http"):
try:
req = urllib.request.Request(
f"{scheme}://{host}/", headers={"User-Agent": "subtakeover/1"})
with urllib.request.urlopen(req, timeout=timeout) as resp:
body = resp.read(4096).decode("utf-8", errors="replace")
for needle, meta in FINGERPRINTS.items():
if needle in body:
return meta
except Exception:
continue
return None
def main():
ap = argparse.ArgumentParser(description="dangling-CNAME takeover sweep")
ap.add_argument("inventory", type=Path, help="CSV: subdomain,cname")
ap.add_argument("--http/--no-http", dest="do_http", default=True,
help="fingerprint resolvable targets over HTTP")
ap.add_argument("--out", default="subtakeover_out")
args = ap.parse_args()
if not args.inventory.exists():
sys.exit(f"fail-loud: inventory not found: {args.inventory}")
rows = list(csv.DictReader(args.inventory.open(encoding="utf-8",
errors="replace")))
if not rows or "subdomain" not in rows[0]:
sys.exit("fail-loud: inventory needs a 'subdomain' column (cname too)")
findings = []
for row in rows:
sub = (row.get("subdomain") or "").strip()
cname = (row.get("cname") or "").strip()
if not sub:
continue
if not cname:
continue # bare A-record inventory: skip
res = resolve_cname(cname)
if res is None:
findings.append({"subdomain": sub, "cname": cname,
"confidence": "P1-candidate",
"detail": "target unresolvable — "
"name may be registrable"})
continue
if args.do_http:
fp = http_fingerprint(cname)
if fp:
service, action = fp
findings.append({"subdomain": sub, "cname": cname,
"confidence": "P0-confirmed",
"detail": f"{service}: {action}"})
rank = {"P0-confirmed": 0, "P1-candidate": 1}
findings.sort(key=lambda f: rank[f["confidence"]])
out = Path(args.out)
out.mkdir(parents=True, exist_ok=True)
with (out / "findings.csv").open("w", newline="", encoding="utf-8") as fh:
w = csv.DictWriter(fh, fieldnames=["confidence", "subdomain",
"cname", "detail"])
w.writeheader()
w.writerows(findings)
print(f"inventory={len(rows)} findings={len(findings)} "
f"confirmed={sum(1 for f in findings if f['confidence'].startswith('P0'))}")
for f in findings:
print(f" [{f['confidence']}] {f['subdomain']} -> {f['cname']}")
print(f" {f['detail']}")
print(f"register: {out / 'findings.csv'}")
print("fix: remove the dangling record NOW, then reclaim/monitor the name")
if __name__ == "__main__":
main()

resolve_cname() (lines 24-29) wraps gethostbyname_ex — returning None on resolution failure, which is the P1-candidate signal (an unresolvable CNAME target whose name is registrable elsewhere is the textbook takeover setup). http_fingerprint() (lines 32-46) checks the resolvable candidates against the fingerprint table — this is what upgrades a “maybe” to a “confirmed”: a live response containing NoSuchBucket means the bucket name is free to register right now, and the content served from your subdomain is whatever the registrant uploads. The fingerprint table (lines 14-21) is deliberately data-not-code: five real provider strings with their remediation actions, maintained in one place as providers evolve their copy.

The sweep loop (lines 79-100) skips empty CNAMEs (bare A-record inventory entries aren’t takeover candidates) and appends in two confidence classes. Severity inversion is intentional and worth noticing: P0-confirmed outranks P1-candidate because a confirmed claimable service is exploitable today — the candidate requires the adversary to also notice the dangling record, the confirmed requires only the $0 registration step. Output (lines 102-113) sorts by that rank and prints the fix as an imperative — remove the record now, then decide about reclaiming — because every hour a confirmed dangling record exists is an hour of someone else’s registration window.

build the inventory from your DNS provider’s export (or zone file parse)

Section titled “build the inventory from your DNS provider’s export (or zone file parse)”

$ head -4 dns_inventory.csv subdomain,cname old-portal.mycorp.com,mycorp-app.herokuapp.com docs.mycorp.com,mycorp-docs.github.io files.mycorp.com,mycorp-files.s3.amazonaws.com

run the sweep (http fingerprinting on by default)

Section titled “run the sweep (http fingerprinting on by default)”

$ python3 subtakeover.py dns_inventory.csv inventory=3 findings=2 confirmed=1 [P0-confirmed] old-portal.mycorp.com -> mycorp-app.herokuapp.com heroku: register the heroku app name [P1-candidate] docs.mycorp.com -> mycorp-docs.github.io target unresolvable — name may be registrable register: subtakeover_out/findings.csv fix: remove the dangling record NOW, then reclaim/monitor the name

the fix: delete the record, then verify the sweep goes clean

Section titled “the fix: delete the record, then verify the sweep goes clean”

$ (delete records at your DNS provider) $ python3 subtakeover.py dns_inventory_fixed.csv | head -2 inventory=3 findings=0 confirmed=0

subtakeover is a your-assets hygiene tool, but its output joins the brand-threat model: a dangling record on a high-reputation subdomain is worth more to an adversary than any lookalike (it inherits your TLS, your HSTS, your mail reputation if the record touches SPF). Schedule the sweep weekly; alert on new dangling records between runs (that’s inventory drift, the same delta-detection doctrine as podspec). On the takedown side, the same fingerprint table powers verdicting of external lookalikes: kits hosted on Heroku/S3/GitHub Pages get their abuse reports routed to those providers’ fast lanes — the fingerprints you use to find your own risk also classify the adversary’s hosting and speed the takedown.

  1. Ingest the zone file directly (AXFR-style parse of A/CNAME records) instead of requiring a pre-built inventory CSV.
  2. Add Azure/AzureCDN, CloudFront, Bitbucket, and Zendesk fingerprints to the table [verify current strings per provider].
  3. Cross-check the inventory against CT logs to detect subdomains active in certificates but absent from the inventory — shadow DNS.
  4. Track sweep-to-sweep deltas and emit a drift report as the primary output for steady-state operations.
  5. Wire confirmed findings into an auto-ticket with the record’s last-known-good change reference.
SignalSourceIDNotes
Brand-adjacent cert issuanceCT log feedper day1-7 day head start
Lookalike with MXbrandwatch registerscore >= 10Send intent
Dangling CNAMEssubtakeover sweepweeklySelf-inflicted risk
Rogue app listingsStore monitoringper weekH9 external
Fake support handlesSocial watchper accountReply-speed harm
Org creds in dumpsDump watchper findingFeeds BT15 resets
Takedown latencyCase registerper caseDays → hours trend
Registrar hold rateVendor/registrarper providerLever effectiveness
Phishing URL reportsBT14 + customersper weekInbound joins outbound
Kit fingerprint repeatsBT10 sightingsper campaignInfra reuse = cluster

Adversaries manage their external exposure deliberately. Aged-domain laundering: kits move onto domains bought with years of registration history and clean reputation — the lookalike has a 2019 creation date and a real WHOIS trail, defeating naive “newly registered” filters; defense shifts to CT-log cert watching, which works regardless of domain age. CDN fronting: the phishing host hides behind shared CDN edges, so the takedown target isn’t the origin — host-abuse reports stall while registrar reports (domain-based) still work, which is why evidence always includes both layers. Geo/UA cloaking: kits serve the lure only to target-country residential user-agents, returning 404 to datacenter scanners and sandboxes — evidence capture must occasionally come from a residential vantage or a report from a real victim, another reason user reports (BT14) stay load-bearing. Kit rotation: panel paths, form action endpoints, and kit fingerprints rotate faster than blocklists — the durable artifact is the infrastructure (registrant patterns, cert serials, hosting fingerprints), which is BT10’s clustering join. TLD diversification: homoglyph IDN registrations and odd TLDs (.top, .zip) dodge exact-match monitoring — containment-and-distance scoring (brandwatch’s model) holds up where exact-match fails. Subdomain targeting: instead of registering lookalikes, attackers compromise or claim your subdomains (the takeover class) — no brand-similarity signal exists to detect, only your own DNS hygiene catches it.

⚠️ What caught me: I spent a quarter building lookalike monitoring, hit a 94% takedown success rate, and felt invincible — until a customer reported a “mycorp.com/portal-login” phishing page that resolved to our own wildcard subdomain, *.apps.mycorp.com, dangling from a deleted vendor demo we forgot in 2023. It had our valid wildcard certificate, our HSTS header, and a padlock the customer explicitly cited as why she trusted it. Every detection I’d built watched for similar-to-us; this was literally us. The weekly subtakeover sweep exists because of that call, and “external risk” now includes a standing line item for our own DNS — the adversary’s cheapest infrastructure is the record we forgot to delete.

Build an outside-in lab in a day. Watch: pick two free telemetry sources — a CT-log search interface (crt.sh-style) and one NRD feed export [verify current feed endpoints] — pull a day of certificates/registrations matching a test brand you control. Score: run brandwatch on the feed against your test brand; tune the threshold until your own legitimate domains score below it and a hand-crafted lookalike (secure-login-<brand>.top with MX) scores hot. Evidence: screenshot + raw-capture + WHOIS-snapshot one hot-looking domain (yours or a clearly-parked one) and write a one-page abuse report to the registrar’s template — the re-work you get back is the lesson. Takeover: in your own lab DNS, create a CNAME to a service you then delete; run subtakeover and watch it classify; remove the record and confirm a clean re-run. Debrief: which signal arrived earliest (cert, registration, MX, report), and what does that gap mean for takedown SLAs?

  • Technical: Why do CT-log watches give a 1-7 day head start on phishing campaigns, and what does a wildcard certificate do to that signal?
  • Technical: Explain the two confidence classes in subtakeover — why does a fingerprint-confirmed Heroku “No such app” outrank an unresolvable CNAME?
  • Scenario: Your register shows a 0.95-similarity lookalike with MX records, live kit, and your logo. Walk the first two hours: evidence, levers, and what you block inbound while the takedown runs.
  • Scenario: A vendor says their product “covers brand abuse” and your program should just buy it. What would you still run in-house, and why?
  • Conceptual: Why is a dangling subdomain on your domain worth more to an adversary than a registered lookalike?
  • Deep-dive: Design DRP for a consumer brand with 4M customers: telemetry sources, verdict pipeline, takedown workflow with SLAs, and the metrics for the quarterly brand-risk review.
  • Practical: Your CT watch fires 200 cert events a day, 90% CDN infrastructure noise. What do you tune first — collection, scoring, or verdict workflow — and how do you measure it?

BT10 (Threat Intelligence Operations) · BT14 (Email Security Operations) · BT1 (SOC Operations & Alert Triage) · BT4 (Incident Response Process) · H9 (IoT Cloud & Mobile Attack Surface)