Talos Vulnerability Report


Asuswrt and Asuswrt-Merlin New Gen httpd unescape memory corruption vulnerability

July 27, 2022
CVE Number



A memory corruption vulnerability exists in the httpd unescape functionality of Asuswrt prior to and Asuswrt-Merlin New Gen prior to 386.7.. A specially-crafted HTTP request can lead to memory corruption. An attacker can send a network request to trigger this vulnerability.


The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Asuswrt-Merlin New Gen prior to 386.7
Asus routers with firmware prior to:
XT8 -
TUF-AX3000_V2 -
XD4 -
ET12 -
GT-AX6000 -
XT12 -
RT-AX58U -
XT9 -
XD6 -
GT-AX11000 PRO -
GT-AXE16000 -
RT-AX86U -
RT-AX68U -
RT-AX56U -
RT-AX82U -
RT-AX55 -
GT-AX11000 -


Asuswrt-Merlin New Gen - https://github.com/RMerl/asuswrt-merlin.ng


5.3 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N


CWE-787 - Out-of-bounds Write


The Asuswrt-Merlin New Gen is an open source firmware alternative for Asus routers.

The Asuswrt-Merlin New Gen’s httpd component, has a file named cgi.c that contains CGI helper functions. One of these functions is unescape:

unescape(char *s)
    char s_tmp[65535];
    unsigned int c;

    while ((s = strpbrk(s, "%+"))) {                                                                    [1]
        /* Parse %xx */
        if (*s == '%') {
            sscanf(s + 1, "%02x", &c);                                                                  [2]
            *s++ = (char) c;                                                                            [3]
            strlcpy(s_tmp, s + 2, sizeof(s_tmp));                                                       [4]
            strncpy(s, s_tmp, strlen(s) + 1);                                                           [5]
        /* Space is special */
        else if (*s == '+')
            *s++ = ' ';

This function takes as argument a string. If URL-encoded, this function will decode it. At [1], a loop takes the next % or + in the string. If a % is found, then at [2], the two characters following it are converted from hex values to a single character. At [3] the converted character replaces the % character and the string pointer advances. At [4] and [5], the string after the already-parsed URL-encoded character is moved left by two positions. This will replace the parsed characters. A string like “A…B%41%42” would go through the following steps:

|A|...|B|%|4|1|%|4|2|NULL|                       at    [1]/[2]
|A|...|B|A|4|1|%|4|2|NULL|                       after [3]
|A|...|B|A|%|4|2|NULL|NULL|NULL|                 after [5]

Eventually, after a second iteration of the loop, the string would end up like this:

|A|...|B|A|B|NULL|NULL|NULL|NULL|NULL|           after [5]

The unescape function assumes, wrongly, that after a % there are always at least two characters. If this is not the case, the instruction at [4] would cause an out-of-bounds read, and the one at [5] could cause the removal of the null terminator. This removal, in the next loop iteration, could cause an out-of-bounds write. Let’s take for instance the following string “A…B%a”. This would go through the following steps:

|A|...|B|%|a|NULL|Q|Q|Q|Q|Q|                      at    [1]/[2]
|A|...|B|\n|a|NULL|Q|Q|Q|Q|Q|                     after [3]
            ^ s points to 'a'                     after [3]

After the string there is other data, in this scenario the string “QQQQQ”, s+2, at [4], which will point to the first Q. So after [4] the string will look like:

|A|...|B|%|a|NULL|Q|Q|Q|Q|Q|                      at    [1]/[2]
|A|...|B|\n|a|NULL|Q|Q|Q|Q|Q|                     after [3]
                   ^ s+2 point to the first Q     at [4]
|A|...|B|\n|Q|Q|Q|Q|Q|Q|Q|                        after [5]

The result would be the string “A…B\nQQQQQQQ…”.


2022-04-11 - Vendor Disclosure
2022-04-11 - Initial Vendor Contact
2022-07-13 - Vendor Patch Release
2022-07-27 - Public Release


Discovered by Francesco Benvenuto of Cisco Talos.