2025软件安全赛pwn encoder
八爪鱼 CTF 346浏览 · 2025-03-25 08:24

赛后总结发现这是一个漏洞百出的题,有各种各样的打法,这里我的打法是基于patch的结果来写的,下面就详细说一下

题目分析

题目是2.31的libc,不是传统的增删改查,出题思路是一个文件上传后压缩解压的功能,但实际上就是增删查都齐全,没有传统意义上的改



upload对应的是增,release对应的是删,download对应的是查,encode,decode需要逆向分析一下

首先看upload



这里要输入一个index和一个size,并且size不能超过0x20000,如果超过,最后的size也是0x20000,不过这题不关心这里

接着有一个指针表一个size表,其实这里我逆向命名的不太好,他应该是一个结构体,先看一下按我的理解



这里面中间空出来的2行是一段buf,由于sizetable的大小是int,所以buf的总长度是0x10+0x4=0x14

更好的理解方式应该是这样



注意size只占4位,这里画的不完全正确,意会即可

然后看程序的逻辑,如果size<=16,那么会直接把指针取出来,这里有另一种打法用到了,由于我没有用到,所以这里不细讲,想看的话可以点击这里

在size>16时,如果本身size表里已经有了,那么判断输入的size是否比size表里的大,是的话会将原先的堆块释放掉,再重新申请一块更大的,放到指针表里,下面的printf经过heshi的提点,猜测这个其实是给checker看的,不是做题需要的,事实也确实如此

如果size不比size表里大,那么不作操作

如果原先size表就为空,那么直接申请该大小,并为指针表赋值



最后会将size表赋值,然后读入你输入的size大小的数据,必须读完,有点像进阶版的calloc,这就导致了想要指针残留是不可行的

再看download



逻辑不难,根据size调用write函数,有多少写多少,就是一个show函数的功能

release函数



一个常规的删除,该置0的都置0了,没有uaf

encode函数



逻辑是这样,根据输入的index找sizetable,然后根据size的大小走不同的分支,如果size<=0x10,那么此时需要encode的数据存在bss上,也就是先前说到的结构体里的buf,反之在堆块上,取的是指针表里的指针,接着v0的计算大概就是预分配一个大一点的堆块,保证空间充足不会溢出,不必太关心,接下来讨论的都是size>0x10的情况

然后,程序会调用encode_0函数,这个函数的逻辑我是让队里的re逆的,大概逻辑是这样的:



三个参数分别对应原数据的地址,原数据的大小,encode之后的数据的存放位置,返回值是encode之后的数据长度,那encode_0干了什么事呢?实际上就是对数据的压缩,这里举个例子说明,如果原数据是“aaaa”(\x61\x61\x61\x61),那么压缩完之后数据就是\x04\x61

程序会把原先的指针释放掉,然后向新申请的指针首先memcpy一个“RLE\n”,这其实就是magic头,接着会将encode后的size放到ptr[1]的位置,从ptr[2]的位置开始存放数据,所有数据存放完之后,还会在数据末尾存放一个sum,这个sum是什么呢?是存放的所有数据的ASCII码之和,拿上面的那个例子来说,sum=0x4+0x61=0x65

然后会判断encode之后的数据长度+12是否小于0x10,是的话存到bss上的buf,否则把指针表的指针更新为ptr,那么这里就存在漏洞,原先存放原数据那块的堆块被释放了,但是size表没清0,所以接下来我把encode函数当成了delete函数来用,他是一个不清size表的delete,而release是一个清除size表的delete

decode函数

最后就是我打出本题的核心decode函数了



首先程序会分析这个堆块的头是不是RLE\n,是的话会取一个sum,这个sum的位置是v3+v3[1]+8,那么v3[1]是什么呢?结合对encode函数的逆向分析可知,这里其实就是encode之后的数据长度,所以这里的sum就是伪造数据的ASCII码之和,所以如果我把这个sum伪造了,那么我就可以在decode的时候解压出更多的数据,从而覆盖下一个堆块的size

decode函数就是encode函数的逆过程,其中sum参数会做减法,减去每次解压的ASCII码,比如压缩后的数据是\x04\x61,如果我伪造的sum是0x67,那么在执行操作之后做减法,sum=0x67-0x4-0x61=0x2,那么我还可以继续写,他的判断是while(sum),也就是sum只有为0才会停止,为正为负都不会停止,这也会导致这样一个问题,如果sum变成了负数,那么就会死循环了,正因为sum必须为0才停止,所以这里的伪造比较复杂,需要不断调试



