记一次失败的漏洞挖掘过程
前言
本文记录了一次失败的漏洞挖掘经历,但正是因为自己都在进行主观的思考,即使是失败了,这一次也学到了一些东西。我认为跟着别人文章照猫画虎成功做一遍,不如失败的独立思考探索有意义😎
事情的起因是想去复现一下 D-Link DIR-825
、 TRENDnet TEW-632BRP
命令注入漏洞 (CVE-2020-10216
)。因为一直没找到命令注入的点,一时兴起,就随便看了看 DIR-825
固件中的 cgibin
。大致看了一下后,又想着下载一个型号相近的 DIR-850
看看 cgibin
里有没有什么漏洞(我希望在不看漏洞披露信息的情况下,寻找一些比较明显的漏洞)
固件下载
在此之前,先记录下固件下载的过程,以 D-Link DIR-825
固件为例,首先我在搜索引擎中输入 D-Link support
这里我是直接搜了 DIR-825
居然没有搜到……这里没有显示这个版本的路由器
然后我又换了一种搜法 D-Link DIR-825 firmware
我找到了这个,这个貌似也是 dlink
的技术支持网站,但是比上面那个网站多了个后缀 au
下载链接:http://support.dlink.com.au/download/download.aspx?product=DIR-825
不过华神又给我了一个更全、更方便的网站 https://ftp.dlink.ru/pub/Router/ ,这里找 D-Link 的固件应该是很方便了。
自主探索
需要注意的是上面固件下载的过程中寻找的是 DIR-825
,但后面的所有分析都是基于固件 DIR-850
。经过简单的分析,我发现了一处疑似命令注入漏洞和一处疑似栈溢出漏洞的地方
疑似的命令注入?
位于 soapcgi_main
函数中,这里 REQUEST_URI
内容给到了 v3
后续判断是否有 ?service=
,做了简单的过滤后最终可以命令执行
疑似的栈溢出?
位于 sessioncgi_main
函数这里 REQUEST_URI
无论匹配到的是 form_login
还是 form_logout
都可以触发 weblogin_log
函数
可以发现 weblogin_log
函数的第二个参数(也就是 getenv("REMOTE_ADDR")
的值 )与 ::ffff:
做对比,如果检查通过则执行 strcpy
函数进行拷贝。如果 REMOTE_ADDR
的值可控,在拷贝时就会造成栈溢出
验证
因为我感觉这两个漏洞都很浅,这种很多年前的设备都被无数人光临过了,大概率已经有了漏洞相关信息或者压根就用不了,但我还是想独立的确定一下漏洞到底能否利用。
直接访问 soap.cgi
,返回了 404
(这里我不是很懂,但也有一点眉目,具体记录在了文章末尾 ),这意味着无法触发 soapcgi_main
函数
这篇 文章 中提到,但是经过真机测试,发现设备并没有开启对 soap
的支持(我不确定能否手动开启它,我尝试了一些已知的方法,均未成功)
如果我们将我们的注入指令放入service参数,即可完成指令注入,现在我们还需要知道这个漏洞如何触发,从soapcgi_main函数名可以得知这个函数是用于处理soap请求的,这是一种简单的基于XML的协议,可以使应用程序在分散或分布式的环境中通过HTTP来交换信息,也就是说首先目标设备要开启对soap的支持此漏洞才可以触发。
去测试 sessioncgi_main
函数,我先用 qemu
用户级仿真了一下,发现如果 REMOTE_ADDR
确实可控的话,在漏洞函数返回时的地址会被破坏,导致程序崩溃。
咸鱼上淘了一个二手的 DIR-850L
真机,我打了一下发现响应包是正常的,返回了 ok
。
返回 ok
就意味着下图中的 printf
已经正常执行了,这说明 weblogin_log
函数正常返回,并没有栈溢出导致崩溃。
于是乎开始各种排查,我开始的重心一直在排查报文编写错误。REMOTE_ADDR
的值没有添加双引号?GET
方式试一下?溢出的字节不够多? 还有什么字段也要伪造,没注意到?还是已经崩溃了,只不过WEB
界面没看到效果?
反正我当时抛出了很多疑问,然后一一进行了排除和验证,最后猜测只可能是 REMOTE_ADDR
字段没有控制成功。
经过搜索后,这篇 文章 里提到 REMOTE_ADDR
字段是无法被客户端控制,尽管我感觉这里还是很奇怪,因为我用 wireshark
抓了一下发送的流量包,看到的 REMOTE_ADDR
还是我控制的那个值🙃
未初始化漏洞?
除了上面提到的两个地方,我还发现了一个奇怪的点。在 captchacgi_main
函数中,貌似 system
执行时可能会存在一个未初始化漏洞?观察下图发现 v10
,在执行 sprintf
函数进行了拼接然后赋值给了 v11
,可是 v10
自始至终并没有被赋值。我考虑有没有机会用栈里的残留值控制 v10
,于是又有了后文的探索
运行程序,发现一直卡住了,并且没有任何回显,说明大概率陷入了某个死循环或者超长循环里。
分金定穴
为了解决这个问题,我采用的方法是下大量断点来缩小范围,确定到底卡在了哪个位置(我自称这种方法为 分金定穴 😆)第一次将断点下到了 cgibin_parse_request
函数执行前
打完断点,发现可以直接 c
过来,排除了在此之前卡住的可能
第二次将断点打到红框中的位置 sess_generate_captcha
函数执行前
发现依然可以 c
过来,第三次将断点打到 0x40A3E0
地址处(也就是 sess_generate_captcha
函数执行后),此时发现 c
的时候,就一直卡住了。因此判断大致范围一定是在 sess_generate_captcha
函数里
于是重调第二次,直接下到 sess_generate_captcha
函数内部,我选择下到 sub_4095B4
函数之前
此时发现依然可以正常 c
过来,第二次我尝试下到了 sub_4095B4
函数执行之后
发现又卡住了,那就说明具体的范围就在 sub_4095B4
函数里被什么东西卡住了
直接分析 IDA
中的代码,发现 v4
没有初始化,而 v0
初始值是 1
,每次循环 +1
,只有 v1
的值大于 v4
时才能跳出这个死循环。
因为没有变量初始化,就导致从栈里取的值是随机的,所以再次调试,看下 v4
的初始值是什么,找到 slt
比较的汇编代码, $v0
寄存器就为 v4
的值,将断点下到这里
通过调试发现 $v0
寄存器中是一个栈地址,这意味着要跑二十多亿次的循环才能继续往下运行
但这是否意味着真实环境里的这个 captcha.cgi
也要等这么久?拿真机测试一下,发现直接回显,这意味着远程环境里的这个 v4
肯定不是栈地址,我猜测大概率可能就是个 0
因此为了和真实环境保持尽可能的接近,我选择用 set
命令直接把 $v0
寄存器改成 0
,这样就可以正常执行到下面的 if
分支,但因为某些条件,导致进入的是 else
。
访问真机可以看到触发的是 if
里的内容,依然是为了和真实环境保持一致,所以继续用 set
命令来改变正常的执行流
此时执行到了 sprintf
函数,查看第四个参数(结合下面两张图)发现其是个栈里残留的地址,在其低地址处没有输入大量字符串的机会,所以此处 system
传入的一个未初始化数组也不可控制(本想着用栈的残留值偷鸡)
😅又是一次失败的分析
问题与解决
这里对探索过程中遇到的问题和解决方法进行了记录
gdbserver连接报错
(gdb) target remote 192.168.110.111:7788 |
解决方法:将启动 qemu
时的 vmlinux-2.6.32-5-4kc-malta 改成 vmlinux-3.2.0-4-4kc-malta
gdb-multiarch xxx 发生段错误
出现上面的问题,不能写成 gdb-multiarch session.cgi
,因为本地加载 session.cgi
的时候会缺少对应的库,触发段错误。直接写成 gdb-multiarch
就行
gdb-multiarch报错gdb.MemoryError
这个问题说明 gdb
没能正确的解析接收的数据,需要提前设置架构和字节序。假如我这个要调试的程序为 mips
架构,大端序。执行 set endian big
set architecture mips
两个命令即可
调试二进制文件报错 ld-uClibc.so.0: No such file or directory
开始直接在 Desktop
目录执行了 sudo chroot . ./qemu-mips-static ./captcha.cgi
命令,但是报错如下
这里是因为 chroot
做了一个隔离环境,以 Desktop
目录作为根目录,而 captcha.cgi
(已经过重命名,源程序名为 cgibin
)为动态链接程序,会去寻找 /lib
目录下的 libc
和 ld
文件,因为我这里的 Desktop
目录下肯定没有 lib
目录,导致报错。
解决方法:进入由路由器固件解压后的文件系统,执行上面相同的命令,以当前目录为根目录做一个隔离环境,这里的 /lib
肯定是存在与 cgibin
配套的 libc
文件(如下)
为什么有的cgi可以被触发,有的则404
我原先也是一直纳闷这个问题的,直到我无意中搜了一下文件系统里的 cgi
文件,我突然感觉这些文件很眼熟。
因为我在真机上测试了一下 cgibin
程序中的哪些名称是可以访问成功的,貌似那些名称就是上面的文件。仔细比对后,确实如此。只要是在 htdocs/web
目录下的文件都可以直接被访问到,而其他文件之所以 404
, 就是因为 web
目录下并没有对应的 cgi
文件?但返回 500
的又会因为什么原因呢,暂且未知。
参考文章
c++ - 通过gdb连接到远程gdbserver时出错 - IT工具网 (coder.work)
D-Link DIR-825和TRENDnet TEW-632BRP命令注入漏洞(CVE-2020-10216) - 知乎 (zhihu.com)