CVE-2018-1158 MikroTik RouterOS漏洞分析之发现CVE-2019-13955
漏洞简介
CVE-2018-1158
是MikroTik
路由器中存在的一个stack exhaustion
漏洞。认证的用户通过构造并发送一个特殊的json
消息,处理程序在解析该json
消息时会出现递归调用,造成stack exhaustion
,导致对应的服务崩溃重启。
该漏洞由Tenable
的Jacob Baines
发现,同时提供了对应的PoC
脚本。另外,他的关于RouterOS
漏洞挖掘的议题《Bug Hunting in RouterOS》
非常不错,对MikroTik
路由器中使用的一些自定义消息格式进行了细致介绍,同时还提供了很多工具来辅助分析。相关工具、议题以及PoC
脚本可在git
库routeros获取,强烈推荐给对MikroTik
设备感兴趣的人。
下面利用已有的PoC
脚本和搭建的MikroTik RouterOS
仿真环境,对该漏洞的形成原因进行分析。
环境准备
MikroTik
官方提供多种格式的镜像,其中可以利用.iso
或者.vmdk
格式的镜像,结合VMware
虚拟机来搭建仿真环境。具体的搭建步骤可参考文章 Make It Rain with MikroTik 和 Finding and exploiting CVE-2018–7445,这里不再赘述。
根据Tenable
的漏洞公告可知,该漏洞在6.40.9
、6.42.7
及6.43
等版本中修复。为了便于对漏洞进行分析和补丁分析,选取的相关镜像版本如下。
6.40.5
,x86
架构,用于进行漏洞分析6.42.11
,x86
架构,用于进行补丁分析
搭建起仿真环境后,由于RouterOS
自带的命令行界面比较受限,只能执行特定的命令,不便于后续进一步的分析和调试,因此还需要想办法获取设备的root shell
。同样,Jacob Baines
在他的议题《Bug Hunting in RouterOS》
中给出了相应的方法,这里采用修改/rw/DEFCONF
的方式。对于该文件的修改,可以通过给Ubuntu
虚拟机添加一块硬盘并选择对应的vmdk
文件,然后进行mount
并修改。
需要说明的是,采用这种方式进行修改后,每次设备启动后/rw/DEFCONF
文件会被删除,如下。
1 | /etc/rc.d/run.d/S12defconf |
这样下次如果需要获取root shell,还需要再重新挂载并修改,比较麻烦。可行的解决方式如下:
- 在修改
/rw/DEFCONF
文件后,创建一个虚拟机快照,下次直接恢复该快照即可; - 在修改
/rw/DEFCONF
文件后,将其拷贝一份保存到其他路径,获取到设备root shell
后再拷贝一份到/rw
路径下。
漏洞分析
根据漏洞公告可知,与该漏洞相关的程序为www
。在设备上利用gdbserver
附加到该进程进行远程调试,然后运行对应的PoC
脚本,在本地的gdb
中捕获到如下异常。
1 | (gdb) |
为了便于分析,临时关闭了系统的
ASLR
机制.
查看栈回溯信息,可以看到存在大量与0x77504bd3 in ?? () from .../jsproxy.p
相关的栈帧信息,与漏洞描述中的”递归解析”一致。根据PoC
中数据内容格式"{m01: {m01: ... }}"
,结合单步调试,定位漏洞触发的地方在sub_77504904()
函数中,其被json2message()
函数调用,核心代码片段如下。
1 | sub_77504904() |
以"{m01: {m01:{m01: " "}}}"
为例,其主要处理逻辑为:先解析前面的"{m01: "
,执行到switch
语句时,匹配"case 'm'"
分支,然后再次调用sub_77504904()
函数,此时数据变为"{m01: {m01: "" }}"
,处理逻辑和之前相同。因此,只需要发送的数据包中包含足够多的重复模式,在解析该数据时会造成函数的递归调用,从而不断开辟栈帧,,最终导致"stack exhaustion"
。
补丁分析
版本6.42.11
中修复了该漏洞,基于前面对漏洞形成原因的分析,在程序jsproxy.p
中定位漏洞触发的代码片段,如下。可以看到,该代码片段的处理逻辑与之前类似,但在调用函数sub_7750DCFC()
时多了一个参数,用来限制递归的深度。
1 | sub_7750DCFC() |
未知漏洞发现
在对补丁进行分析时,通过IDA
的交叉引用功能,发现该函数还存在另一处递归调用,如下。
调用处的部分代码片段如下。可以看到,在处理对应的消息类型M
时,也会调用sub_7750DCFC()
函数自身,但是却没有对递归调用深度的限制,因此猜测这个地方很可能存在问题。
1 | sub_7750DCFC() |
根据Jacob Baines
议题《Bug Hunting in RouterOS》
中对json
消息格式的介绍,消息类型M
与消息类型m
对应,m
表示单个Message
,而M
表示"Message array"
。
图片来源:
Jacob Baines
议题《Bug Hunting in RouterOS
》
通过构造一个简短的payload
: "{M01:[M01:[M01:[]]]}"
,然后利用gdb
进行调试,发现确实可以到达对应的函数调用点,该函数会递归调用自身来对数据进行解析,与之前对消息类型m
的处理逻辑相似。接着,利用一个简单的脚本来产生大量包含这种模式的数据,然后修改CVE-2018-1158
PoC
中对应的数据,在版本为6.42.11
的设备上进行验证,可以看到进程www
确实崩溃了。
1 | msg = "{M01:[M01:[]]}" |
通过代码静态分析,该未知漏洞在"Long-term"
最新版本6.43.16
上仍然存在。
该漏洞(
CVE-2019-13955
)目前已被修复,建议及时升级到最新版本。
小结
- 该漏洞触发的原因为:程序在对某些特殊构造的数据进行解析时存在递归调用,从而造成
"stack exhaustion"
; - 对该漏洞的修复主要是在递归调用函数时增加了一个参数,用来限制递归调用的深度;
- 对该漏洞进行修复时未考虑全面,仅对消息类型为
m
的数据增加了递归调用深度的判断,而通过构造消息类型为M
的数据仍可触发该漏洞。
相关链接
- Mikrotik RouterOS Multiple Authenticated Vulnerabilities
- RouterOS Bug Hunting Materials
- Make It Rain with MikroTik
- Finding and exploiting CVE-2018–7445
- Two vulnerabilities found in MikroTik’s RouterOS
- Mikrotik RouterOS Changelogs
本文首发于安全客,文章链接:https://www.anquanke.com/post/id/183451