 Welcome to this talk, when TLS hacks you. I'm actually going to start with the demo. This is going to be weird. This isn't a concrete attack scenario, but hopefully should make everyone curious about what's going on. For those familiar with SREF, the implications will be fairly clear, but I'll explain them right after the demo for everyone else. After that, the attack examples are going to gradually move towards real stuff, but I find it's easiest to look at the simplest cases and then start expanding to look at real-world stuff. For now though, let's see the essence of the dangerous property of TLS that this talk is about. So, here's MIMcached, a popular cache that Common really runs on port 11211. I have it installed here, so you can first see for the key named Z, there currently is no entry. If you're not familiar with MIMcached, this is how stuff communicates with it, a new line-delimited TCP protocol. But now I'll do something that you might not expect to affect MIMcached. I'll make a request to an HTTPS URL. The dash L enables redirects, and the dash V lets you see what all that's going on. So, you can see that the endpoint is redirecting to itself. I set this up because it's a lightning talk, and the string of redirects takes a couple of minutes. But the redirects don't go on forever. After a bit, we get an error. And now for the magic. Going back to MIMcached, if we rerun the same git command, we see an entry up here. The odd thing is that text never showed up in the curl command or in any of the redirects. So, somehow the server we were talking to used something with TLS to trick curl into putting it in there. So, even though I didn't do anything other than hacking myself, what just happened was weird and potentially useful. But here's where I'm going with this. Because not everyone is into these sorts of attacks, I'm going to start off with exactly what makes this useful as an attacker. I'll then move on to some stuff that shows just how widely these attacks can be applied, and then get some recommendations for what to do about them. So, to start off with, there's an existing class of vulnerability called SSRF. It stands for server-side request forgery. It's where you effectively fish the server into doing something by sending it a URL. Last year at Black Hat, I demonstrated a bunch of these vulnerabilities, in Apple Pay, and in web book receivers. I'm not going to go into those as much here, but if you're curious, you can download these slides later and go to the C-tape to blink right here. SSRF has a ton of different approaches, but it was actually pretty easy in some cases. Wibkit.org had a live sample implementation of Apple Pay, and it was even on AWS with the old Insos metadata service. So all I had to do was pass this IP, and I could poke around an internal API and get some tokens. But not everything was so simple. I had a lot of SSRF vulnerabilities that were almost there, but weren't vulnerable to this easier approach. Sometimes I'd have a web hook where I could hand it a URL, and the server would even fetch the URL, but not give me a response. This is called blind SSRF. Other times the request in question would be a put request, which restricted what endpoints I could hit. Sometimes you can mess with the request method by setting up an endpoint that redirects the target server around, but that's hit or miss. Other times you might get some data back, but it's validated against a specific content type, or you only get back certain fields. Or even worse, it'll only accept HTTPS URLs. But what we really want in order to exploit this stuff is a generic blind SSRF approach. In other words, we'd like to see the server URL have it fire a payload at something internal, like SMTP or Redis, and get a security compromise just from that payload without needing to see the response. Here are some of the common approaches. The issue is these are extremely platform-specific to the point where I've never had any luck with these. But there are some useful ideas in here, particularly in SNI Injection. You wouldn't think that TLS would be particularly useful because its packets are mostly just encrypted data, binary flags, and other stuff an attacker doesn't have much control over, but it's not all that limited. As Orange noticed, the SNI, which is basically just the domain name, is part of the URL. So if you can trick the victim into accepting a weird domain name and parsing it incorrectly, you can include stuff like arbitrary bytes in there and smuggle stuff like SMTP commands. This approach is dependent on a platform-specific URL parsing book, but what if we had something that was inherent to the TLS protocol? This is where my work starts. Let's look at a TLS packet and see what's in there for us to use. As is often helpful when looking at protocols, we can open up Wireshark to see what happens in a TLS connection. Here's the SNI, or server name indication, that was previously explored in Orange's talk. His work involved putting new lines in SNTP commands into the section pointed to by the arrow here. If you poke around, you'll see stuff like random strings, key shares, and other stuff that isn't useful for SSRF. Why? Because the only part of this we can typically feed a server is a URL, and the only part of the URL that will make it into the client-hello packet is domain name that we provided the victim machine. But there's a different way that we as an attacker can get payloads into client-hello messages. It involves a couple of steps and is the main focus of this talk. First, we send over a link to our own server, in this case, jmattox.com. Then jmattox.com responds with a valid server hello that it contains our payload. Now, it would be ideal if we as an attacker could get that payload that jmattox.com sent the victim, and then get the victim to send it somewhere internal. If that raises a question, is there such a field that we can use to deliver these payloads? It might seem too good to be true, but TLS actually provides exactly that in the form of a session ID. Most clients even persist this field for later use. Session IDs are limited to 32 bytes, as you can see here, but depending on the implementation, you might have session tickets. This one is about 200 bytes, but these can be up to around 65 kilobytes. Both session IDs and session tickets are mechanisms for a TLS client to go. Hey, remember that cryptographic key exchange we did earlier? Let's just keep using that key in this new connection. TLS 1.3 includes a slightly more complicated but similar mechanism called a pre-shared key identity, which pretty much does the same thing for our purposes. All of these are about optimization, since key exchanges can be time-consuming, but they provide a way for a server to tell whatever is connecting to it to persist some data for later use, almost like a cookie that lives in plain text. So it's perfect for the assertive attack we're trying to do. Now you might anticipate a problem here. Obviously, if you go to google.com and then to apple.com, those TLS sessions are going to have distinct data from each other. The same applies between google.com and localhost. So we have a bit of a problem getting the target server to reuse the session when communicating with something internal. But when I looked at both the TLS spec and popular session cache implementations, I saw this wasn't quite the case. I first went to the curl source code, since libcurl is used by a lot of PHP stuff. Sessions are keyed by the four properties here, which belong to the struct. As you can see, none of these are the IP address. This means that no matter what IP address jmatux.com resolves to, curl will attempt to reuse the session. This happens to be to have just about everything else cache as TLS sessions. So if you establish a TLS session with jmatux.com, whether it resolves to something on the internet or your own machine, your HTTPS client will not know the difference. Since I as an attacker own jmatux.com, I can combine all of this TLS stuff with a concept called DNS rebinding. So I resolve the first request to 35.x.x.x, some server I control. Then the target server picks up the payload from 35.x.x.x in the form of a session ticket. After some time, I send the exact same URL except the DNS entry has expired. Again, since I control the DNS server, this time around I resolve jmatux.com to something internal that I wouldn't normally have access to from the outside. To simplify things, I'll just use localhost. Once the DNS query finishes, the target server then makes an internal request with that payload. So like what I did in the opening demo, you can manipulate whatever you're attacking to send a TLS session ID ticket or PSK identity like this to itself. Here I'm showing a PSK identity that does a memcached insert. And here's that same TCP stream that just displayed raw. This view shows the really important property of my approach to combining TLS with DNS rebinding. You can get arbitrary characters to be sent, which includes new lines. This is what allows you to interact with a bunch of local services like memcached, and pass with gopher URLs. And here's an exchange more typical of what you use to do real damage. This is a value that in most Python memcached libraries will execute a shell command immediately once it's retrieved from a cache. This is because it's quite common in Python and other languages to use built-in serialization methods like pickle to convert values to something that can be stored. Unfortunately, built-in serialization methods are also common RCE targets. We'll get to exploitation later. But since this is pretty common, I ended up testing a fair amount of slides. I'll get to some specific ones in a second, but first I'll go over some testing infrastructure I set up, along with some tips for actually performing these kinds of exploits. So here are all the VMs I have on the Internet. Say for maybe some IP whitelisting, they need to be on the open Internet so that the target boxes can reach them. I've got a name server record pointing ssaltest.jmattox.com queries to the DNS server on the top left, which is just a Python script running on port 53. It alternates between answering with the IP of the custom TLS server and localhost. Then over there on the right I have my custom TLS server, which is there to deliver my payloads via session tickets. I configure those by just updating values in Redis. But when testing it, it proved to be difficult to just go straight to localhost, because depending on what library is making the requests, I didn't know if ssaltestions were being cached. To address this, I sometimes just ended up adding another box to see if the payload was being persisted. If you're not familiar, Netcat can be set up to just listen for TCP connections, and log the results. If I saw my payload making it into the logs, I knew the general approach could work, and then go back to the setup from the previous slide to get the server to start delivering the payload to itself on different ports. And here are the links to all that I actually used. A big chunk of the work was saved by just messing around with a pure rust implementation of TLS, which is linked to here. A big thanks goes out to everyone mentioned in this slide. I wouldn't have had the time to do any of this work if not for the code they had put out there. I know with everything I just claimed, I need to back things up by showing what actual real world attacks can happen with this. I'll start off conceptually, and then get to some examples along with another demo. But here's the amount I've got. If you're in the middle of this diagram, you're generally vulnerable. The ssalt part refers to when you can send something URL, and it will fetch that URL, and you don't know of a way to exploit that behavior. It's interesting because you would hope by now people would try to eliminate anything even remotely looking like SSRF, but this pattern is in fact required by a few popular specs. This is even common enough that I've seen bug bounties mention SSRF with no proven security impact as an excluded category. But that's what this talk is about. Adding the security impact when you have just something that is just almost SSRF. This one on the left sounds weird, but it's getting bigger. It's just a matter of if whatever the SSRF is going through to make requests has implemented this feature according to the latest spec. Unfortunately, there are quite a few HTTPS client libraries out there that support full-blown TLS session caching. Here are a few that you may be wondering about. I reported this as a vulnerability to some of these, but not all of the entries with yes. And I haven't seen much traction. Understandably, since fixing this requires diverging from the TLS spec, not a whole lot of people are eager to do so. The thing you run into the most trouble with is this last circle, because you have to first do some recon to see what ports might be internally exposed on the server. And then you need to see if what's on those ports is permissive enough to keep the connection open past the opening bytes of the TLS client hello. Here are some examples of what you might be looking for there. That's far from complete, but here are the most notable entries. So now we'll go through a couple of exploitable examples of these TLS session-based SSRF vulnerabilities that I reported and got fixed. Here's one from U-Track. It's rated low because all I did was hit local SMTP, and I didn't try poking around to see if I could exploit the SMTP server further. It originated from U-Track's import feature, which normally wouldn't be easy to exploit being blind SSRF. But with my rebinding code, here's a section from the TLS packet I got U-Track to send itself. This works because SMTP is a new line delimited protocol. So just like Memcache, you can embed working commands within the TLS ticket. And here's what came of that packet. This alone wasn't super severe because it's not signed quite right, and I didn't dig too deeply because I wanted to just report it ASAP. But it did get initiated by a TCP stream I got U-Track to send itself, so it's still pretty bad. Here's the one I reported in NextCloud. The idea behind NextCloud is a cool one. You set up a server in host files and can even share them with other people's servers using a share URL like this. But as it turns out, you can include port numbers in the share URL. Now, since NextCloud automatically puts HTTPS in there, normally there wouldn't be much SSRF potential, and it would have to be blind SSRF. But luckily, I was able to do just that. So as a non-NNN user, I could use my TLS rebinding attack infrastructure to perform arbitrary writes on them gashed. After reporting this and working through the options, there weren't any great ones because not all request libraries allow you to disable TLS sessions. In this case, LibGirl has the option, but it isn't exposed by PHP request libraries such as Gazel. In any case, adding a request timeout made the attack pretty much infeasible, assuming that people don't turn the DNS time to live values super low on their servers. NextCloud was still generous enough to give me a bounty, even though in my opinion, this is LibGirl's fault. Since the last couple were just text slides, let's see a real demo now from the perspective of a Django web developer getting fished. We're planning DNS rebinding with TLS session persistence to make it more powerful. In the past, you had to get people to visit a fully malicious page to get anywhere with DNS rebinding, but with my work, you can do the same with just an image tag. Because of that, you can fish people into RCE even if they just view an email, but don't click in any links. Let's see that now. And now for the actual demo. So as you can see, we're pertaining on a Django application developer running a typical app. If we look at the app, it's pretty simple. But if we refresh too fast, this lot needs to go to sleep. And now for what happens when I get fished. As you can see, I have received some perfectly legitimate emails and I'm viewing them. Normally, these image tags should be benign, but after viewing them, my sloth now opens a calculator via shell command. That's because those image tags point into the domain with my rebinding setup and a simple Python pick-hole payload. So here's most of the source code in the example app I created. You can pretty easily find apps that call Django.core.cache or on GitHub, but I didn't want to demo against a specific one because that seems a little mean and I wouldn't want people watching actual fishing campaigns against such projects. It's a little tough to assign blame here. Nothing is really breaking from established safe practices, but it's clear that there's a potential to compromise real-world developer laptops. On that note, there's a lot more to do that I couldn't possibly do on my own. These slides will be available online if you want to return to this, and if you do anything interesting, I'd love to hear about it. There are certainly a lot more potential vulnerabilities to explore. So all of that is bad, but what can be done about it? Let's start from the root of the problem, TLS clients. Here's something I recommended to the Chromium team a while back. We still aren't in agreement about whether or not this should be fixed, and my report was pretty hastily written, which probably didn't help things. I'd like to remove the ability for sessions to be resumed between servers. But while the Chromium team is marked this as a won't fix, they did suggest an alternative that maintains support for resuming sessions across servers. They probably care about that more than I do, but we can at least have the boast of both worlds. This last option of just partitioning based upon whether the address is something local is a pretty good compromise. It wouldn't affect performance, but would protect against most of the attacks scenarios I can think of. Based on my experiences so far, I'm not hopeful that there's going to be much proactive effort on the part of software packages that make HPS requests. For most of the audience, it's more useful to know what's susceptible and what layers of defense can be applied. However, with all of this, we're taking a step back to consider is TLS session resumption even worth it? There are other issues with it, a couple of which are shown here. I'm not going to make the case that the industry as a whole should give up this optimization, but we should take a step back and see if this is worthwhile trade-off. We may, just like in the previous slide, find that the costs can be eliminated, but if we find we can't tackle these problems, we need to weigh them against the benefits. And here's the main benefit. You get a sizable performance improvement on the small subset of packets that are TLS handshakes. You probably care about this whenever you're in the position of serving up TLS. Whatever percentage of CPU usage is due to TLS handshakes, you probably want to keep that low to minimize costs. But if you're like me, you don't care. I'm taking on all the security and privacy risk of session resumption, but the amount that saves on my power bill is pretty minimal. Additionally, if you're running a web application, you might care about session resumption on incoming connections, but when you're making external requests, it's probably not in your interests. You're placing a lot of trust on random websites not to redirect you and on your local services. So how might you turn it off? It's kind of sad when stuff forces you into this, but at least as an internet user, you can pick Firefox and disable session identifiers if you're paranoid about this stuff. If you're running a web application, you're a bit stuck, though. So what should you do? First off, it's a good idea in general to run your outbound requests through a proxy, especially if you're supporting web hooks or some other SSRF-prone feature, but also where you consider your infrastructure decisions. You'd be surprised to know how just how much stuff is still set up in a way that is just one locally initiated TCP stream away from compromise. But here are the general takeaways. The thing I'd like to highlight is if you're just starting out in the offensive side of security, I'd encourage you to look at the random standards and browser features that come out. Even if you don't fully understand them at first, find something that seems interesting and start hacking around in Wireshark. I know I didn't understand TLS at first, and I still don't really understand it very much after all of this, but as an attacker, you really only need to understand the worst parts, and that's what this is all about. And thank you. Here's my Twitter and a link to Lotakora where I work. They're a great place to work and have been amazingly supportive of random research directions I take. Definitely take a look.