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,497 @@
1
+ # CTF Forensics - Advanced Disk and Memory Techniques
2
+
3
+ ## Table of Contents
4
+ - [Deleted Partition Recovery](#deleted-partition-recovery)
5
+ - [ZFS Forensics (Nullcon 2026)](#zfs-forensics-nullcon-2026)
6
+ - [GPT Partition GUID Data Encoding (VuwCTF 2025)](#gpt-partition-guid-data-encoding-vuwctf-2025)
7
+ - [Windows Minidump String Carving (0xFun 2026)](#windows-minidump-string-carving-0xfun-2026)
8
+ - [VMDK Sparse Parsing (0xFun 2026)](#vmdk-sparse-parsing-0xfun-2026)
9
+ - [Memory Dump String Carving (Pragyan 2026)](#memory-dump-string-carving-pragyan-2026)
10
+ - [Memory Dump Malware Extraction + XOR (VuwCTF 2025)](#memory-dump-malware-extraction--xor-vuwctf-2025)
11
+ - [Linux Ransomware Memory-Key Recovery (MetaCTF 2026)](#linux-ransomware-memory-key-recovery-metactf-2026)
12
+ - [WordPerfect Macro XOR Extraction (srdnlenCTF 2026)](#wordperfect-macro-xor-extraction-srdnlenctf-2026)
13
+ - [Minidump ISO 9660 Recovery + XOR Key (srdnlenCTF 2026)](#minidump-iso-9660-recovery--xor-key-srdnlenctf-2026)
14
+ - [APFS Snapshot Historical File Recovery (srdnlenCTF 2026)](#apfs-snapshot-historical-file-recovery-srdnlenctf-2026)
15
+ - [RAID 5 Disk Recovery via XOR (Crypto-Cat)](#raid-5-disk-recovery-via-xor-crypto-cat)
16
+ - [HFS+ Resource Fork Hidden Binary Recovery (CONFidence CTF 2017)](#hfs-resource-fork-hidden-binary-recovery-confidence-ctf-2017)
17
+ - [Kyoto Cabinet Hash Database Forensics via Incremental Key Insertion (ASIS CTF 2018)](#kyoto-cabinet-hash-database-forensics-via-incremental-key-insertion-asis-ctf-2018)
18
+ - [SQLite Edit History Reconstruction from Diff Table (Google CTF 2017)](#sqlite-edit-history-reconstruction-from-diff-table-google-ctf-2017)
19
+ - [See Also](#see-also)
20
+
21
+ ---
22
+
23
+ ## Deleted Partition Recovery
24
+
25
+ **Pattern (Till Delete Do Us Part):** USB image with deleted partition table.
26
+
27
+ **Recovery workflow:**
28
+ ```bash
29
+ # Check for partitions
30
+ fdisk -l image.img # Shows no partitions
31
+
32
+ # Recover partition table
33
+ testdisk image.img # Interactive recovery
34
+
35
+ # Or use kpartx to map partitions
36
+ kpartx -av image.img # Maps as /dev/mapper/loop0p1
37
+
38
+ # Mount recovered partition
39
+ mount /dev/mapper/loop0p1 /mnt/evidence
40
+
41
+ # Check for hidden directories
42
+ ls -la /mnt/evidence # Look for .dotfolders
43
+ find /mnt/evidence -name ".*" # Find hidden files
44
+ ```
45
+
46
+ **Flag hiding:** Path components as flag chars (e.g., `/.Meta/CTF/{f/l/a/g}`)
47
+
48
+ ---
49
+
50
+ ## ZFS Forensics (Nullcon 2026)
51
+
52
+ **Pattern:** Corrupted ZFS pool image with encrypted dataset.
53
+
54
+ **Recovery workflow:**
55
+ 1. **Label reconstruction:** All 4 ZFS labels may be zeroed. Find packed nvlist data elsewhere in the image using `strings` + offset searching.
56
+ 2. **MOS object repair:** Copy known-good nvlist bytes to block locations, recompute Fletcher4 checksums:
57
+ ```python
58
+ def fletcher4(data):
59
+ a = b = c = d = 0
60
+ for i in range(0, len(data), 4):
61
+ a = (a + int.from_bytes(data[i:i+4], 'little')) & 0xffffffff
62
+ b = (b + a) & 0xffffffff
63
+ c = (c + b) & 0xffffffff
64
+ d = (d + c) & 0xffffffff
65
+ return (d << 96) | (c << 64) | (b << 32) | a
66
+ ```
67
+ 3. **Encryption cracking:** Extract PBKDF2 parameters (iterations, salt) from ZAP objects. GPU-accelerate with PyOpenCL for PBKDF2-HMAC-SHA1, verify AES-256-GCM unwrap on CPU.
68
+ 4. **Passphrase list:** rockyou.txt or similar. GPU rate: ~24k passwords/sec.
69
+
70
+ ---
71
+
72
+ ## GPT Partition GUID Data Encoding (VuwCTF 2025)
73
+
74
+ **Pattern (Undercut):** "LLMs only" + "undercut" → not AI GPT, but GUID Partition Table.
75
+
76
+ **Key insight:** GPT partition GUIDs are 16 arbitrary bytes — can encode anything. Look for file magic headers in GUIDs.
77
+
78
+ ```bash
79
+ # Parse GPT partition table
80
+ gdisk -l image.img
81
+ # Or with Python:
82
+ python3 -c "
83
+ import struct
84
+ data = open('image.img','rb').read()
85
+ # GPT header at LBA 1 (offset 512)
86
+ # Partition entries start at LBA 2 (offset 1024)
87
+ # Each entry is 128 bytes, GUID at offset 16 (16 bytes)
88
+ for i in range(128):
89
+ entry = data[1024 + i*128 : 1024 + (i+1)*128]
90
+ guid = entry[16:32]
91
+ if guid != b'\x00'*16:
92
+ print(f'Partition {i}: {guid.hex()}')
93
+ "
94
+ ```
95
+
96
+ **First GUID starts with `BZh11AY&SY`** (bzip2 magic) → concatenate GUIDs, decompress as bzip2, then decode ASCII85.
97
+
98
+ ---
99
+
100
+ ## Windows Minidump String Carving (0xFun 2026)
101
+
102
+ **Pattern (kd):** Go binary crash dump. Flag as plaintext string constant in .data section survives in minidump memory.
103
+
104
+ ```bash
105
+ strings -a minidump.dmp | grep -i "flag\|ctf\|0xFUN"
106
+ ```
107
+
108
+ **Lesson:** Minidumps contain full memory regions. String constants, keys, and secrets persist. `strings -a` + `grep` is the fast path.
109
+
110
+ ---
111
+
112
+ ## VMDK Sparse Parsing (0xFun 2026)
113
+
114
+ **Pattern (VMware):** Split sparse VMDK requires grain directory + grain table traversal.
115
+
116
+ **Key steps:**
117
+ 1. Parse VMDK sparse header (grain size, GD offset, GT coverage)
118
+ 2. Follow grain directory → grain table → data grains
119
+ 3. Calculate absolute disk offsets across split files
120
+ 4. Mount extracted filesystem (ext4, NTFS)
121
+
122
+ **Lesson:** Don't assume VM images can be mounted directly. Parse the VMDK sparse format manually.
123
+
124
+ ---
125
+
126
+ ## Memory Dump String Carving (Pragyan 2026)
127
+
128
+ **Pattern (c47chm31fy0uc4n):** Linux memory dump with flag in environment variables or process data.
129
+
130
+ ```bash
131
+ strings -a -n 6 memdump.bin | grep -E "SYNC|FLAG|SSH_CLIENT|SESSION_KEY"
132
+ # SSH artifacts reveal source IP and ephemeral port
133
+ # Environment variables may contain keys/tokens
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Memory Dump Malware Extraction + XOR (VuwCTF 2025)
139
+
140
+ **Pattern (Jellycat):** Extract fake executable from Windows memory dump. Cipher: subtract 0x32, then XOR with cycling key (large multi-line string, e.g., ASCII art).
141
+
142
+ **Key lesson:** Always extract and reverse the actual binary from memory rather than trusting `strings` output (string tables may be red herrings). XOR keys can be hundreds of bytes (ASCII art, lorem ipsum).
143
+
144
+ ```python
145
+ # Extract binary, find XOR key in data section
146
+ key = b"..." # Large ASCII art string
147
+ cipher = open('extracted.bin', 'rb').read()
148
+ plaintext = bytes((b - 0x32) ^ key[i % len(key)] for i, b in enumerate(cipher))
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Linux Ransomware Memory-Key Recovery (MetaCTF 2026)
154
+
155
+ **Pattern:** Linux memory dump + encrypted `.veg` files + `enc_key.bin`; ransomware uses hybrid crypto (AES for files, RSA-wrapped key). Volatility may fail process enumeration due symbol/KASLR (Kernel Address Space Layout Randomization) mismatch.
156
+
157
+ **Fast workflow:**
158
+ 1. **Confirm archive integrity before analysis.**
159
+ ```bash
160
+ unzip -l encrypted_files.zip
161
+ # Compare listed files/sizes vs extracted tree; re-extract cleanly if mismatch
162
+ unzip -o encrypted_files.zip -d encrypted_full
163
+ ```
164
+
165
+ 2. **Reverse ransomware binary quickly to identify mode/layout.**
166
+ ```bash
167
+ strings -a ransomware.elf | grep -E "enc_key|EVP_aes|PUBLIC KEY|.veg"
168
+ objdump -d ransomware.elf | less
169
+ ```
170
+ - Typical finding: `AES-256-OFB`, IV prepended to each `.veg`, global 32-byte AES key, RSA public key hardcoded.
171
+
172
+ 3. **Try Volatility normally, then pivot immediately if empty/unstable.**
173
+ ```bash
174
+ vol -f memdump.raw linux.pslist
175
+ vol -f memdump.raw linux.proc.Maps
176
+ vol -f memdump.raw linux.vmayarascan
177
+ ```
178
+ - If Linux plugins return empty/invalid output despite correct banner/symbols, do **raw-memory candidate scanning**.
179
+
180
+ 4. **Recover AES key via anchored candidate scan + magic validation.**
181
+ - Use recurring anchor strings in memory (e.g., `/home/.../enc_key.bin`, HOME path).
182
+ - Derive candidate offsets near anchors (page-aligned windows).
183
+ - Test each 32-byte candidate by decrypting first blocks of multiple `.veg` files and checking magic bytes (`%PDF-`, `PK\x03\x04`, `\x89PNG\r\n\x1a\n`).
184
+ - Keep candidates that satisfy multiple independent signatures.
185
+
186
+ 5. **Decrypt full dataset and verify output completeness.**
187
+ ```bash
188
+ # OFB: iv = first 16 bytes, ciphertext starts at +16
189
+ # Decrypt all *.veg recursively from a clean extraction directory
190
+ ```
191
+ - Validate recovered file count against zip listing.
192
+ - Watch for duplicated mirror trees (e.g., `snap/*/Downloads/...`) and deduplicate logically.
193
+
194
+ 6. **Defend against false flags.**
195
+ - Treat metadata-only flags as suspicious until corroborated by challenge context.
196
+ - Prefer tokens from primary project artifacts and perform uniqueness checks:
197
+ ```bash
198
+ rg -n -a '[A-Za-z]+CTF\\{[^}]+\\}' recovered_full
199
+ pdftotext recovered_full/**/*.pdf - 2>/dev/null | rg '[A-Za-z]+CTF\\{'
200
+ ```
201
+
202
+ **Key lessons:**
203
+ - Don't trust a partial/stale extraction tree; re-extract zip cleanly.
204
+ - In OFB ransomware, magic-byte validation is a fast key oracle.
205
+ - A plausible `CTF{...}` in metadata can be a decoy; confirm with corpus-wide consistency.
206
+
207
+ ---
208
+
209
+ ## WordPerfect Macro XOR Extraction (srdnlenCTF 2026)
210
+
211
+ **Pattern (Trilogy of Death Vol I: Corel):** Corel Linux disk image containing WordPerfect macro file (fc.wcm) with XOR-encrypted byte arrays.
212
+
213
+ **Key insight:** WordPerfect macro files (`.wcm`) can contain executable macros with embedded encrypted data. The XOR formula `(bb + kb) - 2*(bb & kb)` is mathematically equivalent to bitwise XOR.
214
+
215
+ **Brute-force 4-byte XOR key under charset constraints:**
216
+ ```python
217
+ import string
218
+
219
+ docbody = [206, 56, 8, 128, 209, 47, 2, 149, ...] # encrypted bytes from macro
220
+ allowed = set(map(ord, string.ascii_lowercase + string.digits + "_{}"))
221
+
222
+ # Find valid key bytes independently for each position mod 4
223
+ cands = []
224
+ for j in range(4):
225
+ good = []
226
+ for k in range(256):
227
+ if all((docbody[i] ^ k) in allowed for i in range(j, len(docbody), 4)):
228
+ good.append(k)
229
+ cands.append(good)
230
+
231
+ # Try all combinations (usually very few candidates per position)
232
+ for k0 in cands[0]:
233
+ for k1 in cands[1]:
234
+ for k2 in cands[2]:
235
+ for k3 in cands[3]:
236
+ key = [k0, k1, k2, k3]
237
+ pt = ''.join(chr(c ^ key[i % 4]) for i, c in enumerate(docbody))
238
+ if pt.startswith("srd") and pt.endswith("}"):
239
+ print(pt)
240
+ ```
241
+
242
+ **Lesson:** Legacy document formats (WordPerfect, Lotus 1-2-3) can embed executable macros with obfuscated data. When you know the flag charset, brute-forcing a short XOR key is trivial by filtering each key byte independently.
243
+
244
+ ---
245
+
246
+ ## Minidump ISO 9660 Recovery + XOR Key (srdnlenCTF 2026)
247
+
248
+ **Pattern (Trilogy of Death Vol II: The Legendary Armory):** Two relics in volatile memory (minidump) must be XORed; ISO 9660 directory entries in memory fragments point to hidden data.
249
+
250
+ **Technique:**
251
+ 1. Search minidump for ISO 9660 directory entry signatures
252
+ 2. Parse directory entries to locate target file offset and size
253
+ 3. Decrypt file using recovered XOR key (e.g., 8-byte repeating key)
254
+ 4. Parse resulting data as ZIP without central directory (local headers only)
255
+
256
+ **ZIP local header parsing without central directory:**
257
+ ```python
258
+ import struct, zlib
259
+
260
+ pos = 0
261
+ files = {}
262
+ while True:
263
+ off = dec.find(b"PK\x03\x04", pos)
264
+ if off < 0:
265
+ break
266
+ (ver, flag, method, _, _, crc, csize, usize, nlen, xlen) = struct.unpack_from(
267
+ "<HHHHHIIIHH", dec, off + 4)
268
+ name = dec[off + 30:off + 30 + nlen].decode()
269
+ data_off = off + 30 + nlen + xlen
270
+ comp = dec[data_off:data_off + csize]
271
+ if method == 8: # Deflate
272
+ raw = zlib.decompress(comp, -15)
273
+ else:
274
+ raw = comp
275
+ files[name] = raw
276
+ pos = data_off + csize
277
+ ```
278
+
279
+ **Key insight:** When ZIP central directory is missing/corrupt, iterate local file headers (`PK\x03\x04`) directly. Each local header contains enough metadata (compression method, sizes, filename) to extract files independently.
280
+
281
+ ---
282
+
283
+ ## APFS Snapshot Historical File Recovery (srdnlenCTF 2026)
284
+
285
+ **Pattern (Trilogy of Death Vol III: The Poisoned Apple):** APFS volume maintains historical snapshots; recovering earlier state of a key file reveals authentic value before poisoning.
286
+
287
+ **Technique:**
288
+ 1. Extract APFS partition from DMG (locate by sector offset)
289
+ 2. Search for APFS volume superblocks (magic `APSB`) across all snapshots, noting transaction IDs (XIDs)
290
+ 3. Use `icat` (Sleuth Kit with APFS support) to read specific inodes across different snapshot XIDs
291
+ 4. Compare file content across XID boundaries to identify when poisoning occurred
292
+ 5. Use pre-poisoning value for decryption
293
+
294
+ **Finding APFS volume superblocks across snapshots:**
295
+ ```python
296
+ import struct
297
+
298
+ with open("apfs_partition.img", "rb") as f:
299
+ mm = f.read()
300
+
301
+ snaps = []
302
+ pos = 0
303
+ while True:
304
+ idx = mm.find(b"APSB", pos)
305
+ if idx < 0:
306
+ break
307
+ # XID is at offset -16 from magic (in block header)
308
+ hdr_start = idx - 32
309
+ xid = struct.unpack_from("<Q", mm, hdr_start + 16)[0]
310
+ blk = hdr_start // 4096
311
+ snaps.append((xid, blk))
312
+ pos = idx + 1
313
+
314
+ # Read target inode across snapshots
315
+ import subprocess
316
+ for xid, blk in sorted(set(snaps)):
317
+ try:
318
+ out = subprocess.check_output(
319
+ ["icat", "-f", "apfs", "-P", "apfs", "-B", str(blk),
320
+ "apfs_partition.img", "449414"]) # target inode number
321
+ print(f"XID {xid}: {out[:64]}...")
322
+ except:
323
+ pass
324
+ ```
325
+
326
+ **Decryption with recovered authentic key:**
327
+ ```python
328
+ import hashlib
329
+ from Cryptodome.Cipher import AES
330
+
331
+ # Pre-poisoning key value (found in earlier snapshot)
332
+ authentic_key_hex = "39f520679fd68654500f9cd44e8caed2bc897a3227dc297c4520336de2a59dd7"
333
+ key = hashlib.pbkdf2_hmac('sha256', bytes.fromhex(authentic_key_hex), salt, iterations)
334
+ cipher = AES.new(key, AES.MODE_CBC, iv)
335
+ plaintext = cipher.decrypt(encrypted_flag)
336
+ ```
337
+
338
+ **Key insight:** APFS (and other copy-on-write filesystems like ZFS/Btrfs) preserve historical file states in snapshots. When a challenge involves "poisoned" or "tampered" data, always check for older snapshots containing the original values. Use `icat` with different block offsets to read the same inode across different transaction IDs.
339
+
340
+ ---
341
+
342
+ ## RAID 5 Disk Recovery via XOR (Crypto-Cat)
343
+
344
+ **Pattern:** RAID 5 array with one damaged/missing disk. Two working disks are provided and the third must be reconstructed using XOR parity.
345
+
346
+ **How RAID 5 parity works:** Data is striped across N disks with distributed parity. For any stripe, `Disk1 XOR Disk2 XOR ... XOR DiskN = 0`. If one disk is missing, XOR the remaining disks to recover it.
347
+
348
+ **Recovery script:**
349
+ ```python
350
+ # Recover missing disk2 from disk1 and disk3
351
+ with open('disk1.img', 'rb') as f:
352
+ disk1 = f.read()
353
+ with open('disk3.img', 'rb') as f:
354
+ disk3 = f.read()
355
+
356
+ # XOR byte-by-byte to recover the missing disk
357
+ disk2 = bytes(a ^ b for a, b in zip(disk1, disk3))
358
+
359
+ with open('disk2.img', 'wb') as f:
360
+ f.write(disk2)
361
+ ```
362
+
363
+ **After recovery:**
364
+ ```bash
365
+ # Reassemble the RAID array
366
+ mdadm --create /dev/md0 --level=5 --raid-devices=3 \
367
+ disk1.img disk2.img disk3.img
368
+
369
+ # Or mount individual recovered disk if it contains a filesystem
370
+ mount -o loop,ro disk2.img /mnt/recovered
371
+ ```
372
+
373
+ **Key insight:** RAID 5 uses XOR parity across all disks in each stripe. XOR is self-inverse: if `A XOR B XOR C = 0`, then `B = A XOR C`. For N-disk RAID 5, XOR all N-1 working disks together to recover the missing one.
374
+
375
+ **Detection:** Challenge provides multiple disk images of identical size, mentions "array", "redundancy", or "parity". `file` command may identify them as filesystem images or raw data.
376
+
377
+ ---
378
+
379
+ ## HFS+ Resource Fork Hidden Binary Recovery (CONFidence CTF 2017)
380
+
381
+ HFS+ files can have a Resource Fork containing hidden data invisible to most tools. Use HFSExplorer to inspect the catalog and 010 Editor with HFS template to extract.
382
+
383
+ ```bash
384
+ # 1. Mount or open the HFS+ image
385
+ # Standard tools miss Resource Forks:
386
+ binwalk image.dmg # Won't find resource fork contents
387
+ strings image.dmg # May show fragments
388
+
389
+ # 2. Use HFSExplorer to browse the catalog
390
+ # Look for files with non-zero Resource Fork size
391
+ # Suspicious: nodeID 1337 or similar CTF-typical IDs
392
+
393
+ # 3. Check .fseventsd logs for historical file operations
394
+ pip install FSEventsParser
395
+ python FSEventsParser.py -s image.dmg -o events.csv
396
+ # Reveals creation/deletion of files across the volume
397
+
398
+ # 4. Extract Resource Fork data with 010 Editor:
399
+ # - Load disk image with HFS+ template
400
+ # - Navigate to catalog -> target file -> resource fork extents
401
+ # - Note start block and length from extent records
402
+ # - If split across multiple extents, extract and concatenate:
403
+ dd if=image.dmg bs=4096 skip=$BLOCK1 count=$LEN1 of=part1.bin
404
+ dd if=image.dmg bs=4096 skip=$BLOCK2 count=$LEN2 of=part2.bin
405
+ cat part1.bin part2.bin > recovered_binary
406
+ ```
407
+
408
+ **Key insight:** HFS+ Resource Forks are a second data stream attached to files, invisible to most forensic tools that only examine the Data Fork. `binwalk`, `foremost`, and `strings` miss them. HFSExplorer shows both forks in the catalog; 010 Editor with the HFS template reveals extent records for manual extraction. `.fseventsd` logs can reveal that hidden files were created/deleted.
409
+
410
+ **Detection:** DMG or HFS+ disk image where standard carving finds nothing. `file` identifies as "Apple HFS+" or "Apple Partition Map". Challenge mentions "Mac", "Apple", or "hidden data".
411
+
412
+ ---
413
+
414
+ ## Kyoto Cabinet Hash Database Forensics via Incremental Key Insertion (ASIS CTF 2018)
415
+
416
+ **Pattern:** Unknown binary file identified as Kyoto Cabinet (KC) hash database. Flag characters stored as values with zeroed-out keys. Since the database uses a fixed-size hash table, recover ordering by inserting sequential keys one at a time and observing which hash slot reference gets overwritten via binary diff.
417
+
418
+ ```bash
419
+ # Identify format
420
+ file unknown.db # may not recognize KC format
421
+ strings unknown.db | head # look for "KCPH" magic
422
+
423
+ # Enumerate values
424
+ kchashmgr list tokyo.kch
425
+
426
+ # Recover key ordering via incremental insertion + binary diff
427
+ for i in $(seq -w 000 088); do
428
+ cp tokyo.kch test.kch
429
+ kchashmgr set test.kch "$i" "probe"
430
+ diff <(xxd tokyo.kch) <(xxd test.kch) | head -5
431
+ # Changed offset reveals which original entry maps to key $i
432
+ done
433
+ ```
434
+
435
+ **Full recovery script (Python):**
436
+ ```python
437
+ import subprocess, shutil
438
+
439
+ original = 'tokyo.kch'
440
+ # Get all values from the database
441
+ values = subprocess.check_output(['kchashmgr', 'list', original]).decode().splitlines()
442
+
443
+ mapping = {}
444
+ for i in range(len(values)):
445
+ key = f'{i:03d}'
446
+ shutil.copy(original, 'test.kch')
447
+ subprocess.run(['kchashmgr', 'set', 'test.kch', key, 'probe'], check=True)
448
+ # Binary diff to find which slot changed
449
+ orig_hex = subprocess.check_output(['xxd', original]).decode()
450
+ test_hex = subprocess.check_output(['xxd', 'test.kch']).decode()
451
+ for orig_line, test_line in zip(orig_hex.splitlines(), test_hex.splitlines()):
452
+ if orig_line != test_line:
453
+ mapping[i] = orig_line # Record which entry was overwritten
454
+ break
455
+
456
+ # Reconstruct flag from ordered values
457
+ flag = ''.join(values[i] for i in sorted(mapping.keys()))
458
+ print(flag)
459
+ ```
460
+
461
+ **Key insight:** Hash databases store entries at positions determined by key hash values. When keys are zeroed/corrupted, the stored ordering is hash-based, not insertion-order. Insert probe keys one at a time and binary-diff the database to find which slot each probe overwrites, revealing the original key-to-value mapping.
462
+
463
+ ---
464
+
465
+ ## SQLite Edit History Reconstruction from Diff Table (Google CTF 2017)
466
+
467
+ SQLite databases storing note/document edit history as diff entries (operation, position, text, diffset) can be replayed to reconstruct content at any point in time.
468
+
469
+ ```python
470
+ import sqlite3
471
+
472
+ db = sqlite3.connect('notes.db')
473
+ # Table structure: diffs(id, type, position, text, diffset)
474
+ # type: 'insert' or 'remove'
475
+ diffs = db.execute("SELECT type, position, text FROM diffs ORDER BY id").fetchall()
476
+
477
+ document = ""
478
+ for op_type, position, text in diffs:
479
+ if op_type == 'insert':
480
+ document = document[:position] + text + document[position:]
481
+ elif op_type == 'remove':
482
+ document = document[:position] + document[position + len(text):]
483
+ # Check for flag at each step (may have been typed then deleted)
484
+ if 'CTF{' in document or 'flag{' in document:
485
+ print(f"Flag found: {document}")
486
+ ```
487
+
488
+ **Key insight:** Collaborative editing tools store incremental diffs. Replaying all operations sequentially reveals content that existed at any point in the edit history, including secrets that were later deleted. Check for flags at every intermediate state, not just the final document.
489
+
490
+ **Detection:** SQLite database with tables containing `type`/`operation`, `position`, `text` columns. Challenge mentions "notes", "editor", "collaboration", or "history". Schema inspection via `.schema` or `sqlite3 db.sqlite ".tables"` reveals diff-style tables.
491
+
492
+ ---
493
+
494
+ ## See Also
495
+
496
+ - [disk-and-memory.md](disk-and-memory.md) - Core disk and memory forensics (Volatility 3, disk image analysis, VM/OVA/VMDK forensics, VMware snapshots, GIMP raw memory dump visual inspection, coredump analysis, Windows KAPE triage, PowerShell ransomware, Android forensics, Docker container forensics, cloud storage forensics, BSON reconstruction, TrueCrypt/VeraCrypt mounting)
497
+ - [disk-recovery.md](disk-recovery.md) - Disk recovery and extraction patterns (LUKS master key recovery, PRNG timestamp seed brute-force, VBA macro binary recovery, FemtoZip decompression, XFS reconstruction, tar duplicate entry extraction, nested matryoshka filesystem extraction, anti-carving via null byte interleaving)