记录一下近期打的几场比赛,有意思的题会多讲讲。
MAR-DASCTF
fruitepie
程序分析
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
题目十分短,所以需要猜测是不是有什么特殊套路。
这里的套路就是,当 malloc(-1)
时,会申请一个巨大的堆块。然而初始的 heap
大小不够,所以会调用 mmap
分配堆块,而此时新的堆块地址就十分靠近 libc
地址,且和 libc
基址的偏移是固定的。那么我们输出该Heap
的地址,就可以得到 libc
地址。最后覆盖 malloc_hook
即可。
EXP
1 | from pwn import * |
ParentSimulator
程序分析
1 | int Remove() |
Delete
函数,存在 UAF
漏洞。
1 | int backdoor() |
有一个后门,可以用来修改 chunk+8
处的值。
基于 2.31
版本。
利用分析
2.31
版本的double free
,那么就需要修改 freed chunk
+8 处的 key
值即可实现 double free
。而这里的后门就是用来修改该 Key
值的。
利用 UAF
,来泄露 libc
地址。利用 double free
来劫持 free_hook
。
利用 setcontext+61
来执行 orw
.
注意:唯一比较坑的地方就是,程序一开始修改了当前工作路径为根目录,所以本地测试的时候一直读不到当前路劲下的flag
,又调试了很久(但幸运拿了一血)。
EXP
1 | from pwn import * |
clown
程序分析
程序总体功能是一个比较常见的菜单题,实现了Add\Delete\Show
功能。其中能申请的堆块数量为 0x100
,也就导致这道题方法还挺灵活的。并且开启了沙箱,那么我们就只能使用 orw
来获取 flag
1 | int sub_BD5() |
利用分析
程序漏洞存在于 delete
函数中,有一个 UAF
漏洞。libc
版本为 2.32
首先我们需要泄漏地址。由于有一个 UAF
漏洞,那么泄露地址就十分简单。可以直接申请 大于 0x80
的堆块填充满 tcache
后,再释放一个到 unsortedbin
来泄露地址。以此得到libc
地址
此外,我们还需要知道堆地址,原因是由于glibc-2.32
新增了一种safe-linking
机制,该机制用于对单链表tcache以及fastbin
的fd
指针进行加密,从而增强安全性,加密的规则为将fd
指针与右移3个字节的堆基地址进行异或操作
1 |
|
所以,如果后面想通过劫持tcache
实现任意地址堆块分配,需要知道堆地址,来对我们任意地址进行加密。
这里想要泄露heap
地址,可以输出tcache
中的第一个堆块,由于该堆块的next
指针指向 tcache_perthread_struct
结构体,所以加密后也就是 tcache_perthread_struct>>12
,我们很容易得到堆块地址
之后便是劫持tcache
,劫持tcache
的最好思路是利用 uaf
漏洞将tcache
中的空闲堆块的next
指针改为我们想分配的地址。但是这里没有 edit
功能。所以选择利用堆合并后,申请大堆块,来实现堆重叠。
先分别释放 0x90 chunk1
和 0xf0 chunk2
的堆块到 unsortedbin
中造成堆合并,然后再将 chunk2
放入tcache
中 。然后申请一个0x100 chunk3
的堆块,此时 chunk3
就和 chunk2
发生重叠。利用chunk3
修改 chunk2
的next
指针指向 free_hook
最后便是orw
。
EXP
1 | from pwn import * |
babyheap
最后一小时放了道题,差一点做完,有点可惜。
程序分析
1 | int Edit() |
2.31
的 off-by-null
漏洞。一般想到 off-by-null
就是需要利用堆块合并,造成堆重叠来解决。
利用分析
但是 自从 2.29
的 unlink
就加了一个检测:
1 | if (__glibc_unlikely (chunksize(p) != prevsize)) |
首先就是检查了 当前堆块的 prevsize
和前一个以释放堆块的 size
是否相同,如果不相同直接报错。
1 | if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ |
以及对 fd
和 bk
指针的检查。
我们之前的 off-by-null
利用中,只需要伪造 fake-prevsize
和 当前堆块的 inuse
位即可。但是 2.29
之后,我们不仅要在当前要释放堆块中伪造这两个数据。
还需要在上一块中 伪造 fake_size
和 fd
以及 bk
指针。
这里 fake_size
我们直接在数据区伪造即可,fd
和 bk
则是因为这道题没有开启 PIE
,且有 chunk_list
。我们伪造如下布局:
1 | chunk1: | | 0x101 | |
在 chunk1
的数据区伪造 fake_size
,和 fake_fd
和 fake_bk
。chunk1
在 chunk_list
中的地址为 chunk1_ptr
,则 fake_fd
和 fake_bk
的地址分别为 chunk1_ptr-0x18
和 chunk1_ptr-0x10
。那么即可满足对 这两个指针的检查:
1 | *((chunk1_ptr-0x18)+0x18)=*(chunk1_ptr)=chunk1_addr |
然后将 0x100
的tcache
填满,然后释放 chunk3
即会在进入 unsortedbin
中时,构成堆合并。
在申请 0x1f0
的堆块 chunk4
,即可实现对 chunk2
的重叠。
EXP
1 | from pwn import * |
纵横杯线下
Pwn2
程序分析
IDA
分析发现十分复杂,而且不像我们常见的 C
和 C++
等反编译的结构。后面可以在strings
中发现如下字符:
1 | .rodata:0000000000006687 00000011 C PyModule_GetDict |
可以看到,带有大量 Py
字符。所以怀疑是一个 python
脚本通过 pyinstaller
打包成的 ELF
文件。
利用分析
那么就只需要按照这篇文章的方法,将这个 ELF
文件解包反编译为 python
脚本即可。
还原出的脚本,关键部分如下:
1 | def send_head(self): |
用 python
基本实现了一个 httpd
,而且有读文件,虽然过滤了 flag
字符。但是可以想办法绕过。那么这里就是直接构造 Get
数据包,然后绕过读取 flag
即可。发现直接使用 ASCLL
码即可绕过,读取出 flag
。
Pwn3
这里后面LYYL和我说,远程是 2.27新版本,加了tcache保护的,所以这个exp还得改改
程序分析
一个 C++
题目。总体实现了 Create
、Delete
和 View
函数。而且是用 Vector
实现。此外,还有三个不太常见的函数 Clone\get\set
。
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
测试发现,Clone
会把选择的堆块地址,复制一次放入堆块链表的末尾。也就是此时,堆块链表中有两个相同的堆块。那么就存在一个 double free
或 UAF
漏洞。
在 Get
和 set
函数中,都会执行堆结构体前8字节的指针。
1 | void __cdecl Get(std::vector<std::shared_ptr<Drawf>> *list) |
利用分析
题目基于 2.27
,可以先利用 UAF
漏洞 泄露出 Libc
地址。
随后,利用 double free
漏洞,输入与其堆块管理结构体大小相同的数据,以此来实现覆盖堆结构体。例如,此时double free
如下:
1 | 0xf0: chunk1->chunk1 |
那么,再次申请 0xf0
的堆结构,并且输入 0xe0
的数据。那么系统会先分配 chunk1
为堆结构体,再分配 0xf0
用来存储数据。而用来存储数据的堆块,就可以覆盖堆结构体的指针。
最后通过,get
函数,会执行堆结构体的指针来执行 one_gadget
。
EXP
1 | from pwn import * |
- 本文作者: A1ex
- 本文链接: http://yoursite.com/2021/03/30/2021-DASCTF-纵横杯线下/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!