Pwning the TP-Link AX1800 WiFi 6 Router: Uncovered and Exploited a Memory Corruption Vulnerability

In preparation for the Pwn2Own Toronto 2022 hacking contest organized by the Zero Day Initiative, Rocco Calvi (@TecR0c) from TecSecurity dedicated his efforts to uncovering remote code execution vulnerabilities and crafting the corresponding exploits. Pwn2Own is a prestigious competition that rewards security researchers who demonstrate these skills against various targets. Discovered vulnerabilities are then shared with the appropriate vendors to enhance security.


Regrettably, we could not participate in the Pwn2Own competition due to the requirement for a physical flash drive to be connected to the target device. Nonetheless, we made a valuable contribution to the event by coordinating the disclosure of a vulnerability we discovered in a router's secure sharing feature with the vendor. This feature, based on the DLNA standard, enables users to share media such as music, photos, and videos across a home network using the MiniDLNA service (formerly known as ReadyMedia).


Although exploiting this vulnerability requires either physical access to the router or LAN access, it can still be utilized to compromise a victim's device or facilitate a jailbreak. It's important to note that users may have physical access to their home router through the USB port, for example, when using Airbnb or Couchsurfing to play their own media. However, an attacker with LAN access could also exploit this vulnerability, potentially compromising the security of the home network if a USB device is connected.


To mitigate this vulnerability, users must be aware of the potential risks and adopt precautionary measures such as keeping their router's software updated with the latest security patches and disconnecting unnecessary USB devices. The Pwn2Own contest serves as a vital reminder of the importance of staying vigilant about device and software security. By participating in coordinated disclosure, we aim to contribute to the ongoing efforts to improve security for all users.


In summary, my experience preparing for the Pwn2Own 2022 contest in the router category provided a valuable learning opportunity and enabled us to positively impact the field of cybersecurity by contributing to the broader effort of improving security for all users. We look forward to participating in similar events in the future.

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

Impact

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.

Vulnerability walkthrough

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

....

db_dir=/mnt/sda1/.TPDLNA

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[0]", which is the first element in the array. Similarly, the variable "dlna_pn" is assigned the value of "argv[20]", which is the 21st element in the array.

static int

callback(void *args, int argc, char **argv, char **azColName)

{

    struct Response *passed_args = (struct Response *)args;

    char *id = argv[0], *parent = argv[1], *refID = argv[2], *detailID = argv[3], *class = argv[4], *size = argv[5], *title = argv[6],

         *duration = argv[7], *bitrate = argv[8], *sampleFrequency = argv[9], *artist = argv[10], *album = argv[11],

         *genre = argv[12], *comment = argv[13], *nrAudioChannels = argv[14], *track = argv[15], *date = argv[16], *resolution = argv[17],

         *tn = argv[18], *creator = argv[19], *dlna_pn = argv[20], *mime = argv[21], *album_art = argv[22];

    char dlna_buf[128];

    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").

case ESonyBravia:

/* 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)

Therefore, to exploit a stack-based buffer overflow, we can use a technique known as "return-oriented programming" (ROP). This involves chaining together small snippets of code, known as "gadgets," that are already present in the program's memory, in order to perform a desired action. By carefully selecting gadgets that perform specific task, such as moving values into registers or calling functions, an attacker can create a payload that executes their own code.

In this case, the gadget that we have chosen is located at address 0x00015ed4, and it consists of a single instruction: "add r0, sp, #0x180." This instruction adds the value 0x180 (384 in decimal) to the value of the "sp" register (which stands for "stack pointer"), and stores the result in the "r0" register. The "sp" register points to the top of the stack, so this instruction moves the stack pointer down by 384 bytes.

The next instruction in the gadget is a "bl" instruction, which stands for "branch with link." This instruction causes the program to jump to a specified address and store the return address in the link register (LR). In this case, the address being jumped to is the "system" function, which is a C library function that executes a command specified in the "__command" argument.

By leveraging this gadget, we can execute a command of our choosing by providing the appropriate argument in the controlled data that we place on the stack. In this case, we have chosen to execute the "system" function with a command of "AVC_TS_HP_HD_AC3," which is a string of data that we have included in our payload.

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.

Conclusion

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.

References