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.
- package/AGENTS.md +131 -0
- package/Dockerfile +192 -0
- package/README.md +522 -0
- package/agents/ctf-analyzer.md +58 -0
- package/agents/ctf-controller.md +151 -0
- package/agents/ctf-speedrun.md +74 -0
- package/bin/ctf-agent.js +119 -0
- package/docker-compose.yml +13 -0
- package/mcp_config.json +19 -0
- package/package.json +44 -0
- package/references/ctf-triage-ladder.md +68 -0
- package/references/exploit-databases.md +575 -0
- package/references/llm-safety-and-policy-compliance.md +173 -0
- package/references/multi-agent-orchestration-and-policy-routing.md +278 -0
- package/references/security-events-and-intelligence.md +566 -0
- package/references/version-matrix.md +59 -0
- package/rules/ctf-execution-rules.md +44 -0
- package/rules/ctf-safety-framing-rules.md +140 -0
- package/rules/flag-validation-rules.md +25 -0
- package/scripts/__init__.py +1 -0
- package/scripts/ctf-tools.lock +205 -0
- package/scripts/ctf_init.py +1026 -0
- package/scripts/ctf_update.py +583 -0
- package/scripts/ctfd_client.py +81 -0
- package/scripts/cve_lookup.py +453 -0
- package/scripts/extract_flags.py +155 -0
- package/scripts/install_as_agent.py +278 -0
- package/scripts/install_ctf_tools.sh +603 -0
- package/scripts/parallel_triage.py +395 -0
- package/scripts/prompt_policy_sanitizer.py +283 -0
- package/scripts/scope_guard.py +444 -0
- package/scripts/skill_validator.py +504 -0
- package/scripts/workspace_cleaner.py +141 -0
- package/skills/ctf-ai-ml/SKILL.md +120 -0
- package/skills/ctf-ai-ml/adversarial-ml.md +623 -0
- package/skills/ctf-ai-ml/llm-attacks.md +487 -0
- package/skills/ctf-ai-ml/model-attacks.md +422 -0
- package/skills/ctf-crypto/SKILL.md +320 -0
- package/skills/ctf-crypto/advanced-math.md +798 -0
- package/skills/ctf-crypto/classic-ciphers.md +651 -0
- package/skills/ctf-crypto/ecc-attacks.md +347 -0
- package/skills/ctf-crypto/exotic-crypto-2.md +380 -0
- package/skills/ctf-crypto/exotic-crypto.md +528 -0
- package/skills/ctf-crypto/historical.md +113 -0
- package/skills/ctf-crypto/lattice-and-lwe.md +524 -0
- package/skills/ctf-crypto/modern-ciphers-2.md +563 -0
- package/skills/ctf-crypto/modern-ciphers-3.md +453 -0
- package/skills/ctf-crypto/modern-ciphers.md +649 -0
- package/skills/ctf-crypto/prng-attacks.md +257 -0
- package/skills/ctf-crypto/prng.md +664 -0
- package/skills/ctf-crypto/rsa-attacks-2.md +792 -0
- package/skills/ctf-crypto/rsa-attacks.md +487 -0
- package/skills/ctf-crypto/stream-ciphers.md +390 -0
- package/skills/ctf-crypto/zkp-and-advanced.md +456 -0
- package/skills/ctf-forensics/3d-printing.md +121 -0
- package/skills/ctf-forensics/SKILL.md +379 -0
- package/skills/ctf-forensics/disk-advanced.md +497 -0
- package/skills/ctf-forensics/disk-and-memory.md +491 -0
- package/skills/ctf-forensics/disk-recovery.md +699 -0
- package/skills/ctf-forensics/linux-forensics.md +511 -0
- package/skills/ctf-forensics/network-advanced.md +583 -0
- package/skills/ctf-forensics/network.md +645 -0
- package/skills/ctf-forensics/peripheral-capture.md +287 -0
- package/skills/ctf-forensics/signals-and-hardware.md +713 -0
- package/skills/ctf-forensics/steganography.md +694 -0
- package/skills/ctf-forensics/stego-advanced-2.md +475 -0
- package/skills/ctf-forensics/stego-advanced.md +481 -0
- package/skills/ctf-forensics/stego-image.md +691 -0
- package/skills/ctf-forensics/windows.md +625 -0
- package/skills/ctf-malware/SKILL.md +181 -0
- package/skills/ctf-malware/c2-and-protocols.md +274 -0
- package/skills/ctf-malware/pe-and-dotnet.md +108 -0
- package/skills/ctf-malware/scripts-and-obfuscation.md +449 -0
- package/skills/ctf-misc/SKILL.md +498 -0
- package/skills/ctf-misc/bashjails.md +323 -0
- package/skills/ctf-misc/ctfd-navigation.md +465 -0
- package/skills/ctf-misc/dns.md +255 -0
- package/skills/ctf-misc/encodings-advanced.md +504 -0
- package/skills/ctf-misc/encodings.md +431 -0
- package/skills/ctf-misc/games-and-vms-2.md +254 -0
- package/skills/ctf-misc/games-and-vms-3.md +690 -0
- package/skills/ctf-misc/games-and-vms-4.md +229 -0
- package/skills/ctf-misc/games-and-vms.md +529 -0
- package/skills/ctf-misc/linux-privesc.md +333 -0
- package/skills/ctf-misc/pyjails.md +671 -0
- package/skills/ctf-misc/rf-sdr.md +91 -0
- package/skills/ctf-osint/SKILL.md +198 -0
- package/skills/ctf-osint/geolocation-and-media.md +464 -0
- package/skills/ctf-osint/social-media.md +312 -0
- package/skills/ctf-osint/web-and-dns.md +341 -0
- package/skills/ctf-pwn/SKILL.md +214 -0
- package/skills/ctf-pwn/advanced-exploits-2.md +579 -0
- package/skills/ctf-pwn/advanced-exploits-3.md +598 -0
- package/skills/ctf-pwn/advanced-exploits-4.md +590 -0
- package/skills/ctf-pwn/advanced-exploits-5.md +119 -0
- package/skills/ctf-pwn/advanced-exploits.md +773 -0
- package/skills/ctf-pwn/advanced.md +326 -0
- package/skills/ctf-pwn/field-notes.md +245 -0
- package/skills/ctf-pwn/format-string.md +694 -0
- package/skills/ctf-pwn/heap-fsop.md +285 -0
- package/skills/ctf-pwn/heap-techniques-2.md +333 -0
- package/skills/ctf-pwn/heap-techniques.md +513 -0
- package/skills/ctf-pwn/kernel-bypass.md +421 -0
- package/skills/ctf-pwn/kernel-techniques.md +366 -0
- package/skills/ctf-pwn/kernel.md +636 -0
- package/skills/ctf-pwn/overflow-basics.md +611 -0
- package/skills/ctf-pwn/rop-advanced.md +725 -0
- package/skills/ctf-pwn/rop-and-shellcode.md +659 -0
- package/skills/ctf-pwn/sandbox-escape.md +313 -0
- package/skills/ctf-reverse/SKILL.md +163 -0
- package/skills/ctf-reverse/anti-analysis-ctf.md +204 -0
- package/skills/ctf-reverse/anti-analysis.md +693 -0
- package/skills/ctf-reverse/field-notes.md +376 -0
- package/skills/ctf-reverse/languages-compiled.md +666 -0
- package/skills/ctf-reverse/languages-platforms.md +592 -0
- package/skills/ctf-reverse/languages.md +553 -0
- package/skills/ctf-reverse/patterns-ctf-2.md +397 -0
- package/skills/ctf-reverse/patterns-ctf-3.md +797 -0
- package/skills/ctf-reverse/patterns-ctf.md +670 -0
- package/skills/ctf-reverse/patterns-runtime.md +274 -0
- package/skills/ctf-reverse/patterns.md +572 -0
- package/skills/ctf-reverse/platforms-hardware.md +387 -0
- package/skills/ctf-reverse/platforms.md +664 -0
- package/skills/ctf-reverse/tools-advanced-2.md +421 -0
- package/skills/ctf-reverse/tools-advanced.md +407 -0
- package/skills/ctf-reverse/tools-dynamic.md +679 -0
- package/skills/ctf-reverse/tools-emulation.md +319 -0
- package/skills/ctf-reverse/tools.md +573 -0
- package/skills/ctf-web/SKILL.md +153 -0
- package/skills/ctf-web/auth-and-access-2.md +82 -0
- package/skills/ctf-web/auth-and-access.md +783 -0
- package/skills/ctf-web/auth-infra.md +321 -0
- package/skills/ctf-web/auth-jwt.md +186 -0
- package/skills/ctf-web/client-side-advanced.md +739 -0
- package/skills/ctf-web/client-side.md +529 -0
- package/skills/ctf-web/cves.md +373 -0
- package/skills/ctf-web/field-notes.md +482 -0
- package/skills/ctf-web/node-and-prototype.md +200 -0
- package/skills/ctf-web/server-side-2.md +337 -0
- package/skills/ctf-web/server-side-advanced-2.md +559 -0
- package/skills/ctf-web/server-side-advanced-3.md +125 -0
- package/skills/ctf-web/server-side-advanced-4.md +480 -0
- package/skills/ctf-web/server-side-advanced.md +378 -0
- package/skills/ctf-web/server-side-deser.md +443 -0
- package/skills/ctf-web/server-side-exec-2.md +799 -0
- package/skills/ctf-web/server-side-exec.md +457 -0
- package/skills/ctf-web/server-side.md +629 -0
- package/skills/ctf-web/sql-injection.md +790 -0
- package/skills/ctf-web/web3.md +374 -0
- package/skills/ctf-writeup/SKILL.md +90 -0
- package/skills/solve-challenge/SKILL.md +269 -0
- package/skills.json +16 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# CTF Reverse - Emulation and Side-Channel Tooling
|
|
2
|
+
|
|
3
|
+
Emulation frameworks (Qiling, Triton) and side-channel measurement tools (Intel Pin, LD_PRELOAD hooks) for CTF challenges where anti-debug, self-modifying code, or cross-architecture targets make plain GDB/Frida impractical.
|
|
4
|
+
|
|
5
|
+
For core dynamic analysis tools (Frida, angr, lldb, x64dbg), see [tools-dynamic.md](tools-dynamic.md).
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
- [Qiling Framework (Cross-Platform Emulation)](#qiling-framework-cross-platform-emulation)
|
|
9
|
+
- [Qiling Installation](#qiling-installation)
|
|
10
|
+
- [Basic Usage](#basic-usage)
|
|
11
|
+
- [Anti-Debug Bypass via Emulation](#anti-debug-bypass-via-emulation)
|
|
12
|
+
- [Input Fuzzing with Qiling](#input-fuzzing-with-qiling)
|
|
13
|
+
- [Triton (Dynamic Symbolic Execution)](#triton-dynamic-symbolic-execution)
|
|
14
|
+
- [Intel Pin Instruction-Counting Side Channel (Hackover CTF 2015)](#intel-pin-instruction-counting-side-channel-hackover-ctf-2015)
|
|
15
|
+
- [Intel Pin Instruction Counting with Genetic Algorithm (hxp CTF 2017)](#intel-pin-instruction-counting-with-genetic-algorithm-hxp-ctf-2017)
|
|
16
|
+
- [Opcode-Only Trace Reconstruction (0CTF 2016)](#opcode-only-trace-reconstruction-0ctf-2016)
|
|
17
|
+
- [LD_PRELOAD time() Freeze for Deterministic Analysis (EKOPARTY 2017)](#ld_preload-time-freeze-for-deterministic-analysis-ekoparty-2017)
|
|
18
|
+
- [LD_PRELOAD memcmp Side-Channel for Byte-by-Byte Bruteforce (Blaze CTF 2018)](#ld_preload-memcmp-side-channel-for-byte-by-byte-bruteforce-blaze-ctf-2018)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Qiling Framework (Cross-Platform Emulation)
|
|
23
|
+
|
|
24
|
+
Qiling emulates binaries with OS-level support (syscalls, filesystem, registry). Built on Unicorn but adds the OS layer that Unicorn lacks.
|
|
25
|
+
|
|
26
|
+
### Qiling Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install qiling
|
|
30
|
+
# Download rootfs for target OS:
|
|
31
|
+
git clone https://github.com/qilingframework/rootfs
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Basic Usage
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from qiling import Qiling
|
|
38
|
+
from qiling.const import QL_VERBOSE
|
|
39
|
+
|
|
40
|
+
# Linux ELF emulation
|
|
41
|
+
ql = Qiling(["./binary", "arg1"], "rootfs/x8664_linux",
|
|
42
|
+
verbose=QL_VERBOSE.DEFAULT)
|
|
43
|
+
ql.run()
|
|
44
|
+
|
|
45
|
+
# Windows PE emulation (no Windows needed!)
|
|
46
|
+
ql = Qiling(["rootfs/x86_windows/bin/binary.exe"], "rootfs/x86_windows")
|
|
47
|
+
ql.run()
|
|
48
|
+
|
|
49
|
+
# ARM/MIPS emulation (IoT firmware)
|
|
50
|
+
ql = Qiling(["rootfs/arm_linux/bin/binary"], "rootfs/arm_linux")
|
|
51
|
+
ql.run()
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Anti-Debug Bypass via Emulation
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from qiling import Qiling
|
|
58
|
+
|
|
59
|
+
ql = Qiling(["./binary"], "rootfs/x8664_linux")
|
|
60
|
+
|
|
61
|
+
# Hook ptrace syscall — return 0 (success)
|
|
62
|
+
def hook_ptrace(ql, ptrace_request, pid, addr, data):
|
|
63
|
+
ql.log.info("ptrace bypassed")
|
|
64
|
+
return 0
|
|
65
|
+
|
|
66
|
+
ql.os.set_syscall("ptrace", hook_ptrace)
|
|
67
|
+
|
|
68
|
+
# Hook specific address (e.g., anti-VM check)
|
|
69
|
+
def skip_check(ql):
|
|
70
|
+
ql.arch.regs.rax = 0 # Force success
|
|
71
|
+
ql.log.info(f"Skipped check at {ql.arch.regs.rip:#x}")
|
|
72
|
+
|
|
73
|
+
ql.hook_address(skip_check, 0x401234)
|
|
74
|
+
|
|
75
|
+
ql.run()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Input Fuzzing with Qiling
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
# Emulate binary with different inputs to find flag
|
|
82
|
+
import string
|
|
83
|
+
from qiling import Qiling
|
|
84
|
+
|
|
85
|
+
def test_input(candidate):
|
|
86
|
+
ql = Qiling(["./binary"], "rootfs/x8664_linux",
|
|
87
|
+
verbose=QL_VERBOSE.DISABLED, stdin=candidate.encode())
|
|
88
|
+
ql.run()
|
|
89
|
+
return ql.os.stdout.read()
|
|
90
|
+
|
|
91
|
+
for ch in string.printable:
|
|
92
|
+
output = test_input("flag{" + ch)
|
|
93
|
+
if b"Correct" in output:
|
|
94
|
+
print(f"Found: {ch}")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Advantages over GDB/Frida:**
|
|
98
|
+
- No debugger artifacts (bypasses all anti-debug by default)
|
|
99
|
+
- Cross-platform without hardware (ARM, MIPS, RISC-V on x86 host)
|
|
100
|
+
- Scriptable with Python (faster iteration than GDB)
|
|
101
|
+
- Snapshot/restore for brute-forcing
|
|
102
|
+
|
|
103
|
+
**Key insight:** Qiling emulates the entire OS layer (syscalls, filesystem, registry), not just the CPU. This means anti-debug checks like `ptrace(TRACEME)` naturally return success without patching, and you can analyze ARM/MIPS binaries on an x86 host without QEMU or real hardware.
|
|
104
|
+
|
|
105
|
+
**When to use:** Foreign architecture binaries, IoT firmware, heavy anti-debug, automated testing of many inputs.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Triton (Dynamic Symbolic Execution)
|
|
110
|
+
|
|
111
|
+
See [tools-advanced.md](tools-advanced.md#triton-dynamic-symbolic-execution) for full Triton reference. Quick usage:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from triton import *
|
|
115
|
+
|
|
116
|
+
ctx = TritonContext(ARCH.X86_64)
|
|
117
|
+
|
|
118
|
+
# Symbolize input buffer
|
|
119
|
+
for i in range(32):
|
|
120
|
+
ctx.symbolizeMemory(MemoryAccess(0x600000 + i, CPUSIZE.BYTE), f"flag_{i}")
|
|
121
|
+
|
|
122
|
+
# Process instructions and collect constraints
|
|
123
|
+
# At comparison point, solve for flag
|
|
124
|
+
model = ctx.getModel(ctx.getPathConstraintsAst())
|
|
125
|
+
flag = ''.join(chr(v.getValue()) for _, v in sorted(model.items()))
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Key insight:** Triton excels at single-path DSE (Dynamic Symbolic Execution) where angr's path explosion is a problem. Feed it a concrete execution trace, symbolize specific inputs, and solve for constraints at comparison points. Faster than angr for linear code paths with known execution flow.
|
|
129
|
+
|
|
130
|
+
**Best for:** Single-path symbolic execution, deobfuscation, taint analysis. Faster than angr for linear code paths.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Intel Pin Instruction-Counting Side Channel (Hackover CTF 2015)
|
|
135
|
+
|
|
136
|
+
**Pattern:** Brute-force input character-by-character against a binary using Intel Pin's `inscount0` tool. Each correct character causes deeper execution (more instructions) in the comparison logic.
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
import string
|
|
140
|
+
from subprocess import Popen, PIPE
|
|
141
|
+
|
|
142
|
+
pin = './pin'
|
|
143
|
+
tool = './source/tools/ManualExamples/obj-ia32/inscount0.so'
|
|
144
|
+
binary = './target'
|
|
145
|
+
|
|
146
|
+
key = ''
|
|
147
|
+
while True:
|
|
148
|
+
best_count, best_char = 0, ''
|
|
149
|
+
for c in string.printable:
|
|
150
|
+
cmd = [pin, '-injection', 'child', '-t', tool, '--', binary]
|
|
151
|
+
p = Popen(cmd, stdout=PIPE, stdin=PIPE, stderr=PIPE)
|
|
152
|
+
p.communicate((key + c + '\n').encode())
|
|
153
|
+
with open('inscount.out') as f:
|
|
154
|
+
count = int(f.read().split()[-1])
|
|
155
|
+
if count > best_count:
|
|
156
|
+
best_count, best_char = count, c
|
|
157
|
+
key += best_char
|
|
158
|
+
print(f"Found: {key}")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Key insight:** Movfuscated binaries (compiled with `movfuscator`) expand every instruction into sequences of `mov` operations, making static analysis impractical. However, character-by-character comparison still creates measurable instruction count differences. Pin's `inscount0.so` counts total executed instructions — the correct character at each position causes ~1000+ more instructions (proceeding further in the comparison). Also works for obfuscated binaries with sequential input checks.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
### Intel Pin Instruction Counting with Genetic Algorithm (hxp CTF 2017)
|
|
166
|
+
|
|
167
|
+
For self-modifying code that decrypts the next chunk only after each character check passes, standard character-by-character Pin counting fails because the search space is too large and characters may interact. Use a genetic algorithm instead to explore the input space more efficiently.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
import subprocess
|
|
171
|
+
import random
|
|
172
|
+
import string
|
|
173
|
+
|
|
174
|
+
PIN_PATH = '/tmp/pin-3.5/pin'
|
|
175
|
+
TOOL_PATH = 'source/tools/ManualExamples/obj-intel64/inscount0.so'
|
|
176
|
+
|
|
177
|
+
def fitness(candidate):
|
|
178
|
+
"""Run binary under Pin and return instruction count as fitness."""
|
|
179
|
+
proc = subprocess.Popen(
|
|
180
|
+
[PIN_PATH, '-t', TOOL_PATH, '--', './binary'],
|
|
181
|
+
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
182
|
+
stdout, stderr = proc.communicate(candidate.encode())
|
|
183
|
+
# inscount0 writes count to stderr or inscount.out
|
|
184
|
+
try:
|
|
185
|
+
with open('inscount.out') as f:
|
|
186
|
+
return int(f.read().split()[-1])
|
|
187
|
+
except:
|
|
188
|
+
return 0
|
|
189
|
+
|
|
190
|
+
def mutate(individual, rate=0.1):
|
|
191
|
+
"""Randomly mutate characters in the individual."""
|
|
192
|
+
result = list(individual)
|
|
193
|
+
for i in range(len(result)):
|
|
194
|
+
if random.random() < rate:
|
|
195
|
+
result[i] = random.choice(string.printable[:62])
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
# Genetic algorithm parameters
|
|
199
|
+
FLAG_LEN = 40
|
|
200
|
+
POP_SIZE = 100
|
|
201
|
+
SURVIVORS = 20
|
|
202
|
+
|
|
203
|
+
# Initialize random population
|
|
204
|
+
population = [random.choices(string.printable[:62], k=FLAG_LEN) for _ in range(POP_SIZE)]
|
|
205
|
+
|
|
206
|
+
for generation in range(10000):
|
|
207
|
+
# Score each individual by instruction count
|
|
208
|
+
scored = [(fitness(''.join(p)), p) for p in population]
|
|
209
|
+
scored.sort(reverse=True)
|
|
210
|
+
best_score, best_individual = scored[0]
|
|
211
|
+
print(f"Gen {generation}: {best_score} {''.join(best_individual)}")
|
|
212
|
+
|
|
213
|
+
# Keep top survivors, mutate to refill population
|
|
214
|
+
survivors = [s[1] for s in scored[:SURVIVORS]]
|
|
215
|
+
population = survivors + [mutate(random.choice(survivors)) for _ in range(POP_SIZE - SURVIVORS)]
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Modified Pin for Go binaries (table-lookup flag checking):**
|
|
219
|
+
When standard `inscount` fails because counter increments don't correlate with correctness (e.g., table-lookup comparison), modify Pin's icount tool to only count executions at the success-branch address. Brute-force character-by-character with this targeted counter:
|
|
220
|
+
```cpp
|
|
221
|
+
// Modified inscount0.cpp — count only executions of a specific address
|
|
222
|
+
static ADDRINT target_addr = 0x401234; // success-branch address
|
|
223
|
+
static UINT64 target_count = 0;
|
|
224
|
+
|
|
225
|
+
VOID CountAtTarget(ADDRINT ip) {
|
|
226
|
+
if (ip == target_addr) target_count++;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
VOID Instruction(INS ins, VOID *v) {
|
|
230
|
+
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)CountAtTarget,
|
|
231
|
+
IARG_INST_PTR, IARG_END);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Key insight:** When each correct character unlocks a new code section (self-modifying or multi-stage decryption), instruction count increases monotonically with correctness. A genetic algorithm explores the input space more efficiently than character-by-character brute-force because it can discover multiple correct characters simultaneously. Converges in approximately 30 minutes for 40-character flags. For table-lookup comparisons where total instruction count doesn't correlate, target a specific branch address instead.
|
|
236
|
+
|
|
237
|
+
**References:** hxp CTF 2017
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Opcode-Only Trace Reconstruction (0CTF 2016)
|
|
242
|
+
|
|
243
|
+
Given an execution trace with only opcodes (no register/memory values), reconstruct the program: sort/dedup trace by address, split into basic blocks, annotate functions. Sorting algorithms are particularly vulnerable -- branch decisions leak element ordering.
|
|
244
|
+
|
|
245
|
+
**Approach:**
|
|
246
|
+
1. Sort trace entries by address, deduplicate to recover code layout
|
|
247
|
+
2. Identify basic block boundaries (jumps, calls, returns)
|
|
248
|
+
3. Map branch taken/not-taken decisions from trace order
|
|
249
|
+
4. For sorting algorithms, partition comparisons reveal relative ordering of all input elements
|
|
250
|
+
|
|
251
|
+
**Key insight:** Execution traces without data values still leak information through branch decisions. Quicksort partition comparisons reveal which element is greater/lesser at each step, enabling full recovery of the sorted input from branch direction alone.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## LD_PRELOAD time() Freeze for Deterministic Analysis (EKOPARTY 2017)
|
|
256
|
+
|
|
257
|
+
Override `time()` via LD_PRELOAD to return a constant value, freezing any timestamp-seeded PRNG. Once the binary's cipher becomes deterministic, brute-force each output byte without understanding the VM or cipher internals.
|
|
258
|
+
|
|
259
|
+
```c
|
|
260
|
+
// freeze_time.c — compile: gcc -shared -fPIC -o freeze.so freeze_time.c
|
|
261
|
+
#include <time.h>
|
|
262
|
+
|
|
263
|
+
time_t time(time_t *t) {
|
|
264
|
+
if (t) *t = 1234567890;
|
|
265
|
+
return 1234567890;
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# Build and use:
|
|
271
|
+
gcc -shared -fPIC -o freeze.so freeze_time.c
|
|
272
|
+
LD_PRELOAD=./freeze.so ./binary
|
|
273
|
+
|
|
274
|
+
# Byte-at-a-time oracle: run with frozen time, try each candidate byte,
|
|
275
|
+
# observe output — correct byte produces expected output character.
|
|
276
|
+
for byte in $(seq 0 255); do
|
|
277
|
+
output=$(echo -n "$(printf '\x%02x' $byte)" | LD_PRELOAD=./freeze.so ./binary)
|
|
278
|
+
# Check output against known/expected
|
|
279
|
+
done
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
If `srand()` or `rand()` is also involved, override `rand()` too:
|
|
283
|
+
```c
|
|
284
|
+
int rand(void) { return 42; }
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Key insight:** LD_PRELOAD function interception freezes non-determinism sources (time, rand). Once deterministic, even complex VMs become tractable byte-at-a-time oracles.
|
|
288
|
+
|
|
289
|
+
**References:** EKOPARTY CTF 2017
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
### LD_PRELOAD memcmp Side-Channel for Byte-by-Byte Bruteforce (Blaze CTF 2018)
|
|
294
|
+
|
|
295
|
+
**Pattern:** Replace `memcmp` with an LD_PRELOAD library that returns the number of matching bytes instead of the standard -1/0/1 result. This converts any memcmp-based validation into a byte-by-byte oracle. Automate with GDB Python scripting to bruteforce each character position.
|
|
296
|
+
|
|
297
|
+
```c
|
|
298
|
+
// memcmp_hook.c - compile: gcc -shared -fPIC -o hook.so memcmp_hook.c
|
|
299
|
+
int memcmp(const char *s1, const char *s2, int n) {
|
|
300
|
+
int cnt = 0;
|
|
301
|
+
for (int i = 0; i < n; ++i) {
|
|
302
|
+
if (s1[i] == s2[i]) cnt++;
|
|
303
|
+
else break;
|
|
304
|
+
}
|
|
305
|
+
return cnt;
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Use with GDB: LD_PRELOAD=./hook.so gdb ./binary
|
|
311
|
+
# Set breakpoint after memcmp, read return value to count matching bytes
|
|
312
|
+
# Iterate characters at each position to find the one that increases count
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Key insight:** Replacing memcmp via LD_PRELOAD to return match count converts any comparison-based validation into a byte-by-byte oracle. Combined with GDB scripting, this automates bruteforce of password/flag checks without reversing the validation algorithm.
|
|
316
|
+
|
|
317
|
+
**Detection:** Binary uses `memcmp` or `strcmp` for flag validation (visible in `ltrace` output or import table). The comparison function is called with user input and a computed/stored expected value.
|
|
318
|
+
|
|
319
|
+
**References:** Blaze CTF 2018
|