The following is research material from FozZy from Hackademy and Hackerz Voice newspaper (http://www.hackerzvoice.org), and can be distributed modified or not if proper credits are given to them. For educational purposes only, no warranty of any kind, I may be wrong, this post could kill you mail reader, etc. -= OVERVIEW =- On current OpenBSD systems, any local user (being or not in the wheel group) can fill the kernel file descriptors table, leading to a denial of service. Because of a flaw in the way the kernel checks closed file descriptors 0-2 when running a setuid program, it is possible to combine these bugs and earn root access by winning a race condition. -= SOLUTIONS =- The "root exploit" problem was fixed on the CVS a week ago, a few hours after I reported this security bug. Patches for OpenBSD 3.1, 3.0 and 2.9 should be available on the OpenBSD website today. Removing the setuid bit from /usr/bin/skey* is not a good workaround, you must patch your kernel. Increasing kern.maxfiles and lowering the local users hard limits (both number of processes and opened files per process) could be a workaround to the DoS problem. -= TECHNICAL DESCRIPTION =- -=- Local Denial of Service -=- A local user can exhaust all the file descriptor entries in the kernel table, since there is nothing like a "per user limit" (unlike what is done for processes). It is only a "per process limit" which can be reached easily by a process creating pipes in a loop. One pipe means two unique file descriptors. Such a process can fork when its limit is reached, and then go on creating pipes, fork again... and finally reach the system limit. Then only the cracker can execute commands, by freeing some file descriptors when he wants to. Even root cannot run commands: typing "ls" in the console answers "Two many opened files in system" or "No ld.so". Operations of crontabs, logging facilities, daemons and servers, are at risk too. Other systems may be vulnerable to this attack. Linux tries to prevent this with some file descriptors available only to root. NB: Someone might find a way to exploit this remotly. -=- Local Root Exploit -=- Three weeks ago, XXXXXXXX from Pine released an advisory about the following bug on FreeBSD: closing file descriptors 0, 1 and/or 2 before exec'ing a setuid program can make this program open files under these fds, which have special meanings for libc (stdin/out/err). Reading or writing to root-owned files can be made possible, since stdXX==opened_file. Since 1998, there is a check in the OpenBSD kernel, intended to prevent this: in the execve function, if fd 0, 1 or 2 is closed, then it is opened as a new file descriptor assigned to /dev/null. Then, the setuid program can be safely executed. But, unlike the FreeBSD and NetBSD patch (and unlike what does linux in glibc), if there is a failure here, we break out of the current loop and the execve goes on (it should fail: this was pointed out by art in the comments of the code, but not fixed). ------------- In sys/kern/kern_exec.c, in the loop where the kernel tries to open /dev/null on closed fd 0->2: (...) if ((error = falloc(p, &fp, &indx)) != 0) break; (...) ------------- This can be exploited by a local user to gain root ! An attacker can win a race condition with respect to the system file descriptors table: 1) Fill the kernel file descriptors table (see the "local DoS" explanation). 2) Execute a setuid prog with (for instance) fd number 2 closed. In the execve kernel function, fd number 2 will not be opened to /dev/null because the falloc will fail. So, the setuid program will be run with fd 2 closed. 3) Quickly close some fd in order to allow the program to run correctly (ld.so needs free file descriptors, and so does the setuid program). Step 3 timing is crucial: if too early, /dev/null will be assigned to fd 2. If too late, the suid prog execution will fail. But I found that, by tuning a simple "for" loop, the good timing is quite easy to meet... -=- Exploit -=- I exploited successfully this vulnerability on OpenBSD 3.0, and became root from luser using the setuid-root program "/usr/bin/skeyaudit" (keyinit was the FreeBSD exploit by phased, but the OpenBSD skeyinit is not exploitable the same way). The trick is to put the line we want to insert in /etc/skeyskey into argv[0], with new line tags, when running skeyaudit. Any entry for the local user must be removed first, so skeyaudit will complain on stderr, printing its "filename" (argv[0]) and some error text. If /etc/skeyskey is opened on fd number 2, we won. Exploit code here: http://www.dmpfrance.com/fd_openbsd.c -= GREETS =- Uzy, millert@openbsd.org, and Gobbles :) FozZy <<fozzy@dmpfrance.com>> Hackademy, Paris. Hackerz Voice Newspaper International Edition : http://www.dmpfrance.com/inted.html