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,592 @@
1
+ # CTF Reverse - Platform & Framework-Specific Techniques
2
+
3
+ ## Table of Contents
4
+ - [Roblox Place File Analysis](#roblox-place-file-analysis)
5
+ - [Godot Game Asset Extraction](#godot-game-asset-extraction)
6
+ - [Rust serde_json Schema Recovery](#rust-serde_json-schema-recovery)
7
+ - [Android JNI RegisterNatives Obfuscation (HTB WonderSMS)](#android-jni-registernatives-obfuscation-htb-wondersms)
8
+ - [Android DEX Runtime Bytecode Patching via /proc/self/maps (Google CTF 2017)](#android-dex-runtime-bytecode-patching-via-procselfmaps-google-ctf-2017)
9
+ - [Android Native .so Loading Bypass in New Project (Codegate CTF 2018)](#android-native-so-loading-bypass-in-new-project-codegate-ctf-2018)
10
+ - [Frida Firebase Cloud Functions Bypass (BSidesSF 2026)](#frida-firebase-cloud-functions-bypass-bsidessf-2026)
11
+ - [Verilog/Hardware Reverse Engineering (srdnlenCTF 2026)](#veriloghardware-reverse-engineering-srdnlenctf-2026)
12
+ - [Prefix-by-Prefix Hash Reversal (Nullcon 2026)](#prefix-by-prefix-hash-reversal-nullcon-2026)
13
+ - [Ruby/Perl Polyglot Constraint Satisfaction (BearCatCTF 2026)](#rubyperl-polyglot-constraint-satisfaction-bearcatctf-2026)
14
+ - [Electron App + Native Binary Reversing (RootAccess2026)](#electron-app--native-binary-reversing-rootaccess2026)
15
+ - [Node.js npm Package Runtime Introspection (RootAccess2026)](#nodejs-npm-package-runtime-introspection-rootaccess2026)
16
+ - [Frida Android Certificate Pinning Bypass (h1702ctf 2017)](#frida-android-certificate-pinning-bypass-h1702ctf-2017)
17
+ - [Android Anti-Debug: TracerPid, su Binary, System Properties (h1702ctf 2017)](#android-anti-debug-tracerpid-su-binary-system-properties-h1702ctf-2017)
18
+ - [Android Log-Based Key Extraction (HackIT 2017)](#android-log-based-key-extraction-hackit-2017)
19
+ - [Native JNI Key Extraction via Memory Dump and Smali Patching (HackIT 2017)](#native-jni-key-extraction-via-memory-dump-and-smali-patching-hackit-2017)
20
+ - [IBM AS/400 SAVF File EBCDIC Decoding (EKOPARTY 2017)](#ibm-as400-savf-file-ebcdic-decoding-ekoparty-2017)
21
+ - [Intel SGX Enclave Reverse Engineering (Pwn2Win 2017)](#intel-sgx-enclave-reverse-engineering-pwn2win-2017)
22
+ - [Glulx Interactive Fiction Bytecode Matrix Validation (PlaidCTF 2018)](#glulx-interactive-fiction-bytecode-matrix-validation-plaidctf-2018)
23
+ - [Android Smali Injection to Defeat LocalBroadcastManager (TAMUctf 2019)](#android-smali-injection-to-defeat-localbroadcastmanager-tamuctf-2019)
24
+
25
+ For core language reversing (Python, BF/esolangs, DOS, Unity, OPAL), see [languages.md](languages.md).
26
+ For Go and Rust binary reversing, see [languages-compiled.md](languages-compiled.md).
27
+
28
+ ---
29
+
30
+ ## Roblox Place File Analysis
31
+
32
+ **Pattern (MazeRunna, 0xFun 2026):** Roblox game with flag hidden in older version; latest version contains decoy.
33
+
34
+ **Version history via Asset Delivery API:**
35
+ ```bash
36
+ # Extract placeId and universeId from game page HTML
37
+ # Query each version (requires .ROBLOSECURITY cookie):
38
+ curl -H "Cookie: .ROBLOSECURITY=..." \
39
+ "https://assetdelivery.roblox.com/v2/assetId/{placeId}/version/1"
40
+ # Download location URL → place_v1.rbxlbin
41
+ ```
42
+
43
+ **Binary format parsing:** `.rbxlbin` files contain chunks:
44
+ - **INST** — class buckets and referent IDs
45
+ - **PROP** — per-instance fields (including `Script.Source`)
46
+ - **PRNT** — parent-child relationships (object tree)
47
+
48
+ Decode chunk payloads, walk PROP entries for `Source` field, dump `Script.Source` / `LocalScript.Source` per version, then diff.
49
+
50
+ **Key lesson:** Always check version history. Latest version may contain decoy flag while real flag is in an older version. Diff script sources across versions.
51
+
52
+ ---
53
+
54
+ ## Godot Game Asset Extraction
55
+
56
+ **Pattern (Steal the Xmas):** Encrypted Godot .pck packages.
57
+
58
+ **Tools:**
59
+ - [gdsdecomp](https://github.com/GDRETools/gdsdecomp) - Extract Godot packages
60
+ - [KeyDot](https://github.com/Titoot/KeyDot) - Extract encryption key from Godot executables
61
+
62
+ **Workflow:**
63
+ 1. Run KeyDot against game executable → extract encryption key
64
+ 2. Input key into gdsdecomp
65
+ 3. Extract and open project in Godot editor
66
+ 4. Search scripts/resources for flag data
67
+
68
+ ---
69
+
70
+ ## Rust serde_json Schema Recovery
71
+
72
+ **Pattern (Curly Crab, PascalCTF 2026):** Rust binary reads JSON from stdin, deserializes via serde_json, prints success/failure emoji.
73
+
74
+ **Approach:**
75
+ 1. Disassemble serde-generated `Visitor` implementations
76
+ 2. Each visitor's `visit_map` / `visit_seq` reveals expected keys and types
77
+ 3. Look for string literals in deserializer code (field names like `"pascal"`, `"CTF"`)
78
+ 4. Reconstruct nested JSON schema from visitor call hierarchy
79
+ 5. Identify value types from visitor method names: `visit_str` = string, `visit_u64` = number, `visit_bool` = boolean, `visit_seq` = array
80
+
81
+ ```json
82
+ {"pascal":"CTF","CTF":2026,"crab":{"I_":true,"cr4bs":1337,"crabby":{"l0v3_":["rust"],"r3vv1ng_":42}}}
83
+ ```
84
+
85
+ **Key insight:** Flag is the concatenation of JSON keys in schema order. Reading field names in order reveals the flag.
86
+
87
+ ---
88
+
89
+ ## Android JNI RegisterNatives Obfuscation (HTB WonderSMS)
90
+
91
+ **Pattern:** Android app loads native library with `System.loadLibrary()`, but uses `RegisterNatives` in `JNI_OnLoad` instead of standard JNI naming convention (`Java_com_pkg_Class_method`). This hides which C++ function handles each Java native method.
92
+
93
+ **Identification:**
94
+ ```java
95
+ // In decompiled Java (jadx):
96
+ static { System.loadLibrary("audio"); }
97
+ private final native ProcessedMessage processMessage(SmsMessage msg);
98
+ ```
99
+ Standard JNI would have a symbol `Java_com_rloura_wondersms_SmsReceiver_processMessage`. If that symbol is missing from the `.so`, `RegisterNatives` is being used.
100
+
101
+ **Finding the real handler in Ghidra:**
102
+ 1. Locate `JNI_OnLoad` (exported symbol, always present)
103
+ 2. Trace to `RegisterNatives(env, clazz, methods, count)` call
104
+ 3. The `methods` array contains `{name, signature, fnPtr}` structs
105
+ 4. Follow `fnPtr` to find the actual native function
106
+
107
+ ```c
108
+ // JNI_OnLoad registers functions manually:
109
+ static JNINativeMethod methods[] = {
110
+ {"processMessage", "(Landroid/telephony/SmsMessage;)LProcessedMessage;", (void*)real_handler}
111
+ };
112
+ (*env)->RegisterNatives(env, clazz, methods, 1);
113
+ ```
114
+
115
+ **Architecture selection for analysis:**
116
+ ```bash
117
+ # x86_64 gives best Ghidra decompilation (most similar to desktop code)
118
+ # Extract from APK:
119
+ unzip WonderSMS.apk -d extracted/
120
+ ls extracted/lib/x86_64/ # Prefer this over arm64-v8a for static analysis
121
+ ```
122
+
123
+ **Key insight:** `RegisterNatives` is a deliberate obfuscation technique — it decouples Java method names from native symbol names, making it impossible to find handlers by string search alone. Always check `JNI_OnLoad` first when reversing Android native libraries with stripped symbols.
124
+
125
+ **Detection:** Native method declared in Java + no matching JNI symbol in `.so` + `JNI_OnLoad` present. The library is typically stripped (no debug symbols).
126
+
127
+ ---
128
+
129
+ ## Android DEX Runtime Bytecode Patching via /proc/self/maps (Google CTF 2017)
130
+
131
+ Native JNI library patches Dalvik bytecode in memory at runtime: reads `/proc/self/maps` to find loaded DEX, `mprotect`s it writable, then XOR-patches specific bytecode offsets.
132
+
133
+ ```python
134
+ # Reconstruct the patched DEX offline:
135
+ # 1. Extract the embedded DEX from the APK
136
+ # 2. Find the XOR key and patch offsets in the native .so (IDA/Ghidra)
137
+ # 3. Apply the same patches to the static DEX
138
+ import struct
139
+
140
+ with open('classes.dex', 'rb') as f:
141
+ dex = bytearray(f.read())
142
+
143
+ # Patch 144 bytes starting at offset found in .so
144
+ xor_key = 0x5A
145
+ for i in range(patch_offset, patch_offset + 144):
146
+ dex[i] ^= xor_key
147
+
148
+ # 4. Recompute DEX checksum and SHA-1 hash
149
+ # 5. Decompile with jadx or baksmali
150
+ ```
151
+
152
+ **Key insight:** Native libraries can modify DEX bytecode in memory via `/proc/self/maps` + `mprotect`, making static analysis of the APK alone insufficient. The XOR key and patch offsets must be extracted from the native `.so` to reconstruct the actual runtime DEX. Only works on Dalvik (API < 21), not ART.
153
+
154
+ ---
155
+
156
+ ### Android Native .so Loading Bypass in New Project (Codegate CTF 2018)
157
+
158
+ **Pattern:** Instead of reversing complex JNI validation logic, create a new Android Studio project with matching package name, class name, and native method signature. Load the original `.so` library and call the native function directly, completely bypassing all Java-level checks (random number validation, PIN entry, root detection, etc.).
159
+
160
+ ```java
161
+ // Create new project with same package: com.example.puing.a2018codegate
162
+ package com.example.puing.a2018codegate;
163
+ public class Main4Activity extends AppCompatActivity {
164
+ static { System.loadLibrary("hello-libs"); }
165
+ protected void onCreate(Bundle savedInstanceState) {
166
+ super.onCreate(savedInstanceState);
167
+ String flag = stringFromJNI(); // call native directly, skip all Java validation
168
+ Log.d("FLAG", flag);
169
+ }
170
+ public native String stringFromJNI();
171
+ }
172
+ ```
173
+
174
+ **Key insight:** JNI function names encode the package path and class name. Create a new Android project with matching package/class/method names, include the original `.so`, and call the native function directly. All Java-level validation (random checks, PIN entry, root detection) is bypassed entirely.
175
+
176
+ **Detection:** APK with native `.so` library where the flag or secret is computed inside the native code and returned to Java. The Java layer has multiple validation gates (EditText checks, random number comparisons, device checks) before calling the native method.
177
+
178
+ **References:** Codegate CTF 2018
179
+
180
+ ---
181
+
182
+ ## Frida Firebase Cloud Functions Bypass (BSidesSF 2026)
183
+
184
+ **Pattern (vinyl-drop, doremi):** Android app validates actions (QR codes, purchases) via Firebase Cloud Functions. The expected payload format includes the Firebase UID, a value, and a timestamp. Use Frida to hook the app post-login, construct a valid payload, and call the Cloud Function directly.
185
+
186
+ ```javascript
187
+ // Frida hook to bypass QR validation
188
+ Java.perform(function() {
189
+ var FirebaseFunctions = Java.use('com.google.firebase.functions.FirebaseFunctions');
190
+ var FirebaseAuth = Java.use('com.google.firebase.auth.FirebaseAuth');
191
+
192
+ // Get current user UID after login
193
+ var auth = FirebaseAuth.getInstance();
194
+ var uid = auth.getCurrentUser().getUid();
195
+
196
+ // Construct valid payload: uid + amount + timestamp
197
+ var unixMs = Java.use('java.lang.System').currentTimeMillis();
198
+ var payload = uid + "+100+" + unixMs;
199
+
200
+ // Call the Cloud Function directly
201
+ var functions = FirebaseFunctions.getInstance();
202
+ var data = Java.use('java.util.HashMap').$new();
203
+ data.put("payload", payload);
204
+ functions.getHttpsCallable("validateScanPayload").call(data);
205
+ });
206
+ ```
207
+
208
+ **Key insight:** Firebase AppCheck and Cloud Functions rely on the client to construct valid payloads. Post-authentication, Frida can hook the app to call any Cloud Function with arbitrary parameters, bypassing client-side validation (QR scanning, payment processing, etc.).
209
+
210
+ **When to recognize:** Android app with `google-services.json`, Firebase dependencies in `build.gradle`, Cloud Function calls in decompiled code.
211
+
212
+ **References:** BSidesSF 2026 "vinyl-drop"
213
+
214
+ ---
215
+
216
+ ## Verilog/Hardware Reverse Engineering (srdnlenCTF 2026)
217
+
218
+ **Pattern (Rev Juice):** Verilog HDL source for a vending machine with hidden product unlocked by specific coin insertion and selection sequence.
219
+
220
+ **Approach:**
221
+ 1. Analyze Verilog modules to understand state machine and history tracking
222
+ 2. Identify hidden conditions (e.g., product 8 enabled only when `COINS_HISTORY` array has specific values at specific taps)
223
+ 3. Build timing model for each action type (how many clock cycles each operation takes)
224
+ 4. Work backward from required history values to construct the correct input sequence
225
+
226
+ **Timing model construction:**
227
+ ```python
228
+ # Map each action to its cycle count (determined from Verilog state machines)
229
+ TIMING = {
230
+ "insert_coin": 3, # 3 cycles per coin insertion
231
+ "select_success": 7, # 7 cycles for successful product selection
232
+ "select_fail": 5, # 5 cycles for failed selection attempt
233
+ "cancel_with_coins": 4, # 4 cycles for cancel when coins > 0
234
+ "cancel_at_zero": 2, # 2 cycles for cancel when coins = 0
235
+ }
236
+
237
+ # COINS_HISTORY is a shift register updated each cycle
238
+ # History tap requirements (from Verilog conditions):
239
+ # H[0]=1, H[7]=4, H[28]=H[33]=H[38]=6
240
+ # H[63]=H[73]=2, H[80]=9
241
+ # (H[19]+H[21]+H[56]+H[69]) mod 32 = 0
242
+ ```
243
+
244
+ **Key insight:** Hardware challenges require understanding the exact timing model — each operation takes a specific number of clock cycles, and shift registers record history at fixed tap positions. Work backward from the required tap values to determine what action must have occurred at each cycle. The solution is often a specific sequence notation (e.g., `I9C_SP6_CNL_I2C_SP2_I6C_SP6_SP6_SP5_CNL_I4C_SP1`).
245
+
246
+ **Detection:** Look for `.v` or `.sv` (Verilog/SystemVerilog) files, `always @(posedge clk)` blocks, shift register patterns, and state machine `case` statements with hidden conditions gated on history values.
247
+
248
+ ---
249
+
250
+ ## Prefix-by-Prefix Hash Reversal (Nullcon 2026)
251
+
252
+ See [patterns-ctf-2.md](patterns-ctf-2.md#prefix-hash-brute-force-nullcon-2026) for the full technique. This section covers language-specific considerations.
253
+
254
+ **Language-specific notes:**
255
+ - Hash algorithm may be uncommon (MD2, custom) — don't need to identify it, just match outputs by running the binary
256
+ - Use `subprocess.run()` with `timeout=2` to handle binaries that hang on bad input
257
+ - For stripped binaries, check if `ltrace` reveals the hash function name (e.g., `MD2_Update`)
258
+
259
+ ---
260
+
261
+ ## Ruby/Perl Polyglot Constraint Satisfaction (BearCatCTF 2026)
262
+
263
+ **Pattern (Polly's Key):** A single file valid in both Ruby and Perl. Each language imposes different validation constraints on a 50-character key. Satisfy both simultaneously to decrypt the flag.
264
+
265
+ **Polyglot structure exploits:**
266
+ - Ruby: `=begin`...`=end` is a block comment
267
+ - Perl: `=begin`...`=cut` is POD (Plain Old Documentation), `=end` is ignored
268
+ - Different code runs in each language based on comment block boundaries
269
+
270
+ **Typical constraints:**
271
+ - **Ruby:** Character set must form a mathematical property (e.g., all 50 printable ASCII chars except `^` used exactly once, each satisfying `XOR(val, (val-16) % 257)` is a primitive root mod 257)
272
+ - **Perl:** Ordering constraint via insertion sort inversion count (hardcoded inversion table determines exact permutation)
273
+
274
+ **Solution approach:**
275
+ 1. Find the valid character set (mathematical constraint from one language)
276
+ 2. Use the ordering constraint (from other language) to determine exact arrangement
277
+ 3. Compute key hash (e.g., MD5) and decrypt
278
+
279
+ ```python
280
+ # Determine character ordering from inversion counts
281
+ def reconstruct_from_inversions(chars, inv_counts):
282
+ result = []
283
+ remaining = sorted(chars)
284
+ for i in range(len(chars) - 1, -1, -1):
285
+ # inv_counts[i] = number of elements to the left that are greater
286
+ idx = inv_counts[i]
287
+ result.insert(idx, remaining.pop(i))
288
+ return result
289
+ ```
290
+
291
+ **Key insight:** Polyglot files exploit language-specific comment/block syntax to run different code in each interpreter. The constraints from both languages intersect to uniquely determine the key. Identify which code runs in which language by testing the file with both interpreters and comparing behavior.
292
+
293
+ **Detection:** File that runs under multiple interpreters (`ruby file && perl file`). Challenge mentions "polyglot" or provides a file ending in `.rb` that also looks like Perl.
294
+
295
+ ---
296
+
297
+ ## Electron App + Native Binary Reversing (RootAccess2026)
298
+
299
+ **Pattern (Rootium Browser):** Electron desktop app bundles a native ELF/DLL binary for sensitive operations (vault, crypto, auth). The Electron layer is a wrapper; the real flag logic is in the native binary.
300
+
301
+ **Extraction workflow:**
302
+ 1. **Unpack Electron ASAR archive:**
303
+ ```bash
304
+ # Install ASAR tool
305
+ npm install -g @electron/asar
306
+
307
+ # Extract the app.asar archive
308
+ asar extract resources/app.asar app_extracted/
309
+ ls app_extracted/
310
+ ```
311
+
312
+ 2. **Locate native binary:** Search for ELF/DLL files called from JavaScript:
313
+ ```bash
314
+ # Find native binaries
315
+ find app_extracted/ -name "*.node" -o -name "*.so" -o -name "*vault*" -o -name "*auth*"
316
+
317
+ # Check JS for child_process.spawn or ffi-napi calls
318
+ grep -r "spawn\|execFile\|ffi\|require.*native" app_extracted/
319
+ ```
320
+
321
+ 3. **Reverse the native binary** (XOR + rotation cipher example):
322
+ ```python
323
+ def decrypt_password(encrypted_bytes, key):
324
+ """Common pattern: XOR with constant + bit rotation + key XOR."""
325
+ result = []
326
+ for i, byte in enumerate(encrypted_bytes):
327
+ decrypted = ((byte ^ 0x42) >> 3) ^ key[i % len(key)]
328
+ result.append(chr(decrypted))
329
+ return ''.join(result)
330
+
331
+ def decrypt_flag(encrypted_flag, password):
332
+ """Flag uses password as key with position-dependent rotation."""
333
+ result = []
334
+ for i, byte in enumerate(encrypted_flag):
335
+ key_byte = ord(password[i % len(password)])
336
+ decrypted = ((byte ^ 0x7E) >> (i % 8)) ^ key_byte
337
+ result.append(chr(decrypted))
338
+ return ''.join(result)
339
+ ```
340
+
341
+ **Key insight:** Electron apps are JavaScript wrapping native code. Extract with `asar`, then focus on the native binary. The JS layer often contains the password verification flow in plaintext, revealing what the native binary expects. Look for encrypted data in the `.data` or `.rodata` sections of the ELF.
342
+
343
+ **Detection:** `.asar` files in `resources/` directory, Electron framework files, `package.json` with electron dependency.
344
+
345
+ ---
346
+
347
+ ## Node.js npm Package Runtime Introspection (RootAccess2026)
348
+
349
+ **Pattern (RootAccess CLI):** Obfuscated npm package with RC4 encoding, control flow flattening, and flag split across multiple fragments. Static analysis is impractical — use runtime introspection instead.
350
+
351
+ **Dynamic analysis approach:**
352
+ ```javascript
353
+ #!/usr/bin/env node
354
+
355
+ // 1. Load obfuscated modules
356
+ const cryptoMod = require('target-package/dist/lib/crypto.js');
357
+ const vaultMod = require('target-package/dist/lib/vault.js');
358
+
359
+ // 2. Enumerate all exported properties
360
+ for (const mod of [cryptoMod, vaultMod]) {
361
+ for (const key of Object.keys(mod)) {
362
+ const obj = mod[key];
363
+ console.log(`Export: ${key}`);
364
+ // List all methods including hidden ones
365
+ const props = Object.getOwnPropertyNames(obj);
366
+ const proto = Object.getOwnPropertyNames(obj.prototype || {});
367
+ console.log(' Own:', props);
368
+ console.log(' Proto:', proto);
369
+ }
370
+ }
371
+
372
+ // 3. Extract flag fragments
373
+ const Engine = cryptoMod.CryptoEngine;
374
+ const total = Engine.getTotalFragments();
375
+ let flag = '';
376
+ for (let i = 1; i <= total; i++) {
377
+ flag += Engine.getFragment(i);
378
+ }
379
+ console.log('Flag:', flag);
380
+
381
+ // 4. Check for hidden methods (common: __getFullFlag__, _debug, _raw)
382
+ const hidden = Object.getOwnPropertyNames(Engine)
383
+ .filter(p => p.startsWith('__') || p.startsWith('_'));
384
+ console.log('Hidden methods:', hidden);
385
+ ```
386
+
387
+ **Key insight:** Heavily obfuscated JavaScript (control flow flattening, RC4 string encoding, dead code) makes static analysis prohibitively slow. Runtime introspection via `Object.getOwnPropertyNames()` reveals all methods including hidden ones. The module's own decryption runs automatically when loaded — just call the decoded functions directly.
388
+
389
+ **Detection:** npm package with minified/obfuscated `dist/` directory, challenge says "reverse engineer the CLI tool", `package.json` with custom commands.
390
+
391
+ ---
392
+
393
+ ## Frida Android Certificate Pinning Bypass (h1702ctf 2017)
394
+
395
+ APK uses OkHttp `CertificatePinner` for SSL pinning. Rather than setting up a MITM proxy or patching the APK, use Frida to directly invoke native JNI methods on loaded classes.
396
+
397
+ ```javascript
398
+ Java.perform(function() {
399
+ var Requestor = Java.use("com.h1702ctf.ctfone.Requestor");
400
+ console.log("hName: " + Requestor.hName());
401
+ console.log("hVal: " + Requestor.hVal());
402
+ });
403
+ ```
404
+
405
+ Calling `hName()` and `hVal()` returns the HTTP header name and value needed to bypass the server-side check — no cert pinning bypass required because the secret is in the class methods themselves.
406
+
407
+ **Key insight:** Frida can invoke native JNI methods directly on a loaded class — no need to bypass cert pinning at network layer or fully reverse the native binary.
408
+
409
+ **References:** h1702ctf 2017
410
+
411
+ ---
412
+
413
+ ## Android Anti-Debug: TracerPid, su Binary, System Properties (h1702ctf 2017)
414
+
415
+ Native ARM code implements three sequential anti-analysis checks:
416
+ 1. Read `/proc/self/status` and look for a non-zero `TracerPid` (debugger attached)
417
+ 2. Check for existence of the `su` binary (root detection)
418
+ 3. Read a custom system property via `__system_property_get`
419
+
420
+ The checks gate a required register value computation. Bypass via static analysis: use IDA's graph view to trace the control flow and identify the "happy path" through all three checks, then compute what register values must hold at each branch.
421
+
422
+ **Key insight:** Anti-debug checks in native Android code (TracerPid, su, system properties) can be bypassed by static graph analysis to find correct register values without running a debugger.
423
+
424
+ **References:** h1702ctf 2017
425
+
426
+ ---
427
+
428
+ ## Android Log-Based Key Extraction (HackIT 2017)
429
+
430
+ A secure messenger app logs cryptographic material via Android's `Log.d()`:
431
+ - Curve25519 base agreement value
432
+ - Ephemeral shared key per message
433
+ - Message IDs and shift counters
434
+
435
+ The AES-CBC IV derives from the logged ephemeral/shared value; the key derives from the logged base agreement and an accumulated shift counter. Collect all log entries with `adb logcat`, then reconstruct AES-CBC parameters to decrypt intercepted messages.
436
+
437
+ ```bash
438
+ adb logcat | grep -E "(agreement|ephemeral|shared|key)" > crypto_log.txt
439
+ # Parse log entries to reconstruct: key = f(base_agreement, shift_counter)
440
+ # iv = f(ephemeral_shared)
441
+ ```
442
+
443
+ **Key insight:** Overly verbose logging in security-sensitive apps leaks enough state to reconstruct encryption parameters without any private key access.
444
+
445
+ **References:** HackIT CTF 2017
446
+
447
+ ---
448
+
449
+ ## Native JNI Key Extraction via Memory Dump and Smali Patching (HackIT 2017)
450
+
451
+ A JNI native library handles request signing using an XOR-obfuscated key stored in the `.data` section. The key is deobfuscated at runtime just before use.
452
+
453
+ **Workflow:**
454
+ 1. Load the library in IDA with a GDB stub on a rooted device
455
+ 2. Set a breakpoint after the XOR decryption routine
456
+ 3. Dump the memory region containing the decrypted key
457
+ 4. Use `baksmali` to disassemble the APK's DEX, identify the smali file that constructs the signed POST request
458
+ 5. Patch the smali to change which parameter gets signed, then rebuild with `apktool` and reinstall
459
+
460
+ ```bash
461
+ # Decompile APK
462
+ apktool d target.apk -o target_decompiled/
463
+ # Edit smali: change signed parameter from original to desired value
464
+ # Rebuild
465
+ apktool b target_decompiled/ -o target_patched.apk
466
+ # Sign and install
467
+ ```
468
+
469
+ **Key insight:** For JNI signing: memory-dump the decrypted key region during execution, then patch smali to sign desired parameters — avoids fully reversing the native signing algorithm.
470
+
471
+ **References:** HackIT CTF 2017
472
+
473
+ ---
474
+
475
+ ## IBM AS/400 SAVF File EBCDIC Decoding (EKOPARTY 2017)
476
+
477
+ IBM AS/400 SAVF (Save File) binary files use EBCDIC encoding rather than ASCII. The flag is interleaved with dummy text using a take-2-skip-2 pattern.
478
+
479
+ ```python
480
+ import codecs
481
+
482
+ with open('savefile.savf', 'rb') as f:
483
+ data = f.read()
484
+
485
+ # Convert EBCDIC to ASCII
486
+ ascii_data = data.decode('cp500') # cp500 is IBM EBCDIC International
487
+
488
+ # Filter: keep uppercase letters and underscores (flag charset)
489
+ flag_chars = [c for c in ascii_data if c.isupper() or c == '_']
490
+ # Or apply take-2-skip-2 pattern after decoding
491
+ flag = ''.join(ascii_data[i] for i in range(0, len(ascii_data), 4)
492
+ if ascii_data[i].isupper() or ascii_data[i] == '_')
493
+ ```
494
+
495
+ **Key insight:** EBCDIC encoding is IBM mainframe-native. Examine character distribution after decoding to identify interleaving patterns. Filtering for uppercase letters and underscores is an effective shortcut for CTF flag formats.
496
+
497
+ **References:** EKOPARTY CTF 2017
498
+
499
+ ---
500
+
501
+ ## Intel SGX Enclave Reverse Engineering (Pwn2Win 2017)
502
+
503
+ Intel SGX enclave `.so` files expose an ECALL dispatch table. The enclave logic (including key derivation) is fully reversible with IDA since SGX code is standard x86-64.
504
+
505
+ **Workflow:**
506
+ 1. Locate the ECALL table in the `.so` — a function pointer array indexed by ECALL number
507
+ 2. Decompile ECALLs with IDA to identify the remote attestation protocol
508
+ 3. Implement the attestation protocol manually in Python using `sgx_crypto_wrapper`
509
+ 4. Key derivation: ECDH over P-256 followed by CMAC-AES-128 to derive the session key (SK)
510
+ 5. Decrypt the AES-128-GCM-encrypted flag blob using the derived SK
511
+
512
+ ```python
513
+ from cryptography.hazmat.primitives.asymmetric import ec
514
+ from cryptography.hazmat.primitives import cmac, ciphers
515
+
516
+ # ECDH: derive shared secret from server's P-256 public key
517
+ private_key = ec.generate_private_key(ec.SECP256R1())
518
+ shared_secret = private_key.exchange(ec.ECDH(), server_pub_key)
519
+
520
+ # CMAC-AES-128 key derivation (per SGX attestation spec)
521
+ c = cmac.CMAC(ciphers.algorithms.AES(b'\x00' * 16))
522
+ c.update(shared_secret[:16])
523
+ sk = c.finalize()
524
+
525
+ # Decrypt flag with AES-128-GCM using derived SK
526
+ ```
527
+
528
+ **Key insight:** SGX remote attestation key derivation is deterministic given the enclave measurement — reimplementing the protocol in Python recovers the same session key.
529
+
530
+ **References:** Pwn2Win CTF 2017
531
+
532
+ ---
533
+
534
+ ## Glulx Interactive Fiction Bytecode Matrix Validation (PlaidCTF 2018)
535
+
536
+ **Pattern:** Challenge is a Glulx interactive fiction story (`.ulx`/`.blorb`) that accepts player input, scrambles it via a matrix multiplication inside the VM, and compares the result to a hardcoded vector. A hidden `xyzzy`-style command enables a debug room whose side exit reveals the target vector and constant matrix.
537
+
538
+ **Workflow:**
539
+ 1. Disassemble with `glulxd` (Glulx toolkit) or run under `glulxe`/`git` interpreters while dumping memory at each VM instruction.
540
+ 2. Locate the matrix constant (look for aligned 32-bit integers adjacent to the comparison routine) and the target vector referenced by the comparison.
541
+ 3. Type the hidden-command trigger to reach the debug room — Glulx games often respond to `xyzzy`, `plugh`, or developer verbs that still appear in the dictionary table (`.DictTable`).
542
+ 4. Invert the transformation in Python: `input_vec = target_vec * matrix.inverse()` over `Z_{2^32}` (Glulx integers are unsigned 32-bit).
543
+
544
+ ```python
545
+ from sage.all import matrix, Zmod
546
+
547
+ M = matrix(Zmod(2**32), rows) # rows[i] = extracted matrix row
548
+ target = vector(Zmod(2**32), output)
549
+ answer = M.solve_right(target) # required player input as 32-bit words
550
+ print(bytes(answer.list()).decode())
551
+ ```
552
+
553
+ **Key insight:** Adventure-game VMs always keep the original story text in the dictionary and object tables — grep the bytecode for developer verbs (`xyzzy`, `debug`, `god`, `plugh`) to discover hidden rooms before reversing the check itself. Glulx validation routines are typically linear over 32-bit integers, so solving them with Sage matrix inversion is faster than symbolic execution.
554
+
555
+ **References:** PlaidCTF 2018 — writeup 10019
556
+
557
+ ---
558
+
559
+ ## Android Smali Injection to Defeat LocalBroadcastManager (TAMUctf 2019)
560
+
561
+ **Pattern (Local News):** APK registers a `BroadcastReceiver` through `LocalBroadcastManager.getInstance(this).registerReceiver(...)` that only decodes the flag when triggered by the app itself. External `adb shell am broadcast` is silently dropped because local broadcasts never leave the process:
562
+
563
+ ```bash
564
+ $ adb shell cmd package query-receivers --brief -a com.tamu.ctf.START
565
+ # No receivers found
566
+ ```
567
+
568
+ Swapping the registration for `Context.registerReceiver` force-closes the app (manifest mismatch). Instead, clone the `onReceive` body and inline it into `onCreate` so the deobfuscator (e.g., Paranoid's `Deobfuscator$app$Debug.getString(0)`) runs at startup and the plaintext flag lands in `logcat`.
569
+
570
+ Decompile, patch, rebuild, sign with a debug keystore, install, capture logcat:
571
+
572
+ ```smali
573
+ # Inside MainActivity.smali, inserted before the final return of onCreate():
574
+ const/4 v1, 0x0
575
+ invoke-static {v1}, Lio/michaelrocks/paranoid/Deobfuscator$app$Debug;->getString(I)Ljava/lang/String;
576
+ move-result-object v1
577
+ invoke-static {v1, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
578
+ ```
579
+
580
+ ```bash
581
+ apktool d app.apk -o app
582
+ # patch MainActivity.smali ...
583
+ apktool b app -o app/dist/app.apk
584
+ jarsigner -keystore ~/.android/debug.keystore -storepass android \
585
+ app/dist/app.apk androiddebugkey
586
+ adb install -r app/dist/app.apk
587
+ adb logcat | grep -i flag
588
+ ```
589
+
590
+ **Key insight:** `LocalBroadcastManager`-registered receivers cannot be invoked from `adb`, so the obvious "send the intent yourself" trick fails and static deobfuscation of Paranoid strings is painful. Smali patching sidesteps both: move the deobfuscation call to a path that runs automatically and redirect its output to `Log.d()`, turning a local-only receiver into a logcat-visible primitive.
591
+
592
+ **References:** TAMUctf 2019 — Local News, writeup 13565