首页
社区
课程
招聘
[原创]GL-iNet路由器 CVE-2024-39226 漏洞分析
发表于: 2024-9-23 18:16 10954

[原创]GL-iNet路由器 CVE-2024-39226 漏洞分析

2024-9-23 18:16
10954

24年8月网上披露了 CVE-2024-399226 [1],影响多款 GL-iNet 路由器,随后开始漏洞应急。起初对 GL-iNet 路由器不了解导致踩了很多坑浪费了不少时间,因此做完应急后对这次漏洞分析和固件仿真进行记录。

GL.iNet 是一家专注于开发智能路由器和网络设备的公司。该公司的产品通常基于 OpenWrt 操作系统,广泛应用于家庭、企业和工业物联网场景。GL.iNet 路由器以其开源、可定制性和强大的功能而著称,特别适合开发者、网络安全研究人员和高级用户。

OpenWrt 是一个基于 Linux 的开源嵌入式操作系统,专为网络设备(如路由器、网关和接入点)设计。与传统的路由器固件不同,OpenWrt 不是单一的、不可变的固件,而是一个完整且可扩展的操作系统,允许自定义以适应任何应用程序。

OpenResty 是一个基于 Nginx的高性能 Web 平台,它将 Lua 脚本引擎嵌入到 Nginx 中,使开发者可以通过 Lua 脚本编写高度可定制的 Web 服务,用来处理复杂的 web 逻辑和 API 请求。OpenResty 通常用于高并发、低延迟的 Web 应用程序开发,特别是在需要处理复杂逻辑或与外部服务交互时。

这种组合使得 GL.iNet 路由器不仅仅是一个网络设备,还可以作为一个小型的 Web 服务器或应用平台。

GL.iNet 官网提供历史固件下载[2]

sysupgrade-glinet_ax1800 文件夹下存在 root 文件

使用 binwalkroot 中提取 Squashfs 文件系统

查看 bin/busybox 得知是 32位arm 架构

使用 qemu-system-arm 从系统角度进行模拟,此时需要一个 arm 架构的内核镜像和文件系统,可以在这个网站下载[3]

启动虚拟环境

//默认可以不指定 cpu 模型,我在模拟过程中遇到报错所以指定了 cpu

启动后用户名和密码都是 root 即可登录模拟的系统。

接下来在宿主机创建一个网卡,使 qemu 内能和宿主机通信。

宿主机安装依赖

将如下代码保存为 net.sh 并运⾏即可

然后配置 qemu 虚拟系统的路由,在 qemu 虚拟系统中运⾏ net.sh 并运⾏

//虚拟系统可能没有 vimnano ,使⽤ echo ⼀⾏⼀⾏写。

这样宿主机和模拟环境互通,使⽤ scpsquashfs-root ⽂件夹上传到 qemu 系统中的 /root 路径下。

然后挂载 procdev ,最后 chroot 即可

启动 web 服务,前文已经介绍过 GL.iNet 路由器利用 OpenResty 来增强其 web 管理界面和 API 的功能。而 OpenResty 是基于 Nginxweb 平台,内置 Lua 脚本支持,所以首先启动 Nginx 服务。

尝试运行 /etc/init.dnginx 脚本(/etc/init.d 目录通常包含系统启动和管理各种服务的脚本,如果需要启动某个服务,通常可以在该目录中找到相应的脚本)。

查看 /etc/init.d/nginx 如何手动启动 nginx
nginx 脚本源码
启动 nginx 报错
创建缺少的文件夹再次启动 nginx
再次启动 nginx
看样子 nginx 好像起来了,访问 web 却是 404
web 访问 404
这个时候已经没什么头绪了,find 一下所有 nginx 相关文件试试
查找 nginx 相关文件
每个文件都看看,发现 /etc/uci-defaults/80_nginx-oui 脚本。

/etc/uci-defaults/80_nginx-oui 脚本的主要作用是配置和调整Nginx的相关文件,确保Web服务能够正常运行。

尝试运行 /etc/uci-defaults/80_nginx-oui 看看是否能修复 404 问题。

