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
一般为0
struct 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