type
status
date
slug
summary
tags
category
icon
password
这是我复现的第二个漏洞,其实也是命令执行的漏洞,但这个漏洞原始的cve里是内部执行的代码,虽然我在网上也找到了其他师傅开发的远程执行的poc,不过原理其实差不多。
到现在为止复现的这两的虽然架构不一样,不过都是命令执行的算是web端的漏洞,都是比较简单的洞。
 
这个漏洞主要影响的是 GL-iNet 路由器,简单介绍一下这个路由器的基本信息

产品介绍

GL.iNet 是一家专注于开发智能路由器和网络设备的公司。该公司的产品通常基于 OpenWrt 操作系统,广泛应用于家庭、企业和工业物联网场景。GL.iNet 路由器以其开源、可定制性和强大的功能而著称,特别适合开发者、网络安全研究人员和高级用户。
OpenWrt 是一个基于 Linux 的开源嵌入式操作系统,专为网络设备(如路由器、网关和接入点)设计。与传统的路由器固件不同,OpenWrt 不是单一的、不可变的固件,而是一个完整且可扩展的操作系统,允许自定义以适应任何应用程序。
OpenResty 是一个基于 Nginx的高性能 Web 平台,它将 Lua 脚本引擎嵌入到 Nginx 中,使开发者可以通过 Lua 脚本编写高度可定制的 Web 服务,用来处理复杂的 web 逻辑和 API 请求。OpenResty 通常用于高并发、低延迟的 Web 应用程序开发,特别是在需要处理复杂逻辑或与外部服务交互时。
这种组合使得 GL.iNet 路由器不仅仅是一个网络设备,还可以作为一个小型的 Web 服务器或应用平台。
 
这里我们后面会用到很多有关这个平台的一些基础信息,这里后面会介绍。有的iot的洞是基于固定的平台,很多时候就要去了解这个平台的基础配置才方便寻找过程。
 

环境模拟

固件提取

先提取一下固件,直接可以再这里下载,GL-AX1800 Flint 4.5.16 ,这个
notion image
 
下载之后解压,有3个文件
notion image
 
很明显具体的内容再root里面,解压一下,用binwalk(这里我已经解压过了所以报错)
notion image
 
看一看里面 bin/busybox 文件,可以知道是32位的arm架构
notion image
 
我们上一个漏洞的固件用到的是32位mips ,于是我们使用的了FirmAE模拟固件,而现在我们用到是32位arm ,很明显这个架构是不适合用FirmAE模拟,于是我们就要用到qemu对这种架构进行模拟。

qemu模拟

这里我用的虚拟机是ubuntu22.04,并且这个过程没发生什么报错就比较滋润。
 
这里直接使用大佬的镜像,下载armhf-virt的链接下载
notion image
 
这里直接下载之后解压,里面包含这些文件,
notion image
 
这里可以看一看readme.txt里面有具体的启动命令
这里我直接写成了个脚本,直接执行这个脚本就行
 
 
启动之后可以输入root/root登录。

网卡搭建

这里为了让我们能在Ubuntu里直接访问qemu虚拟机,所以我要搭建一个网卡用于这两个的通讯
 
宿主机:
这里直接写一个net.sh,方便用
这里不用改,直接用就行
 
然后配置qemu虚拟系统的路由,在qemu虚拟系统中运行net.sh并运行。
这里有可能再qemu虚拟系统没有这两个命令,这里可以去网上搜一下qemu虚拟系统拓荒,简答下载一下,方便后面使用,
 
这里具体的顺序就是,先再宿主机里
notion image
然后
notion image
启动qemu虚拟系统,输入root/root 登录。
qemu虚拟系统中
notion image
做完这些之后可以试着宿主机和qemu机之间互ping
notion image
如果能ping通就没问题了,就可以开始将固件上传qemu机
 
使用scp将squashfs-root文件夹上传到qemu系统中的/root路径下
 

