Files
Penetration_Testing_POC/iot/CVE-2024-39226 GL-iNet 路由器RPC漏洞复现.md

153 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CVE-2024-39226 GL-iNet 路由器RPC漏洞复现
> AiMiLiYa
> 来源:https://www.iotsec-zone.com/article/477
CVE信息:[https://nvd.nist.gov/vuln/detail/CVE-2024-39226](https://nvd.nist.gov/vuln/detail/CVE-2024-39226)
官方固件下载网址:[https://dl.gl-inet.cn/](https://dl.gl-inet.cn/) (下载固件版本:GL-AX1800 Flint 4.5.16)
宿主机(Ubuntu 18.04.6)配置网卡(用于和虚拟机机通信)
```sh
sudo tunctl -t tap0
sudo ifconfig tap0 192.168.3.1/24 up
```
qemu系统模拟(这里-cpu cortex-a15必须加,默认不加可以,但是模拟机起来执行指令会报Illegal instruction的错误)
```sh
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
```
arm虚拟机模拟起来后,配置虚拟机网卡
```sh
ifconfig eth0 192.168.3.2/24 up
```
将获取到的固件文件系统打包,并传到arm虚拟机内解压。
```sh
tar -zcvf squashfs-root.gz squashfs-root
scp squashfs-root.gz [email protected]:/
tar -zxvf squashfs-root.gz
```
挂载固件文件系统中的proc目录和dev目录到chroot环境
```sh
mount -t proc /proc/ ./squashfs-root/proc/
mount -o bind /dev/ ./squashfs-root/dev/
chroot squashfs-root sh
```
在启动项中/etc/init.d/nginx可以看见web服务是如何启动的
![0.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43ybudp5g0fsgsofe.png)
```sh
/usr/sbin/nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
```
在启动web服务时,报错提醒
![1.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43ycmcbv1153nnlrb.png)
回头在我们找到web服务启动参数的地方,可以看见有对/var/log/nginx目录的创建,下面的也有,然后就是报错没有的目录手动创建。
![2.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43ycyp2iyac39bcn9.png)
最终所需创建下列目录可以成功运行nginx服务
```sh
mkdir -p /var/log/nginx
mkdir -p /var/lib/nginx/body
mkdir -p /var/run
```
创建所需目录之后,启动web服务之后尝试访问,结果是404。
![3.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yfbcyl03ricegqe.png)
上网搜搜看有没有前辈做过,发现有运行80_nginx-oui脚本即可成功的,这边尝试一下,虽然访问到了welcome,但是界面并没有出来,不知道什么原因。(**注意!我这里陷入了一个死胡同,其实漏洞复现不需要web界面起来,只要服务起来直接打也是可以通的**)
![4.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yg1gur2xam8trqm.png)
然后再去查看启动项,在/etc/init.d/boot中可以看到一些初始化的内容,运行一下尝试看看能不能显示web页面
![5.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43ygtdkjgsjz1r8tt.png)
执行/etc/init.d/boot boot尝试,但最终一直在打印”Waiting for wireless generation ...“,去检索一下看看。在此处找到点,可以知道它是循环检查/etc/config/wireless是否为空。
再次检索/etc/config/wireless看看。
![6.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yhhumnskadnvn6b.png)
根据检索信息,在/etc/uci-defaults/network_gl中找到了echo "" > /etc/config/wireless,那么该脚本就是对应配置相关文件。
![7.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yi5gn5sifbggckq.png)
那么接下来尝试运行/etc/uci-defaults/network_gl以及/etc/init.d/boot boot
虽然报了一些错,但是执行完后启动nginx成功访问到web页面
![8.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yikdcj4nmz8iiuu.png)
接下来根据CVE信息去查看漏洞点 (s2s.so中的enable_echo_server函数):
从请求体中的json数据中获取port,下面对获取到的port的字符串进行了校验,但是并不严格。
atoi 函数会尝试将字符串中的字符转换为整数,直到遇到非数字字符为止。
此处仅仅校验了端口是否合法,导致可以拼接字符串触发命令执行。
![9.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yja6eymfvbd66t9.png)
查看poc信息:
```sh
curl -H 'glinet: 1' 127.0.0.1/rpc -d '{"method":"call", "params":["", "s2s", "enable_echo_server", {"port": "7 $(touch /root/test)"}]}'
```
不管三七二十一,ssh连上打一下试试看。
OK还是报错了。
![10.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yjyb6l2c0dpzugf.png)
检索错误信息“internal error",在rpc.lua中找到报错信息。
![11.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43ykh37vnp0176zom.png)
在error_response下紧接着便是访问的权限判断。
这里注意到,使用了ubus.call。
由于该固件是基于OpenWrt开发而来的,且根据OpenWrt官网内容,可以知道:ubus可以和ubusd服务器交互,因此前面报错,可能与ubusd服务未启动有关。
接着看:如果为本地请求且含义glinet标头才返回true
![12.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43ykzdz584bc21iab.png)
![13.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yl8w7vxfvbdizaw.png)
现在我们手动启动一些ubusd服务再打一次试试看(ubusd启动占用终端,再起一个ssh)。OK还是报错。
![14.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yltjv3kzgws0zft.png)
这里想起之前看到的一个gl.conf中有一些配置服务相关的信息,重新来查看一下。
将所有对 /index.html 的请求重定向到根目录 /。(permanent 表示这是一个永久性重定向)
且可以看见rpc相关请求是在/usr/share/gl-ngx/oui-rpc.lua中处理,那么后面可以去看看这块。
location /cgi-bin/{...} 这个 location 块匹配所有以 /cgi-bin/ 开头的请求 URL。
fastcgi_pass unix:/var/run/fcgiwrap.socket: 将请求转发给 FastCGI 进程,使用 Unix 域套接字 /var/run/fcgiwrap.socket。fcgiwrap 是一个用于处理 FastCGI 请求的简单 FastCGI 服务器,它通过这个套接字与 Nginx 进行通信。 (怀疑报错可能和这个FasrCGI有关,后面尝试启动它再打试试)
location ~.\*\\.(html|png|jpg|svg)${...} 配置了对特定文件类型的缓存控制,确保这些文件不会被缓存或在缓存过期后必须重新验证,以保证客户端始终获得最新的内容。
![15.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yn9t34wk1pvwa1q.png)
接下来看看oui-rpc.lua的内容
在rpc_method_call功能主要包括:
检查参数数量是否少于 3
验证 sid、object 和 method 是否都是字符串类型,验证 args 是否存在且为表(table)类型
验证sid,通过rpc.access判断是否有访问权限
最后使用rpc.call调用请求的函数并传递参数。
![16.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43ynlf7y5s3vgppze.png)
另外,在oui-rpc.lua开头
![17.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43yntsyfbvgtgy538.png)
那么rpc.call其实就是加载oui文件下的rpc.lua里面的call函数。
再次返回rpc.lua中查看call函数。
该函数的功能是根据 object 和 method 动态调用方法。如果 object 的方法还未加载,它会尝试从指定路径加载相应的 Lua 脚本,并将脚本中的函数缓存到 objects 表中。之后,它会尝试从缓存中调用指定的方法。如果方法不存在,则调用 glc_call 进行处理。
![18.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43z2wdy5wxbzuikdm.png)
再看glc_call函数:该函数的主要作用是通过 HTTP POST 请求将对象、方法和参数传递给指定的 CGI 脚本 /cgi-bin/glc,并处理该脚本返回的结果。
这里如果直接访问/cgi-bin/glc不报错的话,或许可以直接远程命令执行。后面再看,先解决本地执行的问题。
![19.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43z37bdrttpblg0ny.png)
OK,大概先分析到这,接下来根据前面的分析去启动fcgiwrap服务再次尝试。
首先去查看一下参数:
-f:将 CGI 脚本的标准错误输出(stderr)通过 FastCGI 发送。
-c \<number\>:指定要预先分叉(prefork)的进程数量。预分叉是一种在多进程环境中常用的技术,用于提前创建多个子进程,以便更快地响应请求。
-s \<socket_url\>:指定要绑定的套接字 URL。
-h:显示帮助消息并退出。
-p \<path\>:限制执行到指定的脚本。
![20.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43z3tfd1xku2nme8y.png)
由于nginx服务是依靠多进程进行通信的,感觉运行fcgiwrap是需要-c和-s指定参数。
接下来启动fcgiwrap服务并重启nginx服务然后打poc先试试看。
![21.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43z4v6ys3xdg7p4kf.png)
ok,成功了。
**那么回到之前分析的glc_call函数函数中 /cgi-bin/glc这个点**,先访问一下试试,看看是否会报错,发现没有,可能有点搞头。
![22.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43z5dzx6z6737elq2.png)
改为cgi-bin/glc打试试看,ok,可以看见成功命令执行。
![23.png](https://www.iotsec-zone.com/api/image/v2/20240912/d43z5rsmxptord211a.png)