Digital Risk & Brand Protection
Definition
Section titled “Definition”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.
Why It Matters
Section titled “Why It Matters”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.
Detailed Explanation
Section titled “Detailed Explanation” 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.
Common Techniques
Section titled “Common Techniques”| Technique | What it does | Where it fails | Notes |
|---|---|---|---|
| CT-log monitoring | Pre-activity warning on brand-adjacent certs | Wildcard/multi-label noise | 1-7 day head start |
| NRD feed watch | Day-zero lookalike registration | TLD sprawl | Filter by brand distance |
| Lookalike scoring | Core-label containment + edit distance | Homoglyph/IDN variants | brandwatch’s engine |
| Rogue app monitoring | App-store keyword/asset watch | Volume across stores | H9 external join |
| Social handle watch | Fake support accounts flagged | Platform velocity | Reply-to-customer harm |
| Credential dump watch | Org emails in dumps/pastes | Signal vs rumor | Feeds BT15 resets |
| Dangling-CNAME sweep | Own DNS vs third-party liveness | Inventory drift | subtakeover’s job |
| Evidence capture | Screenshots + raw + WHOIS, hashed | Kits burn before capture | BT5 discipline, external |
| Registrar takedown | Abuse report with evidence | Free-hosting evasion | 24-72h typical |
| Vendor escalation | Volume leverage + relationships | Report re-work cost | Write to their template |
Full Working Code
Section titled “Full Working Code”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 argparseimport csvimport difflibimport jsonimport reimport sysfrom 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()Code Walkthrough
Section titled “Code Walkthrough”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.
How It Reaches The Target
Section titled “How It Reaches The Target”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.
Exploitation Chain
Section titled “Exploitation Chain”- Recon — brand assets harvested: logos, colors, product names, exec names, customer-facing domains
- Registration — lookalike domain (containment or typo trick), privacy-proxied WHOIS, abuse-heavy TLD optional
- Infrastructure — TLS cert via public CA (hits CT logs), hosting behind CDN fronting, kit deployed
- Weaponization — cloned SSO/billing page, MX records set, sender infrastructure warmed
- Delivery — customer/employee phishing at scale or targeted BEC (BT14’s inbound problem)
- Harvest — credentials, session cookies, or payment fraud; victims never touch your network
- Detection — CT/NRD feed flags the cert and registration pre-send; brandwatch scores hot; user reports confirm
- Response — evidence capture (BT5), registrar/host takedown, BT14 blocklist + BT10 sighting, post-mortem into BT9 content
Building Your Own Tool
Section titled “Building Your Own Tool”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).
Design decisions
Section titled “Design decisions”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
Full source
Section titled “Full source”#!/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 argparseimport csvimport socketimport sysimport urllib.requestfrom 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()Walkthrough
Section titled “Walkthrough”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.
Compilation and test procedure
Section titled “Compilation and test procedure”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
Detection considerations
Section titled “Detection considerations”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.
Improvements to add
Section titled “Improvements to add”- Ingest the zone file directly (AXFR-style parse of A/CNAME records) instead of requiring a pre-built inventory CSV.
- Add Azure/AzureCDN, CloudFront, Bitbucket, and Zendesk fingerprints to the table [verify current strings per provider].
- Cross-check the inventory against CT logs to detect subdomains active in certificates but absent from the inventory — shadow DNS.
- Track sweep-to-sweep deltas and emit a drift report as the primary output for steady-state operations.
- Wire confirmed findings into an auto-ticket with the record’s last-known-good change reference.
Detection — Defender View
Section titled “Detection — Defender View”| Signal | Source | ID | Notes |
|---|---|---|---|
| Brand-adjacent cert issuance | CT log feed | per day | 1-7 day head start |
| Lookalike with MX | brandwatch register | score >= 10 | Send intent |
| Dangling CNAMEs | subtakeover sweep | weekly | Self-inflicted risk |
| Rogue app listings | Store monitoring | per week | H9 external |
| Fake support handles | Social watch | per account | Reply-speed harm |
| Org creds in dumps | Dump watch | per finding | Feeds BT15 resets |
| Takedown latency | Case register | per case | Days → hours trend |
| Registrar hold rate | Vendor/registrar | per provider | Lever effectiveness |
| Phishing URL reports | BT14 + customers | per week | Inbound joins outbound |
| Kit fingerprint repeats | BT10 sightings | per campaign | Infra reuse = cluster |
Evasion Techniques
Section titled “Evasion Techniques”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.
Lab Practice
Section titled “Lab Practice”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?
Interview Questions
Section titled “Interview Questions”- 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?
Related Modules
Section titled “Related Modules”BT10 (Threat Intelligence Operations) · BT14 (Email Security Operations) · BT1 (SOC Operations & Alert Triage) · BT4 (Incident Response Process) · H9 (IoT Cloud & Mobile Attack Surface)