挂载文件并启动环境

 
在使用 QEMU 进行跨架构模拟后,挂载 /proc/dev 文件系统是必要的,因为它们提供了与内核和硬件设备交互的关键接口。/proc 是一个虚拟文件系统,包含关于系统内核、进程和系统状态的动态信息,许多系统工具和应用程序依赖于它来获取系统信息和配置参数,此外,运行在模拟环境中的进程也需要通过 /proc 进行管理和监控。如果不挂载 /proc,相关工具可能无法正常工作或获取必要的信息。另一方面,/dev 目录包含设备特殊文件,代表系统中的硬件设备和虚拟设备,用户空间程序通过它们访问和控制硬件设备,如磁盘驱动器、终端和网络接口等。许多应用程序需要与这些设备交互才能正常运行,例如图形应用需要访问图形设备,编译器可能需要访问终端设备。同时,系统服务如 udev 依赖于 /dev 动态管理设备节点。缺少对 /dev 的挂载,模拟环境中的应用程序将无法访问必要的设备,导致功能受限或无法运行。因此,正确挂载 /proc/dev 确保了模拟环境具备必要的系统接口和设备访问能力,使得各种应用程序和系统工具能够正常运行。
 
 
notion image
 
这样我们其实就已经启动这个固件了,但我们如果要访问那个网站,会发现并不能成功,这里我们还有启动一些东西。
 
具体可以执行这些命令,可以实现在宿主机访问 192.168.100.2 进入路由器管理页面,可完成初始密码设置并登录进入管理面板过程,想实现更多功能的启用就要摸索更多配置了,不过我们分析复现授权相关过程已经够用了。
具体为什么,这里我也不是很明白,推荐去看看网上的大师傅们的具体分析文章(我真的站在巨人的肩膀上看的高)
 
notion image
就这样,这个固件就算是模拟成功,接下来就可以看看具体的漏洞流程。
 

漏洞分析

poc
 
这是官方的poc,这个poc,怎么说呢,有点鸡肋。这里我们要在我们模拟的qemu系统内部执行这个命令,才能达成命令执行的漏洞,简单来说,这是一个内部漏洞。前面的-H 'glinet: 1' 就是为了表明这是一个内部的传参,这个命令的执行结果就是在root 文件夹里新建一个text文件,执行一下。
notion image
 
这里我的环境运气比较好,既没有任何的报错直接达到了目的。
 
因为这就是在固件的内部执行的命令,所以127.0.0.1 其实就是这个固件的web页面,我们现在开始简单分析一下这个漏洞的调用,最后如何达到命令执行的地址,
 
首先是一开始我们这里就行了一个/rpc 请求,这里我们在前面提到,这个路由器的web是由OpenResty启动,这个的特点就是将Lua脚本引擎嵌入到Nginx中,开发者可以通过Lua脚本编写高度可定制的Web服务。
所以这里我们的请求可以在 Nginx中找到,在系统中/etc/nginx/conf.d/ 是 Nginx 约定的配置目录。我们可以在其中对应的Lua脚本中找到/rpc 请求对应得作用。
 
这里说一下,我们从网上下载的是一个压缩后的文件,我们在哪在Ubuntu中就行解压,还原系统,这里还原后的系统,如果我们还想把它导出到windows里面看,这里有个问题,就是像Lua脚本这类文件有的权限很高,
notion image
就像这里,我们知道它是Lua脚本,但权限太高导致如果直接导出来是不能的,
所以这里我们可以来一个比较奇妙的办法,先进入root模式,然后把整个系统文件,打包为一个tar包
notion image
 
在给这个包加权限,在拿出来解压,这里注意一定是要在root模式下打包,这样才能把文件全部打包,不减少文件,同时不改变文件性质。
 
 
于是更具上面说的我们就可以在系统的如下文件中找到/rpc 的具体作用。
/etc/nginx/conf.d/gl.conf
notion image
 
很明显,这里程序在接收到这个请教之后就会到/usr/share/gl-ngx/oui-rpc.lua 中执行
 
打开/usr/share/gl-ngx/oui-rpc.lua
 
