The bottleneck of userspace packet capture
Traditional network sniffing tools—such as tcpdump, dsniff, and legacy driftnet—were designed for an era when 100 Mbps or 1 Gbps networks were the ceiling. On modern 10 Gbps and 40 Gbps enterprise backbones, traditional packet capture architectures fail.
The core problem is the journey a packet takes through the Linux network stack:
- NIC Interrupt: The network interface card receives a frame and triggers a hard interrupt (IRQ).
- SKB Allocation: The kernel allocates a
struct sk_buff(socket buffer) in memory. This allocation incurs lock contention, cache misses, and significant memory overhead. - Network Stack Traversal: The packet moves up through IP tables, firewall hooks, routing tables, and transport layers.
- Userspace Copy: The packet reaches the raw socket or
libpcapbuffer, forcing a context switch and a memory copy from kernel space to userspace.
Under sustained gigabit traffic, the CPU spends most of its cycles allocating and freeing sk_buff structures. When packet arrival rates exceed the userspace consumer’s processing capacity, socket receive buffers overflow and packets are silently dropped. For red-team operators, dropped packets mean missed authentication handshakes; for defensive teams, they mean blind spots in network visibility.
This is where eBPF (Extended Berkeley Packet Filter) and XDP (eXpress Data Path) rewrite the architecture.
The dual-path architecture of driftnet2
To eliminate packet drops and extract credentials at line rate, we developed driftnet2. It attaches an eBPF program directly to the network driver via XDP:
1. In-kernel XDP filtering (xdp_sniff.c)
XDP executes verified bytecode at the lowest possible layer—inside the network device driver before the kernel allocates an sk_buff.
The eBPF program parses the Layer 3 and Layer 4 headers directly in the receive ring buffer. If the packet does not match target authentication ports (such as TCP 80, 445, 389, 21, 23, 110, 143, 587 or UDP 53), the hook returns XDP_PASS immediately. Only candidate packets are copied into a high-performance BPF Ring Buffer (BPF_MAP_TYPE_RINGBUF):
// bpf/xdp_sniff.c: Line-rate L4 port filtering
SEC("xdp")
int xdp_credential_filter(struct xdp_md *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return XDP_PASS;
if (eth->h_proto != bpf_htons(ETH_P_IP))
return XDP_PASS;
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end)
return XDP_PASS;
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (void *)ip + (ip->ihl * 4);
if ((void *)(tcp + 1) > data_end)
return XDP_PASS;
__u16 dport = bpf_ntohs(tcp->dest);
__u16 sport = bpf_ntohs(tcp->source);
// Filter: HTTP(80), SMB(445), LDAP(389), FTP(21), Telnet(23), Mail(110,143,587)
if (is_target_port(dport) || is_target_port(sport)) {
submit_to_ringbuf(ctx, ip, tcp);
}
}
return XDP_PASS;
}
By filtering at driver level, the userspace engine only processes packets likely to contain credentials, reducing CPU consumption by up to 90% during high-throughput saturation.
2. Dual-path ingestion & portability
If the target machine does not support native XDP (or when running on macOS), driftnet2 automatically falls back to an optimized libpcap capture loop. Both capture paths feed into the same stream of protocol parsers.
Dissecting credentials across 9 protocol engines
driftnet2 includes zero-dependency protocol dissectors written specifically for credential and hash extraction:
| Protocol | Default Ports | Credential Type & Technique | Hashcat Mode | Output Format |
|---|---|---|---|---|
| SMB / CIFS | 445, 139 | NTLMv2 Challenge/Response handshake reconstruction | 5600 | username::domain:server_challenge:ntproof:blob |
| LDAP | 389 | ASN.1 BER parsing of Simple Bind requests | — | bind_dn:password |
| HTTP | 80, 8080 | Basic Auth, Form POST fields, Bearer tokens, cookies | — | user:password or Bearer <jwt> |
| FTP | 21 | Cleartext USER / PASS command sequencing | — | user:password |
| Telnet | 23 | Interactive session and keystroke reassembly | — | login:password |
| POP3 / IMAP | 110, 143 | USER/PASS, AUTH PLAIN, and LOGIN commands | — | user:password |
| SMTP | 25, 587 | AUTH LOGIN (Base64) and AUTH PLAIN exchanges | — | user:password |
| DNS | 53 | Shannon entropy scoring for covert tunnel detection | — | subdomain.c2.example.com |
Extracting NetNTLMv2 without relay proxies
During an SMB authentication handshake, the client and server exchange three messages:
- Type 1 (Negotiate): The client advertises NTLM capabilities.
- Type 2 (Challenge): The server returns an 8-byte server challenge.
- Type 3 (Authenticate): The client returns the domain, username, an NTProofStr HMAC, and a variable-length target blob containing timestamps and client nonces.
Traditional tools (like Responder) act as rogue servers to force this exchange. driftnet2, however, captures this handshake passively from legitimate traffic. The dissector correlates Type 2 challenges with Type 3 responses across active TCP sessions, extracting the exact tokens required for cracking:
# Hashcat Mode 5600 NetNTLMv2 output emitted by driftnet2:
CORP\jsmith::CORP-DC:1122334455667788:AABBCCDDEEFF00112233445566778899:0101000000000000...
The resulting hash can be fed directly to hashcat -m 5600 hashes.txt /path/to/wordlist.txt without post-processing or manual formatting.
Covert DNS tunnel detection
Adversaries frequently use DNS tunneling (tools like dnscat2 or iodine) to bypass firewall egress restrictions. driftnet2 evaluates every DNS query against a Shannon entropy threshold:
$$H(X) = -\sum_{i=1}^{n} P(x_i) \log_2 P(x_i)$$
When subdomains exhibit high entropy ($H > 3.8$) alongside excessive label length ($> 50$ characters), driftnet2 flags the transaction as an active protocol tunneling indicator ([T1071.004]).
Live execution & offline PCAP triage
driftnet2 operates in both live capture mode and offline analysis mode. When analyzing a network packet capture (.pcap), the tool runs with zero root privileges:
# Analyze test capture, dump credentials, and run exposure audit
./driftnet2 -pcap examples/demo.pcap -output creds.json -audit
The console displays captured authentication events in real time:
____ _ ______ __ ___
/ __ \_____(_)/ __/ /_____ ___ / /_ |__ \
/ / / / ___/ // /_/ __/ __ \/ _ \ __/ __/ /
/ /_/ / / / // __/ /_/ / / / __/ /_ / __/
/_____/_/ /_//_/ \__/_/ /_/\___/\__|____/
kernel-level packet sniffer & credential extractor
[*] offline mode: examples/demo.pcap
[FTP] Login 192.0.2.10 → 198.51.100.20:21 — demo:[redacted]
[HTTP] Basic Auth 192.0.2.11 → 198.51.100.21:80 — demo:[redacted]
[POP3] Login 192.0.2.12 → 198.51.100.22:110 — [email protected]:[redacted]
[IMAP] Login 192.0.2.13 → 198.51.100.23:143 — [email protected]:[redacted]
[SMTP] AUTH PLAIN 192.0.2.14 → 198.51.100.24:587 — postmaster:[redacted]
[TELNET] Session 192.0.2.15 → 198.51.100.25:23 — [redacted payload]
[LDAP] Simple Bind 192.0.2.16 → 198.51.100.26:389 — cn=admin,dc=corp:[redacted]
[SMB] NTLMv2 192.0.2.17 → 198.51.100.27:445 — [redacted hash]
[DNS] Tunnel Detection 192.0.2.18 → 8.8.8.8:53 — DNS: TXlDMkV4ZmlsUGF5bG9hZERhdGFHb2VzSGVyZQ.c2.example.test
[*] 9 packets processed, 9 unique credentials found in examples/demo.pcap
[*] saved → creds.json
Defensive gap analysis: the exposure audit engine
While red teams utilize driftnet2 for passive credential harvesting during internal engagements, blue teams use the built-in -audit engine to quantify unencrypted exposure risks across corporate subnets:
# Generate defensive gap audit directly from a SPAN or TAP port capture
./driftnet2 -pcap network_traffic.pcap -audit -audit-output audit_report.json
The audit engine categorizes findings by severity and supplies concrete remediation steps:
Credential Exposure Audit
=========================
9 findings — 7 High, 1 Medium, 1 Low
[HIGH] SMB — Password hash (1)
Endpoints: 192.0.2.17 -> 198.51.100.27
Fix: Require SMB signing/encryption, restrict NTLM (prefer Kerberos), and enable Extended Protection for Authentication.
[HIGH] HTTP — Cleartext password (1)
Endpoints: 192.0.2.11 -> 198.51.100.21
Fix: Enforce HTTPS/HSTS; never send Basic auth, form credentials, or tokens over cleartext HTTP.
[HIGH] LDAP — Cleartext password (1)
Endpoints: 192.0.2.16 -> 198.51.100.26
Fix: Enforce LDAPS (TCP/636) or StartTLS; reject simple bind authentication over cleartext.
[MEDIUM] DNS — DNS tunnel indicator (1)
Endpoints: 192.0.2.18 -> 8.8.8.8
Fix: Investigate for tunneling/exfiltration; apply DNS filtering policy and query rate limiting.
| Finding Category | Protocol | Threat Mechanism | Remediation |
|---|---|---|---|
| High | SMB / NetNTLMv2 | Offline hash cracking via Hashcat dictionary/rule attacks | Enforce Kerberos authentication, disable NTLMv1/v2, mandate SMB signing |
| High | LDAP Simple Bind | Plaintext directory credential exposure across internal switches | Enforce LDAPS (TCP 636) or require LDAP channel binding and signing |
| High | HTTP Basic / Forms | Unencrypted authentication credentials readable on local wire | Enforce HTTPS with HSTS, terminate legacy HTTP port 80 listeners |
| Medium | DNS Tunneling | Covert outbound C2 communication channel bypassing firewalls | Enforce internal DNS resolvers, implement entropy filtering, block external UDP/53 |
Evidence and limits
All evaluation workflows and packet parsing tests were verified using synthetic, reproducible PCAP test fixtures generated inside isolated laboratory networks. In-kernel eBPF/XDP testing was conducted on Linux kernel 6.x environments using physical and virtual network interfaces. On macOS and non-Linux kernels, packet capture operates via the standard libpcap fallback interface. No production networks or unauthorized communications were monitored during tool testing.
References
- jankesec, driftnet2 (Kernel-Level Network Sniffer & Auditor): github.com/jankesec/driftnet2
- Linux Kernel Documentation, eXpress Data Path (XDP): docs.kernel.org/networking/af_xdp.html
- MITRE ATT&CK, Network Sniffing (T1040): attack.mitre.org/techniques/T1040/
- MITRE ATT&CK, Adversary-in-the-Middle (T1557.001): attack.mitre.org/techniques/T1557/001/
- Hashcat, NetNTLMv2 Cracking Guide: hashcat.net/wiki/doku.php?id=example_hashes
How current is this note?
The latest source-review, content-update, or publication date is shown.
The author completed a technical review. This does not, by itself, claim lab reproduction.