后续操作和decode就差不多了

先说说怎么patch

首先可以肯定的是,这题的check写的就是一坨狗屎,这是毋庸置疑的,我修的是magic头,在数据段找到RLE这个字符串,随便改掉就可以,比如我改成了RJE,没想到就过了?很神奇有没有,正是因为这里patch过了,才有了我上面的思路,因为一开始我也不知道怎么改,我就猜测他可能会伪造encode后的数据进行攻击,所以一定要过memcpy("RLE\n",4)的检查,因此只要魔术头变了,他的exp一定就打不通了,事实估计也正是如此

0xh3y3师傅说把malloc_usable_size patch成malloc也过了,确实难绷



解题思路

前面既然已经说过了会导致溢出的漏洞在decode函数了,那么核心就是看一下该如何构造了

第一步:伪造size

这里我伪造的decode之后的数据长度是0x14,原因是0x14<<6=0x500,目的是为了在encode之后,空出来的那个堆块大小刚好是0x500,正好可以被decode里面的malloc函数再次申请到,同时0x500释放之后直接放入unsorted bin,不进入tcache,这里也是有用的

所以根据对decode的逆向,最终sum的位置应该是(v3+0x14+0x8),所以从RLE头开始到sum,中间应该有0x1c的填充

接着decode函数会根据sum对我们的数据进行解压,这里gdb调试看一下(断点下载encode和decode之间)



左边的框圈出的就是length,右边的框圈出的就是sum

接着,decode函数申请了一个0x14<<6=0x500大小的堆块,申请完之后刚好就是图片中的unsortedbin的位置



会根据RLE的文件体进行解析,首先是0x68个0x20,那么第一次填充,会首先填充0x68个0x20,同时sum-=(0x68+0x20),从0x760变成0x6f8





后面的过程以此类推,要注意的是,由于sum本身伪造的过大,所以sum自己也会被解析成压缩后的数据,即0x60个0x07,0x00个0x00,然后才会继续解析后面的数据



这里经过调试,红框内的payload刚好完成了对上图中绿色的,大小为0x511的堆块的填充,于是后面的绿框内的payload就是用来改下一个堆块的size的,根据payload应该不难看出,我把size改成了0x1241=0x511+0x620+0x710,这里的0x620是encode函数malloc的,0x710是我还没有malloc,即将想要malloc的大小(所以这里其实已经超过了top chunk),黄框部分的payload,目的就是为了在填充完size为之后,让sum继续减到0从而终止while循环进行的调整,是调试出来的结果



即将开始覆盖size:



覆盖结束后:



此时sum还剩0x76=0x6+0x70



既然这样那我们就成功的修改了堆块的size,后面的打法就容易了

接下来我们进行泄露libc的操作,原理就是堆块重叠,类似堆风水

释放伪造堆块,得到一块大的unsortedbin



在申请0x500的大小,于是在index为1的堆块里就会存在残留指针





把堆块1 download(show)一下,我们就得到了libcbase和heapbase

接着再把刚才申请的0x500堆块申请回来,重新获得一块大的unsortedbin(这里就体现出了为什么一开始申请的大小是0x500,因为太小的话会直接放入tcache而不是unsortedbin,导致无法利用)

release(4)之后:



红框部分是一会要伪造的size,对应的其实是堆块1的size,我们要把这里伪造的小一点,让他属于tcache,后续打一个tcache投毒可以实现任意地址写任意数值,从而改__free_hook

upload(8,0x520,b'gggghhhh'*32*5+p64(0)+p64(0x41)+p64(0)*4)之后:



release(8)之后:又恢复了大的unsortedbin,为的是下一次可以覆写tcache的fd和key



release(1)之后:可以看到成功得到了tcache



接着我们在申请一个大堆块,将fd改写为__free_hook,key改写成0,那么__free_hook就被加入到了tcache里,最终就可以申请到这里了





改写成功

最后先申请一个/bin/sh,在申请一次,就申请到了__free_hook,将此处写为system,然后release(10),就实现了执行system(“/bin/sh”)

完整paylaod如下

附打通截图



附件:
3 条评论
某人
表情
可输入 255
1682524363236627
2025-05-29 09:09 0 回复
请问大师傅用的哪个版本的系统或者libc做的题目呀?题目给的libc我patch后报错了,除了libc,ld是不是也得修改呀?
1128798066600659
2025-03-31 03:31 0 回复
太强了八爪鱼
Oxh3y3
2025-03-29 14:28 0 回复
学到了师傅