这里再看一眼poc的内容
可以看到我们传入/usr/share/gl-ngx/oui-rpc.lua 的内容为"method":"call", "params":["", "s2s", "enable_echo_server", {"port": "7 $(touch /root/test)"
 
这里我们看这/oui-rpc.lua 的内容,其中就有一个
notion image
这里用"method" 定义了4个函数,而我们很明显就是用到了其中的rpc_method_call 函数,在看这个函数的具体内容。
 
call定义rpc_method_call函数
 
很明显这个我们用"params":["", "s2s", "enable_echo_server", {"port": "7 $(touch /root/test)"}],传如4个参数,这个函数的最开始就是先验证是否有4个参数。
 
这里就行权限检查分两步:
rpc.is_no_auth(object, method):检查该 object.method 是否属于**免认证(no auth)**的接口(例如公开的读取信息接口可能不需要登录)。
 
如果返回 true,跳过访问控制。
若不免认证,则调用 rpc.access("rpc", object .. "." .. method) 做实际的访问控制判断(可能检查 ngx.ctx.sid、cookie、ACL、角色等)。
如果 rpc.access 返回 false,表示当前请求无权访问该方法,则返回 JSON-RPC 的“访问被拒绝”错误(rpc.ERROR_CODE_ACCESS)并退出。
这段代码负责把 API 的权限策略强制执行到每次 call 请求。
 
这里就是用来判断是否为内部命令的重要函数,
之后就是这个函数最重要的
这里程序会调用rpc.call 函数,对参数就行处理,这里我们就要再次寻找这个rpc.call 函数在哪,
 
rpc_method_call进行参数校验、会话检查和Ubus调用:
  1. 确保params中至少三个元素且元素类型正确。
  1. 检查sid是否有效,并通过rpc.access验证访问权限。
  1. 如果上述判断均通过,使用rpc.call执行指定的Ubus对象和方法。
 
 
这里回到文件头
那么rpc.call其实就是加载oui文件下的rpc.lua里面的call函数。
在 Lua 中:
  • require "oui.rpc"
    • 会去加载一个叫 oui/rpc.lua 的模块(路径可能是 /usr/lib/lua/oui/rpc.lua/usr/share/lua/...)。
  • 返回的内容(通常是一个 table,包含多个函数)会赋值给局部变量 rpc
  • 所以后续 rpc.call(...)rpc.error_response(...)rpc.is_no_auth(...) 都是调用这个模块里的函数。
 
local rpc = require "oui.rpc" → 加载 oui/rpc.lua,把返回的 table 存到 rpc 变量里。
rpc.call → 就是 oui.rpc 里定义的 M.call,用来动态加载 /usr/lib/oui-httpd/rpc/<object> 文件并调用对应函数。
rpc_method_call 里,这个机制相当于通过 object + method 动态调用后端的 Lua 模块函数
 
通过寻找我们就可以在程序中找到/usr/lib/lua/oui/rpc.lua 文件,这就是我们要找的下一个目标,这则里面既可以查看rpc.accessrpc.call实现。
notion image
 
在这里我们重点关注
这就是上面的 rpc.access 函数的具体内容
access通过is_local判断是否本地请求。对于本地请求和glinet标头的请求,总是允许访问(确定了只能本地利用)。
 
程序的重点在于
这个就是我们重点关注的函数,这里调用了我们poc的后面三个参数,
M.call函数是核心的rpc调用处理器,执行以下步骤:
  1. 检查请求的对象是否已加载,如果未加载,则尝试从/usr/lib/oui-httpd/rpc/目录下加载脚本文件。
  1. 如果脚本文件存在且加载成功,将对象的方法注册到objects表中。
  1. 如果无法从/usr/lib/oui-httpd/rpc/目录下加载脚本文件或者找不到对象或方法,则调用glc_call执行。
 
于是这里我们就可以到/usr/lib/oui-httpd/rpc/ 文件夹中寻找,有没有我们传入的s2s 文件,有这个文件,这个文件是一个可执行程序,我们用iad看一下
 
其实到这里,漏洞就已经清晰了,这里程序调用了/usr/lib/oui-httpd/rpc/s2s.so 文件中的enable_echo_server 函数,传入的"port": "7 $(touch /root/test)"}] 参数,达到了命令执行的效果,但这里我们可以看一看内部的具体流程
notion image
 
