DISCLAIMER: The author is not responsible for the misuse of the information contained in this post.
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:
content path right after
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.
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.
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.
POST /ui/dboard/storage/storageusers/addstorageusers HTTP/1.1
The response being the following where you can see the payload being reflected.
HTTP/1.1 200 OK
Date: Thu, 24 Jan 2019 11:19:25 UTC
Server: HTTP Server
<!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">
<title>Residential Gateway - D-Link</title>
<div class="formField" id="password">
<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>
fieldPassword('password', 'passwordShow', 1, 0);
<span class='texterror'>Groups not configured</span>
Which ultimately shows the canonical alert box.
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.
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:
POST http://192.168.1.1/jrd/webapi HTTP/1.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:
POST http://192.168.1.1/jrd/webapi HTTP/1.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.
HTTP/1.1 200 OK
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 …
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
1 and boom … you are now logged in.
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.