1 00:00:00,060 --> 00:00:01,140 - Greetings. 2 00:00:01,140 --> 00:00:04,080 Welcome to Track Four. 3 00:00:04,080 --> 00:00:08,460 This is the 3:30 PM talk. 4 00:00:08,460 --> 00:00:09,810 This is James Kettle. 5 00:00:09,810 --> 00:00:13,984 He's gonna talk about browser-powered desync attacks. 6 00:00:13,984 --> 00:00:16,984 (audience cheering) 7 00:00:20,550 --> 00:00:22,170 - Thank you. 8 00:00:22,170 --> 00:00:23,130 Good afternoon. 9 00:00:23,130 --> 00:00:25,653 And welcome to Browser-Powered Desync Attacks. 10 00:00:26,760 --> 00:00:30,210 Have you ever had an idea and then just dismissed it 11 00:00:30,210 --> 00:00:34,221 because there's no way that that would ever work? 12 00:00:34,221 --> 00:00:37,500 Three years ago I thought, wouldn't it be cool 13 00:00:37,500 --> 00:00:40,980 if you could make browsers launch desync attacks. 14 00:00:40,980 --> 00:00:45,780 That would enable some really interesting possibilities. 15 00:00:45,780 --> 00:00:48,141 But I figured that there was no way 16 00:00:48,141 --> 00:00:51,870 that any web server was doing something foolish enough 17 00:00:51,870 --> 00:00:53,343 to make that possible. 18 00:00:54,510 --> 00:00:57,240 This year I discovered, I was completely wrong. 19 00:00:57,240 --> 00:01:00,180 You can achieve all manner of interesting things. 20 00:01:00,180 --> 00:01:02,583 And in this session, I'll show you how. 21 00:01:03,690 --> 00:01:05,130 Like many discoveries, 22 00:01:05,130 --> 00:01:07,444 the journey that led to it was quite windy. 23 00:01:07,444 --> 00:01:10,483 It started in late 2019 when there was a spate 24 00:01:10,483 --> 00:01:13,270 of request smuggling false positives, 25 00:01:13,270 --> 00:01:18,093 even including an incorrect CVE being issued for NGINX. 26 00:01:18,990 --> 00:01:21,660 I had a look at what the cause of this issue was 27 00:01:21,660 --> 00:01:24,360 and declared that there was a simple solution. 28 00:01:24,360 --> 00:01:29,220 All you had to do was never reuse HTTP/1 connections 29 00:01:29,220 --> 00:01:31,856 when looking for request smuggling. 30 00:01:31,856 --> 00:01:34,410 And after that, well, everything was wonderful 31 00:01:34,410 --> 00:01:37,440 until last year when I realized that 32 00:01:37,440 --> 00:01:39,930 connection locked request smuggling was a thing 33 00:01:39,930 --> 00:01:41,250 and the only way 34 00:01:41,250 --> 00:01:45,627 to find that is to always reuse HTTP/1 connections. 35 00:01:45,627 --> 00:01:47,280 (audience laughing) 36 00:01:47,280 --> 00:01:49,530 So at this point we had a bit of a problem. 37 00:01:49,530 --> 00:01:52,470 And this year, I set out to tackle this. 38 00:01:52,470 --> 00:01:54,720 And after quite a lot of work, 39 00:01:54,720 --> 00:01:58,500 I eventually found that by slowing things down 40 00:01:58,500 --> 00:02:01,290 and paying really close attention 41 00:02:01,290 --> 00:02:03,990 to the exact sequence of events, 42 00:02:03,990 --> 00:02:07,140 I could reuse connections and still distinguish 43 00:02:07,140 --> 00:02:10,143 genuine vulnerabilities from false positives. 44 00:02:11,010 --> 00:02:11,910 And that was nice, 45 00:02:11,910 --> 00:02:16,380 but sometimes when you pay extra close attention to things, 46 00:02:16,380 --> 00:02:19,026 you find more than you bargained for. 47 00:02:19,026 --> 00:02:21,870 And on one website, 48 00:02:21,870 --> 00:02:24,000 I noticed something just wasn't quite right. 49 00:02:24,000 --> 00:02:25,560 And I pulled on that thread 50 00:02:25,560 --> 00:02:29,370 and the attack that I ended up with broke my mental model 51 00:02:29,370 --> 00:02:30,420 for request smuggling, 52 00:02:30,420 --> 00:02:32,296 because this attack could be launched 53 00:02:32,296 --> 00:02:34,680 from the user's browser, 54 00:02:34,680 --> 00:02:36,750 which meant it didn't require an attacker. 55 00:02:36,750 --> 00:02:39,528 And that meant, it didn't even require the vulnerable site 56 00:02:39,528 --> 00:02:41,640 to have a front-end server. 57 00:02:41,640 --> 00:02:46,320 And that opens a whole new frontier of attack surface. 58 00:02:46,320 --> 00:02:49,710 So today, I'm here to share with you a methodology 59 00:02:49,710 --> 00:02:53,010 and tool kit that I've built to navigate this new world, 60 00:02:53,010 --> 00:02:55,500 demonstrated with exploits on targets, 61 00:02:55,500 --> 00:02:57,810 including Apache, Akamai, Varnish, 62 00:02:57,810 --> 00:03:00,513 amazon.com and multiple web VPNs. 63 00:03:01,710 --> 00:03:03,324 First, we're gonna get warmed up 64 00:03:03,324 --> 00:03:06,300 with HTTP handling anomalies. 65 00:03:06,300 --> 00:03:08,040 After that, I'll share the client-side 66 00:03:08,040 --> 00:03:11,580 desync methodology using four in-depth case studies. 67 00:03:11,580 --> 00:03:14,010 Then, I'll introduce pause-based desync 68 00:03:14,010 --> 00:03:15,714 and attempt a live demo 69 00:03:15,714 --> 00:03:18,453 because the attack so cool, I couldn't resist. 70 00:03:19,590 --> 00:03:22,260 Then, I'll talk about defense key takeaways for the research 71 00:03:22,260 --> 00:03:25,803 and wrap up with five minutes of questions. 72 00:03:26,693 --> 00:03:29,137 Now, there's quite a few different techniques 73 00:03:29,137 --> 00:03:31,590 in this presentation. 74 00:03:31,590 --> 00:03:34,590 And I really want them to work for you. 75 00:03:34,590 --> 00:03:38,700 So as part of that, whenever you see this logo on a slide, 76 00:03:38,700 --> 00:03:41,514 that means my team's built an online replica website 77 00:03:41,514 --> 00:03:43,170 with that vulnerability in it. 78 00:03:43,170 --> 00:03:44,559 So you can practice that technique 79 00:03:44,559 --> 00:03:47,550 on a real system online for free. 80 00:03:47,550 --> 00:03:49,800 I'm also releasing the code that powered 81 00:03:49,800 --> 00:03:51,844 every finding in this presentation. 82 00:03:51,844 --> 00:03:53,765 And whenever there's a named target, 83 00:03:53,765 --> 00:03:56,700 you'll find full proof of exploit code in the whitepaper 84 00:03:56,700 --> 00:04:00,153 even if I haven't managed to squeeze it onto a slide. 85 00:04:02,730 --> 00:04:04,774 Now, we're gonna start with a series 86 00:04:04,774 --> 00:04:08,940 of six esoteric vulnerabilities that directly led 87 00:04:08,940 --> 00:04:11,220 to the discovery of client-side desync attacks, 88 00:04:11,220 --> 00:04:15,033 but are also really quite interesting in their own light. 89 00:04:15,960 --> 00:04:18,813 But first, there's something I need to tell you. 90 00:04:19,680 --> 00:04:21,450 The request is a lie. 91 00:04:21,450 --> 00:04:25,439 HTTP requests are a useful abstraction, 92 00:04:25,439 --> 00:04:29,790 but the harder you try to hold onto this concept, 93 00:04:29,790 --> 00:04:33,700 the less sense these techniques are going to make. 94 00:04:33,700 --> 00:04:35,460 At the end of the day, 95 00:04:35,460 --> 00:04:38,280 all we are doing is sending a stream of bytes 96 00:04:38,280 --> 00:04:41,583 and what the server does with that is up to it. 97 00:04:42,960 --> 00:04:45,365 For example, it's all too easy to forget 98 00:04:45,365 --> 00:04:47,837 about HTTP connection reuse 99 00:04:47,837 --> 00:04:51,870 because HTTP is supposed to be stateless, 100 00:04:51,870 --> 00:04:55,230 but sometimes state can creep in. 101 00:04:55,230 --> 00:04:56,451 Take this website that I found, 102 00:04:56,451 --> 00:04:58,290 but sadly can't named. 103 00:04:58,290 --> 00:05:00,390 They had a reverse proxy in front 104 00:05:00,390 --> 00:05:02,636 and it was set up to let me access sites intended 105 00:05:02,636 --> 00:05:06,300 to be public and not to access private sites 106 00:05:06,300 --> 00:05:07,650 on their intranet. 107 00:05:07,650 --> 00:05:09,390 And it was deciding where to route 108 00:05:09,390 --> 00:05:11,090 the request using the host header. 109 00:05:11,950 --> 00:05:16,086 But this front-end was only validating the host header 110 00:05:16,086 --> 00:05:19,590 of the first request on each TCP connection. 111 00:05:19,590 --> 00:05:23,040 So I could just send a request to the legit site 112 00:05:23,040 --> 00:05:26,435 and then gain access to the internal systems. 113 00:05:26,435 --> 00:05:29,700 Now, fortunately, this bug is quite rare, 114 00:05:29,700 --> 00:05:31,498 but there's a more common variation 115 00:05:31,498 --> 00:05:34,890 that I'll call first-request routing. 116 00:05:34,890 --> 00:05:38,178 Here, the front-end server looks at the first request 117 00:05:38,178 --> 00:05:41,400 to work out which back-end to route it onto, 118 00:05:41,400 --> 00:05:43,269 but then it passes all subsequent requests 119 00:05:43,269 --> 00:05:45,353 on that connection straight through 120 00:05:45,353 --> 00:05:47,918 to the same back-end system. 121 00:05:47,918 --> 00:05:51,090 By itself, this behavior is not really a vulnerability, 122 00:05:51,090 --> 00:05:53,010 but you can use it to hit any back-end 123 00:05:53,010 --> 00:05:54,390 with an arbitrary host header. 124 00:05:54,390 --> 00:05:57,270 So you can use that to form part of an exploit chain 125 00:05:57,270 --> 00:05:59,345 that would otherwise be impossible. 126 00:05:59,345 --> 00:06:00,825 In this example here, 127 00:06:00,825 --> 00:06:04,200 I wanna hit the back-end with a poisoned host header 128 00:06:04,200 --> 00:06:06,565 so I can trigger a poisoned password reset email, 129 00:06:06,565 --> 00:06:09,002 but doing that directly isn't possible 130 00:06:09,002 --> 00:06:11,940 because the front-end doesn't know where to route it to. 131 00:06:11,940 --> 00:06:14,850 But once again, I just send a legit request first 132 00:06:14,850 --> 00:06:17,760 and then follow it up with the attack. 133 00:06:17,760 --> 00:06:20,249 And we successfully get a poisoned password reset email, 134 00:06:20,249 --> 00:06:22,863 and hopefully, access to someone's account. 135 00:06:24,090 --> 00:06:26,820 So hopefully, that simple technique will come 136 00:06:26,820 --> 00:06:27,990 and useful for you. 137 00:06:27,990 --> 00:06:30,720 But there's also a broader takeaway here, 138 00:06:30,720 --> 00:06:33,208 which is that it's really good to peel away 139 00:06:33,208 --> 00:06:35,130 these abstractions sometimes 140 00:06:35,130 --> 00:06:37,050 because they can hide behavior 141 00:06:37,050 --> 00:06:38,853 that's really quite important. 142 00:06:40,668 --> 00:06:42,150 Onto request smuggling. 143 00:06:42,150 --> 00:06:43,680 Well, you know the deal with this, 144 00:06:43,680 --> 00:06:46,077 hopefully, you just make the front-end and back-end disagree 145 00:06:46,077 --> 00:06:49,216 about the length of a HTTP request. 146 00:06:49,216 --> 00:06:53,070 Use that to apply a malicious prefix shown in orange 147 00:06:53,070 --> 00:06:54,630 to the victim's request, 148 00:06:54,630 --> 00:06:57,677 and that makes bad things happen to the victim. 149 00:06:57,677 --> 00:07:00,240 To encourage this disagreement to happen, 150 00:07:00,240 --> 00:07:02,835 you generally obfuscate the transfer and coding header 151 00:07:02,835 --> 00:07:05,035 to hide it from one of the servers. 152 00:07:05,035 --> 00:07:08,906 So I was a bit puzzled when I found that 153 00:07:08,906 --> 00:07:11,670 I could trigger really suspicious behavior 154 00:07:11,670 --> 00:07:14,280 on a large number of websites using 155 00:07:14,280 --> 00:07:19,280 AWS's Application Load Balancer using this HTTP/2 request. 156 00:07:21,448 --> 00:07:23,128 If you look at this request, 157 00:07:23,128 --> 00:07:25,842 you might wonder where the attack is 158 00:07:25,842 --> 00:07:29,550 because this is a legitimate HTTP/2 request. 159 00:07:29,550 --> 00:07:30,960 It's spec compliant, 160 00:07:30,960 --> 00:07:34,440 there isn't any obfuscation there or anything like that. 161 00:07:34,440 --> 00:07:37,490 And yet, somehow this was causing some kind of desync 162 00:07:37,490 --> 00:07:40,140 on these websites. 163 00:07:40,140 --> 00:07:43,680 After spending quite a while, investigating it as usual, 164 00:07:43,680 --> 00:07:45,009 I eventually decided 165 00:07:45,009 --> 00:07:49,160 what Amazon must be doing is inexplicably adding a header 166 00:07:49,160 --> 00:07:51,510 that said, actually, this message is chunked 167 00:07:51,510 --> 00:07:53,370 when they forwarded it onto the back-end, 168 00:07:53,370 --> 00:07:55,323 but not actually chunking it. 169 00:07:56,263 --> 00:07:59,397 And well, once I knew that, it was easy to turn this 170 00:07:59,397 --> 00:08:02,310 into an exploitable desync and hack a bunch of sites. 171 00:08:02,310 --> 00:08:03,200 So that was nice. 172 00:08:03,200 --> 00:08:05,940 I reported it to Amazon and they fixed it, 173 00:08:05,940 --> 00:08:07,980 really quite quickly too. 174 00:08:07,980 --> 00:08:11,970 But it left the question, well, why was Amazon doing that? 175 00:08:11,970 --> 00:08:13,220 Why did that even happen? 176 00:08:14,400 --> 00:08:16,020 And I think, 177 00:08:16,020 --> 00:08:18,943 it's because web browsers always send 178 00:08:18,943 --> 00:08:22,590 a content-length header even when they're using HTTP/2, 179 00:08:22,590 --> 00:08:25,713 even though that is not required over HTTP/2. 180 00:08:26,550 --> 00:08:28,777 And so Amazon just ended up with logic that said, 181 00:08:28,777 --> 00:08:30,528 "well, if there isn't a content-length header, 182 00:08:30,528 --> 00:08:32,757 I guess it must be chucked." 183 00:08:34,020 --> 00:08:37,110 So that was a handy finding. 184 00:08:37,110 --> 00:08:41,010 But the real value of it was in the takeaway, 185 00:08:41,010 --> 00:08:43,950 which is that for request smuggling, 186 00:08:43,950 --> 00:08:46,864 you don't necessarily need header obfuscation 187 00:08:46,864 --> 00:08:50,400 or any kind of ambiguity. 188 00:08:50,400 --> 00:08:54,514 All you need is a server taken by surprise. 189 00:08:54,514 --> 00:08:56,853 We're gonna come back to that shortly. 190 00:08:58,050 --> 00:09:01,260 Now, let's take a closer look at the connection locked 191 00:09:01,260 --> 00:09:05,190 HTTP/1 request smuggling issue mentioned earlier. 192 00:09:05,190 --> 00:09:07,710 So to confirm regular request smuggling, 193 00:09:07,710 --> 00:09:09,450 you send two requests. 194 00:09:09,450 --> 00:09:11,460 You confirm the first request effects 195 00:09:11,460 --> 00:09:13,833 in response to the second as shown here. 196 00:09:14,940 --> 00:09:19,230 And this works great provided you send those two requests 197 00:09:19,230 --> 00:09:20,820 over separate connections, 198 00:09:20,820 --> 00:09:22,500 but to find a connection lot vulnerability, 199 00:09:22,500 --> 00:09:26,328 you have to send them over the same connection like this. 200 00:09:26,328 --> 00:09:28,051 Now here, we're sending and receiving 201 00:09:28,051 --> 00:09:32,250 exactly the same bytes as shown on the previous slide. 202 00:09:32,250 --> 00:09:33,930 But now we have a problem 203 00:09:33,930 --> 00:09:35,328 because we can no longer tell 204 00:09:35,328 --> 00:09:39,540 where the front-end thinks the first request ends. 205 00:09:39,540 --> 00:09:40,620 And that means we can't tell 206 00:09:40,620 --> 00:09:43,353 if this system is actually vulnerable or not. 207 00:09:44,280 --> 00:09:47,410 The solution here is to realize that these bytes 208 00:09:47,410 --> 00:09:49,634 that we're getting back aren't the only information 209 00:09:49,634 --> 00:09:51,420 that we have. 210 00:09:51,420 --> 00:09:54,150 We also have timing information. 211 00:09:54,150 --> 00:09:57,630 If the front-end server thinks our message is chunked, 212 00:09:57,630 --> 00:10:00,255 that means it's already starting to generate a response 213 00:10:00,255 --> 00:10:03,240 before we send the orange payload. 214 00:10:03,240 --> 00:10:06,090 So if we pause before sending that payload 215 00:10:06,090 --> 00:10:07,110 and check the socket, 216 00:10:07,110 --> 00:10:08,746 and we see a response coming back, 217 00:10:08,746 --> 00:10:12,060 that tells us they're not using the content-length, 218 00:10:12,060 --> 00:10:13,484 they think this message is chunked 219 00:10:13,484 --> 00:10:16,233 and they're not vulnerable to a CLT desync. 220 00:10:17,130 --> 00:10:21,330 Meanwhile, if we try to read and we don't get any data back 221 00:10:21,330 --> 00:10:23,280 for a few seconds, 222 00:10:23,280 --> 00:10:25,207 and then the rest of the attack plans out as usual 223 00:10:25,207 --> 00:10:28,200 that proves the front-end must be using the content-length 224 00:10:28,200 --> 00:10:31,323 and therefore are actually vulnerable. 225 00:10:33,420 --> 00:10:35,610 So I took that technique. 226 00:10:35,610 --> 00:10:38,157 I automated it and I went scanning, 227 00:10:38,157 --> 00:10:40,111 and I found a few things. 228 00:10:40,111 --> 00:10:42,411 One of the mildly notable things was, 229 00:10:42,411 --> 00:10:44,991 there was a vulnerable system that was vulnerable 230 00:10:44,991 --> 00:10:46,003 because they were running 231 00:10:46,003 --> 00:10:50,013 a Barracuda's web application firewall in front of IIS. 232 00:10:51,390 --> 00:10:52,500 Obviously, it's old news. 233 00:10:52,500 --> 00:10:54,960 You put a web application firewall in front of something, 234 00:10:54,960 --> 00:10:57,420 it makes it easier to hack, 235 00:10:57,420 --> 00:11:00,060 but what was particularly interesting here was, 236 00:11:00,060 --> 00:11:03,720 Barracuda had actually issued a patch for this problem, 237 00:11:03,720 --> 00:11:07,380 but they hadn't flagged it as like a full on security fix. 238 00:11:07,380 --> 00:11:08,213 They just said it was, 239 00:11:08,213 --> 00:11:10,410 "oh, just a speculative hardening measure." 240 00:11:10,410 --> 00:11:13,230 So as such the client, hadn't bothered to install it 241 00:11:13,230 --> 00:11:14,553 and it was vulnerable. 242 00:11:16,020 --> 00:11:18,660 As usual though, the best desync I found 243 00:11:18,660 --> 00:11:21,330 with this technique was the one that initially made 244 00:11:21,330 --> 00:11:23,670 absolutely no sense. 245 00:11:23,670 --> 00:11:25,860 After extensive testing, 246 00:11:25,860 --> 00:11:30,780 I was able to refine the attack sequence into this. 247 00:11:30,780 --> 00:11:34,620 Now, there's two things to unpack here. 248 00:11:34,620 --> 00:11:36,540 First off, as you can see, 249 00:11:36,540 --> 00:11:39,600 the back-end server is completely ignoring 250 00:11:39,600 --> 00:11:41,580 the content-length here. 251 00:11:41,580 --> 00:11:44,670 So that means this is a CL.0 desync, 252 00:11:44,670 --> 00:11:46,590 which is a rarer attack class 253 00:11:46,590 --> 00:11:48,423 that hasn't been widely researched. 254 00:11:49,680 --> 00:11:51,150 Secondly is, well, 255 00:11:51,150 --> 00:11:54,303 why are they ignoring that content-length? 256 00:11:55,320 --> 00:11:56,850 There's no reason. 257 00:11:56,850 --> 00:11:57,810 They're just ignoring it 258 00:11:57,810 --> 00:11:59,670 because they feel like it. 259 00:11:59,670 --> 00:12:00,692 It never occurred to me 260 00:12:00,692 --> 00:12:03,240 that a server might just arbitrarily ignore 261 00:12:03,240 --> 00:12:07,950 the content-length on a completely valid HTTP/1 request. 262 00:12:07,950 --> 00:12:11,070 And that has implications. 263 00:12:11,070 --> 00:12:12,655 It also left me wondering, 264 00:12:12,655 --> 00:12:15,455 given that I found this by accident 265 00:12:15,455 --> 00:12:17,871 with a scanning technique that wasn't even designed 266 00:12:17,871 --> 00:12:18,960 to find this, 267 00:12:18,960 --> 00:12:20,486 well, how many sites am I gonna find 268 00:12:20,486 --> 00:12:24,063 if I go deliberately looking for this type of vulnerability? 269 00:12:25,230 --> 00:12:27,300 The answer was quite a few. 270 00:12:27,300 --> 00:12:29,910 My favorite one was amazon.com. 271 00:12:29,910 --> 00:12:32,580 So they ignore the content-length 272 00:12:32,580 --> 00:12:36,183 on POST requests sent to the path /b. 273 00:12:37,620 --> 00:12:39,810 Using this, I got a server side desync 274 00:12:39,810 --> 00:12:41,790 so I made a simple proof of concept, 275 00:12:41,790 --> 00:12:46,200 which stored random live users HTTP requests, 276 00:12:46,200 --> 00:12:50,100 including their credentials inside my Amazon wishlist. 277 00:12:50,100 --> 00:12:52,680 So I send this request a few times. 278 00:12:52,680 --> 00:12:54,360 I reload my wishlist 279 00:12:54,360 --> 00:12:57,123 and I've got some other random people session cookies. 280 00:12:58,022 --> 00:13:00,451 So I reported that to Amazon. 281 00:13:00,451 --> 00:13:03,060 They fixed it at some point. 282 00:13:03,060 --> 00:13:06,900 And then I realized that I'd made a terrible mistake 283 00:13:06,900 --> 00:13:10,383 because I could've done a much cooler attack. 284 00:13:12,000 --> 00:13:15,270 This request exploits a random live user, right? 285 00:13:15,270 --> 00:13:19,770 And the request is a legitimate HTTP request 286 00:13:19,770 --> 00:13:22,440 that can be triggered by a web browser. 287 00:13:22,440 --> 00:13:26,257 So if I'd used the head technique to execute JavaScript 288 00:13:26,257 --> 00:13:27,966 in the victim's browser, 289 00:13:27,966 --> 00:13:30,474 I could have made every user that got hit by this, 290 00:13:30,474 --> 00:13:33,750 spread the attack to 10 other users, 291 00:13:33,750 --> 00:13:36,930 thereby making a self-spreading desynchronization worm 292 00:13:36,930 --> 00:13:39,963 and compromising every active user on Amazon. 293 00:13:43,438 --> 00:13:46,605 (audience applauding) 294 00:13:50,663 --> 00:13:53,430 Thanks, so that was a cool finding, 295 00:13:53,430 --> 00:13:55,800 missed opportunity too, 296 00:13:55,800 --> 00:13:59,160 And also a hint at an entire new attack class. 297 00:13:59,160 --> 00:14:00,513 Client-side desync, 298 00:14:01,620 --> 00:14:04,353 every desync, every request smuggling vulnerability 299 00:14:04,353 --> 00:14:08,730 that we've seen to date has desynchronized the connection 300 00:14:08,730 --> 00:14:12,510 between the front-end server and the back-end server. 301 00:14:12,510 --> 00:14:15,930 But if you can make a web browser send a request 302 00:14:15,930 --> 00:14:18,314 that causes a desync like you could on Amazon, 303 00:14:18,314 --> 00:14:22,215 then, you can target the browser's own connection pool. 304 00:14:22,215 --> 00:14:23,854 And that means you can exploit sites 305 00:14:23,854 --> 00:14:27,207 that don't have a front-end, back-end architecture. 306 00:14:27,207 --> 00:14:30,450 This attack starts with the victim visiting 307 00:14:30,450 --> 00:14:31,890 the attacker's website, 308 00:14:31,890 --> 00:14:34,860 which sends two requests to the target site. 309 00:14:34,860 --> 00:14:38,160 The first request desynchronizes the browsers connection 310 00:14:38,160 --> 00:14:39,390 to that website. 311 00:14:39,390 --> 00:14:42,150 So that the second request triggers a harmful response 312 00:14:42,150 --> 00:14:46,650 to go back to the victim and give the attacker control 313 00:14:46,650 --> 00:14:47,850 of the victim's account. 314 00:14:49,080 --> 00:14:50,490 To build these attacks, 315 00:14:50,490 --> 00:14:53,790 I've adapted the methodology from classic request smuggling. 316 00:14:53,790 --> 00:14:57,811 The main difference is that our entire exploit here needs 317 00:14:57,811 --> 00:15:00,152 to run in our victim's web browser. 318 00:15:00,152 --> 00:15:03,609 And that is an environment that's a lot more complex 319 00:15:03,609 --> 00:15:08,580 and uncontrolled than a dedicated hacking tool. 320 00:15:08,580 --> 00:15:12,000 So it's crucial to periodically take your technique 321 00:15:12,000 --> 00:15:13,650 as you've got it working inside your tool 322 00:15:13,650 --> 00:15:16,105 and try out in the browser to make sure that it works 323 00:15:16,105 --> 00:15:18,515 as expected in the real environment 324 00:15:18,515 --> 00:15:20,463 that you want the attack to work in. 325 00:15:21,930 --> 00:15:24,823 Tooling wise, I did all of this with custom code, 326 00:15:24,823 --> 00:15:27,127 which I've just released to GitHub, 327 00:15:27,127 --> 00:15:30,270 but I also helped design a new Burp Suite feature called 328 00:15:30,270 --> 00:15:32,040 send request sequence, 329 00:15:32,040 --> 00:15:33,755 which offers similar functionality 330 00:15:33,755 --> 00:15:36,243 with a bit of a gentler learning curve. 331 00:15:37,170 --> 00:15:38,243 For the target browser, 332 00:15:38,243 --> 00:15:41,400 this technique seems to work on all browsers 333 00:15:41,400 --> 00:15:43,260 pretty much equally. 334 00:15:43,260 --> 00:15:44,603 Personally, I focused on Chrome 335 00:15:44,603 --> 00:15:47,345 because Chrome has the best developer tools 336 00:15:47,345 --> 00:15:49,713 for building this kind of exploit. 337 00:15:51,488 --> 00:15:54,475 The first step towards a successful attack is to identify 338 00:15:54,475 --> 00:15:57,840 your client-side desync vector. 339 00:15:57,840 --> 00:16:02,820 This is a HTTP/1 request with three key properties. 340 00:16:02,820 --> 00:16:06,060 First and foremost, the server needs to ignore 341 00:16:06,060 --> 00:16:08,730 the content-length of this request. 342 00:16:08,730 --> 00:16:10,511 This will typically happen 343 00:16:10,511 --> 00:16:13,905 because you've triggered some kind of server error 344 00:16:13,905 --> 00:16:17,160 or because you've taken the server by surprise, 345 00:16:17,160 --> 00:16:21,510 it just wasn't expecting a POST request to that end point. 346 00:16:21,510 --> 00:16:22,680 For example here, 347 00:16:22,680 --> 00:16:24,480 this is one of the more effective techniques. 348 00:16:24,480 --> 00:16:27,720 I'm just doing a POST request to a static file. 349 00:16:27,720 --> 00:16:30,570 They don't expect it and as such, 350 00:16:30,570 --> 00:16:31,774 if this server is vulnerable, 351 00:16:31,774 --> 00:16:34,920 they're likely to ignore the fact that I've said, 352 00:16:34,920 --> 00:16:37,380 I'm gonna send more content than I actually have. 353 00:16:37,380 --> 00:16:39,090 So I've sent content-length five. 354 00:16:39,090 --> 00:16:41,250 I've sent one byte of data. 355 00:16:41,250 --> 00:16:44,790 If the server replies to this request immediately, 356 00:16:44,790 --> 00:16:46,470 it suggests that they're ignoring 357 00:16:46,470 --> 00:16:47,716 the content-length five sent, 358 00:16:47,716 --> 00:16:50,888 and they're quite likely to be vulnerable. 359 00:16:50,888 --> 00:16:54,433 Secondly, this request needs to be triggerable across domain 360 00:16:54,433 --> 00:16:56,640 inside a web browser. 361 00:16:56,640 --> 00:17:00,506 So that means you can't use things like head obfuscation 362 00:17:00,506 --> 00:17:04,650 and you can't even specify any special headers really. 363 00:17:04,650 --> 00:17:06,998 And also, the target server can't advertise 364 00:17:06,998 --> 00:17:09,180 support for HTTP/2, 365 00:17:09,180 --> 00:17:11,730 because this is an attack that exploits the fact 366 00:17:11,730 --> 00:17:16,050 that HTTP/1 is dire and browsers will aggressively prefer 367 00:17:16,050 --> 00:17:17,179 to use HTTP/2. 368 00:17:17,179 --> 00:17:20,610 So the only scenario where a site 369 00:17:20,610 --> 00:17:23,250 that uses HTTP/2 is gonna be exploitable 370 00:17:23,250 --> 00:17:28,250 via this is if your target victim is using a corporate proxy 371 00:17:28,560 --> 00:17:30,870 or something that only supports HTTP/1, 372 00:17:30,870 --> 00:17:32,223 which is fairly unlikely. 373 00:17:33,240 --> 00:17:36,180 Finally, the server needs to leave the connection open, 374 00:17:36,180 --> 00:17:38,493 after it's handled this request. 375 00:17:39,930 --> 00:17:41,344 So once you've found this, 376 00:17:41,344 --> 00:17:43,110 the next step is just take that 377 00:17:43,110 --> 00:17:46,088 and see if it works inside a real browser, 378 00:17:46,088 --> 00:17:48,019 which you can do with some JavaScript 379 00:17:48,019 --> 00:17:50,760 that looks something like this. 380 00:17:50,760 --> 00:17:53,851 So here, we're sending two requests 381 00:17:53,851 --> 00:17:56,294 and the first one is gonna desync the connection. 382 00:17:56,294 --> 00:18:00,180 And the second request is just a browser navigation, 383 00:18:00,180 --> 00:18:03,300 and that should hopefully suffer the consequences 384 00:18:03,300 --> 00:18:06,038 of the connection being desync. 385 00:18:06,038 --> 00:18:09,660 Now, there's two flags worth mentioning 386 00:18:09,660 --> 00:18:11,463 in this attack request. 387 00:18:12,930 --> 00:18:16,530 First off, I'm specifying mode no-cors. 388 00:18:16,530 --> 00:18:19,860 This is not required for a successful attack, 389 00:18:19,860 --> 00:18:22,350 but what it does is it means we can actually see 390 00:18:22,350 --> 00:18:24,810 what's happening inside the developer tools. 391 00:18:24,810 --> 00:18:28,524 So it's useful for debugging things when things go wrong, 392 00:18:28,524 --> 00:18:31,391 which they will do quite a lot. 393 00:18:31,391 --> 00:18:35,187 Secondly, I'm specifying credentials include, 394 00:18:35,187 --> 00:18:37,260 this is really important 395 00:18:37,260 --> 00:18:41,993 because browsers have multiple connection pools per website. 396 00:18:41,993 --> 00:18:44,970 And if you poison the wrong connection pool, 397 00:18:44,970 --> 00:18:48,540 then I can promise you an extremely frustrating time. 398 00:18:48,540 --> 00:18:51,453 So specify that and you'll probably poison the right one. 399 00:18:52,320 --> 00:18:54,398 Now, when you run this, if it's successful, 400 00:18:54,398 --> 00:18:57,090 you should see two requests in the dev tools 401 00:18:57,090 --> 00:18:58,711 with the same connection ID. 402 00:18:58,711 --> 00:18:59,820 And you should see 403 00:18:59,820 --> 00:19:02,400 that the second response has been affected by 404 00:19:02,400 --> 00:19:06,333 the malicious prefix from the first request as shown here. 405 00:19:08,580 --> 00:19:12,570 And at this point, it's just time to build an exploit. 406 00:19:12,570 --> 00:19:15,660 This is quite a powerful primitive. 407 00:19:15,660 --> 00:19:18,510 So you've got three main options. 408 00:19:18,510 --> 00:19:22,302 First off, you can try to store the user's request somewhere 409 00:19:22,302 --> 00:19:24,720 where you can later retrieve it, 410 00:19:24,720 --> 00:19:26,400 kind of like I did on Amazon. 411 00:19:26,400 --> 00:19:28,050 And that works just the same 412 00:19:28,050 --> 00:19:30,630 as regular server side request smuggling. 413 00:19:30,630 --> 00:19:33,180 So I'm not gonna waste any time talking about it 414 00:19:33,180 --> 00:19:34,053 in more detail. 415 00:19:35,070 --> 00:19:37,530 Secondly, there's an all new option, 416 00:19:37,530 --> 00:19:40,470 which is chaining and pivoting. 417 00:19:40,470 --> 00:19:44,374 So a client-side desync means 418 00:19:44,374 --> 00:19:49,230 that you can make your victim's browser send arbitrary bytes 419 00:19:49,230 --> 00:19:51,240 to the target website. 420 00:19:51,240 --> 00:19:53,940 So what it does is it turns their browser 421 00:19:53,940 --> 00:19:55,980 into your personal attack platform 422 00:19:55,980 --> 00:19:59,610 and it puts extra attack surface within your reach. 423 00:19:59,610 --> 00:20:03,299 For example, you can make them put Log4Shell payloads, 424 00:20:03,299 --> 00:20:05,160 wherever you like, 425 00:20:05,160 --> 00:20:08,670 and you can even hit authenticated attack surface using 426 00:20:08,670 --> 00:20:11,430 their credentials in a way that's a bit like 427 00:20:11,430 --> 00:20:12,619 cross-site request forgery, 428 00:20:12,619 --> 00:20:14,340 but not more powerful 429 00:20:14,340 --> 00:20:15,649 because it doesn't have all the limitations 430 00:20:15,649 --> 00:20:19,323 that browsers normally put on cross-domain requests. 431 00:20:20,550 --> 00:20:24,060 What I'm gonna focus on though is exploiting the end user. 432 00:20:24,060 --> 00:20:25,506 I've tried a lot of different techniques 433 00:20:25,506 --> 00:20:29,211 and had the most success with two well-known approaches 434 00:20:29,211 --> 00:20:30,714 from server-side request smuggling 435 00:20:30,714 --> 00:20:35,509 with certain tweaks applied that we're gonna have a look at 436 00:20:35,509 --> 00:20:37,353 in the case studies. 437 00:20:39,360 --> 00:20:42,450 So for our first case study, 438 00:20:42,450 --> 00:20:44,763 we're gonna exploit a straightforward vulnerability 439 00:20:44,763 --> 00:20:47,790 that affected a huge number of websites built 440 00:20:47,790 --> 00:20:49,683 on the CDN Akamai. 441 00:20:50,550 --> 00:20:53,220 This attack vector is nice and simple. 442 00:20:53,220 --> 00:20:55,713 To cause a desync, you just need to do a POST request 443 00:20:55,713 --> 00:20:58,983 that triggers a redirect from the front-end. 444 00:21:00,090 --> 00:21:02,739 Confirming this in a browser is also really easy. 445 00:21:02,739 --> 00:21:05,100 Here, I've just crafted the prefix 446 00:21:05,100 --> 00:21:08,490 so that when the browser follows this redirect 447 00:21:08,490 --> 00:21:09,630 from the front-end, 448 00:21:09,630 --> 00:21:12,573 it ends up seeing the contents of the robots.txt file. 449 00:21:13,920 --> 00:21:17,220 For the exploit, I'm gonna use the head technique. 450 00:21:17,220 --> 00:21:19,302 Now, if you're not familiar with this technique, 451 00:21:19,302 --> 00:21:21,750 it's documented in more detail 452 00:21:21,750 --> 00:21:24,750 in last year's presentation on HTTP/2 453 00:21:24,750 --> 00:21:28,800 but the short version is with the head technique, 454 00:21:28,800 --> 00:21:31,590 you use the method head to queue up multiple responses 455 00:21:31,590 --> 00:21:33,870 that when combined are harmful 456 00:21:33,870 --> 00:21:36,840 and let us execute JavaScript from the target site 457 00:21:36,840 --> 00:21:38,670 in our victim's browser. 458 00:21:38,670 --> 00:21:41,801 And when you are doing service side request smuggling, 459 00:21:41,801 --> 00:21:43,920 it's that simple, 460 00:21:43,920 --> 00:21:45,900 but because this is client-side, 461 00:21:45,900 --> 00:21:49,773 there's a couple of other things that we need to fix first. 462 00:21:50,760 --> 00:21:53,970 The first problem is that the initial request coming back 463 00:21:53,970 --> 00:21:57,255 to the web browser is a 301 redirect. 464 00:21:57,255 --> 00:22:00,550 And as such, the browser is just gonna follow that redirect, 465 00:22:00,550 --> 00:22:02,790 and that's gonna use the poison connection 466 00:22:02,790 --> 00:22:05,003 and mess up our attack. 467 00:22:05,003 --> 00:22:08,970 The second problem is the stacked response problem. 468 00:22:08,970 --> 00:22:12,956 Whenever Chrome reads in a response from the server, 469 00:22:12,956 --> 00:22:15,840 it deliberately does a little overread. 470 00:22:15,840 --> 00:22:17,124 It tries to read more data 471 00:22:17,124 --> 00:22:19,150 than the server said it was gonna send 472 00:22:20,280 --> 00:22:23,580 just to see if there's any extra data lying around. 473 00:22:23,580 --> 00:22:25,200 And if it sees anything, 474 00:22:25,200 --> 00:22:28,983 it quietly dumps the connection and breaks the attack. 475 00:22:29,887 --> 00:22:34,887 So fortunately, both issues are easily resolved on Akamai. 476 00:22:34,950 --> 00:22:37,375 So you can fix the stacked response problem 477 00:22:37,375 --> 00:22:40,320 by delaying the second response. 478 00:22:40,320 --> 00:22:43,710 So it arrives after Chrome does its little check. 479 00:22:43,710 --> 00:22:44,820 So in this scenario, 480 00:22:44,820 --> 00:22:46,517 I was able to do this by adding a cache, 481 00:22:46,517 --> 00:22:50,190 by adding a cache-buster to the request 482 00:22:50,190 --> 00:22:51,396 so that it incurred a cache miss, 483 00:22:51,396 --> 00:22:52,920 went all the way to the back-end, 484 00:22:52,920 --> 00:22:56,041 which was slow and old and therefore took ages to respond 485 00:22:56,041 --> 00:22:59,583 and meant that it arrived late and Chrome didn't see it. 486 00:23:00,828 --> 00:23:04,770 The second problem of the browser redirect is easily fixed 487 00:23:04,770 --> 00:23:08,010 by changing mode, no cause to mode cause, 488 00:23:08,010 --> 00:23:10,110 which means when the browsers sees the redirect, 489 00:23:10,110 --> 00:23:13,515 it throws an exception which we can then catch ourselves 490 00:23:13,515 --> 00:23:15,960 and continue with our attack, 491 00:23:15,960 --> 00:23:18,520 ultimately, leading to a successful exploit 492 00:23:21,240 --> 00:23:25,137 For our next target, we'll hit Cisco's web VPN. 493 00:23:25,137 --> 00:23:27,487 This technique seems to work on lots of web VPNs 494 00:23:27,487 --> 00:23:29,479 for some reason, 495 00:23:29,479 --> 00:23:32,070 I think it's 'cause they tend to code their own web service 496 00:23:32,070 --> 00:23:35,640 for security reasons which backfires. 497 00:23:35,640 --> 00:23:38,370 So here, we can trigger a desync, 498 00:23:38,370 --> 00:23:41,550 simply by doing a POST request to their homepage. 499 00:23:41,550 --> 00:23:43,860 It couldn't be much easier to be honest. 500 00:23:43,860 --> 00:23:46,590 And with that, 501 00:23:46,590 --> 00:23:50,790 we can easily trigger a redirect to our website. 502 00:23:50,790 --> 00:23:53,850 And in theory, that redirect could let us redirect 503 00:23:53,850 --> 00:23:57,276 to JavaScript resource load and let us take full control 504 00:23:57,276 --> 00:23:59,070 over the page. 505 00:23:59,070 --> 00:24:00,600 But there's a bit of a problem 506 00:24:00,600 --> 00:24:03,720 because when a web browser renders a page, 507 00:24:03,720 --> 00:24:06,343 it loads all the resources at the same time. 508 00:24:06,343 --> 00:24:08,118 And that makes it really quite hard 509 00:24:08,118 --> 00:24:11,433 to successfully hijack the correct file. 510 00:24:12,720 --> 00:24:16,200 Fortunately, there was an easy solution on this target 511 00:24:16,200 --> 00:24:19,530 because this redirect response is cacheable. 512 00:24:19,530 --> 00:24:24,530 So if we poison the connection with our redirect, 513 00:24:24,750 --> 00:24:26,580 and then we tell the browser to navigate 514 00:24:26,580 --> 00:24:28,680 to the target JavaScript file, 515 00:24:28,680 --> 00:24:31,463 which is /win.js here, 516 00:24:31,463 --> 00:24:33,630 then they'll get that redirect back 517 00:24:33,630 --> 00:24:36,633 and they'll just get bounced back to our website 518 00:24:36,633 --> 00:24:38,130 by the redirect, 519 00:24:38,130 --> 00:24:41,800 but they'll also save the redirect in their cache 520 00:24:42,780 --> 00:24:44,640 So when they learn back on our website, 521 00:24:44,640 --> 00:24:49,290 we can send them onwards to the web VPN's login page. 522 00:24:49,290 --> 00:24:51,540 And when the log in page starts to get rendered, 523 00:24:51,540 --> 00:24:53,619 it's gonna try and load that JavaScript file 524 00:24:53,619 --> 00:24:56,700 and end up loading our poisoned version from the cache 525 00:24:56,700 --> 00:24:58,650 and importing our JavaScript 526 00:24:58,650 --> 00:25:00,873 and giving us the user's password. 527 00:25:02,070 --> 00:25:04,330 So I reported this to Cisco 528 00:25:06,030 --> 00:25:08,940 and they didn't say anything for a while. 529 00:25:08,940 --> 00:25:13,113 And then they said, they're gonna deprecate this product. 530 00:25:13,980 --> 00:25:15,784 So they're not gonna fix this issue, 531 00:25:15,784 --> 00:25:18,570 but they are issuing a CVE for it. 532 00:25:18,570 --> 00:25:20,490 So that's nice. 533 00:25:20,490 --> 00:25:22,184 I hope you're not using it. 534 00:25:22,184 --> 00:25:25,184 (audience laughing) 535 00:25:26,670 --> 00:25:29,070 On verisign.com. 536 00:25:29,070 --> 00:25:30,525 You could trigger a desync using 537 00:25:30,525 --> 00:25:33,330 a URL encoded forward slash, 538 00:25:33,330 --> 00:25:35,070 Don't ask me how I found that. 539 00:25:35,070 --> 00:25:39,000 And unfortunately, that wasn't the only thing a bit unusual 540 00:25:39,000 --> 00:25:43,680 about their setup for reasons that I don't have time 541 00:25:43,680 --> 00:25:45,960 to explain in any detail. 542 00:25:45,960 --> 00:25:48,060 To get a working exploit, 543 00:25:48,060 --> 00:25:49,710 I had to use the head technique, 544 00:25:49,710 --> 00:25:52,590 but I had to do it with a head request that had a body 545 00:25:52,590 --> 00:25:54,213 and the body had to be chunked, 546 00:25:55,590 --> 00:25:58,020 which meant I had to judge the chunk size, 547 00:25:58,020 --> 00:26:00,750 so the follow-up request would perfectly slot 548 00:26:00,750 --> 00:26:05,013 inside that chunk and close off the request. 549 00:26:06,090 --> 00:26:07,715 This, unbelievably, did actually work. 550 00:26:07,715 --> 00:26:09,660 But the interesting thing here, 551 00:26:09,660 --> 00:26:12,840 the reason this is worth mentioning is because 552 00:26:12,840 --> 00:26:16,860 this approach is exclusive to client-side desync. 553 00:26:16,860 --> 00:26:19,260 If you're doing a server-side desync, 554 00:26:19,260 --> 00:26:20,921 you don't control what the next request 555 00:26:20,921 --> 00:26:23,280 to hit the server is gonna be. 556 00:26:23,280 --> 00:26:26,010 So you can't accurately predict its size. 557 00:26:26,010 --> 00:26:28,803 And this technique is basically completely implausible. 558 00:26:29,700 --> 00:26:31,830 So it's worth bearing in mind that 559 00:26:31,830 --> 00:26:35,310 although client-side desync can be quite painful sometimes, 560 00:26:35,310 --> 00:26:36,753 you do have options. 561 00:26:38,370 --> 00:26:41,910 Speaking of painful, for our final case study, 562 00:26:41,910 --> 00:26:43,833 we're gonna target Pulse Secure VPN. 563 00:26:44,670 --> 00:26:47,424 Here, you can do a desync by POST request 564 00:26:47,424 --> 00:26:49,483 to the robots.txt file. 565 00:26:49,483 --> 00:26:54,000 And just like Cisco's web VPN, they've got a host header, 566 00:26:54,000 --> 00:26:56,280 redirect gadget that we'd like to use 567 00:26:56,280 --> 00:26:58,950 to hijack a JavaScript import, 568 00:26:58,950 --> 00:27:02,700 but this time the redirect isn't cacheable. 569 00:27:02,700 --> 00:27:05,310 So we are in this unpleasant scenario 570 00:27:05,310 --> 00:27:08,176 where our attack timing is crucial. 571 00:27:08,176 --> 00:27:10,061 And I had to take three steps 572 00:27:10,061 --> 00:27:13,023 to make this remotely reliable. 573 00:27:13,980 --> 00:27:16,530 First off, I pre-connect the victim browser 574 00:27:16,530 --> 00:27:19,530 with the target site to reduce the effect of network jitter 575 00:27:19,530 --> 00:27:21,278 on the attack timings. 576 00:27:21,278 --> 00:27:22,605 This might not make any difference, 577 00:27:22,605 --> 00:27:24,273 but I was kinda desperate. 578 00:27:25,110 --> 00:27:28,500 Secondly, our attack is gonna fail sometimes. 579 00:27:28,500 --> 00:27:31,260 So it's essential that we can have multiple attempts, 580 00:27:31,260 --> 00:27:33,157 but a failed attempt leaves the victim 581 00:27:33,157 --> 00:27:36,630 on the target website out of our control. 582 00:27:36,630 --> 00:27:38,070 So to deal with that, 583 00:27:38,070 --> 00:27:41,066 I just run the attack in a separate tab, 584 00:27:41,066 --> 00:27:44,460 which just means we can have as many attempts as we like, 585 00:27:44,460 --> 00:27:46,950 except for one other potential problem, 586 00:27:46,950 --> 00:27:48,917 which is that if an attack fails 587 00:27:48,917 --> 00:27:52,890 and the browser ends up caching the genuine JavaScript file, 588 00:27:52,890 --> 00:27:54,840 then we can't poison that file 589 00:27:54,840 --> 00:27:56,970 until that cache entry has expired, 590 00:27:56,970 --> 00:27:58,980 which could be weeks. 591 00:27:58,980 --> 00:28:02,037 But I was able to avoid that issue by finding a page 592 00:28:02,037 --> 00:28:05,460 on Pulse Secure's VPN that had a JavaScript import 593 00:28:05,460 --> 00:28:06,450 that never got cached 594 00:28:06,450 --> 00:28:08,160 because the file they were trying 595 00:28:08,160 --> 00:28:10,173 to import didn't actually exist. 596 00:28:11,040 --> 00:28:14,790 So by combining all of these, we got a successful attack, 597 00:28:14,790 --> 00:28:18,180 which hopefully, looks something like this. 598 00:28:18,180 --> 00:28:20,220 Yeah, so you can see a tab pops open 599 00:28:20,220 --> 00:28:23,490 and then the victim site gets reloaded a couple of times 600 00:28:23,490 --> 00:28:25,618 and we get control. 601 00:28:25,618 --> 00:28:27,840 I reported that to Pulse Secure VPN. 602 00:28:27,840 --> 00:28:31,023 And to be honest, I'm not sure what happened. 603 00:28:32,430 --> 00:28:34,080 They didn't say anything for ages. 604 00:28:34,080 --> 00:28:35,220 And then they said they fixed it, 605 00:28:35,220 --> 00:28:36,843 but I can't find the fix. 606 00:28:37,770 --> 00:28:39,153 So who knows. 607 00:28:40,860 --> 00:28:42,701 Now, we saw earlier that slowing down 608 00:28:42,701 --> 00:28:46,260 and pausing can reveal useful information. 609 00:28:46,260 --> 00:28:49,620 And as it turns out, pausing can also create 610 00:28:49,620 --> 00:28:52,443 entire new desync vulnerabilities. 611 00:28:53,475 --> 00:28:57,240 To trigger a pause-based desync on a vulnerable server, 612 00:28:57,240 --> 00:29:00,480 you start by sending your headers promising a body, 613 00:29:00,480 --> 00:29:02,880 but not sending it and then just waiting. 614 00:29:02,880 --> 00:29:04,530 Eventually, you'll get a response. 615 00:29:04,530 --> 00:29:07,290 Generally, after like a server timeout is hit. 616 00:29:07,290 --> 00:29:09,030 And then, when you finally send the body, 617 00:29:09,030 --> 00:29:12,090 they'll treat that as a new request. 618 00:29:12,090 --> 00:29:14,310 I initially found this on Varnish. 619 00:29:14,310 --> 00:29:16,500 And just in case you think that was really clever of me, 620 00:29:16,500 --> 00:29:18,030 it was actually, 621 00:29:18,030 --> 00:29:21,660 because of multiple bugs in my code combined 622 00:29:21,660 --> 00:29:23,253 to trigger this condition. 623 00:29:25,140 --> 00:29:26,306 But once I saw it on Varnish, 624 00:29:26,306 --> 00:29:28,440 I was like, oh, that's cool, let's go looking for, 625 00:29:28,440 --> 00:29:30,212 and I found it works on Apache too, 626 00:29:30,212 --> 00:29:32,310 which is pretty cool. 627 00:29:32,310 --> 00:29:36,414 And it tends to happen when the server generates 628 00:29:36,414 --> 00:29:39,523 a response itself instead of forwarding the request 629 00:29:39,523 --> 00:29:42,060 to the back-end server or handing it off 630 00:29:42,060 --> 00:29:43,650 to the application layer. 631 00:29:43,650 --> 00:29:48,650 And this single vulnerability enables two distinct attacks. 632 00:29:49,380 --> 00:29:51,620 So first off, we're gonna use it to cause 633 00:29:51,620 --> 00:29:54,060 a traditional server side desync. 634 00:29:54,060 --> 00:29:55,788 Now, for this to work, 635 00:29:55,788 --> 00:29:58,733 the front-end server must stream the request 636 00:29:58,733 --> 00:30:00,113 to the back-end. 637 00:30:00,113 --> 00:30:02,871 So in particular, it must forward the request headers 638 00:30:02,871 --> 00:30:07,353 without attempting to buffer the entire request body first. 639 00:30:08,670 --> 00:30:10,650 So to find the bug here, 640 00:30:10,650 --> 00:30:13,120 well, you send your headers and you wait for the timeout, 641 00:30:13,120 --> 00:30:15,694 but you will probably won't realize 642 00:30:15,694 --> 00:30:18,360 when the timeout happens on the back-end 643 00:30:18,360 --> 00:30:21,108 because the front-end generally won't forward 644 00:30:21,108 --> 00:30:22,503 the response onto you 645 00:30:22,503 --> 00:30:26,580 until they've seen you send a complete request. 646 00:30:26,580 --> 00:30:28,896 So in practice, you need to send your headers, 647 00:30:28,896 --> 00:30:32,520 wait until you think of timeout has probably happened 648 00:30:32,520 --> 00:30:34,740 on the back-end and then send the rest 649 00:30:34,740 --> 00:30:38,853 and the next request and hopefully, get a successful attack. 650 00:30:39,810 --> 00:30:42,600 So I've updated Turbo Intruder to add 651 00:30:42,600 --> 00:30:44,454 a couple of different ways of saying 652 00:30:44,454 --> 00:30:47,217 where in the request you wanna pause 653 00:30:47,217 --> 00:30:50,463 and how long you want to pause for. 654 00:30:51,540 --> 00:30:53,808 So that's it, it's fairly simple. 655 00:30:53,808 --> 00:30:56,273 And we might be wondering, well, what front-end servers 656 00:30:56,273 --> 00:31:00,093 actually stream requests like this? 657 00:31:01,080 --> 00:31:02,843 To be honest, I couldn't be bothered looking at very many, 658 00:31:02,843 --> 00:31:05,160 but it worked on the first one I tried, 659 00:31:05,160 --> 00:31:07,683 which was Amazon's Application Load Balancer. 660 00:31:08,520 --> 00:31:10,860 However, there's one extra catch, 661 00:31:10,860 --> 00:31:13,860 which is that they've got a defensive measure. 662 00:31:13,860 --> 00:31:17,040 I'll call early response detection. 663 00:31:17,040 --> 00:31:19,110 It's a bit like the Chrome behavior we saw earlier, 664 00:31:19,110 --> 00:31:20,010 but it's slightly different. 665 00:31:20,010 --> 00:31:24,224 So if Amazons ALB sees the response coming in 666 00:31:24,224 --> 00:31:28,200 from the back-end before our request is completed, 667 00:31:28,200 --> 00:31:30,720 then, they'll forward that response on, 668 00:31:30,720 --> 00:31:32,100 but then they'll dump the connection. 669 00:31:32,100 --> 00:31:34,353 They won't reuse it and our attack will fail. 670 00:31:35,940 --> 00:31:39,330 Fortunately, this seems to be designed to prevent bugs 671 00:31:39,330 --> 00:31:42,102 rather than actual attackers. 672 00:31:42,102 --> 00:31:44,730 And there's a really obvious race condition in it. 673 00:31:44,730 --> 00:31:47,010 So it's quite easy to bypass. 674 00:31:47,010 --> 00:31:50,520 All you need to do is identify the back-end timeout 675 00:31:50,520 --> 00:31:53,610 and make the orange payload hit the front-end 676 00:31:53,610 --> 00:31:55,636 and the time window between the back-end generating 677 00:31:55,636 --> 00:32:00,480 that timeout response and the front-end noticing it. 678 00:32:00,480 --> 00:32:01,740 In other words, basically, 679 00:32:01,740 --> 00:32:03,560 this attack may take a few attempts, 680 00:32:03,560 --> 00:32:05,253 but it's worth it. 681 00:32:06,870 --> 00:32:09,210 There's one final challenge that you might encounter, 682 00:32:09,210 --> 00:32:11,520 which is more severe. 683 00:32:11,520 --> 00:32:12,353 This is when the front-end 684 00:32:12,353 --> 00:32:16,120 and the back-end have the same request timeout configured 685 00:32:17,010 --> 00:32:19,843 on ALB that creates a race condition 686 00:32:19,843 --> 00:32:23,880 within a race condition and makes life incredibly painful. 687 00:32:23,880 --> 00:32:28,470 I thought it might be possible to avoid this issue 688 00:32:28,470 --> 00:32:31,170 by resetting the front-end timeout 689 00:32:31,170 --> 00:32:33,120 without resending in the back-end timeout 690 00:32:33,120 --> 00:32:34,835 by sending data that the front-end normalizes 691 00:32:34,835 --> 00:32:37,563 and then doesn't send on to the back-end. 692 00:32:39,390 --> 00:32:40,555 And that might work on some servers, 693 00:32:40,555 --> 00:32:42,420 but it didn't work on ALB. 694 00:32:42,420 --> 00:32:45,480 And after a while of everything I tried failing, 695 00:32:45,480 --> 00:32:47,525 even when it was really cool, conceptually, 696 00:32:47,525 --> 00:32:51,723 I just gave up and I just set a VANILLA attack running. 697 00:32:52,560 --> 00:32:56,130 And 66 hours later, it was successful. 698 00:32:56,130 --> 00:32:58,023 So this is one for the patient. 699 00:33:00,210 --> 00:33:04,650 So that was a server-side pause-based desync. 700 00:33:04,650 --> 00:33:07,380 And it just leaves us with one final question, 701 00:33:07,380 --> 00:33:09,359 which is well, is there such a thing 702 00:33:09,359 --> 00:33:12,123 as a client-side pause-based desync? 703 00:33:13,416 --> 00:33:17,400 Now, I couldn't find a way to make a browser pause halfway 704 00:33:17,400 --> 00:33:19,830 through issuing a request, 705 00:33:19,830 --> 00:33:23,021 but SSL and TLS don't stop attackers 706 00:33:23,021 --> 00:33:24,906 from delaying your traffic. 707 00:33:24,906 --> 00:33:27,660 So there is a potential attack 708 00:33:27,660 --> 00:33:29,191 where the attacker triggers a request 709 00:33:29,191 --> 00:33:31,226 from your browser that's really big, 710 00:33:31,226 --> 00:33:33,720 so it gets split into multiple packets. 711 00:33:33,720 --> 00:33:36,618 And then all they have to do is delay the right packet 712 00:33:36,618 --> 00:33:39,960 and they can trigger a pause-based client-side desync 713 00:33:39,960 --> 00:33:40,793 and exploit you. 714 00:33:42,210 --> 00:33:44,610 Now, that might sound quite theoretical. 715 00:33:44,610 --> 00:33:46,620 It certainly sound theoretical to me. 716 00:33:46,620 --> 00:33:47,880 And this is DEF CON. 717 00:33:47,880 --> 00:33:51,360 So I've made a proof of concept that uses this technique 718 00:33:51,360 --> 00:33:53,790 on a default Apache-based website 719 00:33:53,790 --> 00:33:57,930 to execute arbitrary JavaScript and kind of break TLS. 720 00:33:57,930 --> 00:34:00,123 And now, I'm gonna attempt to live demo it. 721 00:34:01,020 --> 00:34:05,245 A code on the client-side looks fairly 722 00:34:05,245 --> 00:34:07,290 like a regular client-side desync, 723 00:34:07,290 --> 00:34:10,950 but we've got tons of padding to make the request big 724 00:34:10,950 --> 00:34:13,470 so it gets split into multiple packets. 725 00:34:13,470 --> 00:34:15,450 On the attackers middle box, 726 00:34:15,450 --> 00:34:17,937 I used the traffic control facility 727 00:34:17,937 --> 00:34:19,680 with some code like this, 728 00:34:19,680 --> 00:34:22,298 that just says delay the packet to the target site 729 00:34:22,298 --> 00:34:25,440 by 61 seconds 730 00:34:25,440 --> 00:34:28,650 if it's in between 700 and 1,300 bytes, 731 00:34:28,650 --> 00:34:30,393 'cause that seems to work. 732 00:34:31,890 --> 00:34:32,723 As you may guess, 733 00:34:32,723 --> 00:34:35,519 this is not the world's most reliable technique 734 00:34:35,519 --> 00:34:39,060 because you obviously can't decrypt the packets. 735 00:34:39,060 --> 00:34:40,470 They're encrypted. 736 00:34:40,470 --> 00:34:43,050 You just look at how big they are and kind of guess, 737 00:34:43,050 --> 00:34:45,693 but let's have a go and see what happens. 738 00:34:51,160 --> 00:34:51,993 Okay, cool. 739 00:34:51,993 --> 00:34:54,660 So I'm gonna make the victim browser, 740 00:34:54,660 --> 00:34:58,500 it's just gonna SOCKS proxy to a box on Amazon 741 00:34:58,500 --> 00:34:59,965 that's being man in the middle 742 00:34:59,965 --> 00:35:04,170 just so the local network doesn't blow things up too much. 743 00:35:04,170 --> 00:35:05,160 It's just a SOCKS proxy. 744 00:35:05,160 --> 00:35:08,220 It's not breaking SSO, anything like that. 745 00:35:08,220 --> 00:35:10,720 Here, we're gonna connect to the attacker machine. 746 00:35:11,669 --> 00:35:14,550 Apologies about the size of the font. 747 00:35:14,550 --> 00:35:16,350 What if I do this? 748 00:35:16,350 --> 00:35:18,549 Yeah, okay. 749 00:35:18,549 --> 00:35:21,630 (keyboard clanking) 750 00:35:21,630 --> 00:35:23,460 Cool, so here we can see this code. 751 00:35:23,460 --> 00:35:26,340 I've got that injects, the delay, 752 00:35:26,340 --> 00:35:28,479 the only change is I'm just doing a six second delay here, 753 00:35:28,479 --> 00:35:30,360 'cause I reduce the server time out 754 00:35:30,360 --> 00:35:33,510 so that the demo doesn't take ages 755 00:35:33,510 --> 00:35:36,690 and I'm running TCP dump on the attacker's box. 756 00:35:36,690 --> 00:35:39,900 So if the attack works, 757 00:35:39,900 --> 00:35:41,220 if the browser decides to send 758 00:35:41,220 --> 00:35:42,808 the correct packet sites for me, 759 00:35:42,808 --> 00:35:46,200 then we are gonna see a few packets go through 760 00:35:46,200 --> 00:35:49,486 and then one large packet being resent over and over. 761 00:35:49,486 --> 00:35:53,300 That's because the victim machine knows it, 762 00:35:53,300 --> 00:35:55,221 it hasn't been received by the server 763 00:35:55,221 --> 00:35:56,880 'cause we're delaying it. 764 00:35:56,880 --> 00:35:59,280 So it's just trying to resend it. 765 00:35:59,280 --> 00:36:00,113 That's just TCP, 766 00:36:00,113 --> 00:36:03,840 but the attacker is delaying all of these resend attempts 767 00:36:03,840 --> 00:36:05,970 until it finally lets them through. 768 00:36:05,970 --> 00:36:08,370 We get a client-side desync and if it's successful, 769 00:36:08,370 --> 00:36:11,613 we'll see an nice pop-up box in the victim browser. 770 00:36:12,750 --> 00:36:14,490 So as you can see, 771 00:36:14,490 --> 00:36:16,440 I've just got the attack code shown earlier here. 772 00:36:16,440 --> 00:36:19,683 I'm gonna hit Execute JS and we'll see what happens. 773 00:36:21,090 --> 00:36:22,920 Okay, great, we can see one packet being resent 774 00:36:22,920 --> 00:36:27,330 a bunch of times and over here in about few seconds maybe, 775 00:36:27,330 --> 00:36:28,591 yes, there we go. 776 00:36:28,591 --> 00:36:31,758 (audience applauding) 777 00:36:38,271 --> 00:36:39,930 Thanks. 778 00:36:39,930 --> 00:36:42,273 So that was, 779 00:36:43,320 --> 00:36:46,803 oh, so we don't even need the backup video. (chuckles) 780 00:36:50,601 --> 00:36:53,070 All right. So yeah, hope you enjoyed that demo. 781 00:36:53,070 --> 00:36:56,193 That was the final attack of the presentation. 782 00:36:57,090 --> 00:36:58,683 Hopefully, mostly made sense. 783 00:36:59,850 --> 00:37:01,440 Let's talk about defense. 784 00:37:01,440 --> 00:37:05,400 These attacks almost all exploit HTTP/1. 785 00:37:05,400 --> 00:37:09,750 So if you can, I would recommend using HTTP/2 end to end. 786 00:37:09,750 --> 00:37:11,313 That said, if you can't use it end to end, 787 00:37:11,313 --> 00:37:13,150 don't do HTTP/2 downgrading 788 00:37:13,150 --> 00:37:16,509 because that makes things even worse. 789 00:37:16,509 --> 00:37:18,060 Now, secondly, 790 00:37:18,060 --> 00:37:19,710 I don't know how people are gonna react to this, 791 00:37:19,710 --> 00:37:24,480 but my view is, it's really easy to make a HTTP/1 server, 792 00:37:24,480 --> 00:37:29,070 but really difficult to make a HTTP/1 server that's secure. 793 00:37:29,070 --> 00:37:32,700 So I would say don't code your own HTTP server 794 00:37:32,700 --> 00:37:33,933 if at all possible. 795 00:37:35,250 --> 00:37:38,700 That said, software diversity is a healthy thing. 796 00:37:38,700 --> 00:37:41,737 So here's some advice that will help make your server 797 00:37:41,737 --> 00:37:45,930 slightly less prone to these kinds of vulnerabilities 798 00:37:45,930 --> 00:37:48,090 and may be useful if you need to patch 799 00:37:48,090 --> 00:37:50,040 one of these vulnerabilities 800 00:37:50,040 --> 00:37:52,743 and using HTTP/2 end to end is not an option. 801 00:37:55,680 --> 00:37:59,100 Now, I've got a nice bonus slide for DEF CON. 802 00:37:59,100 --> 00:38:01,860 I think this topic has serious potential 803 00:38:01,860 --> 00:38:03,270 for further research. 804 00:38:03,270 --> 00:38:06,758 So I'm gonna outline seven possible research angles 805 00:38:06,758 --> 00:38:10,492 roughly in order of the time commitment required 806 00:38:10,492 --> 00:38:13,800 and how likely I think they are to actually work. 807 00:38:13,800 --> 00:38:18,030 First off, more ways of making a browser, 808 00:38:18,030 --> 00:38:19,080 of making a server ignore 809 00:38:19,080 --> 00:38:21,330 the content-length would be really valuable. 810 00:38:21,330 --> 00:38:23,889 You can use these for both a server-side desync 811 00:38:23,889 --> 00:38:25,743 and a client-side one, 812 00:38:26,610 --> 00:38:29,850 especially, if it is triggered by a request 813 00:38:29,850 --> 00:38:31,650 that you can use from a web browser. 814 00:38:32,640 --> 00:38:34,020 Secondly, as we've seen, 815 00:38:34,020 --> 00:38:37,020 client-side desync exploitation can be quite hard, 816 00:38:37,020 --> 00:38:40,564 so more ways of building exploits would be really valuable. 817 00:38:40,564 --> 00:38:42,780 And in particular, 818 00:38:42,780 --> 00:38:45,544 the whole chain and pivot exploit path is under researched. 819 00:38:45,544 --> 00:38:47,280 It basically didn't occur to me 820 00:38:47,280 --> 00:38:49,473 until I was writing the presentation. 821 00:38:50,670 --> 00:38:52,920 So I think you might even, 822 00:38:52,920 --> 00:38:54,480 because you can send arbitrary bytes, 823 00:38:54,480 --> 00:38:58,140 you might even be able to fake a protocol upgrade 824 00:38:58,140 --> 00:39:00,690 and change protocol to web sockets or something, 825 00:39:00,690 --> 00:39:02,013 which might be fun. 826 00:39:03,039 --> 00:39:05,040 Fourth, 827 00:39:05,040 --> 00:39:07,304 currently, server-side pause-based 828 00:39:07,304 --> 00:39:10,881 desync vulnerabilities are really hard to detect. 829 00:39:10,881 --> 00:39:13,320 Your best off, basically, looking for the server banner 830 00:39:13,320 --> 00:39:15,135 that says they're using Apache 831 00:39:15,135 --> 00:39:17,640 and then trying the technique. 832 00:39:17,640 --> 00:39:20,213 So a reliable way to find this issue 833 00:39:20,213 --> 00:39:22,200 when the vulnerable servers 834 00:39:22,200 --> 00:39:25,020 behind a front-end would be really nice. 835 00:39:25,020 --> 00:39:27,970 And also, it would be amazing if you could trigger 836 00:39:27,970 --> 00:39:31,770 a pause-based desync without needing a man in the middle. 837 00:39:31,770 --> 00:39:34,740 It feels like something that should be possible. 838 00:39:34,740 --> 00:39:36,510 It should be possible to make a browser 839 00:39:36,510 --> 00:39:40,140 just still halfway through a request somehow, 840 00:39:40,140 --> 00:39:41,643 I just don't have no idea. 841 00:39:42,930 --> 00:39:47,100 Sixth, this is a valuable attack class right now, 842 00:39:47,100 --> 00:39:49,560 but it's gonna get less valuable over time 843 00:39:49,560 --> 00:39:51,930 as HTTP/2 adoption increases. 844 00:39:51,930 --> 00:39:55,423 Unless, someone figures out a way of forcing browsers 845 00:39:55,423 --> 00:39:56,883 to use HTTP/1. 846 00:39:58,410 --> 00:39:59,820 Again, I've got no idea, 847 00:39:59,820 --> 00:40:01,740 but might be possible. 848 00:40:01,740 --> 00:40:06,210 And finally, I think this one's a pretty good lead. 849 00:40:06,210 --> 00:40:10,530 Explanation of equivalent attacks on HTTP/2. 850 00:40:10,530 --> 00:40:12,903 I've seen some hints that vulnerability 851 00:40:12,903 --> 00:40:17,790 similar to client-side desync could happen with HTTP/2. 852 00:40:17,790 --> 00:40:19,650 I don't think it's gonna be as common 853 00:40:19,650 --> 00:40:22,650 'cause it kind of requires like a state machine floor 854 00:40:22,650 --> 00:40:24,300 on the server, 855 00:40:24,300 --> 00:40:27,570 but I'm fairly sure it will happen sometimes. 856 00:40:27,570 --> 00:40:29,643 And that would be quite nice. 857 00:40:31,140 --> 00:40:34,111 So there's plenty of further reading available. 858 00:40:34,111 --> 00:40:36,750 The three things I'd suggest are, 859 00:40:36,750 --> 00:40:38,070 check out the whitepaper, 860 00:40:38,070 --> 00:40:40,060 which also includes these slides. 861 00:40:40,060 --> 00:40:42,419 That's the top link on this page. 862 00:40:42,419 --> 00:40:45,997 Have a shot at the online interactive labs 863 00:40:45,997 --> 00:40:48,480 to get some real experience with this technique 864 00:40:48,480 --> 00:40:50,730 and then grab the tools I've released, 865 00:40:50,730 --> 00:40:54,480 do some scanning and find some real vulnerable assistance. 866 00:40:54,480 --> 00:40:55,470 Feel free to chat me an email 867 00:40:55,470 --> 00:40:56,870 and let me know how it goes. 868 00:40:57,840 --> 00:41:00,152 Also, there's a kind of related presentation 869 00:41:00,152 --> 00:41:02,762 tomorrow by Martin, which is really good. 870 00:41:02,762 --> 00:41:05,970 I would suggest checking that out if you've enjoyed this. 871 00:41:05,970 --> 00:41:07,525 And finally, the three key things 872 00:41:07,525 --> 00:41:11,699 to take away are the request is a lie, 873 00:41:11,699 --> 00:41:15,450 HTTP/1 connection-reuse is harmful, 874 00:41:15,450 --> 00:41:20,450 and all you need is a server taken by surprise. 875 00:41:20,460 --> 00:41:22,800 I'll take five minutes of questions now. 876 00:41:22,800 --> 00:41:24,510 If you have any more after that, 877 00:41:24,510 --> 00:41:25,720 feel free to come and chat to me at the back 878 00:41:25,720 --> 00:41:27,510 or just chat me an email. 879 00:41:27,510 --> 00:41:29,400 Don't forget to follow me on Twitter. 880 00:41:29,400 --> 00:41:30,520 Thank you for listening. 881 00:41:30,520 --> 00:41:33,687 (audience applauding) 882 00:41:43,470 --> 00:41:45,087 Any questions? Yep. 883 00:41:45,087 --> 00:41:46,270 - [Participant] Is there a chance 884 00:41:46,270 --> 00:41:49,270 on desyncing HTTP/2 services that being prompted 885 00:41:49,270 --> 00:41:51,687 by HTTP/1 service? 886 00:41:53,250 --> 00:41:55,860 - Could the attack work on HTTP/2 servers 887 00:41:55,860 --> 00:41:57,330 that have a HTTP/1 server in front? 888 00:41:57,330 --> 00:41:58,380 - [Participant] Yep. 889 00:41:58,380 --> 00:42:00,661 - Yes, you could do a client-side desync 890 00:42:00,661 --> 00:42:02,269 on that potentially. 891 00:42:02,269 --> 00:42:07,269 - [Participant] Okay. 892 00:42:09,000 --> 00:42:10,256 - Cool, thank you. 893 00:42:10,256 --> 00:42:12,868 (audience applauding)