这里在enable_echo_server 函数中,使用了get传参,先验证,变量名port ,然后把具体的参数传入v5之中
之后虽然有对v5的验证
atoi(v9) 把字符串转成整数,检查端口范围 >0 && <=65534
但是这里,重要行为atoi 只解析字符串前面的数字,遇到第一个非数字字符就停止并返回已解析的数字。例如:atoi("7;touch") == 7atoi("7 $(touch)") == 7。这就意味着只要字符串前缀是合法数字,检查就会通过——即便后面包含恶意 shell 元字符。
 
于是之后就会来到漏洞点
关键两行(漏洞点):
snprintf(v27,128, "%s -p %s -f", "/usr/bin/echo_server", v9); —— 把用户提供的 v9 直接拼进命令字符串,结果像: /usr/bin/echo_server -p <v9> -f
 
v18 = system(v27); —— 把拼好的字符串交给 system() 执行(system() 会调用 shell:/bin/sh -c "<命令字符串>"),因此 shell 会解析命令字符串里的任何元字符(;, &&, |, $(), ..., > 等),并执行它们。
于是就会发生
这里v9的值在poc中被我们改成了7 $(touch /root/test)
于是就想到与执行
这里我们给touch /root/test 加上$ ,在shell中这个符号代表优先执行,所以这里这个命令,程序就会优先执行我们的输入命令,从达到命令执行的效果。
 
 
其实这个cve到这里就分析完具体的流程了,这里程序先通过/rpc 请求\etc\nginx\conf.d\gl.conf 在之中打开\usr\share\gl-ngx\oui-rpc.lua 在通过这里面的rpc_method_call 函数传入4个参数,程序通过rpc.access 验证为本地请求,通过rpc.call 处理后面3个参数,打开\usr\lib\lua\oui\rpc.lua 文件,通过其中的M.call ,寻找/usr/lib/oui-httpd/rpc/ 文件夹中是否有我们传入的第二个参数的名字的文件,于是就打开/usr/lib/oui-httpd/rpc/s2s.so 文件,中的enable_echo_server 函数,向其中传入"7 $(touch /root/test)" 命令,程序拼接到达
以为$,优先执行touch /root/test 命令,达到我们的目的,
 
到这里就是这个poc的具体分析,但我们可以看出来,这里太鸡肋了,只能本地访问,那有没有什么办法绕过rpc.access 达到远程访问呢,这里我们再来看看。
 
回过头再看一眼 /usr/lib/lua/oui/rpc.lua 的 glc_call 方法
notion image
如果直接请求 /cgi-bin/glc 路径,将会调用 glc_call 函数。glc_call 函数会向另一个内部路径(/cgi-bin/glc)发起一个内部 HTTP POST 请求,并传递方法名称、参数等信息。执行 call 方法并且跳过之前的权限校验,修改 POC 尝试远程利用。

notion image
这样我们就可以绕过之前的内部检测,再次利用漏洞点,不过这次达到了远程命令执行的目的。
 
 
这个漏洞,其实整体看起来还是比较简单的,依然是简单的漏洞命令执行,不过调用的过程多了一点,整体来说还是简单的,不过要注意的一点就是,iot的漏洞无论是成因还是调用过程都与系统平台有关,以后可以多关注看看不同平台的特点于配置文件的位置,或许更好理解调用的过程。
 
下一个CVE打算看看栈溢出一类,更具之前和学长的的聊天,可以看出后面可以多看看与api和协议有关的cve,多多复现吧。
 
老规矩,放几篇大佬的文章,感谢分享
CNVD-2013-11625复现CVE-2024-7436复现
Loading...