Security Advisories: D-Link DSL-2640B

Saturday, Mar 28, 2020


In a previous post we shared our considerations on the impact of vulnerabilities in Internet connected devices that are EoL. We used the vulnerabilities that we identified in the D-Link DSL-2640B DSL gateway as a use case to support our considerations. In this post we describe the technical details of these vulnerabilities.

Before we dive into the technical details, it's important to note that:

  • all vulnerabilities are (at least) applicable to the D-Link DSL-2640B (HW revision B2, Firmware version: EU_4.01B)
  • all vulnerabilities apply to the latest available firmware (as of 27/03/2020)
  • all vulnerabilities have been reported to D-Link
  • we are not aware of any security fix released by D-Link
  • as the device is EoL, following D-Link's policy, no fix may ever be available

The vulnerabilities described in this post may apply to other hardware revisions, other firmware versions and even completely different models. We did not investigate this further and D-Link did not provide any additional insights.

The following vulnerabilities are described in this post:

We hope we provided sufficient technical details of the identified vulnerabilities. Additional information (e.g. video demonstrations) may be provided in the future.

We hope you enjoy the technical remainder of this post! :)

This vulnerability allows retrieval of the administrative password by sending a specific UDP packet to port 65002 of the device.

An attacker connected to the WiFi or the local LAN, or who is able to reach the internal device interface in any other way, can retrieve the device password with a single UDP request.

Most functionality of the device, including the administration panel and the web server, are implemented in a single process named cfm executed at startup. The cfm process listens on UDP port 65002. Likely to support device configuration from a dedicated application. The screenshot below, shows the function implementing the communication protocol. The function name, pcApplication, is taken directly from the binary's symbols.

Communication is done using D-Link's proprietrary protocol for which no information is publicly available. Reversing the cfm binary yields the following structure for the protocol packet.

Several commands are supported and accessible by specifying the command code in the 2 bytes cmd field. Communication is in plaintext and without authentication. The code only checks, for some commands, that the provided MAC address matches the device MAC address.

The command \x00\x01 allows retrieving system information from the device, including the device administrative password which is returned in plaintext. For example:

python -c 'print("\x00\x01"* 5)' | nc -u 192.168.1.1 65002
####MAC_ADDRESS####<boardID=D-4P-W><sysVersion=EU_3-10-02-3B00.A2pB022g2.d20h>
<sysModel=DSL-2640B><local_username=admin><local_password=password>
<local_ipaddress=192.168.1.1>

The MAC address check mentioned earlier is not performed for the \x00\x01 command. Any additional bytes are completely ignored which allowed us to identify the vulnerability in a very trivial manner.

In fact, we found this vulnerability using a rather dumb fuzzing campaign. To start, we simply piped /dev/urandom into UDP port 65002. Obviously, we did not think this approach would yield vulnerabilities in any way. Especially because no traffic monitoring, no payload selection and no target debugging were in place. However, surprisingly, the device kindly returned the administrative password within a few minutes…

time cat /dev/urandom | nc -u 192.168.1.1 65002
&ZLM���<boardID=D-4P-W><sysVersion=EU_3-10-02-3B00.A2pB022g2.d20h>
<sysModel=DSL-2640B><local_username=admin><local_password=a>
<local_ipaddress=192.168.1.1>^C

real    2m53.240s
user    0m0.599s
sys 0m21.439s

Due of the (very) forgiving implementation, any UDP packet with \x00\x01 at the right location would return the administrative password. Even our initial dumb approach yielded the administrative password within minutes.

Our initial test identified the vulnerability is exploitable from the LAN. Nonetheless, the service seems to be listening on all the interfaces (see below).

Unfortunately, we were unable to verify the vulnerability on the WAN side as we lacked a suitable DSL connection. Using the information we have, we cannot exclude that the vulnerability is also exploitable on WAN side. Of course, we would be happy to hear more insight on this.

For this vulnerability we identified is a hard-coded user account. An attacker may use these credentials to login into the device in order to perform administrative tasks.

The vulnerability was identified by analyzing the authentication process accessible via the web interface. While the cfm process provides the communication "plumbing", the actual authentication is delegated to an external library libpsi.so. The library uses an object-oriented approach for handling the authentication credentials and the incoming authentication requests.

An analysis of the cfm process revealed a code path supporting authentication for the user named user.

The default passwords used for authentication are hard-coded in the libpsi.so binary.