web 访问成功
成功修复,接下来尝试漏洞复现,先看一下披露的 Poc

Poc 来看好像只能通过 127.0.0.1 利用,先使用 192.168.100.2 试试
POC 远程利用
拒绝访问,再使用 127.0.0.1 (之前的会话因为启动 nginx ,需要 ssh 再创建一个会话)
POC 本地利用
这次报错为内部错误。继续找问题。根据 Poc 可知请求的路径是rpc ,在 /etc/nginx 下的 nginx 配置文件中查找 rpc 相关信息。

/etc/nginx/conf.d/gl.conf 找到请求 rpc 路径的处理方法

跟进到 /usr/share/gl-ngx/oui-rpc.lua

代码来看 /usr/share/gl-ngx/oui-rpc.lua 是处理 HTTP POST 请求 jSON-RPC 调用的。

以上代码实现模块导入、请求方式验证和读取请求体。

要注意 ubus 服务是 OpenWrt 系统中一个进程间通信框架,需要启动。

HTTP 请求仅允许 POST ,拒绝其他方式访问。

/usr/share/gl-ngx/oui-rpc.lua 定义了多个处理函数,每个函数对应不同的 rpc 方法(因为 POC 通过 call 方法调用 s2s.enable_echo_server 进行攻击,所以只截取 rpc_method_call 方法代码)。


rpc_method_call 进行参数校验、会话检查和 Ubus 调用:

继续跟进 /usr/lib/lua/oui/rpc.lua 查看 rpc.accessrpc.call 实现
M.seesion 和 M.access 函数源码
access 通过 is_local 判断是否本地请求。对于本地请求和 glinet 标头的请求,总是允许访问(确定了只能本地利用)
M.call 函数代码
M.call 函数是核心的 rpc 调用处理器,执行以下步骤:

查看 /usr/lib/oui-httpd/rpc/ 目录下是二进制 s2s.so 文件,无法直接加载,则通过 glc_call 调用 /cgi-bin/glc 执行 C 程序实现的 RPC 方法。

继续跟进 /www/cgi-bin/glc 文件

大致实现逻辑与 /usr/share/gl-ngx/oui-rpc.lua 类似,请求方式验证和读取请求体后动态加载并调用函数,区别在于 /www/cgi-bin/glc 使用 dlopen 动态加载对应的共享库(.so 文件)。

如此看来 POC 满足/usr/share/gl-ngx/oui-rpc.lua/usr/lib/lua/oui/rpc.lua 两段代码逻辑

请求发送一个 POST 请求到 rpc 路径,携带 JSON 数据:

权限校验参数检查均通过但是报内部错误,打印 nginx 日志看看

修改 /etc/nginx/nginx.conf 并重启 nginx

再用 POC 测试一次查看日志
nginx 日志信息
报错信息显示 ubus-proxyfcgiwrap 未启动或未正常配置,尝试启动 ubus fcgiwrap

启动 ubus
启动 fcgi
POC 本地利用成功
成功复现嘻嘻

漏洞只能本地利用未免有些太鸡肋了,继续进行漏洞分析,再尝试寻找远程利用的方法。

通过 POC 可知漏洞通过 s2s API 传递恶意 shell 命令,分析一下 /usr/lib/oui-httpd/rpc/s2s.so

漏洞出现在 s2s.enable_echo_server 检查并启动 echo_server 过程中:

虽然代码中检查了 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,导致命令注入。

漏洞成因分析起来还是比较容易的,最后一个问题,如何通过远程实现漏洞利用。公开的 POC 是通过 rpc 路径执行 call 方法调用 s2s.enable_echo_server 触发漏洞,但是会在 rpc.access 校验权限,所以得找个一个路径不需要校验权限执行 call 方法。

回过头再看一眼 /usr/lib/lua/oui/rpc.luaglc_call 方法
glc_call 函数源码
如果直接请求 /cgi-bin/glc 路径,将会调用 glc_call 函数。glc_call 函数会向另一个内部路径(/cgi-bin/glc)发起一个内部 HTTP POST 请求,并传递方法名称、参数等信息。执行 call 方法并且跳过之前的权限校验,修改 POC 尝试远程利用。

