ctf-agent 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/AGENTS.md +131 -0
  2. package/Dockerfile +192 -0
  3. package/README.md +522 -0
  4. package/agents/ctf-analyzer.md +58 -0
  5. package/agents/ctf-controller.md +151 -0
  6. package/agents/ctf-speedrun.md +74 -0
  7. package/bin/ctf-agent.js +119 -0
  8. package/docker-compose.yml +13 -0
  9. package/mcp_config.json +19 -0
  10. package/package.json +44 -0
  11. package/references/ctf-triage-ladder.md +68 -0
  12. package/references/exploit-databases.md +575 -0
  13. package/references/llm-safety-and-policy-compliance.md +173 -0
  14. package/references/multi-agent-orchestration-and-policy-routing.md +278 -0
  15. package/references/security-events-and-intelligence.md +566 -0
  16. package/references/version-matrix.md +59 -0
  17. package/rules/ctf-execution-rules.md +44 -0
  18. package/rules/ctf-safety-framing-rules.md +140 -0
  19. package/rules/flag-validation-rules.md +25 -0
  20. package/scripts/__init__.py +1 -0
  21. package/scripts/ctf-tools.lock +205 -0
  22. package/scripts/ctf_init.py +1026 -0
  23. package/scripts/ctf_update.py +583 -0
  24. package/scripts/ctfd_client.py +81 -0
  25. package/scripts/cve_lookup.py +453 -0
  26. package/scripts/extract_flags.py +155 -0
  27. package/scripts/install_as_agent.py +278 -0
  28. package/scripts/install_ctf_tools.sh +603 -0
  29. package/scripts/parallel_triage.py +395 -0
  30. package/scripts/prompt_policy_sanitizer.py +283 -0
  31. package/scripts/scope_guard.py +444 -0
  32. package/scripts/skill_validator.py +504 -0
  33. package/scripts/workspace_cleaner.py +141 -0
  34. package/skills/ctf-ai-ml/SKILL.md +120 -0
  35. package/skills/ctf-ai-ml/adversarial-ml.md +623 -0
  36. package/skills/ctf-ai-ml/llm-attacks.md +487 -0
  37. package/skills/ctf-ai-ml/model-attacks.md +422 -0
  38. package/skills/ctf-crypto/SKILL.md +320 -0
  39. package/skills/ctf-crypto/advanced-math.md +798 -0
  40. package/skills/ctf-crypto/classic-ciphers.md +651 -0
  41. package/skills/ctf-crypto/ecc-attacks.md +347 -0
  42. package/skills/ctf-crypto/exotic-crypto-2.md +380 -0
  43. package/skills/ctf-crypto/exotic-crypto.md +528 -0
  44. package/skills/ctf-crypto/historical.md +113 -0
  45. package/skills/ctf-crypto/lattice-and-lwe.md +524 -0
  46. package/skills/ctf-crypto/modern-ciphers-2.md +563 -0
  47. package/skills/ctf-crypto/modern-ciphers-3.md +453 -0
  48. package/skills/ctf-crypto/modern-ciphers.md +649 -0
  49. package/skills/ctf-crypto/prng-attacks.md +257 -0
  50. package/skills/ctf-crypto/prng.md +664 -0
  51. package/skills/ctf-crypto/rsa-attacks-2.md +792 -0
  52. package/skills/ctf-crypto/rsa-attacks.md +487 -0
  53. package/skills/ctf-crypto/stream-ciphers.md +390 -0
  54. package/skills/ctf-crypto/zkp-and-advanced.md +456 -0
  55. package/skills/ctf-forensics/3d-printing.md +121 -0
  56. package/skills/ctf-forensics/SKILL.md +379 -0
  57. package/skills/ctf-forensics/disk-advanced.md +497 -0
  58. package/skills/ctf-forensics/disk-and-memory.md +491 -0
  59. package/skills/ctf-forensics/disk-recovery.md +699 -0
  60. package/skills/ctf-forensics/linux-forensics.md +511 -0
  61. package/skills/ctf-forensics/network-advanced.md +583 -0
  62. package/skills/ctf-forensics/network.md +645 -0
  63. package/skills/ctf-forensics/peripheral-capture.md +287 -0
  64. package/skills/ctf-forensics/signals-and-hardware.md +713 -0
  65. package/skills/ctf-forensics/steganography.md +694 -0
  66. package/skills/ctf-forensics/stego-advanced-2.md +475 -0
  67. package/skills/ctf-forensics/stego-advanced.md +481 -0
  68. package/skills/ctf-forensics/stego-image.md +691 -0
  69. package/skills/ctf-forensics/windows.md +625 -0
  70. package/skills/ctf-malware/SKILL.md +181 -0
  71. package/skills/ctf-malware/c2-and-protocols.md +274 -0
  72. package/skills/ctf-malware/pe-and-dotnet.md +108 -0
  73. package/skills/ctf-malware/scripts-and-obfuscation.md +449 -0
  74. package/skills/ctf-misc/SKILL.md +498 -0
  75. package/skills/ctf-misc/bashjails.md +323 -0
  76. package/skills/ctf-misc/ctfd-navigation.md +465 -0
  77. package/skills/ctf-misc/dns.md +255 -0
  78. package/skills/ctf-misc/encodings-advanced.md +504 -0
  79. package/skills/ctf-misc/encodings.md +431 -0
  80. package/skills/ctf-misc/games-and-vms-2.md +254 -0
  81. package/skills/ctf-misc/games-and-vms-3.md +690 -0
  82. package/skills/ctf-misc/games-and-vms-4.md +229 -0
  83. package/skills/ctf-misc/games-and-vms.md +529 -0
  84. package/skills/ctf-misc/linux-privesc.md +333 -0
  85. package/skills/ctf-misc/pyjails.md +671 -0
  86. package/skills/ctf-misc/rf-sdr.md +91 -0
  87. package/skills/ctf-osint/SKILL.md +198 -0
  88. package/skills/ctf-osint/geolocation-and-media.md +464 -0
  89. package/skills/ctf-osint/social-media.md +312 -0
  90. package/skills/ctf-osint/web-and-dns.md +341 -0
  91. package/skills/ctf-pwn/SKILL.md +214 -0
  92. package/skills/ctf-pwn/advanced-exploits-2.md +579 -0
  93. package/skills/ctf-pwn/advanced-exploits-3.md +598 -0
  94. package/skills/ctf-pwn/advanced-exploits-4.md +590 -0
  95. package/skills/ctf-pwn/advanced-exploits-5.md +119 -0
  96. package/skills/ctf-pwn/advanced-exploits.md +773 -0
  97. package/skills/ctf-pwn/advanced.md +326 -0
  98. package/skills/ctf-pwn/field-notes.md +245 -0
  99. package/skills/ctf-pwn/format-string.md +694 -0
  100. package/skills/ctf-pwn/heap-fsop.md +285 -0
  101. package/skills/ctf-pwn/heap-techniques-2.md +333 -0
  102. package/skills/ctf-pwn/heap-techniques.md +513 -0
  103. package/skills/ctf-pwn/kernel-bypass.md +421 -0
  104. package/skills/ctf-pwn/kernel-techniques.md +366 -0
  105. package/skills/ctf-pwn/kernel.md +636 -0
  106. package/skills/ctf-pwn/overflow-basics.md +611 -0
  107. package/skills/ctf-pwn/rop-advanced.md +725 -0
  108. package/skills/ctf-pwn/rop-and-shellcode.md +659 -0
  109. package/skills/ctf-pwn/sandbox-escape.md +313 -0
  110. package/skills/ctf-reverse/SKILL.md +163 -0
  111. package/skills/ctf-reverse/anti-analysis-ctf.md +204 -0
  112. package/skills/ctf-reverse/anti-analysis.md +693 -0
  113. package/skills/ctf-reverse/field-notes.md +376 -0
  114. package/skills/ctf-reverse/languages-compiled.md +666 -0
  115. package/skills/ctf-reverse/languages-platforms.md +592 -0
  116. package/skills/ctf-reverse/languages.md +553 -0
  117. package/skills/ctf-reverse/patterns-ctf-2.md +397 -0
  118. package/skills/ctf-reverse/patterns-ctf-3.md +797 -0
  119. package/skills/ctf-reverse/patterns-ctf.md +670 -0
  120. package/skills/ctf-reverse/patterns-runtime.md +274 -0
  121. package/skills/ctf-reverse/patterns.md +572 -0
  122. package/skills/ctf-reverse/platforms-hardware.md +387 -0
  123. package/skills/ctf-reverse/platforms.md +664 -0
  124. package/skills/ctf-reverse/tools-advanced-2.md +421 -0
  125. package/skills/ctf-reverse/tools-advanced.md +407 -0
  126. package/skills/ctf-reverse/tools-dynamic.md +679 -0
  127. package/skills/ctf-reverse/tools-emulation.md +319 -0
  128. package/skills/ctf-reverse/tools.md +573 -0
  129. package/skills/ctf-web/SKILL.md +153 -0
  130. package/skills/ctf-web/auth-and-access-2.md +82 -0
  131. package/skills/ctf-web/auth-and-access.md +783 -0
  132. package/skills/ctf-web/auth-infra.md +321 -0
  133. package/skills/ctf-web/auth-jwt.md +186 -0
  134. package/skills/ctf-web/client-side-advanced.md +739 -0
  135. package/skills/ctf-web/client-side.md +529 -0
  136. package/skills/ctf-web/cves.md +373 -0
  137. package/skills/ctf-web/field-notes.md +482 -0
  138. package/skills/ctf-web/node-and-prototype.md +200 -0
  139. package/skills/ctf-web/server-side-2.md +337 -0
  140. package/skills/ctf-web/server-side-advanced-2.md +559 -0
  141. package/skills/ctf-web/server-side-advanced-3.md +125 -0
  142. package/skills/ctf-web/server-side-advanced-4.md +480 -0
  143. package/skills/ctf-web/server-side-advanced.md +378 -0
  144. package/skills/ctf-web/server-side-deser.md +443 -0
  145. package/skills/ctf-web/server-side-exec-2.md +799 -0
  146. package/skills/ctf-web/server-side-exec.md +457 -0
  147. package/skills/ctf-web/server-side.md +629 -0
  148. package/skills/ctf-web/sql-injection.md +790 -0
  149. package/skills/ctf-web/web3.md +374 -0
  150. package/skills/ctf-writeup/SKILL.md +90 -0
  151. package/skills/solve-challenge/SKILL.md +269 -0
  152. package/skills.json +16 -0
