前言
姑且把这篇文章看作对这周做题的一个总结吧,本人的之后还仅限于glibc2.31以及之前的版本,对glibc2.32以及之后的版本了解的少之又少,这周做题遇到了问题也不能及时的解决,所以我还是了解一下吧。
这里们用到的环境都是glibc2.33,从2.32到2.33之间,堆管理系统下的保护斌没有太大的变化,所以我们就直接使用glibc2.33的环境进行测试。
指针异或加密保护
在2.32及其之后的版本中进行了一把大改革,就是将我们之前经常利用tcache fd,bk等指针进行了一把异或的加密让我们的利用更难进行,leak之后更难进行一个切片接受,这样几乎就劝退许多比较慵懒的人(比如说我。
源码对比
我们先进行一个源码的对比:
我们主要是分析tcache的存放问题,之分析源码中tcache_put()和tcache_get()函数
glibc2.31:
我们发现在存取两个函数中没有堆指针的合法性做出检测,就是一个比较单纯的链子,也十分的容易欺骗。
1 | /* Caller must ensure that we know tc_idx is valid and there's room |
glibc2.32
我们发现相比glibc2.31增加了一个东西——PROTECT_PTR,REVEAL_PTR,通过字面意思我们发现这是一个指针的保护,具体怎么保护我们还是需要分析一下PROTECT_PTR、REVEAL_PTR本身。
然后就是在我们取出堆块的时候增加了一个保护,就是检测堆块的指针是否合法
1 | /* Caller must ensure that we know tc_idx is valid and there's room |
我们来看关键的PROTECT_PTR、REVEAL_PTR:
这里对堆块的处理是一次异或运算,就是 tcache_entry->next中存放的地址于自身的地址进行一波异或然后得到的值在放入原来的位置。这就需要我们不仅要获得指针,而是要提前获得堆块的及地址。这就给我们利用带来的不小的麻烦。
1 |
大佬的想法:若是我们能够直接控制 tcache struct
,则仍然可以直接进行任意地址写,这是因为在 tcache struct 中存放的仍是未经异或运算的原始 chunk 地址。
绕过方法
这样的话,我们想到,一个数异或他的本身为0,这几样的话一个数异或0就是他的本身,那么放入第一个他tcache的时候其堆块内就有本身的地址,如果有uaf的话就可以泄露heapbase,正是因为这个机制,为我们提供了一个更加方便的leak_heap的方法。
题目实践-2021VNCTF-FF
例行检查
逆向分析
存在uaf漏洞,只能show一次,edit两次。
leak_heapbase
那我们想来了,之前我们分析的漏洞,就是在我们free掉第一个tcache bin堆块之后,fd上就有heap的地址我们先执行
1 | def pwn(): |
我们gdb看一下其中的东西
我们发现少了下面的三位,不过无伤大雅只要*0x1000就行了。
1 | add(0x80,'powercat') |
成功泄露
实现double free
这里我们只需要进行将key修改了就可以实现double free
1 | edit('powercat') |
修改后
1 | delete() |
实现了double free。
free掉tcache struct
我们只需要修改一下tcache的内容就可以,我们知道前面的一些机制我们就可以相应的取修改一下这个指针
1 | edit1(p64(heap_leak^(heap_base+0x10))) |
成功将tcache_struct链入bin中。
1 | tcache_struct = heap_base + 0x10 |
这里已经成功将tcache_struct free掉
将stdout放入tcache_struct
1 | add(0x40, ('\x00\x00' * 3 + '\x01\x00' + '\x00\x00' * 2 + '\x01\x00').ljust(0x70, '\x00')) |
这里需要我们进行爆破
这里我们爆破到了stdout leak到了libc
leak_libc
1 | libcbase = uu64(ru(b'\x7f')[-5:]+b'\x7f') - 0x1e4744 |
劫持__free_hook
1 | add1(0x10, p64(libcbase + libc.sym['__free_hook']-0x20)) |
okk
触发
1 | delete() |
好!
exp
1 | #encoding = utf-8 |