TSCTF2019 Final cxk
前言
第一次AWD,后来才知道一个题可以放多个漏洞,比赛的时候自己因为代码太多理不清自己乱掉了,心态真的很重要
程序逻辑
这里有两个结构很类似的结构体,一个是AV,另一个是Letter,因为太相似了,只是内部的名字和含义不太一样,这里就写一个好了。
一个AV里面包括des_chunk_addr(描述的堆块指针),av_number,下一个av_chunk,上一个av_chunk,,和des_size。
Letter里面把description换成了reason,即包含reason_addr和reason_size。
程序有非常多功能…这里只介绍我用到的地方,主menu的1和2,menu2的功能1(AddAV)
先看menu2的AddAv,最开始prev_av即0x6030f8位置的值为0x6030e0,所以第一次分配的时候check_addr的next_chunk和prev_chunk为0x6030e0。prev_av->next_chunk即0x6030f0的值写入了第一次分配的check_addr,这个check_addr同时被写入到0x6030f8,自此0x6030f0就作为链表的第一个节点,0x6030f8作为最后一个有效节点(其next_chunk为0x6030e0),故遍历直到0x6030e0作为结束。
这里分配AV的时候先读av_number,不存在已有的av_number方可分配,read_int是按照16进制转换输入,description的size限制在0x400内,每次先分配description,再分配固定大小为0x28的check_chunk。(最多分配48个AV)
下面是程序的第一个主功能(围绕Letter展开)
Letter的结构和AV很像,des改成reason理解就好。SendLawyerLetter可以当成Malloc,每次分配一个av_number的Letter前需要先有一个AV,链表的首节点改为0x603130,最后一个有效节点为0x603138,尾节点为0x603120。(这里有个check不管它)
1 | unsigned __int64 SendLawerLetter() |
1 | unsigned __int64 AddAV() |
EditStatement类似Edit函数,这里的strchr函数在des_chunk中寻找一个用户的字符,改为新字符,这里存在漏洞。比如我们分配的des_chunk大小为0x18,数据填充’a’*0x18,那么我们old_chr为’\x31’,new_chr为’\x91’即可将与des_chunk相邻的letter_chunk的size改为0x61,同理可以往后继续改,只要构造数据得当,可以溢出很远。
1 | unsigned __int64 EditStatment() |
Revoke函数类似Free,先free掉des_chunk再free掉letter_chunk,没有清空数据
1 | unsigned __int64 Revoke() |
ShowLawyerLetter类似Puts函数
1 | unsigned __int64 ShowLawyerLetter() |
漏洞利用
先分配一些AV(0x30)和Letter(0x70),再分配两个0x20的chunk并free(为之后的get shell准备)利用刚才strchr的漏洞将每个des_chunk的相邻letter_chunk的size改为0xa0(0x70+0x30,其下一个chunk的prev_in_use需要为1),释放7个伪造的块,第8次释放的时候得到了unsorted bin里的chunk。
分配一个0x28大小的chunk,这时候先分配des_chunk后分配letter_chunk,des_chunk里的fd和bk都为main_arena+88,ShowLawyerLetter可以泄露libc。
现在注意我们刚刚释放了2个0x20大小的chunk,它们都进了tcache[0x20]。而我们刚刚通过Unsorted bin分配得到的那个0x28的块恰和0x1ddd4e0相邻,我们用strchr的漏洞多次edit即可修改其fd为我们的__free_hook,改为system_addr,最后释放一个’/bin/sh\x00’的块即可得到shell。
exp.py
1 | #coding=utf-8 |