DASCTF8月赛,PWN题题解,学习中。
magic_number
程序分析
程序功能很简单,存在一个栈溢出漏洞可以。
但是,程序开了 PIE 和 NX 保护,很难通过栈溢出修改 ret地址 和 直接写入 shellcode 执行。
利用分析
这里需要用到一个不常用的知识,在程序内存中有一处基址是不会改变的, 即 0xffffffffff600000 这个地址永远都是 vsyscall,而如果系统执行了 这个指令,其实整个栈是会直接向后滑动的,也就是说 这个 syscall
可以直接当作 滑板指令,使我们的 rsp 滑到后面的地址去。
如果我们先利用 这个地址来填充 ret 和其后面的地址,再将一个 栈上的地址后一位 改成 system 的地址,那么就可以通过 syscall
滑板指令不断滑到 system 地址执行。
EXP
1 | from pwn import * |
SoSafeMinePool
glibc 2.32 Safe Linking
题目给的glibc 是 2.32的,引入了新的保护机制:Safe Linking
在之前的 glibc 中,tcache 和 fastbin 非常不安全,其机制都是单链表储存了chunk,一个空闲chunk指向下一个空闲chunk,而 Safe Linking引入了两个宏:
1 |
|
对于在 tcache chunk或者 fastbin chunk被放入链上的时候,会调用 PROTECT_PTR
,在从链上取下来的时候,会调用 REVEAL_PTR
,拿 tcache 的 代码举例。
1 | /* Caller must ensure that we know tc_idx is valid and there's room |
同一段代码在 2.31 和 2.32 的对比:
1 | long *a = malloc(0x100); |
对于 2.31来说,执行第一次 free 的时候,会做三件事:把 key 这个 field 填上tcache 结构的地址,把 next 这个 field 填上对应链表的尾部,然后把之前链表的尾部更新为自己的地址。
而对于 2.32来说,则是把第二步更换为了:把 next 这个field 填上 PROTECT_PTR(&自己,对应链表尾部)
内存区别:
1 | // 2.31 |
2.32版本在第一次 free 时,0x405290 的 next 被写成了 PROTECT_PTR(0x4052a0, 0)
,第二次 free,0x4053a0 的 next为 PROTECT_PTR(0x4053b0, 0x4052a0)
。后续,如果尝试 malloc(0x100)
,程序就会从链尾取 chunk,也就是 0x4053a0。同时,计算 REVEAL_PTR(0x4056a5)
,将原本的 指针放回链上,这个计算也就等价于 PROTECT_PTR(0x4053b0, 0x4056a5)
。
还有一个改动,即 tcache 现在需要与 0x10 对齐。
程序分析
在 Edit 函数中,循环读取每一位时,由于 i 使用了 float类型,存在一个浮点数溢出,当输入size 为 0.4 时,其实会输入 41次。即存在 off-by-one 漏洞。
利用分析
- 泄露heap地址
申请7块size1(size1 小于fastbin范围)的chunk,申请7块size2(size2大于fastbin范围)的chunk。随后申请 4 个 chunk,其大小分别为 size1, size2, size1, size1,块号分别用 chunk14, chun15, chunk16, chunk17代替。
然后释放size1的7个chunk填满tcache,释放7个size2的chunk填满tcache。 再将释放 chunk15,则 chunk15会被放入 unsortedbin 块中。然后利用 chunk14 的 off-by-one漏洞 修改 chunk15 的 size为 size2+size1。然后通过 chunk16 的 off-by-one 漏洞 修改 chunk17 的 prev_size 为 size1+size2,修改 chunk17 的 prev_inuse 位为0。
然后再申请一个 size1+size2 大小的 chunk,此时编号为 chunk0,则chunk0 就能够覆盖 chunk16的数据。由于 add函数时 会通过memset 清空chunk,所以我们要先 恢复 chunk16 的size。再释放 chunk16,此时chunk 16会被放入 fastbin中,并且其 fd 指针的值为 (&fd >> 12) ^0,也就是只需要 将得到的fd指针右移12位即可得到真实的 heap地址。
- 泄露 libc 地址
这里有一种新的泄露Libc的操作
1 | 1. fastbin中的chunk会在malloc_consolidate之后进入unsorted bin |
所以可以再 scanf 时输入超长字符串,然后会去申请一个 large chunk,将 chunk16从 fastbin中 合并到 unsortedbin中,泄露libc 地址。
当然,也可以通过传统的方法,修改 chunk16的地址 为size2,并在 chunk17 中 由 chunk16_addr - size2的地方伪造 一个size 和 prev_inuse位,再释放chunk16到 unsortedbin中,最后泄露。
- tcache poisoning
得到 Heap 地址和 libc 地址后,就可以伪造出 tcache 的 fd 值了。
先释放一个 size1的 chunk和 chunk16到 tcache中,此时tcache 链为:chunk16 -> chunk.
计算 (chunk_16_addr >> 12)^free_hook 的值,修改为 chunk16 的fd值。
最后两次申请 chunk,即可将 free_hook 申请出来
- getshell
将 free_hook 改为 system 地址,将一个堆块内容设为 /bin/sh ,再释放该堆块即可 getshell。
此处尝试 one_gadget 报错,有可能是 2.32 版本下的 gadget 更难利用。
EXP
1 |
|
参考
https://dere.press/sosafeminepool/
https://publicki.top/2020/08/25/SoSafeMinePool/
- 本文作者: A1ex
- 本文链接: http://yoursite.com/2020/09/25/DASCTF8月赛/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!