跳转至

Safe heap

safe_heap

glibc 2.31,保护全开,无seccomp

首先input_name里面存在一个整形溢出,读入的时候是unsigned,但是在sprintf里面是%d,可以输入2147483648,然后%d会变成-2147483648,然后你会发现这样恰好能够填满v3,sprintf把'\0'打到了v4[0]的位置,再输入v4后,v3里面就没有了'\0'结束符,作为字符串直接接上了v4,就出现了格式化字符串漏洞,但由于check_name里面不允许数字出现,(大概?)没办法直接格串打通,但可以leak,调试一下就会发现栈里面有libc/栈地址,%p leak即可

image-20210919225041521

在堆的分配上,可以发现程序没有直接调用malloc/free,而是调用了两个奇怪的函数,从字符串可以看出是safe_malloc/safe_free,红框中的代码实际上是libc中的request2size宏,通过用户申请大小计算实际堆chunk大小,通过与保存/申请的大小做对比,防止伪造堆块大小

image-20210920122255140

无法伪造堆大小,申请的堆大小又限制在了(0x40F,0x500],其实能想到的比较简单的做法就是利用unlink,它需要的条件是UAF+已知指向堆块指针的地址,因为我们这题有一个在堆上的0x20的头部,里面恰好存储了堆地址,所以我们是可以通过unlink来控制头部,修改data指针,实现任意地址写

所以我们现在需要一个UAF,可以发现add里面如果输入的size不符合要求,会直接return,没有将ptrs free并清空,又因为rm的时候只清空了ptrs里的指针,没有把头部里的指针清空。所以只要我们在tcache里面预先安排一些已经被free掉的头部chunk,再输入错误的size,利用里面残留的data指针,就可以实现UAF

UAF之后show一次leak堆地址,unlink条件就齐全了,剩下就是怎么排堆了(((,这得靠你自己慢慢调试了

image-20210920121639310

源码&exp:https://git.hit.edu.cn/lilac-2021-tryout/pwn-problems/-/tree/master/pwn-safe_heap/src