Reverse engineering this library told us that the following default credentials can be used for logging into the web interface of the device.

Username: user
Password: 00202b004720

Although the password of the user user can be changed, no web interface control is provided for modifying it. Therefore, this password will be set to its default state for the entire life time of the device. It is important to note that the account is valid for authenticating to any service relying on lipsi.so, such as ftp, telnet and ssh. As far as we know, the user user has similar capabilities as the admin account.

Interestingly, even though libpsi.so is only released in binary format, the credentials are clearly visible in the GPL source code of the device (see picture below).

Also, interestingly, these credentials are referred to as ASUS_USER_ACCOUNT. One may wonder how an ASUS related account ends up in a D-Link device.

The source code itself does not reveal any intention of obfuscation. The credentials seem to be valid for other ASUS devices as well. ASUS refers to them as the ASUS Super account in some old pages.

One could say that it's basically an ASUS feature ( :-) ) which ended up for some unknown reason in a D-Link device. The supply chain of IoT devices are definitely not devoid any mysteries. We believe it may be the result of some "supply chain magic" rather than malicious intent.

The last remark we would like to make is that this vulnerability may be exploited through browser pivoting. A malicious website, visited with any device connected to the WiFi/LAN, may perform crafted requests towards the gateway.

This vulnerability allows an attacker to reset the device to its default configuration by accessing a specific URL. No authentication is required.

In fact, the following URLs can be accessed without authentication.

  • rebootinfo.cgi
  • ppppasswordinfo.cgi
  • qosqueue.cmd?action=savReboot
  • restoreinfo.cgi

Specifically, the device can be reset to default factory configuration by simply requesting the following URL:

http://<device_IP_address>/restoreinfo.cgi 

An attacker may reset the administrative password to its default value admin, log in and perform any administrative tasks on the device, such as upload of malicious firmware or configuration of malicious DNS servers.

While the exploitation of this vulnerability requires access to the device LAN interface, it can also be remotely exploited via browser pivoting. An attacker in control of a malicious website may blindly reset the configuration of the device and, under some conditions, take full control of the device.

The CVE-2020-9277 vulnerability allows bypassing the authentication process for authenticated resources. An attacker may be able to directly access administrative functions of the web interface, without the need to supply valid credentials.

The web server first identifies (1) whether the requested URL requires authentication. The check is carried on by analyzing the requested file extension, located at the end of an URL. For instance, accessing administrative cgi modules requires authentication. The authentication is not performed immediately but delayed at a later point in the code.

The code then identifies (2) special resources for which the authentication is not necessary. Examples are images or Javascript utilities. The code matches the start of an URL with the specific string (e.g: /images/, utils.js) using the strncmp() function. If a match is found, no authentication is performed, regardless of the outcome of (1). The request is then further processed as it had been successfully authenticated.

Finally, if a cgi module is requested, the do_cgi() function determines (3) the module to be executed by searching the module name anywhere in the URL, using the strstr() function.

All the above checks act in isolation, no state is carried over. An attacker can then craft malicious URLs for bypassing authentication for cgi modules. The attack URL below changes the device admin password to newpass without any need for authentication:

Original URL: http://<device_IP_address>/redpass.cgi?sysPassword=newpass
Attack URL: http://<device_IP_address>/images/redpass.cgi?sysPassword=newpass

This vulnerability gives an attacker full device control and allows performing unauthenticated administrative functions. This vulnerability requires accessing the device LAN interface, but it is suitable for exploitation via browser pivoting, allowing for remote attacks over the Internet.

This vulnerability is a buffer overflow occurring in the do_cgi() function, while parsing the requested cgi module name. An attacker may execute arbitrary code on the device with administrative privileges, by supplying a malicious cgi module name in the URL.

The do_cgi function, in order to identify the module to be executed, copies the module name on the stack. However, it does not check whether the length of the supplied module name fits in the allocated buffer (see picture).

A long cgi module name will overwrite the return address saved on the stack, allowing for a classical stack buffer overflow which is easy to exploit.

The do_cgi() function can, in principle, only be accessed after authentication. However, unauthenticated exploitation of this vulnerability is possible by combining it with CVE-2020-9277. In the picture below we exploit this vulnerability without authentication, execute a reverse shell payload.

While the vulnerability is potentially exploitable via browser pivoting, exploitation may not be trivial due to the URL mangling introduced by the browser when applying URL encoding on the outgoing requests.