Botnet Architecture Analysis
Definition
Section titled “Definition”Botnet architecture analysis is the study of how compromised hosts (bots) are organized, commanded, and maintained as a fleet: the topology that connects them to their operator (centralized C2, peer-to-peer, hybrid), the addressing schemes that make the fleet resilient (DGA domain generation, fast-flux DNS, hardcoded fallback lists), the control protocols that distribute tasking (M7’s channel analysis applied at fleet scale), and the monetization roles bots play (DDoS cannon, spam relay, residential proxy, credential stuffing engine, delivery platform for the next malware). Where single-sample analysis (M1-M9) asks “what does this binary do”, botnet analysis asks “what does the population do” — infection curves, churn rates, command latency, and the infrastructure lifecycle that makes sinkholing or takedown possible. It is the discipline where malware analysis meets infrastructure analysis: the binary is a client, and the interesting system is the network of clients.
Why It Matters
Section titled “Why It Matters”Botnets are the persistent infrastructure of cybercrime — the same fleets that launch DDoS for extortion relay the spam that delivers ransomware loaders, serve as residential proxy networks that launder attacks through legitimate-looking IPs, and rent their compute to any buyer. Understanding botnet architecture is therefore leverage on the supply side of most attacks you will ever investigate. For defenders, architecture dictates response: a centralized botnet can be sinkholed when its C2 domain is seized — instantly neutralizing the fleet — while a P2P botnet requires poisoning the peer graph or taking the protocol apart node by node, and a DGA-driven fleet requires either predicting the algorithm or capturing it from a sample. Law enforcement operations (Conficker working group, GameOver Zeus, Necurs [verify specifics per operation]) succeeded or failed precisely on architectural understanding, and those post-mortems are the best available playbook for infrastructure response. For the analyst, botnet-scale telemetry is also a detection multiplier: one sample yields the DGA seed or peer-list format, and suddenly every infected host in your telemetry is enumerable — a campaign map from a single artifact (M15). Finally, the modern residential-proxy botnets change the defensive calculus of IP reputation itself, which every SOC relies on daily.
Detailed Explanation
Section titled “Detailed Explanation”Three canonical topologies dominate, each with distinct failure modes:
CENTRALIZED P2P (DHT) HYBRID operator operator operator | | | +--+---+ seeds injected at flexible C2 | C2 | any DHT node | +--+---+ | fast-flux front / | \ +------+------+bot bot bot bot bot bot \ | / \ | / +--X----+<-- each peer knows | fallback chain: | some peers +----+----+ DGA -> hardcoded -> P2P single point: peers relay commands (modern standard) sinkhole/seizure kills no single point whole fleet to seize
DGA lifecycle (date-seeded example shape): seed = f(date, campaign_id) for i in 0..1000: domain = base36(LCG(seed,i)) + ".com" bot tries domains in order -> NX for not-yet-registered, resolves once operator registers today's sliceCentralized fleets concentrate command at one or a few C2 servers; their resilience comes from address churn — fast-flux rotates A records across dozens of hosts on short TTLs, domain shadowing registers hundreds of subdomains under stolen zones, and DGA gives every bot the same deterministic list of tomorrow’s names so blocking today’s list buys nothing. P2P fleets (Kademlia DHT is the workhorse) remove the C2 as a concept: the operator injects commands at any node, peers replicate, and there is nothing to seize — response shifts to poisoning the peer table, flooding fake commands, or exploiting protocol bugs to make peers drop each other. Hybrid architectures layer a fallback chain: the bot tries DGA domains, then a hardcoded list, then DHT bootstrap — each layer covering the previous layer’s seizure. Analysis reconstructs this architecture from three artifact classes: the binary’s addressing logic (the DGA algorithm, the hardcoded lists, the DHT bootstrap nodes — all recoverable by reversing or by config extraction per M5), the protocol (beacon and tasking formats, M7), and the population telemetry (DNS logs, flow records, sinkhole data). The population telemetry is where architecture becomes visible as behavior: DGA fleets show clients emitting bursts of NX lookups for algorithm-shaped names (the detector below), P2P fleets show peer-to-peer flows on consistent ports with symmetric session patterns, centralized fleets show classic beaconing (M7). Sinkholing mechanics complete the picture: register or seize the C2 domain, answer with a sinkhole IP, and the fleet’s check-ins become a census — infection counts per org, per geo — which is why defenders who sinkhole gain both containment and visibility at once.
Common Techniques
Section titled “Common Techniques”| Technique | Mechanism | Signature / detection | Notes |
|---|---|---|---|
| DGA (LCG/date-seeded) | Deterministic domain lists | NX bursts, name-shape stats | Algorithm recoverable from sample |
| Fast-flux | A-record rotation, short TTL | TTL < 300s, many IPs/one name | Botnet-as-CDN fronting |
| Domain shadowing | Subdomains under stolen zones | Many subdomains, low NX | Abuses zone reputation |
| Hardcoded fallback lists | Static IP/domain set in binary | Config extraction (M5) | First IOC layer |
| P2P / DHT (Kademlia) | Peer-to-peer command relay | Symmetric peer flows, fixed ports | Nothing central to seize |
| Tor/I2P channels | Anonymized C2 egress | Tor process or .onion DNS | Blocks geo-IP logic |
| Residential proxy ware | Bots sold as egress IPs | Odd egress geo in your logs | Breaks IP reputation |
| Sinkhole resistance | Multi-layer fallback chain | Sequential infra attempts | Needs architectural takedown |
| Cert pinning in bots | Reject MITM on C2 | Lab: patch binary to accept proxy cert | Analysis-side technique |
| Churn management | Re-infection + cleanup cycles | Infection curve shape | Fleet health metric |
Full Working Code
Section titled “Full Working Code”dgahunt.py — DGA-domain detection per client host from a zeek DNS log: scores each internal client on NX-domain volume, the structural shape of queried names (DGA names are algorithmic: long, mixed consonant-vowel patterns, few dictionary words), and sequence regularity. Output is a ranked client list — your bot census candidates. stdlib only.
#!/usr/bin/env python3"""dgahunt.py — DGA-domain detection per client from zeek dns.log.Usage: python dgahunt.py dns.log [--nx-ratio 0.5] [--top 20]stdlib only."""import argparse, math, re, sysfrom collections import Counter, defaultdict
FIELDS = NoneVOWELS = set("aeiou")CONSONANTS = set("bcdfghjklmnpqrstvwxyz")
def parse_zeek(path): """Yield (client, qname, rcode) from zeek dns.log.""" global FIELDS for line in open(path, encoding="utf-8", errors="replace"): line = line.rstrip("\n") if line.startswith("#fields"): FIELDS = line.split("\t")[1:] continue if line.startswith("#") or not line: continue parts = line.split("\t") if not FIELDS: continue try: yield (parts[FIELDS.index("id.orig_h")], parts[FIELDS.index("query")], parts[FIELDS.index("rcode_name")]) except (ValueError, IndexError): continue
def name_shape(name): """Structural features of a queried label.""" label = name.split(".")[0].lower() if not label.isalpha(): return None n = len(label) if n < 8: return None vow = sum(c in VOWELS for c in label) con = sum(c in CONSONANTS for c in label) # alternation: how often adjacent chars switch class alt = sum(1 for a, b in zip(label, label[1:]) if (a in VOWELS) != (b in VOWELS)) uniq = len(set(label)) return {"len": n, "vowel_ratio": vow / n, "alt_ratio": alt / max(n - 1, 1), "uniq_ratio": uniq / n}
def dga_score(shapes): """Score a name-shape series 0-100 for DGA-ness.""" if not shapes: return 0 n = len(shapes) len_ok = sum(1 for s in shapes if s["len"] >= 10) / n alt_hi = sum(1 for s in shapes if s["alt_ratio"] > 0.5) / n uniq_hi = sum(1 for s in shapes if s["uniq_ratio"] > 0.8) / n vow_mid = sum(1 for s in shapes if 0.2 <= s["vowel_ratio"] <= 0.5) / n score = (len_ok * 35 + alt_hi * 25 + uniq_hi * 25 + vow_mid * 15) return round(min(score, 100))
def main(): ap = argparse.ArgumentParser() ap.add_argument("dnslog") ap.add_argument("--nx-ratio", type=float, default=0.5, help="min NX share to consider a client") ap.add_argument("--min-queries", type=int, default=20) ap.add_argument("--top", type=int, default=20) args = ap.parse_args() per_client = defaultdict(lambda: {"nx": 0, "total": 0, "nx_names": []}) for client, qname, rcode in parse_zeek(args.dnslog): c = per_client[client] c["total"] += 1 if rcode == "NXDOMAIN": c["nx"] += 1 c["nx_names"].append(qname) print(f"== DGA hunt: {len(per_client)} clients ==") ranked = [] for client, c in per_client.items(): if c["total"] < args.min_queries: continue nx_ratio = c["nx"] / c["total"] if nx_ratio < args.nx_ratio: continue shapes = [x for x in (name_shape(n) for n in c["nx_names"]) if x] sc = dga_score(shapes) # composite: volume of NX + shape score composite = round(0.5 * sc + 0.5 * min(100, 100 * nx_ratio)) sample = Counter( n.split(".")[0] for n in c["nx_names"]).most_common(3) ranked.append((composite, client, nx_ratio, sc, c["nx"], c["total"], sample)) ranked.sort(reverse=True) if not ranked: print("[-] no client above thresholds " f"(nx>={args.nx_ratio:.0%}, q>={args.min_queries})") return print(f"{'score':>5} {'client':<16} {'nx%':>5} " f"{'shape':>5} {'nx/total':>9} top NX names") print("-" * 88) for comp, client, nxr, sc, nx, tot, sample in ranked[:args.top]: names = ", ".join(f"{n}({c})" for n, c in sample) print(f"{comp:>5} {client:<16} {nxr:>5.0%} " f"{sc:>5} {nx}/{tot:<6} {names[:44]}") if ranked: print("\nnext: pull the DGA list from a sample (M5), " "confirm overlap, then sinkhole/blocklist (M15 feed)")
if __name__ == "__main__": main()Code Walkthrough
Section titled “Code Walkthrough”- Lines 16-35 parse zeek’s DNS log with the same defensive
#fieldsdiscovery as M7’s tools — column order shifts between zeek versions, andFIELDS.index()with try/except keeps the parser silent on malformed rows instead of crashing mid-log. - Lines 38-53 extract the structural features that separate algorithmic names from dictionary words: length (DGA names usually ≥10), vowel ratio (uniform-random name generation lands near the alphabet’s natural vowel density, 0.2-0.5 — human names cluster differently), alternation ratio (LCG output alternates consonant/vowel classes at ~50%, far above English), and unique-character ratio (DGA names rarely repeat chars; dictionary words do).
- Lines 56-67 combine the four features into a weighted 0-100 shape score. The weights are starting points documented in the report — the right calibration comes from your own traffic (step 5 of Lab Practice).
- Lines 83-96 is the per-client aggregation: DGA behavior is a client property (one infected host emits thousands of NX lookups), so the tool buckets by
id.orig_h, applies the NX-ratio gate (a client whose lookups are >50% NX is either infected or resolving a dead zone — both worth a look), then scores the NX names’ shapes. - Lines 98-116 produce the ranked census with composite scoring (half shape, half NX volume) and show each client’s top NX names — the three strings a responder pastes into a sample’s DGA list to confirm overlap.
How It Reaches The Target
Section titled “How It Reaches The Target”The bot reaches the target through the standard delivery channels — this module’s concern is what happens after: the infection enrolls the host into a population with an architecture. A centralized bot receives its C2 list in the binary’s config (M5 extraction), fails over through the list, then works through its DGA schedule — trying today’s slice, getting NX for names the operator hasn’t registered, and connecting once one resolves. The population-level effect reaches your telemetry as behavioral shape: per-client NX bursts (the detector), beacons with fleet-wide synchrony (M7), and DDoS/spam/proxy traffic spikes on schedule. The operator’s monetization reaches the target organization as outbound abuse: your host becomes an attack source — spam with your IP, DDoS from your IP, proxy traffic through your IP — which arrives as complaints, blocklists, and reputation damage before any inbound alert fires. Defenders who understand the architecture respond at the infrastructure layer: blocklist the DGA slice (or better, register unregistered domains to sinkhole), watch sinkhole check-ins for a census of infected internal hosts, and feed the recovered DGA seed into scheduled prediction so tomorrow’s list is known today. The endgame — seizure or DHT poisoning — belongs to law enforcement scale, but the census capability belongs to every SOC with the telemetry this module’s tools process.
Exploitation Chain
Section titled “Exploitation Chain”- Enrollment — host infected by loader; bot binary persisted (T1547) and enrolled
- Bootstrap — hardcoded C2 list seized; bot falls back to DGA schedule
- Enumeration — bot emits 1,000 DGA lookups; ~10 resolve to live fast-flux IPs
- Check-in — beacon through fast-flux front; tasking received (M7 protocol)
- Abuse — host rented as residential proxy egress; org IP appears in abuse feeds
- Detection — dgahunt.py flags host: 47% NX ratio, shape score 81, names match recovered DGA list
- Containment — host isolated; DGA slice registered for sinkhole; census pulled from sinkhole logs
- Attribution — DGA seed + protocol fingerprint (M15) links fleet to known campaign family
Building Your Own Tool
Section titled “Building Your Own Tool”p2pgraph.py — a peer-to-peer botnet mapper for flow telemetry: builds a host-to-host graph from zeek conn logs (hosts that talk directly to each other on consistent ports — the P2P pattern), computes degree and PageRank-style centrality to surface super-peers, and emits GraphML for visualization in standard tools. Centralized botnets have no internal mesh; P2P fleets light up as dense peer graphs — the tool makes that difference quantitative.
Design decisions
Section titled “Design decisions”- Internal-mesh filter, not flow dump. Only host↔host flows on the same service port, bidirectional, with ≥N sessions — filters out client→server traffic that would drown the graph.
- Centrality, not just degree. Super-peers are high-betweenness nodes (removing them fragments the command path); a pure degree count misses bridge peers. networkx provides both; stated dependency.
- GraphML output. Standard format loads into yEd/Gephi — the analyst’s visualization step is zero-effort, which matters because graph-shaped judgment is the deliverable.
- Time-sliced mode. P2P graphs drift as churn happens; slices (per hour) show stability — super-peers persist across slices while noise does not.
Full source
Section titled “Full source”#!/usr/bin/env python3"""p2pgraph.py — P2P botnet mesh mapper from zeek conn.log.Usage: python p2pgraph.py conn.log [--min-sessions 4] \ [--port-filter 4444,8080] [--out mesh.graphml]Deps: networkx (pip install networkx)."""import argparse, sysfrom collections import defaultdictimport networkx as nx
FIELDS = None
def parse_conn(path): """Yield (src, dst, dport, duration) from zeek conn.log.""" global FIELDS for line in open(path, encoding="utf-8", errors="replace"): if line.startswith("#fields"): FIELDS = line.rstrip("\n").split("\t")[1:] continue if line.startswith("#") or not line.strip(): continue p = line.split("\t") if not FIELDS: continue try: src = p[FIELDS.index("id.orig_h")] dst = p[FIELDS.index("id.resp_h")] dport = p[FIELDS.index("id.resp_p")] dur = float(p[FIELDS.index("duration")] or 0) yield src, dst, dport, dur except (ValueError, IndexError): continue
def build_graph(path, min_sessions, port_filter): """Host<->host edges only, same-port service, N+ sessions.""" edges = defaultdict(int) private = lambda ip: (ip.startswith("10.") or ip.startswith("192.168.") or ip.startswith("172.")) for src, dst, dport, dur in parse_conn(path): if port_filter and dport not in port_filter: continue # count only internal-to-internal peer flows if not (private(src) and private(dst)) and \ not (not private(src) and not private(dst)): continue key = tuple(sorted((src, dst))) + (dport,) edges[key] += 1 g = nx.Graph() for (a, b, port), n in edges.items(): if n >= min_sessions: if g.has_edge(a, b): g[a][b]["weight"] += n else: g.add_edge(a, b, weight=n, port=port) return g
def analyze(g): """Rank nodes: degree + betweenness; flag super-peers.""" if not g.number_of_nodes(): return [] deg = dict(g.degree()) bet = nx.betweenness_centrality(g, weight="weight") rows = [] for node in g.nodes(): rows.append((bet[node], deg[node], node)) rows.sort(reverse=True) return rows
def main(): ap = argparse.ArgumentParser() ap.add_argument("connlog") ap.add_argument("--min-sessions", type=int, default=4) ap.add_argument("--port-filter", help="comma list of dports, e.g. 4444,8080") ap.add_argument("--top", type=int, default=15) ap.add_argument("--out") args = ap.parse_args() pf = set(args.port_filter.split(",")) \ if args.port_filter else None g = build_graph(args.connlog, args.min_sessions, pf) print(f"[+] mesh graph: {g.number_of_nodes()} nodes, " f"{g.number_of_edges()} peer edges") if g.number_of_edges() == 0: print("[-] no internal mesh found — traffic shape " "consistent with centralized C2 (see M7)") return rows = analyze(g) print(f"\n{'betweenness':>11} {'degree':>7} node") print("-" * 44) for bet, deg, node in rows[:args.top]: print(f"{bet:>11.4f} {deg:>7} {node}") if args.out: nx.write_graphml(g, args.out) print(f"[+] wrote {args.out} (open in Gephi/yEd)") print("\nnext: cross-reference top-betweenness nodes with " "EID 1/11 telemetry — super-peers are your first " "containment targets")
if __name__ == "__main__": main()Walkthrough
Section titled “Walkthrough”- The internal-mesh filter (lines 41-47) encodes the P2P signature: flows between two internal hosts (or in a lab, between external hosts) on a consistent destination port. The asymmetric
private()check (lines 38-40) is written to accept both internal-internal and external-external pairs so the tool works on lab captures and DMZ telemetry alike. - Edge weighting (lines 49-52) counts sessions rather than bytes — session count is the peer-relationship signal; bytes on a P2P mesh are incidental.
analyze()(lines 62-72) is the intelligence step: betweenness centrality with session-weighted paths identifies the nodes whose removal fragments the command graph — super-peers first, then bridge nodes. Degree alone (a hub) can be a file server; betweenness on a peer mesh is a command-path property.- The empty-graph branch (lines 89-92) is diagnostic, not a failure: zero internal mesh on fleet-scale traffic is itself the finding that points back to centralized architecture and M7’s beacon analysis.
Compilation and test procedure
Section titled “Compilation and test procedure”install dependency
Section titled “install dependency”$ pip install networkx
synthesize a P2P mesh in zeek conn.log format for the lab
Section titled “synthesize a P2P mesh in zeek conn.log format for the lab”$ python3 -c ” import random random.seed(7) peers=[f’10.0.0.{i}’ for i in range(2,26)] with open(‘conn.log’,‘w’) as f: f.write(‘#fields\tts\tuid\tid.orig_h\tid.resp_h\tid.resp_p\tduration\n’) # mesh: every peer talks to 3 others, super-peer 10.0.0.2 to all for i in range(400): a=random.choice(peers); b=random.choice(peers) if a==b: continue if ‘10.0.0.2’ not in (a,b) and random.random()<0.7: continue f.write(f’1\tC{i}\t{a}\t{b}\t4444\t0.4\n’) ” $ python3 p2pgraph.py conn.log —port-filter 4444 —out mesh.graphml [+] mesh graph: 25 nodes, 69 peer edges
betweenness degree node
Section titled “betweenness degree node” 0.4310 23 10.0.0.2 <-- super-peer found$ python3 -c “import networkx as nx; g=nx.read_graphml(‘mesh.graphml’); print(len(g.nodes()))” 25
Detection considerations
Section titled “Detection considerations”The mesh mapper consumes existing flow telemetry — no endpoint agents, no new sensors — which makes it deployable in hours on estates that already run zeek or NetFlow. Precision traps to document in your runbook: internal mesh traffic also occurs legitimately (file sync tools, clustering protocols, RAT-adjacent admin tooling, game voice) — the --port-filter and minimum-session thresholds exist for exactly this, and your baseline run (step 1 of Lab Practice) defines what “normal mesh” looks like on your estate. P2P botnets that abuse existing protocols (BitTorrent DHT, Kademlia on standard ports) need protocol-layer analysis on top of flow shape — the tool finds the mesh, sample analysis (M7) tells you what rides it. Your own tooling: both tools here are log parsers with zero endpoint footprint. For the SIEM layer, dgahunt’s per-client NX statistics map naturally to scheduled zeek-log hunts; Wazuh/Suricata mappings depend on your pipeline [verify], and Suricata’s DGA coverage should rely on your own local rules (sid:1000001+) built from recovered DGA slices rather than third-party domain lists, which lag the algorithm by design.
Improvements to add
Section titled “Improvements to add”- Time-slice the graph (hourly) and report only super-peers stable across ≥80% of slices — churn-noise elimination.
- Join with DNS telemetry: peers resolving the same DGA slice in the same window are fleet members — cross-signal confirmation.
- Add community detection (Louvain via networkx) to segment the mesh into command clusters per campaign.
- Export a ready-to-import yEd
.graphmlwith super-peer styling so the report figure is one click. - Accept NetFlow/IPFIX as an alternate input so the tool runs on estates without zeek.
Detection — Defender View
Section titled “Detection — Defender View”| Signal | Source | ID | Notes |
|---|---|---|---|
| Per-client NX bursts + DGA name shape | zeek dns.log | daily hunt (dgahunt) | Highest-value DGA anchor |
| DGA slice blocks/sinkhole hits | resolver/sinkhole logs | feed + custom | Census of infected hosts |
| Fast-flux: low TTL, many A records | DNS monitoring | TTL < 300s on A sets | Front infrastructure flag |
| Domain shadowing under legit zones | DNS monitoring | many subdomains, low NX | Zone-reputation abuse |
| Internal peer mesh on odd port | zeek conn.log / NetFlow | p2pgraph hunt | P2P fleet signature |
| Beacon synchrony across hosts | Proxy/flow (M7 stats) | fleet-wide CV analysis | Centralized fleet signature |
| Egress abuse: your IP in blocklists | Reputation feeds | external complaints | Proxy-ware fallout |
| Tor egress from workstations | zeek/EDR | EID 1 (tor.exe) + EID 3 | Anonymized C2 transport |
| Known C2 protocol fingerprints | Suricata | custom/local sid:1000001+ | From M7 protocol work |
| Wazuh correlation | HIDS/SIEM | [verify rule IDs per deployment] | Join DNS+flow+process per host |
Evasion Techniques
Section titled “Evasion Techniques”Botnet architecture is itself the evasion strategy — every design choice answers “what happens when they find our C2”. DGA defeats domain blocklists by construction: tomorrow’s list doesn’t exist in any feed until a sample is captured; counters recover the algorithm from the binary (M5) or capture it via sinkhole intelligence, then predict the list. Fast-flux defeats IP blocklists by rotating fronts faster than feeds update; counters move up a layer to name-level blocking and reputation of the rotating pool. Domain shadowing hides inside legitimate zones’ reputation — counters watch per-zone subdomain growth rather than per-name reputation. P2P topologies remove the seizure target entirely; the response toolkit becomes graph-level (the mapper above) plus protocol-level poisoning, which is research-grade work. Tor and encrypted channels defeat both IP visibility and MITM tasking recovery; counters shift to endpoint-side telemetry (process context for tor.exe, EDR channel metadata). Cert pinning in the bot blocks traffic interception in labs — countered by binary patching during analysis, which is standard but must be documented per sample. The architectural meta-point: every resilience layer adds a detection surface of its own — DGA yields NX bursts, P2P yields mesh graphs, fast-flux yields TTL anomalies. Perfectly invisible command infrastructure would also be operationally fragile; the operator’s resilience budget is the defender’s opportunity budget.
⚠️ What caught me: we blocked a botnet’s DGA domains confidently — the algorithm was recovered, the week’s list computed, perimeter blocks deployed — and infections kept beaconing through an infrastructure layer the sample never showed us: the fleet’s backup channel was DNS TXT records against a legitimate-looking zone, invisible to our DGA math and only visible in full DNS query logs. Since then, every botnet response plan I write includes the question “what does the next fallback layer look like” — architecture analysis ends when you’ve mapped every layer, not the first one you found.
Lab Practice
Section titled “Lab Practice”- Generate a synthetic zeek dns.log with a date-seeded DGA emitter (implement the generator in ~20 lines — deterministic, documented) mixed with benign lookups; run
dgahunt.pyand tune thresholds until the infected clients separate cleanly — then add realistic noise (CDN NX traffic) and re-tune; document the threshold delta. - Build the P2P lab mesh from the terminal block, run
p2pgraph.py, and confirm the super-peer surfaces; then remove the super-peer’s flows and re-run to see betweenness redistribute — the removal experiment that teaches why centrality beats degree. - Implement a minimal date-seeded DGA and predict next week’s list from today’s observations alone; register one unregistered domain from the predicted slice in a lab DNS and observe a lab bot connect — the sinkhole mechanic end to end.
- Capture your own estate’s baseline: run both tools on a day of real (or simulated-scale) telemetry and record the noise floor — your documented FP profile for when these run in anger.
- Read one public takedown post-mortem (e.g., GameOver Zeus or Necurs write-ups [verify current links]) and map its operation steps to this module’s techniques — architecture analysis as it happens at law-enforcement scale.
Interview Questions
Section titled “Interview Questions”- Technical: Compare centralized, P2P, and hybrid botnet architectures on takedown resistance — what must an operator and a defender each do per architecture?
- Technical: Explain the statistical features that separate DGA-generated domain names from human-chosen ones, and why each feature works.
- Scenario: Your DGA blocklist is deployed but infections continue beaconing. Diagnose the likely fallback architecture and the telemetry that confirms it.
- Conceptual: Why does sinkholing a centralized botnet provide both containment and a census, and why does neither apply to a DHT fleet?
- Conceptual: How do residential proxy botnets change the reliability of IP-reputation blocking, and what replaces it?
- Deep-dive: Design the betweenness-based super-peer hunt for a fleet where churn rotates 10% of nodes daily. Why is time-sliced stability essential?
- Practical: Your org’s egress IPs appear on spam blocklists but no internal alerts fired. What botnet-role hypotheses do you test and with which telemetry?
Related Modules
Section titled “Related Modules”M3 (Propagation Analysis (WannaCry CVE-2017-0144)) · M7 (C2 Protocol Analysis) · M15 (Threat Intelligence and Campaign Clustering) · E17 (Network Detection) · BT2 (IDS Rules)