-
-
[原创]看雪6月 京东 2018CTF 第六题——名字叫noheap我偏要heap的非预期解
-
发表于: 2018-6-28 19:51 4615
-
这题自己思维僵化了,看到经典菜单题就对着堆开始刚了,结果刚出个非预期解。。。刚了两天最后还因为不会重定向没拿到flag,可以说是很尴尬了。。。但是对着堆刚的确需要对堆的理解比较深刻,我这种菜鸟就刚了两天。。。最后发现好像另外一种堆利用是用fastbin attack,但是我这边是用的house of orange,前者貌似简单的多,因为只用leak一个libc就好了,heap不用leak,然而leak heap真的想了我好久。。。然而最近刚好也在学习house of orange,就当是练习了吧。。。然后破解hash我就不说了,这个比较简单大佬们的WP都有写,我这篇主要是分享非预期解的实现,以及给自己做一个总结。相关资料可以看
house of orange
4.13_io_file.md
不考虑预期解的VM部分,程序的漏洞有
这里有个知识点,当调用malloc(0)
的时候,会返回一个0x20的fastbin的chunk,然后后面get_input_noterm
的时候对这个size减了1,能写0xff
个字节(因为强转成了byte),所以会有整形溢出导致的堆溢出
而且show
的时候,是直接write(1, chunk_0.buf, (unsigned __int8)chunk_0.size);
的,所以可以直接通过这个leak
我们知道,当unsorted bin里有一个chunk的时候,可以leak出libc,而有两个chunk的时候,可以leak出libc和heap,这个具体可以看一下堆的实现,因为是个双向链表。
接着就是如何构造unsorted bin了,这题目就很蛋疼了,他只会记录最近一个chunk,而且只能free最近一个malloc的堆,这看似没法构造出unsorted bin,因为会直接跟top chunk merge掉,但是其实是有办法的,这里又涉及到一个知识点。
如果我们
这样的话,在malloc(0x80)
之后,堆管理器会把fastbin里面的chunks合并成一个chunk塞到unsorted bin里面去。malloc_until(0, "A" * 0x18 + p64(0x71))
是为了防止这些fastbin里面的chunk跟top chunk直接merge了,所以用这个来分割一下。至于溢出部分是改写top chunk大小,这个待会会说。
执行完for循环后,堆的状态如图所示
执行完后面的free()
后,堆的状态如图所示
可见,原本在fastbin里面的那些chunks,被merge并且塞到了unsorted bin中(0x30的地址跟unsorted bin的chunk的地址一样,大小也能算一下,是对的)
但是这个时候只有一个chunk,house of orange需要堆基址泄露,需要在unsorted bin中构造两个chunks,怎么办?
emm这个又是一个卡了我很久的地方,本来想house of orange把top chunk变成unsorted bin结果发现这个top chunk太小(最多只能0x80,因为程序限制最大malloc的大小为0x80,即最大能分配的chunk大小为0x90,而house of orange相当于free掉top chunk,所以这个大小会直接被塞到fastbin里面去。有趣的是,如果top chunk大小为0x70,他并不会把chunk直接塞到0x70的fastbin中,而是会塞到0x50的fastbin中,并且分割出两个0x10的chunk,没错就是0x10的chunk,不知道为什么,有大神知道可以讨论一下)
然后我又试了另一个方法,就是如果不能构造两个unsorted bin的chunk,构造两个fastbin的chunk也能leak堆,但是此时发现了一个很神奇的事情,如果此时fastbin中有一个0x50的chunk,top chunk为0x70,此时malloc(0x80)
,理论上来讲根据上面所说会把这个0x70分出两个0x10的chunk并且把剩下的0x50的chunk塞到fastbin,此时理论上来讲会0x50的fastbin会有两个chunk,但是神奇的是,malloc(0x80)
调用完后,原来在fastbin的0x50的chunk被移到了unsorted bin,所以0x50的fastbin还是只有一个chunk。。。我的猜想是,无论是consolidate top chunk,还是extend堆换top chunk,都会对fastbin做清理并且移到unsorted bin,当然这只是猜想,具体的要看libc源码才能了解透。。。
最后就是成功leak的方法了,首先extend堆是肯定要的,所以最开始要把堆先往右边"shift"一下
这样最后就能在malloc_until(0, "A" * 0x18 + p64(0x71))
的时候让top chunk是0xXXX071,这样就可以保证top chunk + size
是page align的
接下来我就想,如何在unsorted bin构造两个chunks,如果是house of orange的extend堆的方法的话,那么是否可以在新的堆区塞一个chunk到unsorted bin中呢?因为这样就不会merge了。
答案是对的,同样是用之前的方法,把fastbin里面的东西放到unsorted bin中
跟前面那个类似。只不过前面那个top chunk是0x70,malloc_until
会从fastbin的chunks那边拿(注意不是直接拿fastbin的chunk,可以理解为从fastbin merge后的大chunk中拿);而这个会从top chunk里面拿,consolidate的时候再把fastbin的那些chunks转成unsorted bin。。不过结果都是把fastbin的chunks变成unsorted bin就是了。。
此时堆的情况如上,这样的话就可以leak出heap的基址了
接着就是unsorted bin实现house of orange,这个不具体说了,可以看看资料,注意那几个参数要设对。
有个坑就是0x0106040f01130301
和0x4000161302011409
还有后面一个0
,一开始怎么都会segfault,一开始以为是malloc的问题,结果其实根本不是,是这两个数在全局变量区被覆盖而导致的,所以payload覆盖不能动它们。也就是这个时候我大概意识到了预期解是什么。。。
最后其实还有个问题,如果malloc(0)
之后紧跟用来house of orange的unsorted bin
,system
的地址刚好跟那几个magic number重合。。。尴尬,所以要先再在0x20的chunk后面malloc一个chunk,然后再覆盖。。。
PS:我就在刚刚,写完这段话的时候,意识到把虚表位置改一下就能解决这个问题。。。看来是当时犯蠢了。。。
然后这又有问题,因为只能写0xff
个字节,而padding + sizeof _IO_FILE_plus
大于这个数,所以办法就是跨chunk分两次放。。。payload如下。。。
其实house of orange还有一个坑,只有当libc的低DWORD为负数的时候,才有效,具体是要让_IO_list_all
中第一个元素(在main arena上)不过这个check,不然就会通过虚表call到main arena上面去,具体跟一下就知道了。。。
所以,根据他的逻辑,只要fp->_mode <= 0
就会循环到链表中第二个元素call虚表,即我们的system
利用fastbin attack写malloc hook,应该也可行。
这个还不用leak heap,多简单。。。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!