MikroTik RouterOS漏洞CVE-2019-13954分析
漏洞简介
CVE-2019-13954是MikroTik RouterOS中存在的一个memory exhaustion漏洞。认证的用户通过构造并发送一个特殊的POST请求,服务程序在处理POST请求时会陷入”死”循环,造成memory exhaustion,导致对应的服务程序崩溃或者系统重启。
该漏洞与CVE-2018-1157类似,是由于对漏洞CVE-2018-1157的修复不完善造成。下面通过搭建MikroTik RouterOS仿真环境,结合漏洞CVE-2018-1157的PoC脚本及补丁,对漏洞CVE-2019-13954进行分析。
CVE-2018-1157漏洞分析
MikroTik RouterOS环境的搭建、root shell的获取及相关资料可参考文章《CVE-2018-1158 MikroTik RouterOS漏洞分析之发现CVE-2019-13955》,这里不再赘述。
根据Tenable的漏洞公告可知,漏洞CVE-2018-1157在6.40.9、6.42.7及6.43等版本中修复。为了便于对漏洞CVE-2018-1157进行分析,选取的相关镜像版本如下。
- 6.40.5,- x86架构,用于进行漏洞分析
- 6.42.11,- x86架构,用于进行补丁分析
为了便于分析,临时关闭了系统的
ASLR机制。
与该漏洞相关的程序为www,在设备上利用gdbserver附加到该进程进行远程调试,然后运行对应的PoC脚本,发现系统直接重启,在本地gdb中捕获不到任何异常信息。根据漏洞公告中提到的"/jsproxy/upload",在函数JSProxyServlet::doUpload()内设置断点,进行单步跟踪调试,发现会一直执行如下的代码片段。
| 1 | int __cdecl JSProxyServlet::doUpload(int a1, int a2, Headers *a3, Headers *a4) | 
其中,函数sub_77464E9F()用于读取POST请求数据并将其保存在s1指向的内存地址空间,其伪代码如下。
| 1 | char *__usercall sub_77464E9F@<eax>(istream *a1@<eax>, char *a2@<edx>) | 
可以看到,当满足以下任一条件时会跳出while循环。
- 调用sub_77464E9F(),未读取到数据
- 调用Headers::parseHeaderLine(),解析失败
查看对应的PoC脚本,其对应的部分POST请求数据为Content-Disposition: form-data; name="file"; filename="<filename>"\r\n。
| 1 | std::string filename; | 
当filename参数的值过长时,调用istream::getline()读取的内容一直为Content-Disposition:form-data; name="file"; filename="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..."(长度超过0x100)。由于上面的2个条件都不满足,造成while循环无法退出,一直执行最终导致"memory exhaustion"。
CVE-2018-1157补丁分析
版本6.42.11中对CVE-2018-1157进行了修复,根据前面的分析,定位到JSProxyServlet::doUpload()中对应的代码片段,如下。可以看到,在补丁中增加了对读取的POST请求数据长度的判断:当长度超过0x100(包括最后的'\x00')时,会跳出while循环。
由于两次
jsproxy.p的加载基址不一样,所以部分函数的名称可能不一致。
| 1 | int __cdecl JSProxyServlet::doUpload(int a1, int a2, Headers *a3, Headers *a4) | 
CVE-2019-13954发现
通过对漏洞CVE-2018-1157分析可知,调用istream::getline(a1, a2, 0x100u, '\n')读取数据时,如果请求数据过长(在遇到分隔符'\n'前0x100个字符已被写入a2中),那么每次a2中的数据内容都是一样的。而在对应的补丁中,增加了对读取数据长度的判断。
注意到,在调用istream::getline(a1, a2, 0x100u, '\n')读取数据时,分隔符为'\n'。也就是说,即使filename参数的值中包含'\x00',读取时也不会造成截断,但是会影响后面的长度计算。因此,只需要在filename参数后面追加大量的'\x00',即可绕过补丁,再次触发该漏洞。
在原有PoC的基础上进行简单修改,在版本为6.42.11的设备上进行验证,发现系统直接重启了。
| 1 | std::string filename; | 
通过代码静态分析,该漏洞在"Long-term"版本6.43.16上仍然存在。
6.43.16为发现该问题时”Long-term”系列的最新版本。该漏洞(CVE-2019-13954)目前已被修复,建议及时升级到最新版本。
小结
- 由于对漏洞CVE-2018-1157的修复不完善,通过在filename参数后面追加大量的'\x00',可绕过对应的补丁,再次触发该漏洞(CVE-2019-13954)。
相关链接
- Mikrotik RouterOS Multiple Authenticated Vulnerabilities
- Two vulnerabilities found in MikroTik’s RouterOS
- Mikrotik RouterOS Changelogs
本文首发于信安之路,文章链接:https://mp.weixin.qq.com/s/HXl-QLlNi4y9EKBY_zqKUg