We recently discovered two vulnerabilities in TP-Link’s WR841N V8 router that we exploited to obtain custom code execution on the router. After working closely with the vendor to patch the router’s firmware, we are disclosing the details of our work.
Our team conducts research into networked embedded devices in order to improve our product and spread security knowledge among the embedded device manufacturing and security communities. The WR841N is the same router model we use to teach students about hardware hacking in our classes, and the focus of our JTAG Explained blog post. During the process of our research into this router, we found a logic flaw in a configuration service which allowed us to circumvent its access controls and reset the router’s credentials (CVE-2017-9466). We then used our increased access to gain code execution by exploiting a stack overflow vulnerability available through the configuration service. In this proximity-based attack, we used a smartphone’s hotspot capability to reset the router’s credentials by taking advantage of a protocol that had been removed from the firmware for newer hardware models. Unfortunately, although older models may no longer be supported, they often remain in critical positions. Fortunately, TP-Link agreed to remove the configuration service from this model once we brought the issue to their attention.
We are sharing the details, step by step, in case our work sparks any ideas or discussion regarding proximity-based attacks, unsupported versions, logic flaws in encryption, or vulnerable configuration services.
Click here to skip to the full technical details, or read on for a high level summary of our work.
The first steps we took were to purchase the router, download the router’s firmware, and begin firmware analysis. We have previously discovered vulnerabilities in configuration services, so we specifically searched for, and found, such a service in the firmware. This service allowed a user on the network to read and write system settings. It was secured by requiring the arguments to commands to be encrypted with a key based on the username and password.
The method used to encrypt the arguments was DES, which encrypted the text in 8 character blocks. We soon recognized the logic flaw in using block encryption. Since we knew the plaintext version from the firmware, and we could retrieve the encrypted version of that same text from the router’s service, we could then copy the encrypted text and send it back to the router as a valid argument. In addition, not all commands required arguments, leaving those commands’ functionalities wide open to anyone on the network. Picture Hotspot settings on an iPhone To take advantage of this, we first found a command that took no arguments but returned predictable encrypted text. We copied the first 8 characters of the encrypted text to use as a signature. Then we set the name of a smartphone to the signature and turned on its hotspot capability. We sent a command (again requiring no arguments) over the network to trigger the router to search for nearby hotspots. After adding the word “init” to the end of the signature in the phone’s name, we requested the encrypted list of all discovered hotspots from the router and used the signature to find our entry in the list. We knew we would find the encrypted version of “init” in the 8 characters following the signature in our entry. This allowed us to use the encrypted “init” as an argument to a command that reset the router to its default settings, including the credentials to the default username and password.
Once we had reset the username and password, we encrypted the arguments ourselves rather than using the hotspot technique. If an attacker wanted to avoid alerting the user to the intrusion by doing something so obvious as resetting the password, they could continue using the hotspot technique to obtain the encrypted text needed for the next step of the exploit. For this, we exploited a stack overflow from another command and gained custom code execution on the router. To demonstrate our low level control, we used our code to blink a light on the router to say “Hi Senrio” in morse code. A real attacker could take advantage of this to exfiltrate data from an air-gapped network, or modify the router’s settings to reroute traffic to a malicious server.
Click here to skip to the demo video or read on for the full technical details.
We began by downloading the latest firmware for our hardware model from TP-Link’s support website and extracted the squashfs filesystem with binwalk.
After we removed the top of the router's case, we found a 4 pin header which gave us UART access to a password protected console. We tried cracking the password hash in the shadow file we found on the filesystem, but by the time we had found and exploited the vulnerabilities, our password cracker hadn’t discovered the cleartext. However, we were able to interrupt the boot process, and drop into a console designed to accept updates for the firmware and filesystem from an external FTP server. We made a copy of the extracted squashfs file system, modified the shadow file to give the device a new root password, then updated the router’s filesystem from our FTP server to gain access to the root console. This allowed us to observe debug printouts and export core dumps for analysis when our target process crashed.
Since we’ve seen a trend of vulnerable configuration services, we searched the extracted filesystem for code that called recvfrom() and sendto(), good indicators of a UDP service. After a short search, we found tddp, a configuration service buried as a task in the webserver, which listens for commands that allow a user on the network to read and write system settings.
The binary included the names for most of tddp’s functions, as well as frequent debug prints, which helped us considerably when reverse engineering the service. We found two parsing functions which handled different classes of commands. Class 1 handled a few simple commands with plaintext arguments and Class 2 handled system level commands with encrypted arguments.
We reversed the protocol and determined that Class 1 commands were not useful to an attacker. We went on to make a list of all the available Class 2 commands, which included commands that set the Wide Area and Local Area Network configurations, changed the system mode, and retrieved a list of all nearby access points. Picture The two command parsers The arguments to Class 2 commands were encrypted with DES, a symmetric-key block cipher which encrypts in 8 byte blocks. We reversed the function which returned the encryption key, and found that it generated the key from the first 8 bytes of the unsalted md5 of the concatenated username and password. This was intended to prevent anyone but the router’s owner from running these commands.
We used the hotspot capability on a cellphone to execute a proximity attack in combination with sending a series of commands to gain access to all Class 2 commands. The Class 2 SetUserConfig command provides write access to the router’s entire configuration, so we could have done any number of things, such as change the DNS to a malicious server, or pivot to attack other devices on the network. However we wanted to demonstrate in the most obvious way possible that we could exercise the commands without knowing the credentials and gain control over the router. Picture sysCommand() looking for the argument “init,” “save,” or “reboot” We had discovered a stack overflow vulnerability in an argument parsing function. At this point we were unable to exploit it because the overflow data came from command arguments which needed to be properly encrypted. To get around this requirement, we decided to reset the router to its factory default settings, which included resetting the users credentials to the default username and password. To do this, we targeted the command sysCommand, which reset the router when it received the encrypted version of the word init.
Because DES is a symmetric-key cipher and encrypts in 8 byte blocks, it is not a good mechanism to protect command arguments, since they can easily be copied and re-used against the target. One command, getAPList, returned the encrypted names of nearby access points, so we set up our own access point using a cellphone’s hotspot capability. Picture Hotspot settings on an iPhone We used the output of the command getPhyLinkStatus as a signature to identify our phone’s entry in the list of access points. getPhyLinkStatus required no arguments and always returned an encrypted string beginning with the 8 characters “wan_ph_l.” We turned on the router’s search mode with another command that did not require arguments and set the phone’s name to the plaintext “wan_ph_l” with padding to make it 8-byte aligned in the access point list. We queried getAPList and easily identified our phone’s name by searching for the encrypted version of “wan_ph_l.” We updated the phone’s name by placing the string init immediately after “wan_ph_l” and retrieved the encrypted init from getAPList to send into sysCommand and reset the router.
In short, the steps to reset the router to its factory default settings were as follows: 1. Retrieved encrypted “wan_ph_l” from getPhyLinkStatus 2. Set up nearby access point with the plaintext name “wan_ph_l” + “init” with padding for alignment 3. Enabled the access point search interface with the activateAth0 command 4. Requested the access point status list with getAPList and save the encrypted “init” 5. Sent sysCommand the retrieved encrypted “init” To take this a step further, if we wanted to gain access to the system without requiring the access point to be in range, we could generate a rainbow table with “wan_ph_l” encrypted with common usernames and passwords to discover the username and password that generated the hash. If it matched, we would immediately gain full access to the service.
During the course of our analysis, we discovered a stack overflow in a function that processed incoming data. Having gained the ability to generate encrypted arguments, we could finally exploit it. Because the patch prevents a remote attacker from reaching this vulnerability, TP-Link declined to patch it. Despite the low risk to users, since it remains un-patched and there are no plans to patch it, we have decided not to disclose the details at this time.
We gained code execution and chose to have our shellcode blink an LED to say “Hi Senrio” in morse code, demonstrating our low level control of the router. A real attacker could take advantage of this to exfiltrate data from an air-gapped network, or modify the router’s settings to reroute traffic to a malicious server.
We learned that this hardware model had been discontinued when we reported the vulnerability to TP-Link because there was no indication on the site that earlier models had been discontinued. TP-Link agreed to remove the service from our hardware model’s firmware revision, which was great news. At the time we reported the vulnerability, Shodan displayed at least 93,328 users of WR841N routers world wide, and while there’s no way to tell, it seems highly unlikely that they are all running the latest firmware on the newest hardware revision.
This drove home an important point for us. Across industries, we are dependent on legacy hardware because industries, companies, and consumers cannot afford the time, effort, and cost to replace old versions with the latest models. While TP-Link provided a patch for this out-dated model, we need new and innovative ways to protect these outdated devices.
The video below shows a demo of the complete exploit. At the end be sure to watch for the LED on the far right blinking and saying hello!