TQLCTF复现
TQLCTF高校挑战赛由清华大学(网络研究院)奇安信集团网络安全联合研究中心主办,由清华大学Redbud战队及奇安信技术研究院联合命题。
总体来说比较难,很多新鲜东西,比较贴近实战。有道pwn应该是第一天放上来的,赛后看write up,exp手撕了几页的汇编……还有什么模拟器,调试器,zic编译
unbelievable_write
程序逻辑很简单
c1: 申请一个0x10~0x1000之间大小的chunk,进行一次写入,然后free掉
c2:仅限一次的任意偏移free
c3:如果target被改写,就能get flag
这题有很多解法,还有一种较简单的非预期,最后我复现时还发现了一种极简单的非预期。
调试
c2中offset可以为负数,因此可以轻易free掉 tcache_perthread_struct
)
这个结构体长这样
1 | typedef struct tcache_perthread_struct |
counts共占2*0x40==0x80byte,知道了这个,我们就可以随意控制tcache了
非预期解
这道题出题人关PIE的时候忘了这会导致RELRO不是FULL RELRO,因此可以打GOT表。
既然能够控制tcache,很容易想到把target放进去,然后将其申请出来,但是由于申请伴随着free,在free target的地址的时候是会报错的。那我们可以先把free劫持掉,然后再劫持target。
注意这里题目没进行stdio初始化,这时候tcache就不能乱设置了,否则puts在申请堆块的时候会出错。
1 | add(0x90, "aaaa") |
预期解
1.利⽤⼀次任意地址free, free tcache perthread的chunk.
2.修改mp_ 中配置最⼤tcache的数量。半字节爆破(1/16概率),malloc到mp_+16的位置。这样0x1000⼤⼩的
chunk也在tcache中,并且能通过free的检查。
3.利⽤stdout完成任意地址写。题⽬没进⾏stdio初始化,故在第⼀次puts()时,stdout才会申请buffer,并在
申请的buffer中写⼊输出的字符串内容,完成任意地址写
由于需要爆破,所以整个IO需要重写。
1 | from pwn import * |
非预期解的非预期
由于我们可以把target放入tcache里,而由于未关缓冲区(实际上这倒是预期的点),puts之后会创建并free一个堆块,这个堆块肯定会进入tcache,而我们又将每个tcache打入了target,得益于tcache的特性,target一定会指向这个堆块,内容也就被改写了。
1 | add(0x90, "aaaa") |
没错,3步出flag,而且就算出题人开了FULL RELRO也可以奏效,踩到了出题人每一个想考查的点……