Pwning the TP-Link AX1800 WiFi 6 Router: Uncovered and Exploited a Memory Corruption Vulnerability
Details of the Vulnerability
A vulnerability has been discovered in the TP-Link AX1800 WiFi 6 Router Archer AX20(EU) that allows remote attackers to execute malicious code on the device. The vulnerability exists in the ".TPDLNA/files.db" database file, which is created on the USB device when Media Sharing is toggled to enabled on the router. By default, Samba for Windows and Local FTP are enabled, and Media Sharing is enabled on the device, which means that the MiniDLNA, ProFTPd, and Samba services will start automatically for the USB share.
The vulnerability was discovered in version 1.1.2 of the MiniDLNA service in firmware version 2.1.6 Build 20220128 rel.15823(4555) of the Archer AX20 router. An attacker with access to the router's media server via samba or FTP could exploit the vulnerability by executing an SQL query against the details table in the ".TPDLNA/files.db" database file and providing attacker-controlled data as a result. If the MIME type meets certain criteria, the data is copied to a fixed-size buffer on the stack, potentially leading to a stack-based buffer overflow and allowing the attacker to gain remote code execution. This vulnerability was patched in firmware version Archer AX20(EU)_V3_1.1.4 Build 20230219. It is possible that this vulnerability may exist in later versions of the MiniDLNA service as well.
USB Storage Device settings
The vulnerability in the TP-Link AX1800 WiFi 6 Router Archer AX20(EU) can allow an attacker to execute arbitrary code on the device, potentially leading to unauthorized access and control of the router. This can pose a significant risk to users, as an attacker could use this vulnerability to perform malicious actions on the device or to gain access to sensitive information.
In this blog post, we will be discussing a vulnerability that we discovered in MiniDLNA, a media server used by many routers to share media such as music, photos, and videos over a home network. While testing a router, we noticed that it was using an outdated version of MiniDLNA (version 1.1.2), but we found that this vulnerability is still present in the latest code base.
During our static code analysis of the file "minidlna-1.1.2/upnpsoap.c," we identified a vulnerability that resulted from improper bounds checking. This occurs when a program fails to properly verify the size of a buffer before writing to it, which can lead to memory corruption and potentially allow an attacker to execute arbitrary code. This vulnerability was previously identified by Zachary Cutlip, but it was not properly addressed by the vendor. The vendor assumed that the vulnerability was not reachable by an attacker and therefore did not patch it, which is why it remains present in the latest version of MiniDLNA.
Upon further investigation, we discovered that the vulnerability in MiniDLNA is caused by the fact that the "files.db" file can be modified by remote attackers through the shared USB device on the router. This is because the "db_dir" property in "/tmp/minidlna.conf" is set to a location that can be accessed and modified via SMB or FTP. This means that an attacker with access to the USB share can modify the ".TPDLNA/files.db" file and potentially inject malicious code.
root@Archer_AX20:~# cat /tmp/minidlna.conf
# this file is generated automatically, don't edit
In the code snippet below, the function sqlite3_exec is being called with several arguments. The third argument, callback, is a function pointer to a function that will be called for each row of the result set returned by the SQL statement. This function is usually used to process the data returned by the query. The fourth argument, (void *) &args, is a pointer to a user-defined data structure that can be used to pass additional information to the callback function. The final argument, &zErrMsg, is a pointer to a string that will be used to store any error message that may be generated during the execution of the SQL statement.
ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);
The purpose of the callback function is to process the results of a database query. The "argv" argument is an array of strings that contains the values for each row of the query result, and the "azColName" argument is an array of strings that contains the names of the columns in the query result.
The code then defines several variables and assigns them the values of the corresponding elements in the "argv" array. For example, the variable "id" is assigned the value of "argv", which is the first element in the array. Similarly, the variable "dlna_pn" is assigned the value of "argv", which is the 21st element in the array.
callback(void *args, int argc, char **argv, char **azColName)
struct Response *passed_args = (struct Response *)args;
char *id = argv, *parent = argv, *refID = argv, *detailID = argv, *class = argv, *size = argv, *title = argv,
*duration = argv, *bitrate = argv, *sampleFrequency = argv, *artist = argv, *album = argv,
*genre = argv, *comment = argv, *nrAudioChannels = argv, *track = argv, *date = argv, *resolution = argv,
*tn = argv, *creator = argv, *dlna_pn = argv, *mime = argv, *album_art = argv;
const char *ext;
struct string_s *str = passed_args->str;
int ret = 0;
This code is part of a function that appears to process data related to a DLNA (Digital Living Network Alliance) profile. The DLNA is a group of companies that work together to create a set of standards and guidelines for sharing digital media over home networks.
The code begins with a "case" statement, which is a control flow statement that allows a program to execute different branches of code based on the value of a given expression. In this case, the "case" statement is checking the value of a variable called "ESonyBravia". If the value of this variable matches the case label "ESonyBravia", the code within the case block will be executed.
The code within the case block contains an "if" statement that checks whether the "dlna_pn" variable is non-NULL and whether the first 16 characters of the string stored in "dlna_pn" match one of three specific strings: "AVC_TS_MP_SD_AC3", "AVC_TS_MP_HD_AC3", or "AVC_TS_HP_HD_AC3".
If the "if" statement evaluates to true, the code inside the "if" block will be executed. This code uses the "sprintf" function to create a new string called "dlna_buf". The "sprintf" function formats a string and stores it in a character array. In this case, the function is creating a new string by concatenating the string "DLNA.ORG_PN=AVC_TS_HD_50_AC3" with the characters in "dlna_pn" starting at the 17th character (i.e., "dlna_pn + 16").
/* BRAVIA KDL-##*X### series TVs do natively support AVC/AC3 in TS, but
require profile to be renamed (applies to _T and _ISO variants also) */
if( dlna_pn &&
(strncmp(dlna_pn, "AVC_TS_MP_SD_AC3", 16) == 0 ||
strncmp(dlna_pn, "AVC_TS_MP_HD_AC3", 16) == 0 ||
strncmp(dlna_pn, "AVC_TS_HP_HD_AC3", 16) == 0))
sprintf(dlna_buf, "DLNA.ORG_PN=AVC_TS_HD_50_AC3%s", dlna_pn + 16);
add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
resolution, dlna_buf, mime, detailID, ext, passed_args);
If certain conditions are met, the code copies the value of the "dlna_pn" variable to a fixed-size buffer on the stack, which is where the memory corruption vulnerability occurs. If the "dlna_pn" variable contains more characters than the buffer can hold, it will cause a stack-based buffer overflow, allowing an attacker to overwrite the stack and potentially execute arbitrary code on the router.
Exploiting a vulnerability in the TP-Link AX1800 WiFi 6 Router requires a few steps. In this blog post, we'll outline the process we followed to successfully execute an exploit on the router.
First, we encountered a memory corruption vulnerability when the "dlna_pn" variable was copied to a fixed-size buffer on the stack. If the "dlna_pn" variable contained more characters than the buffer could hold, it resulted in a stack-based buffer overflow, allowing an attacker to overwrite the stack and potentially execute arbitrary code on the router.
To overcome the exploit mitigations of ASLR and the NX bit, as well as the null byte restriction imposed by sprintf, we found a one gadget that allowed us to redirect execution to a single instruction despite the null byte limitation. This allowed us to demonstrate RCE capability by redirecting execution to the following location:
00015ed4 06 0d 8d e2 add r0,sp,#0x180
00015ed8 cb f6 ff eb bl <EXTERNAL>::system int system(char * __command)
We were able to successfully exploit the vulnerability by creating a database connection, uploading the "files.db" file, and making a web request to the ContentDir. This allowed us to disclose files, such as "/etc/shadow", or gain a remote shell.
In conclusion, exploiting a vulnerability in the TP-Link AX1800 WiFi 6 Router requires a deep understanding of the underlying software and the ability to bypass various exploit mitigations.
Proof of Concept
As exploit writers, our goal is to weaponize vulnerabilities to demonstrate their severity and leave no question of doubt. In this case, we worked together to create a piece of code that would exploit a security flaw in a router and demonstrate the vulnerability to the Zero Day Initiative team.
Our first step was to create a function called insert_into_db that would allow us to insert data into the database. This function took a database connection, a buffer of data, and an object ID as arguments.
We also needed a way to upload the database file to the target, so we created two functions for this purpose: upload_via_smb and upload_via_ftp. Both functions took a target and a database file as arguments and used different protocols (SMB and FTP, respectively) to achieve the same goal.
To verify that the target was vulnerable before sending the exploit, we added a step to the code that performs a hash check on a remotely served JPG file from the router.
To make the exploitation attempt even more effective, we created a payload using the build_payload function. This payload was a string of data that was carefully crafted to exploit the vulnerability.
Before we could use the payload, we had to make sure that the database had the correct structure. To do this, we created the create_db_structure function, which created the objects, details, and album_art tables.
Once the vulnerability in the MiniDLNA service was identified, the next step was to exploit it. This was accomplished using the trigger_overflow function, which took a target IP address and an object ID as arguments. When called, the function sent a request to the target device using the HTTP protocol on port 8200, specifically targeting the /ctl/ContentDir resource.
The request included a specially crafted header with a specific value for the x-av-client-info field. This value was designed to trigger the vulnerability in the MiniDLNA service and potentially allow an attacker to gain access to the device or service.
Overall, our thought process when writing this code was to reliability exploit the specific vulnerable router and gain a remote interactive shell. We created this code to demonstrate the security flaw we had discovered and to participate in the pwn2own competition. It was a challenging process, but the end result was worth it.
It is important to note that this code is just an example and should not be used without a thorough understanding of its effects and any potential legal consequences. It is also essential to ensure that any systems you are responsible for are properly secured and updated to prevent exploitation.
In conclusion, the vulnerability discovered in the TP-Link AX1800 WiFi 6 Router Archer AX20(EU) could allow an attacker to execute arbitrary code on the device, potentially leading to unauthorized access and control of the router. This vulnerability is caused by an improper bounds checking in the MiniDLNA service, which could lead to a stack-based buffer overflow and remote code execution. While this vulnerability requires either LAN access or physical access to the router, it is important for users to be aware of the potential risks and take precautions, such as keeping their router's software up to date with the latest security patches and disconnecting any unnecessary USB devices. The Pwn2Own contest serves as an important reminder of the importance of staying vigilant about IoT and software security, and we hope that our participation in coordinated disclosure will contribute to the ongoing efforts to improve security for all users.
It is recommended that users of the TP-Link AX1800 WiFi 6 Router Archer AX20(EU) download and install the latest firmware version from https://www.tp-link.com/en/support/download/archer-ax20/#Firmware. We were successful in doing coordinated disclosure with TP-Link to ensure that the vulnerability was addressed and patched in a timely manner.