基本信息概述
产品:AX3000 双频WI-Fi6无线路由器
产品型号:RG-EW3000GX
固件版本:ReyeeOS 1.380.2222;EW_3.0(1)B11P380,Release(12222219)
UART固件提取
前期准备
首先摸到真机先暴力拆掉,然后焊接杜邦线 GND TX RX
配合USB 转 TTL 串口模块进行串口调试 ,那么本次就使用最新研发的 iot多功能调试板 其中的UART调试功能

非常的贴心,还准备了网卡功能,对于我这轻薄本没有网口接口太友好了。
以往我们在遇到板子上面没有显示TX RX 又或者还得边插边调,他这个有个特别好用的点,你不用管谁接TX,谁接RX ,直接俩根线连URAT0或URAT1就可以了

本次就插UART0进行串口调试
手动工作做好后,现在用MobaXterm 进行串口调试

因为无脑插上去的,因为RX和TX没有对上,没有输出日志信息

连接规则
GND ---------------> GND
TX ---------------> RX
RX ---------------> TX
这个时候只需要按一下UART0 交换按钮 ,他会自动切换RX TX,按一下马上就对上了,真的特别简单好用


已经打印信息了
启动信息分析
MTD分区信息

观察启动信息其中就有选项 0 可以进入u-boot

最后正常执行会到登录认证

但是我又不知道设置的啥密码,弱口令又进不去
先进入u-boot 再观察观察,重新上电 狂按0进入

按help 看看有没有可用指令

其实像tftpboot bootm md 等可利用的命令都挺多的,但是我们直接暴力绕过u-boot进入shell 才更方便,这里我们的思路是把u-boot传给内核系统执行的启动参数 bootargs 加个init=/bin/sh 绕过
简单描述下绕过流程
bootargs 是 U-Boot(引导程序)传给 Linux 内核的启动参数
u-boot --> 把内核加载到内存 --> 其中把bootargs传给内核读取执行 --> 执行init=/bin/sh --> shell
绕过u-boot改密码
setenv bootargs 'console=ttyS0,115200n8 init=/bin/sh'
mtkboardboot

进入shell ,接下来我们要做几件事
#挂载虚拟文件系统
mount -t sysfs sysfs /sys
mount -t tmpfs tmpfs /dev
#喂看门狗,不喂的话过了30秒就会重启后面挂载一系列操作就会白搞,所以先搞这个
#查看看门狗设备号
cat /sys/class/watchdog/watchdog0/dev

拿到251:0
mknod /dev/watchdog c 251 0
挂载 /proc 定位 rootfs_data 可写数据分区
mount -t proc proc /proc
cat /proc/mtd

找到对应的 mtd9 ,接下来 手动挂载 Overlay 文件系统并 chroot 进入真实系统环境
mount -t tmpfs tmpfs /tmp
mkdir -p /tmp/ov /tmp/root
# 创建 mtdblock9 设备节点并挂载 rootfs_data 分区(jffs2)到 /tmp/ov
mknod /dev/mtdblock9 b 31 9
mount -t jffs2 /dev/mtdblock9 /tmp/ov
#挂载 OverlayFS
mount -t overlay overlay -o lowerdir=/,upperdir=/tmp/ov/upper,workdir=/tmp/ov/work /tmp/root
chroot /tmp/root /bin/sh

sync && umount /tmp/root && umount /tmp/ov && sync
reboot -f
把内存中的缓存数据写入flash,然后卸载 前面挂载的俩个文件系统,然后再次同步写入flash 确保保存成功
提取固件

临时关闭内核日志输出,不然太烦了
echo 0 > /proc/sys/kernel/printk
配置网络
ip addr

可以看到网桥 br-wan 也就是 10.44.77.254 ,拿到路由器ip ,那么接下来根据10.44.77.0网段 去配置ip
windows主机

虚拟机
sudo ifconfig ens33 10.44.77.200/24 up

测试连通

查找固件
mount -t proc proc /proc
cat /proc/mtd

可以看到 我们需要的 mtd6 "firmware" 分区 15KB左右 ,接下来就是对这个分区
查看可使用的命令
ls /bin

有dd命令 配合 nc 传
dd if=/dev/mtd6 of=/tmp/firmware.bin bs=1M
#虚拟机
nc -l -p 1314 > firmware.bin
#路由器
nc 10.44.77.200 1314 < /tmp/firmware.bin

没毛,接下里binwalk 提取下文件系统
binwalk -Me firmware.bin

启动项分析
老规矩先从 /etc/inittab 看

系统初始化最先执行 /etc/init.d/rcS ,没有找到该文件再往下找/etc/rc.common

