2021 年 5 月 24 日,ZDI 发布了关于影响群晖某些型号设备的 RCE 漏洞 CVE-2021-31439,2022 年 3 月 28 日,DEVCORE 发布了对于此漏洞的分析文章,文中只介绍了此漏洞的成因和利用思路,在实际复现过程中还存在一些问题,本文对一些问题进行分析。
环境准备
受影响的组件是开源项目 Netatalk,该项目实现了苹果公司提出的 AFP 文件传输协议,并应用在很多 NAS 设备上,由于暂时没有目标设备,我在 ubuntu 16.04 虚拟机上编译安装了 Netatalk 3.1.12 模拟设备环境。
Netatalk 项目官网:https://netatalk.sourceforge.io/
注:如果使用 apt 安装,得到的 afpd 程序一般是开启了所有保护措施,不方便进行复现。
利用分析
详细的漏洞分析文章可在DEVCORE 技术博客找到,这里不再赘述,我们要解决一些博客中没提到的问题。
1. 协议实现
存在漏洞的程序实现了 AFP 文件传输协议,协议由苹果公司提出,它和 SMB 类似,不过在后续的发展过程中逐渐被抛弃了。
为了利用此漏洞,我们需要实现基本的 DSI 和 AFP 协议,以便于和服务器交互。网络上有一些 C 语言实现的 AFP 客户端,不过难以从其中剥离出有用的代码,我参考了 metasploit 中 AFP 信息扫描以及密码破解模块的实现,在 python 中简单实现了相关协议。
参考链接:https://github.com/rapid7/metasploit-framework/pull/216/files
2. 触发前提
在文章中提到漏洞是程序调用 dsi_stream_receive 函数读取 DSI 数据是出现问题,对应 DSI 中 command = 2 情况,需要注意的是直接发送相关数据并不能来到漏洞点,首先需要构造 DSI_OpenSession 包新建一个 Session。
3. 利用思路
文中笼统地指出利用思路为覆盖 _tls_dtor_list 指针,当程序调用 exit 时会来到 __call_tls_dtors 函数,在可控内存中构造好各个结构体即可劫持控制流进而实现 RCE。
在实际调试过程中存在一些需要注意的点,如在泄露出 canary 之后还要继续泄露到 libc 地址,否则覆盖 TLS 结构时会由于某些指针异常导致程序崩溃。
泄露 libc 地址时在某些特殊的内存布局情况下会导致泄露出来的地址有一些偏移,需要编写代码过滤这些情况。
清空 pointer_guard 后再调用某些功能会由于指针解密失败导致程序崩溃,所以我们要仔细编排布置 payload 的顺序,经过测试发现需要先布置 bss 结构,然后再布置 TLS 结构。
另外也是最重要的一点,文章中写到劫持控制流后可以通过调用 execl 来实现任意代码执行,但 execl 需要控制好各个参数。这里用到的思路是 CTF 中的 SROP,先劫持控制流到 setcontext 函数,调整好各个寄存器的值,再跳转到 IO_proc_open 函数中调用 execl 函数的位置。
调试好各个变量的偏移之后可以写出能够执行任意命令的 poc:
POC
1 | import struct |
- 本文作者: CataLpa
- 本文链接: https://wzt.ac.cn/2022/04/02/CVE-2021-31439/
-
版权声明:
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。