POC 远程利用成功
POC 远程利用成功

漏洞应急最烦环境弄不好,这次就因为一开始不了解 GL-iNet 路由器导致纯在瞎折腾浪费时间,最后在大佬的指点下搭建好环境。记录过程中尽量复刻了当时工作的操作顺序,逻辑上有很多地方其实有更简单的解决方法不用绕这么多弯,诸君见笑。

[1] 漏洞详情

https://github.com/gl-inet/CVE-issues/blob/main/4.0.0/s2s%20interface%20shell%20injection.md

[2] 固件下载

https://dl.gl-inet.cn/

[3] 内核镜像和文件系统下载

https://people.debian.org/~aurel32/qemu/

固件版本:GL-AX1800 Flint 4.5.16
固件版本:GL-AX1800 Flint 4.5.16
$ 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
$ 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 -Me root
$ binwalk -Me root
$ 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
$ 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
vmlinuz-3.2.0-4-vexpress  linux内核镜像文件
initrd.img-3.2.0-4-vexpress  RAM磁盘映像文件
debian_wheezy_armhf_standard.qcow2  虚拟磁盘映像文件
vmlinuz-3.2.0-4-vexpress  linux内核镜像文件
initrd.img-3.2.0-4-vexpress  RAM磁盘映像文件
debian_wheezy_armhf_standard.qcow2  虚拟磁盘映像文件
$ 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
$ 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
Illegal instruction
Illegal instruction
$ sudo apt-get install bridge-utils uml-utilities
$ sudo apt-get install bridge-utils uml-utilities
#!/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
#!/bin/bash

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (11)
雪    币: 22
活跃值: (90)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
创建虚拟环境,按照文章的命令是提示不支持 cortex-a15的
2024-9-29 11:00
0
雪    币: 1782
活跃值: (440)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
3
qemu版本太高了应该,我用的版本比较低
2024-10-2 13:19
0
雪    币: 220
活跃值: (99)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
4
牛逼呀
2024-10-14 18:29
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
mb_llyvpbyk 创建虚拟环境,按照文章的命令是提示不支持 cortex-a15的
师傅解决了吗?
2024-10-21 08:08
0
雪    币: 1782
活跃值: (440)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
慕雪岑 师傅解决了吗?
qemu版本太高了应该,我用的版本比较低
2024-10-22 15:47
0
雪    币: 4983
活跃值: (19110)
能力值: ( LV13,RANK:317 )
在线值:
发帖
回帖
粉丝
7
debian_wheezy_armhf太老了,ubuntu18安装的低版本qemu-system-arm能绕过来指定仿真开发板非支持的cpu,qemu后面修复这个问题(https://lists.gnu.org/archive/html/qemu-devel/2020-07/msg03297.html),用低版本qemu绕过不算是好方法,换个高版本内核镜像仿真就行了,https://people.debian.org/~gio/dqib/ 这里有制作好的。
2024-10-23 10:51
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
摆烂星君 qemu版本太高了应该,我用的版本比较低
我更新到最新版好像也没用0...0
2024-10-25 17:54
0
雪    币: 1782
活跃值: (440)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
9
慕雪岑 我更新到最新版好像也没用0...0
我当时做的时候用的是老版本的qemu
2024-10-28 14:45
0
雪    币: 409
活跃值: (778)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
10
慕雪岑 我更新到最新版好像也没用0...0
https://download.qemu.org/qemu-4.2.0.tar.xz编译一个老版本跑就行
2024-11-12 21:24
0
雪    币: 13
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
不知道为什么我的nginx跑起来后,访问welcome界面是空白。有大佬知道怎么解决吗
2024-11-28 16:01
0
雪    币: 1782
活跃值: (440)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
12
mb_kdyaokdn 不知道为什么我的nginx跑起来后,访问welcome界面是空白。有大佬知道怎么解决吗
运行
/etc/uci-defaults/network_gl
/etc/init.d/boot boot
2024-11-29 15:06
1
游客
登录 | 注册 方可回帖
返回
// // 统计代码