@@ -0,0 +1,583 @@
1
+ # CTF Forensics - Network (Advanced)
2
+
3
+ For USB/HID/Bluetooth peripheral capture analysis (mouse/pen drawing recovery, keyboard scan codes, LED Morse exfiltration, RFCOMM reassembly), see [peripheral-capture.md](peripheral-capture.md). For basic network forensics, see [network.md](network.md).
4
+
5
+ ## Table of Contents
6
+ - [Packet Interval Timing-Based Encoding (EHAX 2026)](#packet-interval-timing-based-encoding-ehax-2026)
7
+ - [NTLMv2 Hash Cracking from PCAP (Pragyan 2026)](#ntlmv2-hash-cracking-from-pcap-pragyan-2026)
8
+ - [TCP Flag Covert Channel (BearCatCTF 2026)](#tcp-flag-covert-channel-bearcatctf-2026)
9
+ - [DNS Query Name Last-Byte Steganography (UTCTF 2026)](#dns-query-name-last-byte-steganography-utctf-2026)
10
+ - [DNS Trailing Byte Binary Encoding (UTCTF 2026)](#dns-trailing-byte-binary-encoding-utctf-2026)
11
+ - [Multi-Layer PCAP with XOR + ZIP (UTCTF 2026)](#multi-layer-pcap-with-xor--zip-utctf-2026)
12
+ - [Brotli Decompression Bomb Seam Analysis (BearCatCTF 2026)](#brotli-decompression-bomb-seam-analysis-bearcatctf-2026)
13
+ - [SMB RID Recycling via LSARPC (Midnight 2026)](#smb-rid-recycling-via-lsarpc-midnight-2026)
14
+ - [Timeroasting / MS-SNTP Hash Extraction (Midnight 2026)](#timeroasting--ms-sntp-hash-extraction-midnight-2026)
15
+ - [ICMP Payload Steganography with Byte Rotation (HackIM 2016)](#icmp-payload-steganography-with-byte-rotation-hackim-2016)
16
+ - [Packet Reconstruction via Checksum Validation (Break In 2016)](#packet-reconstruction-via-checksum-validation-break-in-2016)
17
+ - [dnscat2 Traffic Reassembly from DNS PCAP (BSidesSF 2017)](#dnscat2-traffic-reassembly-from-dns-pcap-bsidessf-2017)
18
+ - [Unreferenced PDF Objects with Hidden Pages (SharifCTF 7 2016)](#unreferenced-pdf-objects-with-hidden-pages-sharifctf-7-2016)
19
+ - [RDP Session Decryption via Extracted PKCS12 Key (HITB 2017)](#rdp-session-decryption-via-extracted-pkcs12-key-hitb-2017)
20
+ - [RADIUS Shared Secret Cracking (UConn CyberSEED 2017)](#radius-shared-secret-cracking-uconn-cyberseed-2017)
21
+ - [RC4 Stream Identification in Shellcode PCAP (CODE BLUE 2017)](#rc4-stream-identification-in-shellcode-pcap-code-blue-2017)
22
+ - [ICMP Ping Time-Delay Covert Channel (DefCamp 2018)](#icmp-ping-time-delay-covert-channel-defcamp-2018)
23
+
24
+ ---
25
+
26
+ ## Packet Interval Timing-Based Encoding (EHAX 2026)
27
+
28
+ **Pattern (Breathing Void):** Large PCAPNG with millions of packets, but only a few hundred on one interface carry data. The signal is in the **timing gaps** between identical packets, not their content.
29
+
30
+ **Identification:** Challenge mentions "breathing", "void", "silence", or timing. PCAP has many interfaces but only one has interesting traffic. Packets are identical but spaced at two distinct intervals.
31
+
32
+ **Decoding workflow:**
33
+ ```python
34
+ from scapy.all import rdpcap
35
+
36
+ packets = rdpcap('challenge.pcapng')
37
+
38
+ # 1. Filter to the right interface (e.g., interface 2)
39
+ # tshark: tshark -r challenge.pcapng -Y "frame.interface_id == 2" -T fields -e frame.time_epoch
40
+
41
+ # 2. Compute inter-packet intervals
42
+ times = [float(pkt.time) for pkt in packets if pkt.sniffed_on == 'interface_2']
43
+ intervals = [times[i+1] - times[i] for i in range(len(times)-1)]
44
+
45
+ # 3. Identify binary mapping (two distinct interval values)
46
+ # E.g., 10ms → 0, 100ms → 1 (threshold at ~50ms)
47
+ threshold = 0.05 # 50ms
48
+ bits = [0 if dt < threshold else 1 for dt in intervals]
49
+
50
+ # 4. May need to prepend a leading 0 bit (first interval has no predecessor)
51
+ bits = [0] + bits
52
+
53
+ # 5. Convert bits to bytes (MSB-first)
54
+ data = bytes(int(''.join(str(b) for b in bits[i:i+8]), 2)
55
+ for i in range(0, len(bits) - 7, 8))
56
+ print(data.decode(errors='replace'))
57
+ ```
58
+
59
+ **Key insight:** When identical packets appear on a single interface with only two practical interval values, it's almost certainly binary encoding via timing. The content is noise — the signal is in the gaps. Filter by interface and count unique intervals first.
60
+
61
+ **Scale tip:** Large PCAPs (millions of packets) often have the signal in a tiny subset. Triage with `tshark -q -z io,phs` to find which interface has the fewest packets — that's likely the data carrier.
62
+
63
+ ---
64
+
65
+ ## NTLMv2 Hash Cracking from PCAP (Pragyan 2026)
66
+
67
+ **Pattern ($whoami):** SMB2 authentication in packet capture.
68
+
69
+ **Extraction:** From NTLMSSP_AUTH packet, extract: server challenge, NTProofStr, and blob.
70
+
71
+ **Brute-force with known password format:**
72
+ ```python
73
+ import hashlib, hmac
74
+ from Crypto.Hash import MD4
75
+
76
+ def try_password(password, username, domain, server_challenge, blob, expected_proof):
77
+ nt_hash = MD4.new(password.encode('utf-16-le')).digest()
78
+ identity = (username.upper() + domain).encode('utf-16-le')
79
+ ntlmv2_hash = hmac.new(nt_hash, identity, hashlib.md5).digest()
80
+ proof = hmac.new(ntlmv2_hash, server_challenge + blob, hashlib.md5).digest()
81
+ return proof == expected_proof
82
+ ```
83
+
84
+ ---
85
+
86
+ ## TCP Flag Covert Channel (BearCatCTF 2026)
87
+
88
+ **Pattern (pCapsized):** Suspicious TCP packets with chaotic flag combinations (FIN+SYN, SYN+RST+PSH+URG, etc.). The 6 TCP flag bits encode base64 characters.
89
+
90
+ **Decoding:**
91
+ ```python
92
+ from scapy.all import rdpcap, TCP
93
+
94
+ pkts = rdpcap('capture.pcap')
95
+ suspicious = [p for p in pkts if TCP in p and p[TCP].dport == 5748]
96
+
97
+ # Map 6-bit flag value to base64 alphabet
98
+ b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
99
+ encoded = ''.join(b64[p[TCP].flags & 0x3F] for p in suspicious)
100
+
101
+ import base64
102
+ flag = base64.b64decode(encoded).decode()
103
+ ```
104
+
105
+ **Key insight:** TCP has 6 standard flag bits (FIN, SYN, RST, PSH, ACK, URG) = values 0-63, matching the base64 alphabet exactly. Unusual flag combinations on otherwise normal-looking packets indicate covert channel usage. Filter by destination port or source IP to isolate the channel.
106
+
107
+ **Detection:** Packets with nonsensical flag combinations (e.g., FIN+SYN simultaneously). Consistent destination port. Packet count is a multiple of 4 (base64 alignment).
108
+
109
+ ---
110
+
111
+ ## DNS Query Name Last-Byte Steganography (UTCTF 2026)
112
+
113
+ **Pattern (Last Byte Standing):** PCAP with DNS queries where data is encoded in the last byte of each query name.
114
+
115
+ **Identification:** Many DNS queries to unusual or sequential subdomains. The meaningful data is NOT in the query name itself but in the final byte/character of each name.
116
+
117
+ **Decoding workflow:**
118
+ ```python
119
+ from scapy.all import rdpcap, DNS, DNSQR
120
+
121
+ packets = rdpcap('last-byte-standing.pcap')
122
+
123
+ data = []
124
+ for pkt in packets:
125
+ if pkt.haslayer(DNSQR):
126
+ qname = pkt[DNSQR].qname.decode(errors='replace').rstrip('.')
127
+ if qname:
128
+ data.append(qname[-1]) # Last character of query name
129
+
130
+ # Reconstruct message from last bytes
131
+ message = ''.join(data)
132
+ print(message)
133
+ # May need additional decoding (hex, base64, etc.)
134
+ ```
135
+
136
+ **Variants:**
137
+ - Last byte of each subdomain label (split on `.`)
138
+ - Specific character position (first, Nth, last)
139
+ - Hex-encoded bytes across multiple queries
140
+ - Subdomain labels as base32/base64 chunks (DNS tunneling)
141
+ - **Trailing byte after DNS question structure** (see below)
142
+
143
+ **Key insight:** DNS exfiltration often hides data in query names. When queries look random but follow a pattern, extract specific character positions. The "last byte" pattern is simple but effective — each query contributes one byte to the message.
144
+
145
+ **Detection:** Large number of DNS queries to a single domain, queries with no legitimate purpose, sequential or patterned subdomain names.
146
+
147
+ ### DNS Trailing Byte Binary Encoding (UTCTF 2026)
148
+
149
+ **Pattern (Last Byte Standing variant):** Each DNS query packet contains a single extra byte appended AFTER the standard DNS question structure (after the null terminator + Type A + Class IN fields). The extra byte is `0x30` ('0') or `0x31` ('1'), encoding one bit per packet.
150
+
151
+ **Decoding workflow:**
152
+ ```python
153
+ from scapy.all import rdpcap, DNS, DNSQR, Raw
154
+
155
+ packets = rdpcap('challenge.pcap')
156
+
157
+ bits = []
158
+ for pkt in packets:
159
+ if pkt.haslayer(DNSQR):
160
+ # Get raw DNS payload
161
+ raw = bytes(pkt[DNS])
162
+ # Standard DNS question ends at: header(12) + qname + null(1) + type(2) + class(2)
163
+ qname = pkt[DNSQR].qname
164
+ expected_len = 12 + len(qname) + 1 + 2 + 2 # +1 for leading length byte
165
+ if len(raw) > expected_len:
166
+ trailing = raw[expected_len:]
167
+ for b in trailing:
168
+ bits.append(chr(b)) # '0' or '1'
169
+
170
+ # Convert bit string to ASCII (MSB-first, 8-bit chunks)
171
+ bitstring = ''.join(bits)
172
+ flag = ''.join(chr(int(bitstring[i:i+8], 2)) for i in range(0, len(bitstring) - 7, 8))
173
+ print(flag)
174
+ ```
175
+
176
+ **Key insight:** Data is hidden not in the DNS query name but in extra bytes padding the packet after the question record. Wireshark hex inspection reveals non-standard packet lengths. Each trailing byte represents ASCII '0' or '1', forming a binary stream that decodes to the flag.
177
+
178
+ **Detection:** DNS packets slightly larger than expected for their query name. Hex dump shows `0x30`/`0x31` bytes after the Class IN field (`00 01`). Consistent query domain across all packets.
179
+
180
+ ---
181
+
182
+ ## Multi-Layer PCAP with XOR + ZIP (UTCTF 2026)
183
+
184
+ **Pattern (Half Awake):** PCAP with multiple protocol layers hiding data. Requires protocol-aware extraction, XOR decryption with a key found in-band, and merging parallel data streams.
185
+
186
+ **Detailed workflow:**
187
+
188
+ 1. **Inspect HTTP streams** for instructions or hints (e.g., "mDNS names are hints", "Not every TCP blob is what it pretends to be")
189
+ 2. **Identify fake protocol streams:** A TCP stream labeled as TLS may actually contain a raw ZIP file (PK magic bytes `50 4b`). Check raw hex of suspicious streams
190
+ 3. **Extract XOR key from mDNS:** Look for mDNS TXT records (e.g., `key.version.local`) containing the XOR key
191
+ 4. **XOR-decrypt** the extracted data using the mDNS key
192
+ 5. **Merge parallel datasets** using printability as selector
193
+
194
+ ```python
195
+ import string
196
+ from scapy.all import rdpcap, Raw, DNS, DNSRR
197
+
198
+ packets = rdpcap('half-awake.pcap')
199
+
200
+ # 1. Extract XOR key from mDNS TXT record
201
+ xor_key = None
202
+ for pkt in packets:
203
+ if pkt.haslayer(DNSRR):
204
+ rr = pkt[DNSRR]
205
+ if b'key' in rr.rrname.lower():
206
+ xor_key = int(rr.rdata, 16) # e.g., 0xb7
207
+
208
+ # 2. Extract fake TLS stream (look for PK header in raw TCP data)
209
+ # Use Wireshark: tcp.stream eq N → Export raw bytes
210
+ # Or extract with scapy by filtering the right stream
211
+
212
+ # 3. XOR-decrypt two datasets from ZIP contents
213
+ def xor_decrypt(data, key):
214
+ return bytes(b ^ key for b in data)
215
+
216
+ p1 = xor_decrypt(stage1_data, xor_key)
217
+ p2 = xor_decrypt(stage2_data, xor_key)
218
+
219
+ # 4. Merge using printability: take the printable character from each position
220
+ flag = ''.join(
221
+ chr(p1[i]) if chr(p1[i]) in string.printable and chr(p1[i]).isprintable()
222
+ else chr(p2[i])
223
+ for i in range(len(p1))
224
+ )
225
+ print(flag)
226
+ ```
227
+
228
+ **Key insight:** When a PCAP contains two XOR-decoded byte arrays of equal length where neither alone produces readable text, merge them character-by-character using printability as the selector — take whichever byte at each position is a printable ASCII character. The XOR key is often hidden in an in-band protocol like mDNS TXT records rather than requiring brute-force.
229
+
230
+ **Indicators:**
231
+ - HTTP stream with meta-instructions ("not every TCP blob is what it pretends to be")
232
+ - TCP stream with mismatched protocol dissection (Wireshark shows TLS but raw bytes contain PK/ZIP headers)
233
+ - mDNS queries for suspicious service names (e.g., `key.version.local`)
234
+ - Two data files of identical length in extracted archive
235
+
236
+ ---
237
+
238
+ ## Brotli Decompression Bomb Seam Analysis (BearCatCTF 2026)
239
+
240
+ **Pattern (Cursed Map):** HTTP download of a file that decompresses to gigabytes (decompression bomb). The flag is sandwiched between two bomb halves at a seam in the compressed data.
241
+
242
+ **Identification:** Compressed data shows a repeating block pattern (e.g., 105-byte period). One block breaks the pattern — the flag is at this discontinuity.
243
+
244
+ ```python
245
+ import brotli
246
+
247
+ with open('flag.txt.br', 'rb') as f:
248
+ data = f.read()
249
+
250
+ # Find the repeating block size
251
+ block_size = 105 # Determined by comparing adjacent blocks
252
+ for i in range(0, len(data) - block_size, block_size):
253
+ if data[i:i+block_size] != data[i+block_size:i+2*block_size]:
254
+ seam_offset = i + block_size
255
+ break
256
+
257
+ # Decompress only the anomalous block
258
+ dec = brotli.Decompressor()
259
+ result = dec.process(data[seam_offset:seam_offset+block_size])
260
+ # Flag is in the decompressed output
261
+ ```
262
+
263
+ **Key insight:** Decompression bombs use highly repetitive compressed data. The flag breaks this repetition, creating a detectable anomaly in the compressed stream. Compare adjacent fixed-size blocks to find the discontinuity, then decompress only that region — no need to decompress the entire multi-gigabyte output.
264
+
265
+ **Detection:** File with extreme compression ratio (MB → GB), HTTP Content-Encoding: br, or file identified as Brotli. Tools hang or OOM when trying to decompress.
266
+
267
+ ---
268
+
269
+ ## SMB RID Recycling via LSARPC (Midnight 2026)
270
+
271
+ **Pattern (UntilTime):** PCAP with SMB2 authentication followed by RPC calls over `\pipe\lsarpc`. The attacker enumerates Active Directory accounts by iterating RIDs (Relative Identifiers) through LSARPC functions.
272
+
273
+ **Identification:** SMB2 session setup with multiple authentication attempts (null session, Guest, random username), followed by RPC bind to LSARPC and repeated `LsaLookupSids` calls with incrementing RIDs.
274
+
275
+ **Wireshark analysis:**
276
+ ```bash
277
+ # Filter SMB2 authentication attempts from attacker IP
278
+ tshark -r capture.pcapng -Y "ip.src == 198.51.100.16 && smb2.cmd == 1"
279
+
280
+ # Look for LSARPC RPC calls
281
+ tshark -r capture.pcapng -Y "dcerpc.cn_bind_to_str contains lsarpc"
282
+ ```
283
+
284
+ **RPC call sequence:**
285
+ 1. `LsaOpenPolicy` — opens a policy handle on the target
286
+ 2. `LsaQueryInformationPolicy` — extracts the domain SID (e.g., `S-1-5-21-...`)
287
+ 3. `LsaLookupSids` — resolves SIDs to account names by iterating RIDs (1000, 1001, 1002, ...)
288
+
289
+ **Key insight:** Guest account authentication (often enabled by default) grants enough access to enumerate domain accounts via LSARPC. The attacker constructs SIDs by appending incrementing RIDs to the domain SID and calling `LsaLookupSids` for each. Valid accounts return their name; invalid RIDs return errors. This technique is called **RID cycling** or **RID brute-forcing**.
290
+
291
+ **Detection indicators:**
292
+ - Multiple `LsaLookupSids` requests with sequential RIDs
293
+ - Guest authentication success followed by RPC pipe connection
294
+ - High volume of LSARPC traffic from a single source
295
+
296
+ ---
297
+
298
+ ## Timeroasting / MS-SNTP Hash Extraction (Midnight 2026)
299
+
300
+ **Pattern (UntilTime):** After enumerating valid machine account RIDs via RID recycling, the attacker sends NTP requests with those RIDs to extract HMAC-MD5 authentication material from the domain controller's MS-SNTP responses.
301
+
302
+ **Background:** Microsoft's MS-SNTP extends standard NTP with Netlogon authentication in Active Directory environments. The client places a domain RID in the NTP `Key Identifier` field (4 bytes, little-endian). The domain controller responds with an HMAC-MD5 signature derived from the machine account's NTLM hash — leaking crackable authentication material.
303
+
304
+ **Wireshark extraction:**
305
+ ```bash
306
+ # Filter NTP traffic from attacker
307
+ tshark -r capture.pcapng -Y "ntp && ip.src == 10.16.13.13" -T fields -e udp.payload
308
+ ```
309
+
310
+ **Convert Key Identifier to RID:**
311
+ ```bash
312
+ # NTP Key Identifier is 4 bytes, little-endian
313
+ echo "<key_id_hex>" | sed 's/\(..\)/\1 /g' | awk '{print "0x"$4$3$2$1}' | xargs printf "%d\n"
314
+ ```
315
+
316
+ **NTP response payload structure (68 bytes):**
317
+
318
+ | Offset | Length | Field |
319
+ |--------|--------|-------|
320
+ | 0-47 | 48 | Salt (NTP header + extensions) |
321
+ | 48-51 | 4 | Key Identifier (RID, little-endian) |
322
+ | 52-67 | 16 | HMAC-MD5 crypto-checksum |
323
+
324
+ **Hash reconstruction for Hashcat (mode 31300):**
325
+ ```python
326
+ import sys
327
+ from struct import unpack
328
+
329
+ def to_hashcat_form(hex_payload):
330
+ data = bytes.fromhex(hex_payload.strip())
331
+ salt = data[:48]
332
+ rid = unpack('<I', data[-20:-16])[0]
333
+ md5hash = data[-16:]
334
+ return f"{rid}:$sntp-ms${md5hash.hex()}${salt.hex()}"
335
+
336
+ if len(sys.argv) != 2:
337
+ print("Usage: python sntp_to_hashcat.py <hex_payload>")
338
+ sys.exit(1)
339
+
340
+ print(to_hashcat_form(sys.argv[1]))
341
+ ```
342
+
343
+ **Cracking with Hashcat:**
344
+ ```bash
345
+ # Mode 31300 = MS-SNTP (Timeroasting)
346
+ hashcat -m 31300 -a 0 -O hashes.txt rockyou.txt --username
347
+ ```
348
+
349
+ **Example hash format:**
350
+ ```text
351
+ 1108:$sntp-ms$d7d0422d66705c6189c1d20aed76baa4$1c0111e900000000000a09314c4f434ced4c979d652b89f1e1b8428bffbfcd0aed4ca3bbb1338716ed4ca3bbb133cf3a
352
+ ```
353
+
354
+ **Key insight:** MS-SNTP responses from domain controllers leak HMAC-MD5 authentication material tied to machine account NTLM hashes. Unlike Kerberoasting (which targets service accounts), Timeroasting targets **machine accounts** whose passwords are often weak or predictable (e.g., lowercase hostname). Any valid RID triggers a response — no special privileges required beyond network access to the DC's NTP service (UDP 123).
355
+
356
+ **Full attack chain:**
357
+ 1. Authenticate to SMB as Guest
358
+ 2. Enumerate valid RIDs via LSARPC RID recycling
359
+ 3. Send MS-SNTP requests with discovered RIDs
360
+ 4. Extract HMAC-MD5 hashes from NTP responses
361
+ 5. Crack offline with Hashcat mode 31300
362
+
363
+ ---
364
+
365
+ ## ICMP Payload Steganography with Byte Rotation (HackIM 2016)
366
+
367
+ Data hidden in ICMP echo request/reply payloads with byte-level rotation encoding:
368
+
369
+ ```python
370
+ from scapy.all import rdpcap, ICMP
371
+
372
+ packets = rdpcap('challenge.pcap')
373
+ icmp_data = b''
374
+ for pkt in packets:
375
+ if pkt.haslayer(ICMP) and pkt[ICMP].type == 8: # Echo request
376
+ icmp_data += bytes(pkt[ICMP].payload)
377
+
378
+ # Apply byte rotation (Caesar cipher on bytes)
379
+ SHIFT = 42
380
+ decoded = bytes((b - SHIFT) % 256 for b in icmp_data)
381
+
382
+ # Result may be base64-encoded
383
+ import base64
384
+ plaintext = base64.b64decode(decoded)
385
+ ```
386
+
387
+ **Key insight:** ICMP payloads are often ignored by analysts focused on TCP/UDP. Check for non-standard payload sizes or non-zero data in ICMP packets. Common encoding layers: byte rotation -> base64 -> shell commands.
388
+
389
+ ---
390
+
391
+ ## Packet Reconstruction via Checksum Validation (Break In 2016)
392
+
393
+ Reconstruct corrupted/incomplete packets by using protocol checksums as validation:
394
+
395
+ 1. **Identify missing bytes** from packet structure analysis (Ethernet, IP, TCP headers)
396
+ 2. **Brute-force missing values** and validate against:
397
+ - IP header checksum (16-bit ones' complement)
398
+ - TCP checksum (includes pseudo-header)
399
+ 3. **Extract data** from reconstructed payload
400
+
401
+ ```python
402
+ import struct
403
+
404
+ def ip_checksum(header_bytes):
405
+ """Compute IP header checksum"""
406
+ words = struct.unpack('!' + 'H' * (len(header_bytes) // 2), header_bytes)
407
+ s = sum(words)
408
+ while s >> 16:
409
+ s = (s & 0xFFFF) + (s >> 16)
410
+ return ~s & 0xFFFF
411
+
412
+ # Brute-force missing byte to match expected checksum
413
+ for candidate in range(256):
414
+ header = header_template[:missing_offset] + bytes([candidate]) + header_template[missing_offset+1:]
415
+ if ip_checksum(header) == 0: # Valid checksum sums to 0
416
+ print(f"Missing byte: 0x{candidate:02x}")
417
+ ```
418
+
419
+ **Key insight:** Protocol checksums constrain missing data. For single missing bytes, brute-force is instant. For multiple missing bytes, use TCP sequence numbers and MAC/IP header structure to reduce the search space.
420
+
421
+ ---
422
+
423
+ ## dnscat2 Traffic Reassembly from DNS PCAP (BSidesSF 2017)
424
+
425
+ **Pattern (dnscap):** Extract data tunneled via dnscat2 from a DNS pcap. Decode base32 subdomain labels from DNS queries, strip the 9-byte dnscat2 protocol header from each chunk, deduplicate retransmitted packets by comparing consecutive queries, then reassemble the payload (e.g., PNG image).
426
+
427
+ ```python
428
+ from scapy.all import rdpcap, DNSQR
429
+
430
+ packets = rdpcap('capture.pcap')
431
+ domain = '.skullseclabs.org.'
432
+ prev = None
433
+ data = b''
434
+
435
+ for p in packets:
436
+ if not p.haslayer(DNSQR):
437
+ continue
438
+ qname = p[DNSQR].qname.decode()
439
+ if domain not in qname:
440
+ continue
441
+ # Strip domain, join hex-encoded labels
442
+ labels = qname.replace(domain, '').split('.')
443
+ chunk = bytes.fromhex(''.join(labels))
444
+ chunk = chunk[9:] # strip 9-byte dnscat2 header
445
+ if chunk == prev:
446
+ continue # skip retransmission
447
+ prev = chunk
448
+ data += chunk
449
+
450
+ with open('extracted.png', 'wb') as f:
451
+ f.write(data)
452
+ ```
453
+
454
+ **Key insight:** dnscat2 encodes data in DNS query subdomain labels (hex or base32). Each query carries a 9-byte header (session ID, sequence, acknowledgment). Retransmissions are common — deduplicate by comparing consecutive payloads. The reassembled stream may contain files (PNG, documents) identifiable by magic bytes.
455
+
456
+ ---
457
+
458
+ ## Unreferenced PDF Objects with Hidden Pages (SharifCTF 7 2016)
459
+
460
+ **Pattern (Strange PDF):** A PDF contains objects not referenced by the page tree. To reveal hidden content: (1) examine raw PDF objects with `qpdf --show-xref` or a text editor, (2) identify unreferenced content stream objects, (3) modify the `/Kids` array in the Pages object to include hidden page references, (4) increment the `/Count` value, (5) re-render the PDF to display previously hidden pages containing flag data.
461
+
462
+ ```bash
463
+ # List all objects in the PDF
464
+ qpdf --show-xref suspicious.pdf
465
+
466
+ # Find pages object and hidden content objects
467
+ strings suspicious.pdf | grep -E '/Type /Page|/Contents|/Kids'
468
+
469
+ # Manual fix: edit PDF to add hidden page references
470
+ # Change: /Kids [1 0 R] -> /Kids [1 0 R 5 0 R]
471
+ # Change: /Count 1 -> /Count 2
472
+ # Rewrite xref table or use qpdf --linearize to fix offsets
473
+ qpdf --linearize modified.pdf fixed.pdf
474
+ ```
475
+
476
+ **Key insight:** PDF viewers only render pages reachable from the `/Pages` tree root. Unreferenced objects are invisible but still present in the file. Check object cross-references: any content stream object not in `/Kids` may contain hidden data. `mutool clean -d` and `qpdf --show-object N` help inspect individual objects.
477
+
478
+ ---
479
+
480
+ ## RDP Session Decryption via Extracted PKCS12 Key (HITB 2017)
481
+
482
+ PCAP contains a PKCS12 (.p12/.pfx) file transmitted over UDP. Extract the private key from the PKCS12 container, then load it into Wireshark to decrypt the RDP session and recover transmitted data.
483
+
484
+ ```bash
485
+ # Extract private key from PKCS12 (no cert, no passphrase protection)
486
+ openssl pkcs12 -in cert.p12 -out key.pem -nocerts -nodes
487
+
488
+ # In Wireshark: Edit > Preferences > Protocols > TLS > RSA keys list
489
+ # Add entry: IP=<rdp_server_ip>, Port=3389, Protocol=tpkt, Key file=key.pem
490
+ ```
491
+
492
+ **Key insight:** PKCS12 files in network captures provide the private key needed to decrypt encrypted RDP sessions in Wireshark. Look for .p12/.pfx file transfers (often in UDP or FTP streams) before the RDP session begins.
493
+
494
+ ---
495
+
496
+ ## RADIUS Shared Secret Cracking (UConn CyberSEED 2017)
497
+
498
+ Extract the RADIUS authenticator hash from a PCAP using `radius2john.pl`, crack the shared secret with john, then enter the cracked secret in Wireshark to decrypt obfuscated password fields.
499
+
500
+ ```bash
501
+ # Extract hash for john
502
+ perl radius2john.pl capture.pcap > radius_hash.txt
503
+ john radius_hash.txt --wordlist=rockyou.txt
504
+
505
+ # Wireshark: Edit > Preferences > Protocols > RADIUS > Shared Secret = <cracked_secret>
506
+ # RADIUS Access-Request packets will now show decrypted User-Password fields
507
+ ```
508
+
509
+ `radius2john.pl` is part of the JohnTheRipper jumbo package (`src/radius2john.pl`).
510
+
511
+ **Key insight:** RADIUS uses MD5(shared_secret + authenticator + password) for password obfuscation — cracking the shared secret via john exposes all credentials in the capture. The shared secret is typically a short dictionary word.
512
+
513
+ ---
514
+
515
+ ## RC4 Stream Identification in Shellcode PCAP (CODE BLUE 2017)
516
+
517
+ A backdoor sends 32 bytes of `/dev/urandom` as an RC4 key, then encrypts all subsequent traffic. Identify RC4 by the characteristic 256-byte KSA (Key Scheduling Algorithm) table initialization pattern visible in the shellcode. Extract the key from the first 32 bytes of the TCP stream and decrypt the remainder.
518
+
519
+ ```python
520
+ from scapy.all import rdpcap, TCP
521
+
522
+ packets = rdpcap('capture.pcap')
523
+ stream = b''
524
+ for pkt in packets:
525
+ if TCP in pkt and pkt[TCP].payload:
526
+ stream += bytes(pkt[TCP].payload)
527
+
528
+ # First 32 bytes = RC4 key (from /dev/urandom)
529
+ key = stream[:32]
530
+ ciphertext = stream[32:]
531
+
532
+ # RC4 decryption
533
+ def rc4(key, data):
534
+ S = list(range(256))
535
+ j = 0
536
+ for i in range(256):
537
+ j = (j + S[i] + key[i % len(key)]) % 256
538
+ S[i], S[j] = S[j], S[i]
539
+ i = j = 0
540
+ out = []
541
+ for byte in data:
542
+ i = (i + 1) % 256
543
+ j = (j + S[i]) % 256
544
+ S[i], S[j] = S[j], S[i]
545
+ out.append(byte ^ S[(S[i] + S[j]) % 256])
546
+ return bytes(out)
547
+
548
+ plaintext = rc4(key, ciphertext)
549
+ ```
550
+
551
+ **Key insight:** RC4 in shellcode is identifiable by the 256-byte permutation table initialization loop (KSA). The key is typically the first N bytes transmitted over the connection before encrypted data begins. Look for a fixed-length initial burst followed by encrypted traffic.
552
+
553
+ ---
554
+
555
+ See also: [network.md](network.md) for basic network forensics techniques (tcpdump, TLS/SSL decryption, Wireshark, port scanning, SMB3 decryption, credential extraction, 5G protocols).
556
+
557
+ ---
558
+
559
+ ## ICMP Ping Time-Delay Covert Channel (DefCamp 2018)
560
+
561
+ **Pattern:** An attacker exfiltrates data inside ICMP echo replies by modulating the server's response time. Latency under 200 ms encodes "ignore" (frame), 200–1000 ms encodes binary `0`, and >1000 ms encodes binary `1`. Reconstruct the data by pairing each request with its reply (matching `icmp.ident`/`icmp.seq`) and converting the time delta to bits.
562
+
563
+ ```python
564
+ from scapy.all import rdpcap, ICMP
565
+ pkts = rdpcap("broken_tv.pcap")
566
+ pairs = {}
567
+ for p in pkts:
568
+ if ICMP in p and p[ICMP].type == 8: # echo request
569
+ pairs[p[ICMP].seq] = p.time
570
+ bits = []
571
+ for p in pkts:
572
+ if ICMP in p and p[ICMP].type == 0: # echo reply
573
+ dt = p.time - pairs[p[ICMP].seq]
574
+ if dt < 0.2: # <200 ms: filler
575
+ continue
576
+ bits.append("1" if dt > 1.0 else "0")
577
+ data = int("".join(bits), 2).to_bytes(len(bits)//8, "big")
578
+ print(data)
579
+ ```
580
+
581
+ **Key insight:** ICMP timing covert channels split a continuous latency distribution into discrete bins. The two thresholds matter more than the exact values: any bimodal "fast vs slow" distribution flanked by a "filler" region lets the receiver self-clock. Detect this channel by plotting the histogram of `reply_time - request_time` for all ICMP pairs — legit traffic forms a single Gaussian, covert traffic shows clear modes.
582
+
583
+ **References:** DefCamp CTF Qualification 2018 — Broken TV, writeup 11415