You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: pocs/linux/kernelctf/CVE-2026-23231_cos/docs/exploit.md
+12-5Lines changed: 12 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,9 +28,9 @@ Outside the kernelCTF separate-leak repro path, the exploit leaks the kernel tex
28
28
29
29
-**Fallback scan**: On non-Intel hosts, scans mapped ranges by treating averaged timings above the calibrated threshold as mapped kernel addresses. The threshold is capped at 190 cycles by default and 130 cycles on Xeon hosts, then adjusted down to the measured unmapped baseline plus a 24-cycle margin when the host timing is lower.
30
30
31
-
-**Vulnerability verification**: The verifier invokes the exploit with `--vuln-trigger` on a KASAN `nokaslr` kernel. In that mode the exploit skips the LPE payload, KASLR leak, physmap spray, and candidate schedule. It saturates IPv6 hooks, uses four IPv4 LOCAL_OUT flood threads, and keeps the same INET rollback race running until the verifier's outer timeout with a msg_msg poison reclaim instead of a ROP blob. Vulnerable kernels can report the packet-path `nft_do_chain()` UAF or panic on the poison blob pointer; patched kernels should time out without a KASAN report.
31
+
-**Vulnerability verification**: The verifier invokes the exploit with `--vuln-trigger` on a KASAN `nokaslr` kernel. In that mode the exploit skips the LPE payload, KASLR leak, physmap spray, and candidate schedule. It first checks `/proc/kallsyms` for KASAN report symbols so non-KASAN release kernels exit immediately as a negative verifier result. On KASAN builds it runs nine fresh child attempts; each child creates a new user+net namespace, saturates IPv6 hooks, and races the same INET rollback path for 17 seconds with eight IPv4 LOCAL_OUT flood threads, three INET `NEWCHAIN` producers, two chain dump threads, and a msg_msg poison reclaim pool instead of a ROP blob. Vulnerable kernels can report either the packet-path `nft_do_chain()` UAF or the chain-dump `nf_tables_dump_chains()` UAF; patched kernels should exit without a KASAN report.
32
32
33
-
-**Candidate arrays**: The repro path uses the verified primary `kbase`. When pagemap is unavailable, it tries a priority list of observed-good freed `__init` aliases, fills the rest from prefetch timing, and runs 18 candidates for 20 seconds each with a fresh user/net namespace per candidate. The highest-priority aliases are retried once before the direct-map fallback.
33
+
-**Candidate arrays**: The repro path uses the verified primary `kbase`. When pagemap is unavailable, it tries prefetch-ranked freed `__init` aliases first, appends observed-good aliases as backups, and runs up to 26 candidates for 20 seconds each with a fresh user/net namespace per candidate. The first few aliases are retried once before the direct-map fallback.
34
34
35
35
**Objects**: None (pure side-channel, no kernel objects involved).
36
36
@@ -45,7 +45,7 @@ A 2.5 GB region is allocated via `mmap(MAP_SHARED | MAP_ANONYMOUS | MAP_POPULATE
45
45
46
46
The direct-map base (`phbase`) provides a 1:1 virtual mapping of physical RAM, so these user pages are accessible from the kernel at predictable KVAs (`phbase + pfn * 0x1000` when pagemap is readable). When pagemap is blocked in repro, the exploit first uses the freed `__init_begin..__init_end` linear aliases as stable kernel mappings for sprayed pages.
47
47
48
-
If `/proc/self/pagemap` is readable, the exact physical frame number is read to compute the precise KVA. Otherwise, the exploit tries prioritized`__init` page aliases first, fills the remaining slots with prefetch-ranked aliases, gives each candidate a short fresh-child race window, and keeps the parent polling for late `core_pattern` hits. If that fails, it falls back to direct-map offsets in the `[0.5, 2.5] GB` range from `phbase` (avoiding the QEMU MMIO hole at 3-4 GB physical).
48
+
If `/proc/self/pagemap` is readable, the exact physical frame number is read to compute the precise KVA. Otherwise, the exploit tries prefetch-ranked`__init` page aliases first, appends observed-good aliases if they were not already selected, gives each candidate a short fresh-child race window, and keeps the parent polling for late `core_pattern` hits. If that fails, it falls back to direct-map offsets in the `[0.5, 2.5] GB` range from `phbase` (avoiding the QEMU MMIO hole at 3-4 GB physical).
49
49
50
50
**Objects**: Physical pages via physmap (direct map).
51
51
**Cache**: N/A (physmap pages, not slab objects).
@@ -74,6 +74,13 @@ An INET table (`ti`) is also created for the race loop.
74
74
75
75
Three threads run concurrently on 2 CPUs:
76
76
77
+
In `--vuln-trigger` verifier mode, the race is repeated across nine fresh child
78
+
namespaces with 17-second windows. Each verifier attempt also runs two
79
+
`NFT_MSG_GETCHAIN` dump threads over the `NFPROTO_INET` chain list and two extra
80
+
INET `NEWCHAIN` producers with separate chain-name ranges. This only increases
81
+
KASAN reachability for vuln-verify; the full LPE path still uses the
82
+
packet-path race plus `msg_msg` reclaim described below.
83
+
77
84
#### Thread 1: UDP Flood (CPU 1)
78
85
79
86
Sends UDP packets to `127.0.0.1:12345` via `sendmmsg()` in batches of 128. Each packet traverses `NF_INET_LOCAL_OUT`, calling `nft_do_chain_inet()` -> `nft_do_chain()` on any registered IPv4 hook. Yields every 8th batch (`sched_yield()`) to report RCU quiescent states, ensuring `synchronize_rcu()` on CPU 0 completes quickly on a PREEMPT_NONE kernel.
@@ -82,7 +89,7 @@ Sends UDP packets to `127.0.0.1:12345` via `sendmmsg()` in batches of 128. Each
82
89
83
90
#### Thread 2: INET NEWCHAIN Loop (CPU 0)
84
91
85
-
Sends nfnetlink batch messages creating `NFPROTO_INET` base chains. Each attempt:
92
+
Sends nfnetlink batch messages creating four independent `NFPROTO_INET` base chains per batch. Each chain attempt:
86
93
87
94
1. Chain is allocated (`nft_base_chain`, kmalloc-256) and published to `table->chains` via `list_add_tail_rcu()`.
88
95
2. IPv4 hook registers successfully -- packets can now reach `nft_do_chain()` on this chain.
@@ -99,7 +106,7 @@ Sends nfnetlink batch messages creating `NFPROTO_INET` base chains. Each attempt
99
106
100
107
Runs on the **same CPU** as the race thread. When the race thread blocks in `synchronize_rcu()`, the scheduler switches to this thread. It performs:
101
108
102
-
1. Burst of 8 `msgsnd()` calls across 512 queues: allocates `msg_msg` objects from CPU 0's `kmalloc-cg-256` per-CPU SLUB freelist. The **first**`msgsnd` after the chain free should pick up the just-freed `nft_base_chain` slot (SLUB LIFO order), while the remaining burst covers scheduler jitter before RCU quiescence.
109
+
1. Burst of 8 `msgsnd()` calls across 1024 queues: allocates `msg_msg` objects from CPU 0's `kmalloc-cg-256` per-CPU SLUB freelist. The **first**`msgsnd` after the chain free should pick up the just-freed `nft_base_chain` slot (SLUB LIFO order), while the remaining burst covers scheduler jitter before RCU quiescence.
103
110
2.`sched_yield()`: forces context switch -> `rcu_note_context_switch()` -> quiescent state for CPU 0, unblocking the pending `synchronize_rcu()`.
0 commit comments