0ctf freenote:从逆向分析到exploit编写!
鄙人的上一篇文章中,还算较为详细的讲解了unlink漏洞的原理,在阅读本篇文章之前建议先学习上一篇blog
现在我们开始学习,这道ctf题目现在已经挂在Jarvis OJ上了,下面给出nc的url和程序下载链接:
nc pwn2.jarvisoj.com 9886
https://files.cnblogs.com/files/Magpie/level6_x64.rar
首先分析程序功能:
程序是典型的记事本程序,功能上基本就是添加记录、打印列出所有记录、编辑记录、删除记录。
各位既然已经开始接触堆溢出类题目,相信入坑二进制应该已经有段时间的,基本的逆向分析能力应该都是有的,因此不一一讲解IDA中的分析过程,在此本人只简单地说一下各个函数的功能和程序的数据结构:
一、函数功能说明:
main函数如上,主要功能函数我们一一说一下(不按图中顺序,按逻辑顺序):
0x01.sub_400a49:创建记录索引表,具体就是分配一个大堆,堆里面存了各条记录存储区的指针(看后面分析就知道各条记录都malloc了一个堆来保存),就好像物业存了各个房间的钥匙,每把钥匙都对应了一个具体房间,挂钥匙的板子就是这个大堆,钥匙对应的房间就是存储各个记录的堆内存。
0x02.sub_400998:就是让你输入一个操作选项,没什么好说的,这里没有漏洞可以利用。
0x03.sub_400bc2:新建记录,进去以后的具体实现就是,让你输入记录内容长度和记录内容,然后检查长度有没有超最大限制,正常的化就malloc一个存储这条记录的堆块,然后以你输入的长度为标准一个一个把记录内容读进这个堆块。注意malloc堆块时有这样一个操作: ,这表示分配堆块的大小是0x80的整数倍。最后就是把这条记录的有关信息写进索引表;当然开始的时候也检查了索引表中还有没有空间,这个大家应该看的懂,待会看数据结构分析的时候会理解的更好一点。
0x04.sub_400b14:输出功能,遍历索引表,打印所有记录的标号和记录内容,标号从0开始。
0x05.sub_400d87:编辑功能,依据上述记录标号找到相应记录,然后edit。
0x06.sub_400f7d:删除功能,仍旧依据上述标号找到相应记录,然后重置其索引表为未使用态,并free掉对应的记录堆块。
二、数据结构分析:
索引表的数据结构:
head不用管,是索引表大堆块的块首,不属于用户区;
max_size表示能存储的最大的记录数量,exist_num表示已有的记录个数;
再往后就是每三个数据构成一个索引项,索引项的结构体的三个数据分别代表:0/1是指该项有无记录引用,0是没有,1是有记录,size_user是记录的长度,ptr_heap是存储记录的堆块的用户区指针。
注意:
这里有一点大家必须清楚,就是我们所说的记录编号,并不是将所有已经存在的记录进行编号,这个编号指的是,索引表中的每个索引项,无论是否存在记录,都进行线性的、固定的编号,这个编号在索引表建立后就有了,和记录是否存在无关,它是索引项的编号。
这就好像高中不可以自己配钥匙的时候,宿管大爷的钥匙板上无论是否挂了钥匙,对应位置都会贴一个房间号标签,即使不挂钥匙也有,只不过没挂钥匙说明有人取走了,说明这个寝室里有人,对应过来就是,没挂钥匙相当于第一个结构体成员值为1,对应的记录堆块已经存在!
但是也有一点不同,大家不要过度将两个例子对号入座,钥匙房间一一对应,钥匙还回(free)的时候从哪来放哪去,下次拿还是从同一个地方,但是假如开始有记录1234,你把2删了,下次新建的时候,根据代码逻辑可以知道是把索引表中原来索引项2的位置拿来放新的,因此这个索引表是允许碎片化的。
三、漏洞挖掘:
这样一来,结合各函数功能和索引表机制的原理,程序的数据处理思路也就清晰了,下面我们来找程序的漏洞:
0x01、即便堆漏洞接触不多,细心的读者可能也很容易发现第一处漏洞,漏洞出现在新建记录的那个函数里面,上图:
这个就是新建记录函数中实现读入记录内容的子函数,a2是用户输入的记录的长度,通过循环read读进a2个字符
注意,读进a2个字符!!不是长度为a2的字符串!!!
正常情况下长度为n的字符串,是有包含‘\x00‘结束符在内的n+1个ascii,但是这里并没有把结束符读进来,少了结束符,在打印记录时就不会正确的停下来,也就可以实现内存泄露!
0x02、本程序的核心漏洞,double free漏洞:
漏洞出现在记录删除函数中,我们仔细分析一下这个函数的逻辑实现就可以发现一个惊人的事实:在你输入一个标号后,程序并没有检查索引表中标号位置的索引项的第一个成员变量是否已经为0、也没有检查对应索引项的堆指针成员变量指向的堆内存是否已经被free,也就是说,即使这个索引项已经删过记录了,你还可以再删它一次,再像没事儿人一样对ptr_heap再进行一次free,而在程序代码中,free之后并没有将对应堆指针置空(点名批评!),这就对同一堆块free了两次,造成了double free漏洞!
(我知道你们不感兴趣但是还是要吐槽一下修补措施:哪怕你做到随手置空、检查标志位、检查ptr、或删除时清除索引项的ptr中的任何一点也不至于出现这个漏洞。。。)
0x03、巧夺天工unlink——“块内切割”大法:
这个方法也有人叫“堆块重构”技术
反正就是一通骚操作,违章改造房屋结构,是要犯法的。。。(画风秒变皮)
好了,我们正经来讲一下:
(为什么没有了呢?博主写累了,一次写不完啊....很快就更!别急!....未完待续)
原文地址:https://www.cnblogs.com/Magpie/p/9806572.html