/etc/rc.d/S*,紧跟查看
/etc/rc.d/S50lighttpd

这里就能很明显发现前端是 /usr/sbin/lighttpd ,对应的配置文件是 /etc/lighttpd/lighttpd.conf
启动的方法是/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf ,这里都明确说明
再看下 /etc/lighttpd/lighttpd.conf

开始这部分 加载功能模块其中 mod_unifyframe 就是锐捷自定义模块,定义了网站根目录 /www/ ,包括默认首页
这里定义了 CGI 映射关系,当url匹配到 /cgi-bin/luci 默认 走 /www/cgi-bin/luci 执行 ,匹配到.lua 就交给 /usr/bin/lua 去解释执行。
当匹配到 路径单独匹配到 uri.path 和 requst.pathinfo 同时满足的话就由mod_unifyframe 模块接管处理。
现在针对 自定义模块进行查找下

init.d下面unifyframe-sgi

USE_PROCD=1 表示它运行在 procd 下,procd依赖 ubusd 总线 ,还得启动 ./sbin/ubusd
/usr/sbin/unifyframe-sgi.elf 后端主程序,建 socket
/usr/sbin/uf_ubus_call.elf ubus 代理
启动锐捷 Web 后端服务(unifyframe-sgi.elf + uf_ubus_call.elf),建立 socket 通信并支持 ubus 调用。
漏洞挖掘
直接真机测试


漏洞一
unifyframe-sgi.elf 内置插件模块 configChange 的 cc_set(0x414D58)→ uf_get_url_config 把用户提交的 data.url存在命令注入
把 /usr/sbin/unifyframe-sgi.elf 丢进ida,定位函数 module_init_configChange

这里看几个关键动作,它设置模块名为configChange ,然后绑定的是 sub_414D58 ,双击进入

获取三个参数 url ,module , function 的值 ,其中url的值给到string

这里往下走其实还是做了一丢丢检测 ,检查 string 是否包含 $( 或反引号。然后通过snprintf 进行拼接,最后交给ufm_popen也就是popen执行命令
POST /cgi-bin/luci/api/cmd?auth=这里填认证后的token HTTP/1.1
Host: 10.44.77.254
Content-Length: 202
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
Accept: application/json, text/plain, */*
Content-Type: application/json;charset=UTF-8
Origin: http://10.44.77.254
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
暂不公开
执行

漏洞二
dev_config user_list_note 模块 name 字段命令注入
首先先定位没有安全检测的
grep -c "param_safety_check" usr/local/lua/dev_config/*.lua

主要就是先看这些为0的,然后再结合是否有相关危险函数的
grep -n "os.execute\|io.popen\|luci.sys.exec\|luci.sys.call\|forkExec" \
usr/local/lua/dev_config/dev_mode.lua \
usr/local/lua/dev_config/hwnat.lua \
usr/local/lua/dev_config/mesh.lua \
usr/local/lua/dev_config/repeater_config.lua \
usr/local/lua/dev_config/repeater_hostrouter.lua \
usr/local/lua/dev_config/user_list_note.lua

这里其实就是找可控参数,像dev_mode.lua 几乎就是全是固定字符串的execute ,我们进一步缩小到比如 os.execute(cmd) 类似可控的位置去做分析,比如该漏洞 user_list_note.lua
gedit usr/local/lua/dev_config/user_list_note.lua

这里就有很明显命令注入的特征了,接下里就是着重去看下sync_list 也就是 child_data ,接着往上翻分析child_guard_packaged_data

这里就是说存在相同的mac 才 存入 child_data ,这样才满足能执行到下面 child_guard_sync 里面的 os.execute(cmd),那么就可以利用可控参数name进行注入。
接下里整个思路就是先注册一个儿童mac,然后利用相同的mac,进行name注入。现在第一步就需要去知道怎么注册
gedit ./usr/local/lua/dev_config/child_guard.lua
请求入口



这里有说明如果是已存在的mac只能注册一次只能改字段,不存在就table.inset 重新设置 ,
最好会生成写入到配置文件里 持久化

废话不多说先上exp
先注册
POST /cgi-bin/luci/api/cmd?auth=这里填认证后的token HTTP/1.1
Host: 10.44.77.254
Content-Length: 122
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
Accept: application/json, text/plain, */*
Content-Type: application/json;charset=UTF-8
Origin: http://10.44.77.254
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
暂不公开
后利用
POST /cgi-bin/luci/api/cmd?auth=这里填认证后的token HTTP/1.1
Host: 10.44.77.254
Content-Length: 157
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
Accept: application/json, text/plain, */*
Content-Type: application/json;charset=UTF-8
Origin: http://10.44.77.254
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
暂不公开

