前言
最近,Tenable指出了MikroTik路由器的一系列漏洞,CVE编号从2019-3976到2019-3979。这四个漏洞组成的利用链可以巧妙的获得RouterOS的Root Busybox Shell
漏洞分析
我使用了 6.45.6_x86 版本的 RouterOS 作为靶机,IP 为 192.168.231.200,修改密码为 routerpass

RouterOS 使用二进制文件 resolve 向 DNS 服务器发出解析请求,然后将响应结果中的所有 A 记录添加到其 DNS 缓存中,这就是 CVE-2019-3979 所指出的问题。路由器在进行 DNS 查询时,即使响应结果中存在和查询域名无关的记录,也依然会添加到缓存表中,而且这里的 DNS 服务器是可以在命令中手动指定的,这就给 DNS 污染提供了必要条件。

那么如何在未授权的情况下触发路由器的 DNS 查询呢?默认情况下,RouterOS 开放了 8291 端口用于和 Winbox 通信。Winbox 下的操作可以表示为 json 格式的消息,这个 json 格式的消息会被转换为二进制数据通过 TCP 传输到 RouterOS 的 8291 端口。RouterOS 在收到消息后,解析器解析 json 消息,将指令发送给 json 消息中给定编号所对应的二进制文件。由于是闭源协议,因此想要了解整个消息解析的过程,需要对路由器系统内的二进制文件进行逆向分析,为避免占用过多篇幅,这里就不详细介绍了,有兴趣的话可以直接阅读 Tenable 给出的说明文档,文末有链接。
Tenable 也提供了 x3_parse 工具,编译后执行可以从 .x3 文件中解析出编号和系统内二进制文件的对应关系

可以看到,resolver 文件的编号为 14,因此在 json 消息中给定 14 就可以调用 RouterOS 中的 resolve 命令。完整 json 消息的构造需要深刻理解 Winbox 和 RouterOS 的通信过程,自己实现相对复杂,所以直接使用 Tenable 提供的 winbox_dns_request 工具向 RouterOS 发送恶意 json 数据。
发送之前需要先准备好一个自动响应恶意 DNS 解析记录的 DNS 服务器,这里使用了 pklaus 编写的简易 ddnsserver,修改 dns_response 函数,使其在响应查询时始终返回几条恶意的记录,这几条记录将 RouterOS 用于下载新版本系统的域名解析到我们自己的攻击机 IP 上

修改完成后在攻击机上运行,udp 协议,53 端口

使用 winbox_dns_request 工具向 RouterOS 发送恶意 json 数据

winbox_dns_request 默认只会触发 RouterOS 对 example.com 域名的查询,但由于指定了 DNS 服务器为我们自建的恶意服务器,所以响应中包含了额外的几条记录,此时查看 RouterOS 的 DNS 缓存记录,则可以看到路由器系统升级的域名地址已经被投毒为我们的攻击机地址了

接着是 CVE-2019-3977 的内容。RouterOS 对升级包的验证不足,通过修改系统升级包版本号和时间戳,可以诱骗路由器下载低版本路由器系统,从而实现路由器系统的降级。根据官方的 Changelog 的说法,降级到 6.43 之前的版本均会导致密码清空

而在 bug_hunting_in_routeros_derbycon_2018.pdf 中介绍了 6.41.4 版本中存在的后门,所以这里可以尝试诱骗路由器将系统降级为 6.41.4

使用 Wireshark 抓包可知,RouterOS 在检测版本更新时,会发出两个 HTTP 请求,第一个请求用于获取 RouterOS 的最新版本
http://upgrade.mikrotik.com/routeros/LATEST.6

查询结果显示,目前最新版本为 6.45.7,时间戳为 1571906675。查询到最新版本号后,RouterOS 发出第二个请求
http://upgrade.mikrotik.com/routeros/6.45.7/CHANGELOG
这个请求用于查询最新版的 CHANGELOG,然后显示在界面上

现在由于我们毒化了路由器上的 DNS 缓存,所以此时路由器检测更新的话,根据 DNS 记录,请求会发送给我们的攻击机,所以我们需要构造一个和官方更新服务器同样结构的 Web 页面,用于供路由器下载 "系统升级包"

在 DNS 缓存被毒化的情况下,路由器检测更新则会请求我们自己搭建的 Web 服务器

点击下载则会不停向服务器请求 /routeros/6.45.7/system-6.45.7.npk

为了实现路由器的降级,我们需要将 6.41.4 版本的 system-6.41.4.npk 改名为 system-6.45.7.npk 后放入 6.45.7 文件夹下。system-6.41.4.npk 可以在官网 all_packages-x86-6.41.4.zip 内找到

将文件改名后放入 6.45.7 文件夹内,路由器获取到 system 文件后更新重启,重启后系统变为 6.41.4,此时之前的 routerpass 密码失效,需使用空密码登录

成功以空密码登录路由器后,可以利用 Tenable 提供的 6.41.4 的后门利用工具 cleaner_wrasse 启用后门。之后使用 telnet 连接,便成功获取到了 Root Busybox Shell

参考
https://medium.com/tenable-techblog/routeros-chain-to-root-f4e0b07c0b21
https://github.com/tenable/routeros/blob/master/slides/bug_hunting_in_routeros_derbycon_2018.pdf
作者: JenI 转载请注明出处,谢谢
Comments !