CVE-2024-41592 漏洞分析
0x0 漏洞信息
DrayTek Vigor3910 devices through 4.3.2.6 have a stack-based overflow when processing query string parameters because GetCGI mishandles extraneous ampersand characters and long key-value pairs.
DrayTek Vigor3910 设备(4.3.2.6 版本)在处理查询字符串参数时存在基于栈的溢出,因为 GetCGI 处理不当的多余&符号和长键值对。
固件下载:https://fw.draytek.com.tw/Vigor3910/Firmware/
0x1 固件解密
v3910_431.all这个固件是被加密过的,无法直接使用binwalk解出来。可以在旧版本寻找解密程序。这里找到v3910_3972这个固件:
Shell
binwalk -Me v3910_3972.all
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
273054 0x42A9E Unix path: /home/eason_jhan/1000b/cavium/firmware/bdk/libbdk-os/bdk-rlock.c
280056 0x445F8 AES S-Box
280568 0x447F8 AES Inverse S-Box
283704 0x45438 SHA256 hash constants, little endian
............
5259312 0x504030 Linux kernel ARM64 image, load offset: 0x80000, image size: 44011520 bytes, little endian, 4k page size,
......
45601353 0x2B7D249 gzip compressed data, ASCII, from VM/CMS, last modified: 2008-04-20 10:46:28
47200461 0x2D038CD SHA256 hash constants, little endian
47222790 0x2D09006 AES Inverse S-Box
在0x504030的位置处找到了一个linux内核镜像,binwalk没有办法直接把他提取出来,手动切一下:
Shell
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1/v3.9.7.2$ dd if=v3910_3972.all of=kernel.img iflag=sk
ip_bytes,count_bytes skip=$((5259312)) count=$((44011520)) bs=4M status=progress
29360128 bytes (29 MB, 28 MiB) copied, 1 s, 26.2 MB/s
10+1 records in
10+1 records out
43719168 bytes (44 MB, 42 MiB) copied, 1.53026 s, 28.6 MB/s
切出来后再跑一下binwalk:
Shell
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1/v3.9.7.2$ binwalk -Me kernel.img
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 Linux kernel ARM64 image, load offset: 0x80000, image size: 44011520 bytes, little endian, 4k page size,
240144 0x3AA10 SHA256 hash constants, little endian
7434240 0x717000 ELF, 64-bit LSB shared object, version 1 (SYSV)
..........
10353688 0x9DFC18 LZ4 compressed data, legacy
..........
41941149 0x27FF89D SHA256 hash constants, little endian
41963478 0x2804FD6 AES Inverse S-Box
文件系统通常是镜像里最大的一块压缩数据,这里就找到一个一个LZ4压缩的数据,再把它切出来:
Shell
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1$ dd if=kernel.img of=initramfs.cpio.lz4 bs=4M iflag=skip_bytes skip=10353688
7+1 records in
7+1 records out
31333864 bytes (31 MB, 30 MiB) copied, 0.818864 s, 38.3 MB/s
尝试解压:
Shell
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1$ file initramfs.cpio.lz4
initramfs.cpio.lz4: LZ4 compressed data (v0.1-v0.9)
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1$ lz4 -d initramfs.cpio.lz4 initramfs.cpio
Error 53 : Decoding Failed ! Corrupted input detected !
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1$ ls -lh initramfs.cpio
-rwxrwxrwx 1 one one 72M May 24 02:52 initramfs.cpio
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1$ file initramfs.cpio
initramfs.cpio: ASCII cpio archive (SVR4 with no CRC)
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1$ cpio -idmv < initramfs.cpio
成功得到文件系统。接下来就要寻找解密程序了,设备要能安装新固件,自己就得先能解密它,所以我们根据update、upgrade、firmware、decrypt等这些字符串来寻找。
最后找到etc/runcommand/fw_upload这个脚本,阅读后找到几个关键的步骤:
Shell
dray_firmware_format_identify()
{
# 68 is offset of model in fw
MODEL=v1000b
SIZE_OF_MODEL_STR=${#MODEL}
FW_MODEL=$(head -c $((68 + $SIZE_OF_MODEL_STR)) $1 | tail -c $SIZE_OF_MODEL_STR)
if [ $FW_MODEL = $MODEL ]; then
return 1;
fi
MODEL=v3910
SIZE_OF_MODEL_STR=${#MODEL}
FW_MODEL=$(head -c $((68 + $SIZE_OF_MODEL_STR)) $1 | tail -c $SIZE_OF_MODEL_STR)
if [ $FW_MODEL = $MODEL ]; then
return 1;
fi
return 0;
}
init_folder $TMP_FW $TMP_FW_OUTPUT_FOLDER
cat /tmp/*.frmpart > $TMP_FW
dray_firmware_format_identify $TMP_FW
if [ $? -gt 0 ]; then
fw_unpacker -i $TMP_FW -o $TMP_FW_OUTPUT_FOLDER/ -m $MODEL || err=1
else
old_firmware_process $TMP_FW $TMP_FW_OUTPUT_FOLDER
fi
if [ -f $TMP_FW_OUTPUT_FOLDER/nonce ]; then
enc_file=$(ls $TMP_FW_OUTPUT_FOLDER/enc_* 2>&1 | awk 'NR==1')
denc_file=$(echo -n $enc_file | sed 's/enc_//g')
while [ -f "$enc_file" ]; do
chacha20 $enc_file $denc_file $TMP_FW_OUTPUT_FOLDER/nonce
rm $enc_file
enc_file=$(ls $TMP_FW_OUTPUT_FOLDER/enc_* 2>&1 | awk 'NR==1')
denc_file=$(echo -n $enc_file | sed 's/enc_//g')
done
rm $TMP_FW_OUTPUT_FOLDER/nonce
fi
可以看到这段脚本先对固件的型号进行了识别,根据型号的不同执行不同的解压行为。手上的固件型号是v3910,执行的是这一个命令:
Shell
fw_unpacker -i $TMP_FW -o $TMP_FW_OUTPUT_FOLDER/ -m $MODEL || err=1
先去sbin/fw_unpacker看看这三个参数的作用是什么(并不需要去把整个解包程序看一遍,了解这几个参数的作用就好了):

可以分析出这三个选项的作用:
-i:输入固件文件
-o:输出目录
-m:型号字符串
在解包成功后,会检查输出目录中是否存在 nonce 文件,若存在则循环查找 enc_* 文件,对每个加密文件调用 chacha20 解密并删除原加密文件。
到这里就可以了解整个解密的过程了,先把加密后的固件使用fw_unpacker解包出来:
Shell
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1$
qemu-aarch64 -L v3.9.7.2/rootfs ./v3.9.7.2/rootfs/sbin/fw_unpacker -i v3910_431.all -o v4.3.1/ -m v3910
============================output file
v4.3.1/nonce v4.3.1/enc_Image v4.3.1/enc_thunder-bootfs-uboot-t81.img v4.3.1/fw_release v4.3.1/fw_ver v4.3.1/pid v4.3.1/oid v4.3.1/uver v4.3.1/bdk_ver v4.3.1/linux_ver v4.3.1/drayqemu_ver v4.3.1/fw_release v4.3.1/fw_ver v4.3.1/pid v4.3.1/oid v4.3.1/uver v4.3.1/bdk_ver v4.3.1/fw_release v4.3.1/fw_ver v4.3.1/pid v4.3.1/oid v4.3.1/uver v4.3.1/bdk_ver
============================ output file
Unpackage firmware successfully.
把之前的sbin/chacha20复制过来,写一段简单的脚本来模拟原本的解密过程:
Shell
for enc in enc_*; do
[ -e "$enc" ] || continue
dec="${enc#enc_}"
qemu-aarch64 -L ../v3.9.7.2/rootfs ./chacha20 "$enc" "$dec" nonce || exit 1
done
解密完成后得到的thunder-bootfs-uboot-t81.img就是需要的固件了。但是这个也不能直接用binwalk提取,要像之前那样切下来再手动解包。过程和之前一样,这里就不再赘述了。
0x2 漏洞逆向
直接搜索GetCGI看看能不能找到:
Shell
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1/v4.3.1/de/rootfs$ grep -rinl "GetCGI"
firmware/vqemu/sohod64.bin
用binwalk查看后发现这是一个特殊的镜像。从文章中了解到:
Draytek 3910采用了Linux+Qemu+RTOS的框架,即在arm linux操作系统上用qemu运行drayos的RTOS操作系统。
所以这里直接把整个sohod64.bin放入ida中,shift+F12搜索GetCGI就可以定位到目标函数了。
我试了一些恢复符号表的方法,但是都失败了,所以手动逆了一下:

整个函数有分别对应GET和POST的处理流程,POST有长度检验,所以这里只看GET的溢出。
溢出点在makeword函数中:

这个函数的主要作用是给源字符串和一个分隔符(这里是&),它会找到第一个分隔符的位置,把分隔符之前的那段子串复制到一块新分配的堆内存里并以 \0结尾,同时把源字符串原地缩短为分隔符之后的剩余内容(即跳过那个分隔符)。
这里的返回值是一个堆地址,而在GetCGI函数中,这个返回值被放到了*(a2 + 8 * n650_1)这样一个地方,a2是GetCGI的参数,我们对GetCGI进行交叉引用,随便找一个函数看一看:

可以看出,a2应该是父函数的一个栈上的地址。
由于GET没有对长度作出限制,所以这里我们可以通过传入大量&造成溢出,把父函数的返回地址覆盖为一个堆地址。并且这个堆里面的内容是可控的。
到这里已经可以劫持程序的控制流了,但是在调用GetCGI的函数中,在返回时会有一个这样的清理函数:

result就是之前的a2,这个会把在栈上写的堆地址低四字节清零,导致最终溢出到的返回地址也被清0(这个已经足够造成Dos了,但是我们最终的目的是打一个RCE)。
所以需要找到办法来绕过这个问题,仔细观察上面的清理函数,它会在遇到0时停止。所以如果我们能找到在返回地址之前写0的函数就可以绕过这个了。
可以找一些类似于atoi,strtol(query_string)的函数,query_string 是 HTTP 请求传入的参数。
那么首先需要找到一些未授权的cgi,可以先将所有的.cgi字符串提取出来,然后使用脚本或者burp suite测试。(这样做需要先把仿真跑起来,仿真的步骤后面再说)
参考的文章中了解到,只要函数里没有 CGIbyFieldName = GetCGIbyFieldName(v6 + 32, “sFormAuthStr”);的调用就不需要授权。
这些字符串在IDA中形成了一个表单,我在ida里寻找时也遇到了一些问题,这里写一下:
搜索.cgi:

随便定位一个:

这里无法直接交叉引用定位到对应的处理函数,使用ALT+I,搜索一个地址:
搜索结果:

定位过去:

按O进行解析:

就可以找到对应的处理函数了。
经过测试,找到了这样一个函数:

看到在v14(栈上存储的堆地址)和返回地址之间有一些可以被strtol()的返回值覆盖的参数,这样就可以写0了,并且这也是一个未授权的函数。
到此,我们距离rce已经很近了,最后的问题就是找到一个命令执行点。
这里阅读了网上的参考文章,知道了virtcons_out 这个函数,可以执行一些特殊的命令,这里就不放这个函数的具体逆向过程了,就简单解释一下整个过程:
void virtcons_out(const char *cmd , int n); 实际上我们不需要第二个参数,给w0置为我们的命令字符串指针即可。
virtcons_out 对普通命令(cmd 不含 v3910_ram_flash.bin/frmsave/uffs)调用 __virtcons_out,把 cmd组成报文挂进发送 virtqueue,
由 QEMU 经 virtio serial0 端口异步搬到 host busybox Linux 的/firmware/serial0(.out);
/firmware/recvCmd 读入后只取第一个 token对比白名单/etc/runcommand/command_list,
如果命中,就把整行(命令名+其后参数,仅命令名被校验过)拼进system(“/etc/runcommand/<整行> >> /var/log/drayos/logpipe 2>&1”),由 host 的 busybox /bin/sh 以 root 执行/etc/runcommand/ 下的脚本。
上面的校验不严谨,所以我们可以通过类似set_linux_time ;echo pwned;这样的命令进行绕过。
0x3 仿真与动态调试
首先先来看看启动脚本:
etc/inittab
::sysinit:/sbin/rc boot
::respawn:-/bin/login
根据这个找到sbin/rc,分析后发现:

再找到run.sh:

发现还有一个purelinux模式,先看看run_linux.sh:
Shell
#!/bin/bash
# 1. do "fw_setenv purelinux 1" first , then reboot
# 2. do setup_qemu_linux.sh (default P3 as WAN, P4 as LAN, for both 1Gbps connection only)
# 3. remember to recover to normal mode by "fw_setenv purelinux 0"
rangen() {
printf "%02x" `shuf -i 1-255 -n 1`
}
rangen1() {
printf "%x" `shuf -i 1-15 -n 1`
}
wan_mac(){
idx=$1
printf "%02x\n" $((0x${C}+0x$idx)) | tail -c 3 # 3 = 2 digit + 1 terminating character
}
A=$(rangen); B=$(rangen); C=$(rangen);
LAN_MAC="00:1d:aa:${A}:${B}:${C}"
if [ ! -p serial0 ]; then
mkfifo serial0
fi
platform_path="./platform"
echo "x86" > $platform_path
enable_kvm_path="./enable_kvm"
echo "kvm" > $enable_kvm_path
cfg_path="/cfg/draycfg.cfg"
echo "GCI_SKIP" > gci_magic
uffs_flash="/data/uffs/v3910_ram_flash.bin"
echo "0" > memsize
if [ ! -f $cfg_path ];then
cfg_path="/firmware/magic_file"
fi
route add default gw 192.168.1.1
qemu-system-aarch64 -M virt,gic_version=3 -cpu host --enable-kvm -m 512 \
-kernel /firmware/vqemu/sohod64.bin $serial_option -dtb DrayTek \
-nographic $gdb_serial_option $gdb_remote_option \
-device virtio-net-pci,netdev=network-lan,mac=${LAN_MAC} \
-netdev tap,id=network-lan,ifname=qemu-lan,script=no,downscript=no \
-device virtio-net-pci,netdev=network-wan,mac=00:1d:aa:${A}:${B}:$(wan_mac 1) \
-netdev tap,id=network-wan,ifname=qemu-wan,script=no,downscript=no \
-device virtio-serial-pci -chardev pipe,id=ch0,path=serial0 \
-device virtserialport,chardev=ch0,name=serial0 \
-monitor telnet:127.0.0.1:7777,server,nowait \
-device loader,file=$platform_path,addr=0x25fff0 \
-device loader,file=$cfg_path,addr=0x260000 \
-device loader,file=gci_magic,addr=0x4de0000 \
-device loader,file=$enable_kvm_path,addr=0x25ffe0 \
-device loader,file=$uffs_flash,addr=0x00be0000 \
-device loader,file=memsize,addr=0x25ff67
比之前的简单了很多。还有一个set_up_linux.sh,是用来设置网络的。由于我是在wsl上进行的仿真,一些地方和纯linux仿真不太一样,想要在纯linux上仿真的可以参考以下文章:
这里我就只贴出wsl的部分仿真的过程了。
使用这个脚本时,目录下至少需要以下文件:
Shell
one@LAPTOP-N3B6U5LC:/mnt/c/Users/whitedog/Desktop/Vigor3910_v4.3.1/v4.3.1/de/rootfs/firmware/vqemu/test/run$ ls
boot.sh draycert_def.cfg efi-virtio.rom magic_file qemu-system-aarch64 sohod64.bin v3910_ram_flash.bin
其中v3910_ram_flash.bin可以是一个空文件,qemu-system-aarch64需要自己从官方源码编译一份,其他文件都可以在文件系统中找到:
根据README的内容安装依赖,然后./build。完成后看看有没有qemu-2.12.1这个文件夹,没有的话进入source目录下解压:
Shell
tar -xvjf qemu-2.12.1.tar.bz2
然后:
Shell
cd qemu-2.12.1
Shell
./configure --enable-kvm --enable-debug --target-list=aarch64-softmmu --python=python3
make
新版编译器可能会出现兼容问题,需要把disas/arm-a64.cc文件中的:
C++
extern "C" {
#include "qemu/osdep.h"
#include "disas/bfd.h"
}
改成:
C++
#include "qemu/osdep.h"
extern "C" {
#include "disas/bfd.h"
}
通过对上面启动脚本的分析,可以有以下仿真脚本:
Shell
#!/bin/bash
set -u
cd "$(dirname "$0")" || exit 1
# ---- binaries / images (all local) ----
QEMU="./qemu-system-aarch64"
[ -x "$QEMU" ] || chmod +x "$QEMU" 2>/dev/null
[ -x "$QEMU" ] || QEMU="$(command -v qemu-system-aarch64 || true)"
[ -n "${QEMU:-}" ] && [ -x "$QEMU" ] || { echo "[!] qemu-system-aarch64 not found." >&2; exit 1; }
KERNEL="./sohod64.bin"
[ -f "$KERNEL" ] || { echo "[!] missing kernel: $KERNEL" >&2; exit 1; }
# ---- loader source files (all local) ----
magic_file="./magic_file"
[ -f "$magic_file" ] || { echo "[!] missing magic_file" >&2; exit 1; }
cert_path="./draycert_def.cfg"; [ -f "$cert_path" ] || cert_path="$magic_file"
cfg_path="$magic_file"
license_path="$magic_file"
fw_ver_path="$magic_file"
uffs_flash="./v3910_ram_flash.bin"; [ -f "$uffs_flash" ] || { touch "$uffs_flash"; }
# ---- generate loader/status files ----
echo "x86" > platform
echo "xkvm" > enable_kvm
echo "3" > model
echo "0" > memsize
printf "0" > err_code
printf "0" > tst_mode
printf "0" > env_test
printf "0" > fw_release
echo "GCI_SKIP" > gci_magic
printf "0#" > gexp_flag
printf "19831026" > gexp_file
/usr/bin/od -vAn -N8 -tx4 /dev/urandom > devrand 2>/dev/null || echo "00000000 00000000" > devrand
printf '\x00\x00\x1d\xaa\x00\x00\x00\x00\x00\x00\x00\x00' > drayf2.cfg
# ---- MAC addresses ----
FW_LAN_MAC="001DAA000000"
A=${FW_LAN_MAC:6:2}; B=${FW_LAN_MAC:8:2}; C=${FW_LAN_MAC:10:2}
LAN_MAC="${FW_LAN_MAC:0:2}:${FW_LAN_MAC:2:2}:${FW_LAN_MAC:4:2}:${A}:${B}:${C}"
wan_mac(){ printf "%02x\n" $((0x${C}+0x$1)) | tail -c 3; }
WAN1_MAC="${FW_LAN_MAC:0:2}:${FW_LAN_MAC:2:2}:${FW_LAN_MAC:4:2}:${A}:${B}:$(wan_mac 1)"
# ---- host port forwards (127.0.0.1 -> DrayOS LAN 192.168.1.1) ----
HTTP_PORT="${HTTP_PORT:-8000}"
HTTPS_PORT="${HTTPS_PORT:-4443}"
TELNET_PORT="${TELNET_PORT:-2323}"
SSH_PORT="${SSH_PORT:-2222}"
HOSTFWD="hostfwd=tcp:127.0.0.1:${HTTP_PORT}-192.168.1.1:80"
HOSTFWD="$HOSTFWD,hostfwd=tcp:127.0.0.1:${HTTPS_PORT}-192.168.1.1:443"
HOSTFWD="$HOSTFWD,hostfwd=tcp:127.0.0.1:${TELNET_PORT}-192.168.1.1:23"
HOSTFWD="$HOSTFWD,hostfwd=tcp:127.0.0.1:${SSH_PORT}-192.168.1.1:22"
# ---- gdb stub (always on) ----
GDB_PORT="${GDB_PORT:-1234}"
gdb_remote_option="-gdb tcp::${GDB_PORT}"
[ "${GDB_WAIT:-0}" = "1" ] && gdb_remote_option="$gdb_remote_option -S"
echo "[*] GDB stub: tcp::${GDB_PORT} (attach: gdb-multiarch -ex 'target remote :${GDB_PORT}')"
echo "[*] Web UI : http://127.0.0.1:${HTTP_PORT}/ (https ${HTTPS_PORT}, telnet ${TELNET_PORT}, ssh ${SSH_PORT})"
echo "[*] Monitor: telnet 127.0.0.1 7777 | Console: this terminal (Ctrl-A X to quit)"
# ---- launch ----
"$QEMU" -M virt,gic_version=3 -cpu cortex-a57 -m 512 \
-kernel "$KERNEL" -dtb DrayTek \
-nographic $gdb_remote_option \
-device virtio-net-pci,netdev=network-lan,tx_queue_size=512,rx_queue_size=512,mac=${LAN_MAC} \
-netdev user,id=network-lan,net=192.168.1.0/24,host=192.168.1.254,${HOSTFWD} \
-device virtio-net-pci,netdev=network-wan1,tx_queue_size=512,rx_queue_size=512,mac=${WAN1_MAC} \
-netdev user,id=network-wan1 \
-device virtio-serial-pci -chardev file,id=ch0,path=serial0.log \
-device virtserialport,chardev=ch0,name=serial0 \
-device virtio-serial-pci -chardev file,id=ch1,path=serial1.log \
-device virtserialport,chardev=ch1,name=serial1 \
-monitor telnet:127.0.0.1:7777,server,nowait \
-device loader,file=platform,addr=0x25fff0 \
-device loader,file=enable_kvm,addr=0x25ffe0 \
-device loader,file="$fw_ver_path",addr=0x25ffac \
-device loader,file=fw_release,addr=0x25ffab \
-device loader,file=tst_mode,addr=0x25ff8b \
-device loader,file=devrand,addr=0x25ff70 \
-device loader,file=err_code,addr=0x25ff6b \
-device loader,file=model,addr=0x25ff69 \
-device loader,file=memsize,addr=0x25ff67 \
-device loader,file=env_test,addr=0x25ff61 \
-device loader,file=drayf2.cfg,addr=0x200000 \
-device loader,file="$license_path",addr=0x1E0000 \
-device loader,file="$cfg_path",addr=0x260000 \
-device loader,file="$cert_path",addr=0x860000 \
-device loader,file=gci_magic,addr=0x4de0000 \
-device loader,file=gexp_flag,addr=0x55e0000 \
-device loader,file=gexp_file,addr=0x55e0010 \
-device loader,file="$uffs_flash",addr=0x00be0000 \
-device nec-usb-xhci,id=usb
上面的仿真脚本可通过gdb来调试,端口为1234。
0x4 漏洞利用
gdb连上后,可以在GetCGI的开头,调用清理函数之前和未授权函数的结尾下断点,调试出偏移量:
刚进入GetCGI时:

看到栈地址应该是0x46840f40,继续执行到调用清理函数之前:

根据栈信息可以看出,父函数的FP在0x46841590的位置,查看一下:

注意到返回地址和FP已经被覆盖为一个堆地址了,并且可以看到0x46841588的低四字节已经被置零了,清理函数就会在这里停下来,不会影响到后面的返回地址。便可以在这里写入shellcode拼凑字符串参数并且跳转执行virtcons_out了。
写exp时候可以利用GetCGI的URL编码来绕过例如\x00之类的坏字符。
如果使用我上面的基于wsl的脚本进行仿真,由于无法创建管道,所有最后的命令执行会卡在virtcons_out,像这样:
Text
Memsave Config addr 0x4bc98fc0 size 5997760
[_virtcons_out:2749]=======set_linux_time ;touch /tmp/pwn
[_virtcons_out:2749]=======
[_virtcons_out:2749]=======
[_virtcons_out:2749]=======
[_virtcons_out:2749]=======
[_virtcons_out:2749]=======
纯linux环境模拟就没有问题了。
参考文章:
https://www.cnblogs.com/Sta8r9/p/18707817
https://xz.aliyun.com/news/17712
https://bestwing.me/CVE-2024-41592-vigor-stack-overflow.html