Episode 163: Best Technical Takeaways from Portswigger Top 10 2025


Episode 163: In this episode of Critical Thinking - Bug Bounty Podcast It’s that time of year again! We’re looking at the Portswigger Research list of top 10 web hacking techniques of 2025.
Follow us on twitter at: https://x.com/ctbbpodcast
Got any ideas and suggestions? Feel free to send us any feedback here: info@criticalthinkingpodcast.io
Shoutout to YTCracker for the awesome intro music!
====== Links ======
Follow your hosts Rhynorater, rez0 and gr3pme on X:
Critical Research Lab:
====== Ways to Support CTBBPodcast ======
Hop on the CTBB Discord at https://ctbb.show/discord!
We also do Discord subs at $25, $10, and $5 - premium subscribers get access to private masterclasses, exploits, tools, scripts, un-redacted bug reports, etc.
You can also find some hacker swag at https://ctbb.show/merch!
====== Resources ======
Parser Differentials: When Interpretation Becomes a Vulnerability
https://www.youtube.com/watch?v=Dq_KVLXzxH8
XSS-Leak: Leaking Cross-Origin Redirects
https://blog.babelo.xyz/posts/cross-site-subdomain-leak/
Playing with HTTP/2 CONNECT
https://blog.flomb.net/posts/http2connect/
Next.js, cache, and chains: the stale elixir
https://zhero-web-sec.github.io/research-and-things/nextjs-cache-and-chains-the-stale-elixir
SOAPwn: Pwning .NET Framework Apps Through HTTP Client Proxies And WSDL
https://watchtowr.com/wp-content/uploads/SOAPwnwatchtowr_soappwn-research-whitepaper_10-12-2025.pdf
Cross-Site ETag Length Leak
https://blog.arkark.dev/2025/12/26/etag-length-leak
Lost in Translation: Exploiting Unicode Normalization
https://www.youtube.com/watch?v=ETB2w-f3pM4
ORM Leaking More Than You Joined For
https://www.elttam.com/blog/leaking-more-than-you-joined-for/
Novel SSRF Technique Involving HTTP Redirect Loops
https://slcyber.io/research-center/novel-ssrf-technique-involving-http-redirect-loops/
Successful Errors: New Code Injection and SSTI Techniques
https://github.com/vladko312/Research_Successful_Errors
====== Timestamps ======
(00:00:00) Introduction
(00:02:33) Parser Differentials: When Interpretation Becomes a Vulnerability
(00:11:02) XSS-Leak: Leaking Cross-Origin Redirects
(00:18:25) Playing with HTTP/2 CONNECT
(00:22:10) Next.js, cache, and chains: the stale elixir
(00:29:15) SOAPwn: Pwning .NET Framework Apps Through HTTP Client Proxies And WSDL
(00:34:27) Cross-Site ETag Length Leak
(00:41:47) Lost in Translation: Exploiting Unicode Normalization
(00:47:27) ORM Leaking More Than You Joined For
(00:54:07) Novel SSRF Technique Involving HTTP Redirect Loops
(00:58:40) Successful Errors: New Code Injection and SSTI Techniques
Title: Transcript - Thu, 05 Mar 2026 15:32:23 GMT
Date: Thu, 05 Mar 2026 15:32:23 GMT, Duration: [01:08:24.59]
[00:00:00.96] - Justin Gardner
We're reading through this research and we're like, oh shit, I can definitely hack something I'm hacking on right now with this.
[00:00:29.69] - Justin Gardner
i'm not sure y'all know this, but two of the most respected hackers in the CTBB community, BusFactor and XSS Doctor, are now running monthly hackalongs on the CTBB Discord. Okay, you gotta check this out. ctbb.show/discord. We find bugs almost every time we hack. It's crazy. And oftentimes it's not even the people running the hackalongs, it's the community members that are hacking along with us. It, you definitely increase your chance of finding a bug by being on these hackathons. So check them out, ctb.show/discord, join Bus, XSS Doctor, and yours truly, and let's pop some bugs. All right, let's go back to the show. All right, man. Well, we just had a false start where I didn't press the record button.
[00:01:11.98] - Joseph Thacker
Yeah. I made so many good jokes.
[00:01:14.14] - Justin Gardner
Oh my gosh. But we're, we're, we're back. Um, guys, this is the Port Swigger top 10 Web Hacking Techniques of 2025 episode. And guys, I know I complain about it every year, but this episode is a monster to prep for because these are very technical pieces of research. And now we are going to try to articulate them to you in the audio medium, which is still challenging after 3 years.
[00:01:39.95] - Joseph Thacker
Yeah. Hacking is kind of like text first in the same way that coding is, right?
[00:01:43.87] - Justin Gardner
Yeah. Yeah. So. It's going to be a beast, but we're going to give it a shot. There was a lot of other awesome research also in the nominations, which I think we definitely should go back and review. I think 100— no, 63 pieces of research contended this year, which is significantly fewer than 121 that was submitted last year.
[00:02:07.09] - Joseph Thacker
Yeah, you and Brandon should do an episode on that.
[00:02:10.44] - Justin Gardner
Yeah. I see you trying to weasel your way out of that research. All right. Well, let's, let's get to it, man. We've got it kind of divvied up by capability rather than by like order. So what's going to happen is Joseph is going to take 10 and 9. I'm going to take 8 and 7 and we're going to kind of go back and forth. So we're going to jump around a little bit. But first one up is parser differentials when interpretation becomes a vulnerability. And you're going to take that one, right?
[00:02:42.58] - Joseph Thacker
That's right. Yeah. So this got 10th place. It's a video. Not— it's not like an official white paper or anything like that. Yeah, it's just a YouTube video. It was a talk at OffensiveCon 25 by Jorchen, but he is a GitLab engineer and it's a pretty great talk. I think I really love parser issues. And so it's the reason I grabbed this one because I got into the doc before Justin did and was able to snag this one. So, I mean, you should just go watch the talk, anyone. I mean, it's not that long. It's 28 minutes. And I'm sure if you're watching on 2x, it's even faster. But what's really neat was that he looked at a bunch of different parser differentials. And he was mostly thinking about them in the differences of like, if there's a tech stack that has like Go running at one point and then Python later, how does it handle specifically YAML? Or actually, sorry, no, YAML was one of the later ones. Yeah, no, yeah, almost like the, the bulk of what he found in the end. But, um, he was looking at different things between like Kubernetes and Java and other things. Um, but I just wanted to bring to the audience like basically the, the lowest hanging fruit here, like the stuff that's like most valuable to try. And basically the first one was just parameter injection. So there was the two role parameters. One was like sending it to admin, one was just setting it as like role like an empty list.. And because the, the parsers handled it separately, um, it basically bypassed the JavaScript check, which was, was checking the second one. And then whenever it actually got to the Java implementation, it, it read the, like, the backend server-side code, read the first one, set them to an admin. And I think I can actually share my screen on that exact screenshot. Yeah, let me share it real quick for the people who are watching on YouTube. Um, this one, right?
[00:04:27.32] - Justin Gardner
So this is in a parser differential— in HTTP rather than in YAML or something like that?
[00:04:33.45] - Joseph Thacker
No, it's JavaScript and Erlang. I don't know where it was actually— like what repo—
[00:04:40.81] - Justin Gardner
Oh, I know what it is.
[00:04:41.77] - Joseph Thacker
It's JSON here. Yeah, that's what I'm saying. It's like a JSON input. And Erlang, like whenever it's parsing this, it accepts the first role. But whenever JavaScript is parsing this, it accepts the latter role here. So, for the listeners, basically, it's what I said just a minute ago. It's just the key is roles and there's two of those keys. And the first one is a list that includes admin and the second one's not. And so you're able to get a, you know, JavaScript saw an empty array, whereas Erlang saw the _admin role and granted that to the user. So, um, you know, kind of neat, pretty cool. We've seen stuff like this before. Um, the second one, the second example he thought he showed, which I thought was pretty neat, was a double auth tag, a double authorization header. I bet this exists in way more places. So the first one was a non-signed fake admin JWT and then the second auth header was a legit bearer token that the application gave you. And you could put— exactly, it's really cool. You could put basically attributes that you want into that first non-signed one. You know, I think all of us have tried this, but I've only ever done it with one, right? You change the legit one to non-signed.
[00:05:46.25] - Justin Gardner
You don't do both. Wow. Okay, so he's smuggling in a second authorization bearer and the middleware, you know, the front piece is checking for that second bearer, which is a legit JWT signed lowpriv, but the backend is actually grabbing the auth bearer that is algorithm none with all of our stuff in it, and that's not checking the signature and it's just accepting all of our crazy attributes.
[00:06:10.51] - Joseph Thacker
Yep, that's exactly right. Dang, dude, it's really cool. Yeah, and then the rest of this talk is really neat. It's all about YAML, and there's a bunch of cool things that YAML had that I didn't know. So one is this like binary thing. You can just like prefix a line with like !!binary and then that key, or it's like sometimes it's keys, sometimes it's values, get like, are allowed to be interpreted as base64 encoded content. Oh, it's really weird. So for the listeners, I'm sharing on screen a couple different things, but he, uh, yeah, yeah.
[00:06:45.20] - Justin Gardner
So that, that one is— go back to that really quickly.
[00:06:47.25] - Joseph Thacker
Okay, so here, well, here, let me read this to you. The bang, the bang bang binary tag can be used to base64 encode arbitrary binary data inside of YAML. So So I think this is most useful for like sending in like audio or images or things like that.
[00:07:03.06] - Justin Gardner
Wow. Yeah, that makes sense. So essentially, and you can use that in a key or in a value, right? So it's like an operation that is being run on top of a, you know, the value that is being provided in YAML here. So in the example right there that you've got up on the screen, it's saying lang is Python. Okay. Okay. Okay. It's saying lang is Python, but then it's doing bang binary and then providing the basic 64 encoded version of Lang.
[00:07:31.93] - Joseph Thacker
Of Lang engine.
[00:07:32.85] - Justin Gardner
Yes.
[00:07:33.20] - Joseph Thacker
Yes. Oh my God. And then bang binary and then it again. And so the output of this, as you watch here, or as like the listeners, is like, I don't know why this is first and second instead of— he should have kept this going with the same vernacular. But I see what you're saying. But anyways, yeah. So basically you can trick things. And then later on he does this other thing with it's Merge, or actually, so actually I'm— I get this a little conflated because listeners, honestly, I took this and I told Claude Code, extend this research and look for more stuff like this. There's another thing called Merge, and I can't remember if he used Merge or if my Claude Code thought of the idea of using Merge, but there, there is a YAML, and there's a YAML thing called Merge. Yeah, I think it's totally my Claude Code and not in this talk. Basically, there is a, um, another YAML declarative or whatever it's called function that's like greater— it's less than, less than, and it merges the next key that you have into the first key. And this is also very problematic for parser differences. So anyways, pretty cool talk. I think people would get a lot out of it and you should definitely go watch it, dude.
[00:08:43.58] - Justin Gardner
Yeah. What the heck are these shorthand tags in YAML?
[00:08:46.95] - Joseph Thacker
Do you just want me to share? Do you want to share?
[00:08:49.50] - Justin Gardner
No, no, no, no, no. You go ahead. I'm just like, you got me nerd sniped a little bit right now. How— wow. There's like all sorts of transformations you can do on YAML. Man, there's just going to be an absolute insane number of problems because of this.
[00:09:07.42] - Joseph Thacker
Yeah, actually, this is the table I showed you. There's lots of crazy stuff too where like Uh, if you put like yes or no or true or false and like in some it gets interpreted as a string and therefore results out as true even though you wrote the word false, but it's a string and strings equate to true. Like dude, there's so many YAML parser issues. And what's— what I'm really hopeful on this with, with my own research and finding is that, um, in places like GitHub CI/CD, this will be like mega critical, uh, as far as actual impact.
[00:09:38.08] - Justin Gardner
Wow, dude. Yeah, that's sick. So definitely something to look into there. I'm very surprised to see that being used in keys as well. Right. Cause the, the YAML file that you showed, you know, it defined an attribute lang equals whatever, or lang, um, colon, whatever, but then it provided a binary version of lang, right. And then provided an alternative value. And then depending on how those are prioritized with the binary decoding, one may do the, the, you know, tag resolution before parsing the line above it, or it might be after. That's right. So, so much opportunity for parser differentials there. Yes.
[00:10:14.60] - Joseph Thacker
That's sick. Very sick.
[00:10:16.08] - Justin Gardner
Wow.
[00:10:16.20] - Joseph Thacker
And I just, and you know, of course I love AI stuff. All the AI stuff is Markdown to HTML, same exact thing, right? A bunch across a bunch of different libraries. And so I want to actually apply the same research logic that he did and that I had Claude Code do like as like a follow-up to that research to the Markdown to HTML parser. So I'll let you know if that finds anything.
[00:10:37.19] - Justin Gardner
Yeah, dude, it was so funny because I did the exact same thing that you did with this, which is like we're reading through this research and we're like, oh shit, I can definitely hack something I'm hacking on right now with this. So you just like, you know, kicked off a Cloud Code and I'll explain the one that I just did later. But I kicked off a Cloud Code instance as well and it found a vuln while I was reading this research, which is nuts. So crazy world we're living in here. It really is. All right, number 9 is playing with HTTP/2 Connect. I'm gonna let you take that, but first I'm gonna jump down to XSS Leak, leaking cross-origin redirects. And this was kind of an interesting one because it's called XSS Leaks, but it doesn't have anything to do with XSS. It's a cross-site subdomain leak, which is a variety of XSS leak, okay? And essentially what this research is about is leaking cross-origin redirects. Uh, redirects, which is a very specific situation. Uh, it's kind of CTF-y, to be honest.
[00:11:35.29] - Joseph Thacker
But, um, the website is a little CTF-y too. Yeah, it looks great. If you look at that, for all the listeners, it's like a beautiful, like, um, yeah, purple and black website that's like very typical, like, hackery.
[00:11:47.86] - Justin Gardner
Yeah, terminal vibes. Uh, yeah. Um, but, and there are some, some applications for this research for sure. Like, I've run into a situation where I need to know a subdomain like this in cloud environments where it'll generate like an arbitrary subdomain or whatever. And if you can leak that subdomain itself, then that will be helpful for like exploiting an XSS or something. But essentially what this write-up is focused on is how do we abuse the Chrome connection pool to leak those cross-origin hosts. And the TL;DR behind this is that Chrome has a connection pool cap at 256 concurrent requests and 6 requests for a given origin, okay? So what the attacker here does is he fills all the way up the Chrome connection pool and then abuses the order that free spots are filled within Chrome, okay? So you might think it's like first in, first out. It's not, that would make too much sense. The way that Chrome actually does this is it will sort by port, scheme, and then host, right? So if you have, let's say you have 255, you know, concurrent requests in the connection pool and a slot opens up, right? And there's 2 requests pending. Uh, Chrome will first look at the priority of those requests, right? Which we can override to high using like fetch. Um, and then it will, you know, if the priority is high for both of them, then it will then sort by port, then scheme, then host, right? So the lower port is better. So if you've got a request to port 80, that's going to be prioritized over something that's, you know, port 1337 or something like that. But the real magic comes in when you make the port and the scheme the same. So all you're doing is actually having Chrome compare the host lexicographically. And I had to look up that word, Joseph, lexicographically. But essentially, I can't say it. Yeah, well, I was also mispronouncing it, so I had to look up how to say it, you know, before this episode. But essentially, it's just like in alphabetical order, right? So if you have a host that, you know, starts with an A or whatever, that's going to be prioritized over a host that starts with a B, right? And because of that, you can do a binary search and figure out whether the request on the cross-origin page was issued or was let into the connection pool first or whether your request was. So here's how you do that, okay? So first you fill up the connection pool, then you use, in this scenario, it was a hash change event to trigger a cross-origin request, okay? So now that request is requesting to get access to the Chrome connection pool, but then it cannot. Right? And then we also at the same time trigger our guess for what that subdomain might be. Right? So now there's two requests competing for that, for an open slot in the connection pool. Then you release one of the slots in the connection pool and you trigger how long it takes for your request to go through. Okay? And if it's a longer piece, you know, if it takes longer, then you know that it actually let to send first the cross-origin request through and then your request through. Right? And if it's shorter response time, then it knows that, you know, your request got priority over the cross-origin request, right? Which means your request was lexicographically smaller than the one that you wanted to trigger on the cross-origin request, right? So this gives you a binary yes or no on whether your guess is lexicographically greater or less than the target origin. And you can then just perform a binary search on that to very, very quickly extract the full subdomain of that target page. So not going to lie, it's pretty, pretty big brain, dude. And I had to read it like 3 times to understand it. I think there's very few situations where this will be helpful. But one of the things they did I wanted to, you know, mention as well is that you can use this to determine things about the user, sort of like a cross-site leak on whether the user is an admin or not. Um, and that just requires, you know, you being able to do one or two of these things by— because you already know the host. Exactly. So you can just hit, you know, slash slash login or whatever, and then that makes the user redirect to either admin.localhost.com or you know, regularuser.localhost.com. And by, you know, creating a request that splits the difference between those two, you can determine where the user landed, which will give you information about the user. So there's lots of sort of hackery ways that you can abuse this, but understanding the nuances of the Chrome connection pool is yet another cross-site leak vector that we have in our pocket. So I don't know if it'll be useful to anyone, but that it definitely was for this CTF.
[00:16:55.34] - Joseph Thacker
Is it possible that— I'm just wondering, like, if there are ever— um, so this can't— this doesn't do anything with parameters though, right? No, no, just the host thing. Well, if there's ever a secret stored as a subdomain, then you could binary search it out. What I was thinking was, like, if it worked in parameters, if there's ever, like, an endpoint that takes a secret as a parameter and then like does something different if it's valid versus if it's not, then you could binary search it to exfiltrate out the parameter. Yeah.
[00:17:25.27] - Justin Gardner
Yeah. There, there are some weird situations with some of Google's, um, like user-generated content stuff that where the actual hash of the content gets placed as a subdomain and they use that as an isolation factor. So there could be some applications there. There could be some applications in cloud where you need to know, you know, the, the subresource that is being loaded there. Um, so yeah, it was a cool, it was a cool one for sure. I definitely see why it made the, the, um, the top 10. So shout out to Salvatore and, uh, shout out to, uh, CTBB's own Jorian as well, uh, who was able to solve that, uh, in the CTF it looks like.
[00:18:03.61] - Joseph Thacker
Hey, question, what happens if the host also matches?
[00:18:08.29] - Justin Gardner
I don't know, man.
[00:18:09.18] - Joseph Thacker
Yeah, it's probably just some Chrome, it's just in Chrome logic, right? Maybe it becomes random or something, but I, that I was just curious if they touched on that.
[00:18:14.82] - Justin Gardner
That is interesting. I think it would actually— I think it's a hash, actually. So I think it would just overwrite. But that doesn't make any sense. It would just conflict. I don't know. Maybe that would trigger an error or something. That's an interesting edge case. Yep. All right.
[00:18:28.06] - Joseph Thacker
You want to jump back up to 9? Yes. Yep. So, um, until we just did a whole another breakdown of this, uh, personally, me and Justin off camera, we— where we realized the actual impact of this.
[00:18:39.47] - Justin Gardner
You can't call us out like that, man.
[00:18:41.02] - Joseph Thacker
Yeah, I can. It's fine. The audience needs to know the back back room, the back, what's it called, the back office talk that happens here. Yeah, yeah. So basically this was a, this is a blog by Flomb, F-L-O-M-B, on their blog which is blog.flomb.net. But the connect verb, first of all, I just love like underlying stuff, right? Like HTTP verbs and stuff like that for some reason, like they're just so OG and cool. But anyways, yeah, and you just, you don't get to use them that often. Like when trace works, it's, uh, it's always like neat. And so anyways, this is the connect verb, and in HTTP/1, the CONNECT verb was just like kind of like just a raw connection. Everything from the client went to the server, everything from the server went to the client. But what he talks about in this is how HTTP/2 CONNECT— sorry that I speak so fast, listeners— it, um, it's completely different. And so it uses this binary protocol where the fundamental unit is called a frame, just like usual, and the frame carries like a stream identifier, and there's like Um, yeah, a bunch of different fields. This is the structure of HTTP/2. Yeah, that's right. And it, it even like does like a, like a settings setup at the beginning. Uh, but the TL;DR is that it's complex and it's like, so there's, you know, lots of room for new things to happen, basically new vulnerabilities or just new issues to happen.
[00:20:02.24] - Justin Gardner
So what I think the, uh, the, the key line is here. I'm going to read from it. It says streams allow multiplexing, which means a single HTTP/2 connection can host multiple simultaneous connect tunnels, right?
[00:20:15.35] - Joseph Thacker
Yep. And what's really interesting is it allows you to connect to arbitrary IP and ports through that tunnel. So what this person did, which was they built a scanner in Go, and even though it wasn't like good support for HTTP/2 yet, there is a package and they basically Swiss Army knifed what is effectively a port scanner and full-blown SSRF through the connect verb. Yeah, I don't understand how this is not just like a straight up vulnerability. I guess if you're supporting HTTP/2, you should just block the connect verb because I don't even know where you would add the logic to protect against this. But they show here that whenever it's a successful connection, it shows a status 200. If it fails, it shows a headers frame with a status 503. And so after this, if you listen to this podcast, you should go check if any of your bug bounty targets support the connect verb on HTTP/2, because if they do, you can basically scan via SSRF for any internal hosts.
[00:21:15.55] - Justin Gardner
Yeah, it was kind of weird, you know, cause we read through this thing and we're like control-Fing for SSRF. Like we can't find, you know, why he's not like just calling this SSRF. And I guess it's because it is actually sort of like the intended functionality of the, you know, HTTP server at that point, right? Doing these connect things. But I mean, if you scroll down to the bottom, he's like, yeah. And then you can just write a raw HTTP/1. You know, 1.1 request here and then just request example. Yeah, example.com. Um, and that'll be returned back to you. So, uh, definitely another attack surface here for HTTP/2, um, to go after these guys.
[00:21:51.41] - Joseph Thacker
Is that also basically a free proxy? Like, could you just like use this as Tor nodes? Yeah.
[00:21:55.80] - Justin Gardner
Any company that has connect turned on, just like— I'm sure it's rare, bro. I'm sure it's rare.
[00:22:00.17] - Joseph Thacker
Okay.
[00:22:00.28] - Justin Gardner
It's gotta be rare. But it is, it is good to see nonetheless. Um, So I'll definitely add that to the mass scanning for sure. All right. Any, any final callouts on that one or should I jump to the next one? No, no, let's move on. All right. Next up is the OG research, of course, that we've covered on the pod before. Next.js pwnage by Zero. Like, like I said, we've covered this, but I do want to go back and kind of break it down again because there are some things that I realized after looking through it as a part of the top 10 list. That I think are really valuable. Okay. So of course, um, this research was, you know, caused a big, a big problem in the Next.js community and essentially was a cache poisoning vulnerability, um, that allowed you to just DoS any page and, uh, later on get stored XSS, um, under a certain set of conditions. Um, and this vulnerability breaks down to two component parts. One, getting the route perceived as a data request, which I'll, I'll talk about in a second. And two, making sure that the correct, uh, cache control response header was getting passed through. And since he was able to do that, he was actually able to affect the, um, internal caching mechanism of Next.js as composed or as, uh, compared to, you know, how we're normally doing cache poisoning, which is the difference between a CDN and a backend server, um, and break any Next.js implementation that was just sitting out there in the raw. Um, which was pretty sick. So, um, first I just wanted to double-click into that, um, internal cache poisoning piece, which is like not something that we've seen a ton of. And these frameworks like Next.js have their own internal caching mechanism sometimes. And that is what Zero is going after here. Um, so when you're doing source code review of frameworks, uh, this is a really good thing to look at, uh, because it can often result in, this sort of, uh, internal cache poisoning scenario.
[00:23:54.23] - Joseph Thacker
And I feel like everything, everything now with AI writing code and stuff, and then also just companies wanting to converge onto the biggest frameworks, things like Next.js are super impactful because there's so many sites running on them.
[00:24:06.43] - Justin Gardner
Totally. Yeah. And so, I mean, targeting these frameworks as a whole, I think is just a really good idea and it worked out really well for him. I think he said in here, let's see if I can find it. Yeah. Uh, this led to numerous reports being submitted cumulatively amounting to a beautiful 6-figure sum. In bounties. So he definitely like cleaned up with this. Um, and so yeah, that's the first piece is like, make sure you're looking for that internal caching mechanism piece. And then the other thing here, which, uh, Xur does a really good job of is looking for, um, these internal, uh, query parameters and headers. So Next.js here is looking for this, um, uh, underscore underscore next data request or req query parameter. And that is being used to determine whether this is a data request or not, right? And what, what a data request is, is a request that should be responding, you know, with, with just the data associated with this endpoint, not the like HTML, you know, that goes back to the user to be displayed on the page. Yeah. Um, and for me, that's kind of reminiscent of like the Ruby on Rails, you know,.json thing, right? Where you're able to like hit an endpoint, it'll return HTML. But then, you know, if you hit.json at the end, then it'll just dump a bunch of JSON data back to you for that endpoint, right? Yep. Yeah. And, and so like, I feel like there's definitely other situations where this sort of data request concept is, um, is present. Um, so I could see that being applicable to other frameworks. Um, and then the other, uh, thing he identified here was this X-Now-Route-Matches header. And what that allowed him to do was gain control of the cache control header, um, to get a value where he could get this actual, you know, um, data request cached. And, um, so as I was reading through this, I was like, yeah, but like, you know, that, that next data request query parameter is going to be a part of the, the cache key and that's going to, you know, hack it up. And that's actually a really essential part of this is that Next.js is not actually considering, uh, the query parameters as a part of the cache key. Um, which is, I think, fairly unique, uh, because I think normally they are, but not in this scenario. Um, but what comes along with that is that now you don't have a cache buster, right? So how the heck do you not just like nuke the homepage of these websites when you're trying to detect for this? Right. Um, And that's where the technique, another technique that I really liked came in, which is Zero, because he didn't have a cache buster that he could put in the query parameter, he actually addressed this problem using the Accept-Encoding header. And so the Accept-Encoding header was a part of the cache key here. And if we send them, I'm going to read from the actual write-up here. To address this, we can check if the Accept-Encoding header is a part of the cache key. And if it is, we can send the malicious request without the Accept-Encoding header, allowing us to check if the site is vulnerable without impacting it, right? Because the browser will always add that header to the request, which is gonna make sure that the normal users don't get hit with that same cache-poisoned page. That's so genius.
[00:27:21.10] - Joseph Thacker
What do you think he would've done if he didn't have that opportunity?
[00:27:24.19] - Justin Gardner
I don't know, man. He would've had to find like, he would've just had to nuke shit left and right and things would've been going down. It would've been bad.
[00:27:30.52] - Joseph Thacker
Seriously? Seriously, it's just like, oh man, how convenient. There just happens to be a header that allows you to test this without bringing down everything. Yeah.
[00:27:37.17] - Justin Gardner
No, I think that's a really great technique. And, um, so at the end of the day, you know, what he ended up doing was, you know, being able to hit the endpoint with the underscore underscore next data request equals 1 and the X-Now-Route-Matches header. And that would return a cacheable JSON representation of that specific page, right. That has some of the data passed into it. Um, and so then that JSON representation would be cached. Anybody going there, you know, to access the HTML version of the page would just see the, you know, JSON version, which would cause, you know, catastrophic problems. Um, and then later on he was actually able to get stored XSS because it was still actually rendering that JSON with the text/html content type since the underscore Yeah, exactly.
[00:29:21.40] - Joseph Thacker
So it's like, oh no. Genius.
[00:29:23.44] - Justin Gardner
And then anything, any data passed into that, you know, JSON blob could be used to trigger.
[00:29:27.83] - Joseph Thacker
So he said a lot of the times the user agent was actually getting passed in, you know, into that JSON. Um, and then, you know, triggering on the page of stored XSS, which is just like an absolute nightmare situation for, for Next.js. Absolutely insane, dude. Yeah.
[00:29:45.40] - Justin Gardner
So that's a really good— Zero puts out so much stuff. Yeah, it's, it's good, man. Um, it's really good stuff. So definitely a good thing to take a look at, uh, if you're ever targeting Next.js or if you're gonna go look at a framework in general, these internal caching mechanisms, these, um, you know, internal, uh, headers and query parameters that affect the internal functioning of these frameworks.
[00:30:03.42] - Joseph Thacker
Really, really valuable scope, I think. Yeah. Genius. You wanna jump to the next one? Yeah. Um, you wanna take it or you got it? Yeah, exactly. So it's like, oh, no, genius. And then anything, any data passed into that, you know, JSON blob could be used to trigger. So he said a lot of the times the user agent was actually getting passed in, you know, into that JSON and then, you know, triggering on the page of stored xss, which is just like an absolute nightmare situation for Next js. Absolutely insane, dude.
[00:30:23.29] - Justin Gardner
Yeah. So Zero puts out so much stuff.
[00:30:24.94] - Joseph Thacker
It happens every year, man. I think Joel last year got like a 120-page paper as well that he had to go into on.NET stuff. It's always the.NET stuff that's just so long and complicated. Yes. I'm not going to share my screen. It's 93 pages. I don't need to scroll through it, but I did actually use AI to process it and understand it really well. I'll give you a good overview. If you're obsessed with.NET or you think this is interesting, then you should go dive into it.
[00:30:47.01] - Justin Gardner
Basically, actually, let me get a little bit of author attribution here. So this is from Watchtower by Piotr Bazydlo. Oh dude, yeah, this guy's a beast. This guy's a freaking beast.
[00:30:55.75] - Joseph Thacker
Yeah, he's the principal vulnerability researcher at Watchtower. So, uh, the full title is Pwning.NET Framework Applications Through HTTP Client Proxies and WSDL, which by the way, I loved finding WSDL files back in the day all over Yahoo. I don't really find them much anymore, which is kind of frustrating, but Did you ever run into those? Oh yeah, dude, those things were great and they really just give a bunch of insight. So I'm excited to see how he's going to handle these here because I've read, I've read a bunch of his stuff, but I have not sorted through this 93-page paper. So yeah, so it's a quirk in how.NET handles HTTP client proxies and then the WSDL. So the— and just upfront, I know sometimes I think things are way more interesting when you state the, the impact first. So the downstream impact of this was arbitrary file write then NTLM, like credential theft, and then actually RCE. And so, and there's some drama around it, but I'll get to that in a second. So the core vulnerability is basically there's some code that is casting input into, and the classes that it's in, it's HTTP WebClient protocol. But this class is used to cast like SOAP messages into HTTP web requests. And whenever it's initializing it or whatever, it's always attempting it to cast it with the as operator. Operator. And what that means is if instead of using like an HTTP URI, you use a file or NTLM or whatever URI, it gets cast to a different object type. Dude, I did see this. I did see this. And here's the really weird part is that when it fails, if it doesn't cast, it returns null. But then it—
[00:32:20.30] - Justin Gardner
and then there's no checks.
[00:32:21.54] - Joseph Thacker
Oh, at the end it does check if it's null, but if it is null, it just skips the HTTP-specific setup stuff, but continues in the rest of the flow without throwing any other errors. So, so your object, which you got past all of the HTTP processing, is now just going to be processed normally by the rest of the code. And it only skipped the part that would have broken and saved the day had it actually occurred, right? That's amazing. Yeah. So it gets like, so with the file operator, it gets cast into a file web request instead of an HTTP web request. So then it skips the HTTP processing, but then the rest of the code still basically just writes it straight to disk. So you're able to write the XML straight to disk. And when you do that, apparently you were able to— I didn't actually get that deep into the document, but apparently he's able to store that as a CSHTML or an ASPX file. And so then it's just an instant web shell. Yeah. And then, yeah, which is amazing, right? And then in the cases where that doesn't work, you can supply UNC path. And when the server tries to write to that UNC share instead, you get the NTLM hash, like auth hash from that request, and then you can go crack it offline and then now you've cracked the NTLM hash.
[00:33:24.91] - Justin Gardner
So yeah, yeah, insane. Oh my gosh, dude, this is— yeah, this is lit. I— so I realized what happened with this. There was a, you know, I don't know, friendly version of this research that was in a, you know, in a web write-up, not like a 93-page research paper. And that was what I read. And I just, and I think we actually even covered it on the podcast where, you know, it's just like, oh, just like yeet this whole HTTP request into a file, you know? Um, so dude, I freaking love stuff like this, man. It is, uh, it is hilarious to see that this actually worked and that it skipped that one block of code that it needed to actually break everything.
[00:33:57.41] - Joseph Thacker
Because if it tried to parse it and it was a file instead of a web request, it would have just broken at that point.
[00:34:03.90] - Justin Gardner
So anyways. Wow. All right, dude. Um, okay. So back to number 6. Definitely, definitely gonna, uh, take a look at that again and understand a little bit more about, um, when this is actually applicable. Uh, it, it says it's set up here. It's basically a quirk in how.NET handles HTTP client proxies. So whenever we're dealing with client proxies and stuff like that, that's when we should look into this and maybe get our cherry file right. Yes. And our C. That's crazy, man. Um, Okay. Where am I jumping? Am I jumping back up to 6? Yes. All right. Jumping up to 6. Uh, we actually also covered this on the pod. Um, but this is the, um, research by, uh, Kaneko Takeshi, uh, which is cross-site ETag length leaks. Um, and I actually feel like last time on the pod, we did a pretty good job of explaining how this works. Yep. Um, so I'm not going to like go super in depth on this. But I did want to highlight a couple things that I just want to make sure you guys understand about this because I think there are some misconceptions about how— Yeah, well, I think a lot of these things that are originating in CTFs have some pretty difficult preconditions that are, of course, are contrived for the CTF purpose, right? But are not always present in the actual exploitation scenarios. The first piece here was that if you read this cross-site ETag length leak and had a difficult time understanding it, one of the preconditions for this attack to actually work was that you have to have a CSRF that can allow you to control the length of the response where you are doing this, you know, cross-site request here. So, you know, what they do here is they use— let's see if I can find it here. They use a CSRF to create notes, right? Which you can see right here for those of you watching on YouTube. Where they, you know, butt the, you know, the response size of their guess right up to the ETag juncture point. And the reason that's important is because the way that this cross-site leak is triggered is by creating a 1-byte differential in the ETag header. Mm-hmm. Right? Which is returned in the response of the request. And then gets included by the browser in the next request that gets sent to that same endpoint. Mm-hmm. And that 1-byte difference, you butt right up to the Node.js 431 maximum request size. Mm-hmm. You know, buffer, right? Where it says, okay, we only can have 16 kilobytes as the request size. And if you go over that, we're just gonna return a 431, right? Mm-hmm. Um, so it's very important that you have a CSRF to be able to get the response size right up to, um, a certain juncture point where just your notes and the actual secret, if searched via a cross-site search, will push it over that, um, juncture point with the hex representation of the content length., and create a, you know, 1-byte difference in the ETag header, which will then result in the 431. Um, so it's kind of a monster to explain from a verbal perspective, but what I want the user to understand here, or the listener to understand here, is that the CSRF is a really important part of this exploit. So, just keep that in mind. And really what I, what I think is more applicable for bug bounty is the Chrome History API piece here where the 431, if you're able to trigger it, right, which is an error status code, request header fields are too large, Chrome is not embedding that, is not considering that as a navigation in the Chrome History API. It is actually replacing the current navigation. So if you are able to trigger this, you will not see a change in the history length. It will just be the same. And I think that is a very applicable piece that can be used in other cross-site leaks in other areas, in other applications. That's a little bit more applicable than the whole ETag piece here. So I know that that's a little bit complicated. Joseph, did you get any of that or am I just like, am I talking to the wind here?
[00:38:28.03] - Joseph Thacker
No, it's perfect. I think that, like you said, the initial explanation that we did on the other episode was really, really good. And then on top of that, I think that the clarification— the most important thing you're saying is that basically in this case there was like— it was a bit contrived. It's not that realistic that you'd have CSRF where you can control the content length in that type of granularity. Yeah, but I do think, I do think that really blogs like this, when you read them, what it helps you hone is your ability to spot, um, things that should not be possible, right? Like, I think that anomalies here— yeah, anomalies like, like, you know, the fact that the ETag can like move by one character and that that's influenceable cross-site is very interesting. And I think that things like that will lead to bugs when people are thinking about, you know, specifically like the little gadgets they have for anything that happens cross-site.
[00:39:21.69] - Justin Gardner
That's a great point because it's actually that, and that goes to another point that I had on this too, which is, you know, if the ETag header that is returned in the response, right, is actually getting reflected into the If-None-Match header when you re-request the same URL, right? And so actually what's happening here is something that's extremely unique, which is, you know, a response header is being reflected back into a request header. And that request header is being used to, you know, overflow the request header maximum size, right? Mm-hmm. Um, which is, I think, very interesting. And that you don't have access to that If-None-Match header value. You know, if you did, even though it's a part of the request, you know, you don't have access to that. Uh, you have no control over it. That's something that's controlled by the browser. So I think that principle is a lot more, you know, cross-applicable to other, uh, exploitation scenarios probably than exactly this ETag scenario. Right. Yeah. So anyway, not to shit on the research at all. It is like mind-blowingly beautiful. As a client-side hacker, when I read this, I weep tears of joy. Yeah. You know, because I'm like, wow, what a masterpiece. What a breakdown of primitives, right? Like, you know, in the one byte differential allows you to, you know, exploit this. So yeah, I think that it still makes me very happy to read for sure.
[00:40:41.76] - Joseph Thacker
Oh yeah, it's insane that's even possible. Yeah. All right, we got 20 minutes. We got to crank these out. I have 2 left and you have 2 left. Perfect. All right, number 4 is Lost in Translation Exploiting Unicode Normalization by our boy Ryan. And he also presented this to the Critical Thinkers on top of doing it at Black Hat USA. And so, yeah, I would say go check out the recording, but I don't even know if we recorded it because it's on YouTube under Black Hat USA. The link will be in the bio. But what Ryan—
[00:41:14.38] - Justin Gardner
and it was his daughter, right? Yeah. Yeah. Isabella as well. And we did, we did record the Critical Thinkers. Oh, cool. So that would be— I don't know if it was different or what. No, it'll be in the exclusive content in Discord. And it was different. So. Okay, perfect.
[00:41:27.21] - Joseph Thacker
Very good content there. Sweet. Yeah. So anyone who has not done that, I'll do a quick overview of basically what he— what they covered. The first was that the agenda really is talking all about Unicode and Unicode normalization. But to break that down, break that down further, they talked about decoding errors, truncation, confusables, casing, and then combining diacritics.
[00:41:49.44] - Justin Gardner
What are you laughing about over there, dude? It's just such a, you know, crazy piece of research. Like, yeah, like, you wait to hear you, like, just rattle off those various sections to this research is, like, insane.
[00:42:01.96] - Joseph Thacker
They did such a good job on it, man. Yeah, yeah. So I think the coolest part about the decoding errors was that they talked about overlong encodings.. And I loved his analogy of basically it's like a box in a box in a box you get from Amazon or whatever. Yeah. Yeah. And so basically you can overlong encode typical ASCII. So you can have an A that instead of being 1 byte is 2 bytes or 3 bytes. And it's still just a normal A. It's not like some weird Unicode character. It's still just a typical A, but it's called an overlong encoding. And so sometimes that'll let it bypass specific regular expressions. That are checking for malicious things on the like at the beginning before it's then decoded or normalized later. And so that can be really valuable. The second thing they talked about was byte truncation, which is where certain bytes which are long, like the 3-byte Chinese character, that can then be, you know, converted down or truncated down into like a CRLF feed or like a carriage return or line feed. So this specifically had a lot of impact in a bug bounty finding where And by the way, I'm not showing anything on screen because it's a video, but their slide deck's really good though. And so you can— and they suspect— they suspected that this was due to differences between little Indian and big Indian ordering. And they show a really good example that uses CyberChef to actually, you know, show how this would happen.
[00:43:29.53] - Justin Gardner
Then I love that diagram, man. That was such a good representation of it. I never have I looked so closely at Unicode and felt like I understood what I was looking at because Unicode Unicode is hacked, bro.
[00:43:39.40] - Joseph Thacker
Dude, it's actually— so hot take, I think that's why this did so well, both in the top 10 and just for people. They finally made it super understandable. Like, even just the way that I just described overlong encoding, I like— you could have talked to me about that 10 different times over the last 2 years and I wouldn't have remembered it. But the way he described it with the boxes, it's just like, okay, now I understand this, right? Same for what you're talking about. Um, so then there's one called Confusables, which are— I think this is probably what most people think of when they think of Unicode normalization. It's like these characters get confused due to like best fit mappings or worst fit. And this is like kind of hearkening back to all of the OrangeSci research for worst fit. And they share a few more examples of that, which I thought was good. Then casing, I'd never heard of this one until the talk, which I thought was really neat. And it's so good that they use something so like web app specific. They use a script tag where the i was a Latin small letter dotless i. And then basically if the backend application did something like decoded it and then called like.upper, that weird dotless I becomes an I even though it wasn't normally an I. It now gets converted into an I. And so if there's any kind of detection logic or any kind of WAF that's before that, it would have never been caught. But when that happens on the second half, it actually does get caught and works quite well. And then they did combining diacritics, which, to be clear, is the one I understood the least., but it's also really cool. So like there's some Unicode that's basically combining characters. For example, like greater than and then a slash becomes like not greater than, and then the not greater than symbol sometimes gets like, uh, converted to greater than, which obviously when you're looking for like HTML tags or script tags, that's like when that's really valuable.
[00:45:28.19] - Justin Gardner
So yeah, I think that's all the things. Um, that I just wanted to add, because I had to pull it up while you were talking about that, like the casing thing was actually my first introduction into, you know, Unicode. Oh, really? Yeah, actually it was from a live hacking event back in like, man, it's almost 10 years ago now. And I was talking to BitK, the researcher at Yes We Hack. Actually, let me flex my Yes We Hack hoodie right here. And, and he gave me this website, you know, that he built that showed some of the Unicode transformations on JavaScript whenever you run.toUpperCase or.toLowerCase. And, you know, his handle is BitK. And one of the most impactful transformations is actually one of these weird Unicode Ks that I couldn't find the Unicode value for in this moment. And if you do.toLowerCase on that, it turns to a lowercase K. K, right? A lot of the other ones are like two uppercase, right? Which is a lot more rare. But, uh, there's a ton of situations where you do.lowercase. Yeah. Um, and so this special K will get converted into a lowercase K, uh, if you run.lowercase on it. That's sick. Um, yeah. So it's pretty, it's pretty cool stuff. And I've, I've, I always thought it was awesome too that it aligns with his name, BitK. Yeah. Cause he can use the special K in his, as the last character and when it gets That's so sick.
[00:46:52.94] - Joseph Thacker
Turned into a K. Yeah, I need to come up with a handle that has some like acrylics or something in it. Yeah, they used a K in their talk too, and it was the Kelvin K, so it might be the same one.
[00:47:02.75] - Justin Gardner
Oh, it might be the— it actually looks like the Kelvin K.
[00:47:05.11] - Joseph Thacker
I gotta grab the Unicode code point for this thing. Hold on. Yeah, yeah, they talked specifically about Kelvin K and how there was like some check in Active Scanner or something in Burp or something that like checks for that.
[00:47:17.44] - Justin Gardner
I don't know how to get the Unicode code point for this quick enough here. No, you're fine. Oh, yeah. So it's Unicode code point 212A. So whatever that is.
[00:47:29.96] - Joseph Thacker
All right. What's up next?
[00:47:31.59] - Justin Gardner
You're up on ORM leaking more than you joined for. This is second place. This was such cool research, man. Okay. I'm excited to talk about this. So we've covered some of this research before on the podcast. I don't know whether we Covered this exact one, but after reading this, I am thoroughly convinced that ORM links are the new SQLite, bro. Because if you're looking through each one of these, um, you know, frameworks that they're just like slaughtering in this, uh, write-up, uh, it's pretty, it's pretty gnarly. And, um, so the TL;DR of the situation is you get a lot of, um, frameworks that allow you to do, you know, filters and searches and queries and stuff like that. And, um, depending on the way that that is implemented and mapped to SQL on the backend, uh, there are a lot of crazy things you can do. For example, filtering. If you're searching through users, filtering on like their password field. Yeah. Um, yeah. And it affects a ton of different, um, uh, ORM, um, you know, frameworks for, for all this stuff.
[00:48:34.44] - Joseph Thacker
My brain is like a slot machine, like rolling and just thinking, or maybe a Rolodex of all the times I've had like, for example, like user, like user searches where like you put in like one or two characters and it shows a few of them.
[00:48:46.59] - Justin Gardner
Is that the, is that the times when this would be, would this be applicable? Yes. Yes. And, and also, you know, you can, you can, um, also do some situations where, uh, like if you have a field and an operation that you're able to like search on, then you can do, for example, like password__starts_with or whatever. So anyway, the write-up goes through a bunch of different ways to bypass, you know, filters and blacklists for these types of ORM filters. And there's a lot of really good takeaways in there. There's actually a section in a lot of the areas called key takeaways. Yeah, you can see right here I'm showing on the screen. Yeah. Where it talks about what things they learned about these ORM leaks from each framework., that they audited. Um, and a couple of them are, uh, that I'll, I'll, I'll just mention right here are look for areas where you can provide a field and an operator, cuz that really like enhances your ability to, to search. Um, definitely look for these sort of underscore underscore situations, right? Like I've seen these in query parameters all the time where, where is it right here? Me too. Yeah. Uh, there it is right here. You know, like email. Email__starts_with or something like that, right? If you're seeing something like that, then that's definitely something you wanna double-click into. Additionally, they say even if the operator like starts with and stuff like that are not available, don't sleep on the logical operators as well, like greater than or less than, because you can still do sort of binary searches, right? On that, you know, using that operation. Yeah, it's huge. Yeah, and they, they do add a whole list at the end. Let me see if I can find it. But you do run into some difficulties with that based on database collation. So some of the databases will not sort characters the way that you think they will sort characters. So you may have some difficulty performing those searches based off of the way that the database collation works. But they do some really in-depth research on each of the databases here, MSQL, MariaDB, MySQL, Postgres, all of these sort of explaining how to understand how the databases compare strings if you're trying to do like a greater than or less than comparison. So really good stuff there. Some databases are also like not case sensitive. Which causes a problem as well.
[00:51:21.17] - Joseph Thacker
Uh, but they have solutions for a lot of this stuff inside the article. Dude, I cannot wait to add this as a skill to my Cloud Code.
[00:51:26.28] - Justin Gardner
I'm like dying to do it right now. I'm not joking. Well, dude, okay. That's what I was saying before, um, was when I looked at this, I was like, oh man, you know, I feel like I've seen something similar to this in this target I was hacking on. So I kicked off Cloud Code to go hack it while I was finishing up research and it found, I just gave it the article. I was like, hey, Hey, like apply some of these techniques to this host. And then it came back with a bunch of PII, dude. You know, it's crazy when you're, when you're, your cloud code just like, and here's PII.
[00:51:57.57] - Joseph Thacker
I'm like, oh my gosh, I love it. Mine's usually the opposite. It's like critical CSRF CORS issue.
[00:52:04.21] - Justin Gardner
And I'm like, no, it's not. Shut up. No, no, stop. Um, the, the other big takeaway I had from this one is that a lot of times, you know, there will be some filtering on. Like what, um, operations you can use, like not or starts with or whatever. And one of the, um, tricks they gave to bypass that is if you use the, um, sort of query parameter format of key and then square bracket, another value, square bracket equals, you know, value, right? Then sometimes that will map to the JSON representation that is needed for some of these ORM operations. So, the example they gave is reset token square bracket not close square bracket equals E. Right? So, that would map to reset in JSON, reset token colon new object not colon E. Yeah. Right? And so, when that maps out, a lot of times that will allow you to bypass middleware parsers for sort of malicious queries going into the ORM.
[00:53:11.50] - Joseph Thacker
Mm, nice. Yeah, I love the way that when you convert embed— or like deep JSON back to URL encoded forms or whatever, not—
[00:53:23.34] - Justin Gardner
or POST body forms. Yeah, x-www-form-urlencoded.
[00:53:25.05] - Joseph Thacker
Yeah, yeah, I'll never forget that string. I'll never forget it. Yeah, it's super useful to do those conversions. I still find GraphQL where Uh, it's like you where you don't have CSRF because it's JSON, but then you convert it and it works fine and you have CSRF via GraphQL. Yeah, that's a good one. Yeah. Uh, for the listener, in case that wasn't super obvious, basically, uh, what we were just talking about— or not what we were just talking about, the comment I just made was for anytime you have GraphQL that's cookie protected and it's happening via JSON request, if you convert it to a URL encoded form, it will— if it still works, then you have CSRF. And you can just go ahead and report that. So yeah, assuming there's mutations. Yeah, exactly. You have to have mutation because otherwise it's just a GET request, which you don't— you can't see the response of. All right, we have like 5 to 10 minutes left and we've got 2 techniques to do. Luckily, number 3, which I'm doing from Searchlight Cyber Team, Shubh's company that was acquired, is—
[00:54:26.38] - Justin Gardner
and actually, did you see like their CTO or something?
[00:54:29.69] - Joseph Thacker
His partner Michael became the CEO, dude. Right. But it was—
[00:54:32.71] - Justin Gardner
he was the CTO for Asset Nova. Right. And then he became— Shubs was CTO. Michael was CEO of Asynote. And then he got—
[00:54:39.92] - Joseph Thacker
he became CEO of Searchlight Cyber, which is pretty sick. So who's acquiring who? I'm the captain now, right? Come on. Yeah. So we covered this actually really well back then, and it's a very short write-up, so it will take just a second here. But just as a recap, they were— they had a blind SSRF that they wanted to escalate. And as everyone knows, blind SSRF kind of sucks. So they were trying to make it full read, and the first thing they do is play with redirects. And it was kind of— it was not— it was not useful. Basically, they just kept getting exception: invalid JSON. And they— but they increased the number of redirects, and for some reason they could like get it to follow up to 30, and then it got a different exception and it said like network exception instead of invalid JSON. So that kind of got them like thinking like, oh, what should we do here, or whatever. And, um, they moved on to testing other status codes because they thought they might be treated differently until Sure enough, on a 500, it gave them the full response. So that kind of just like got them thinking like maybe there's some way to get a full response if we do other response codes. So they wrote a little script that would— and this is so still so confusing that it worked. I don't know if you remember how confusing this is. Basically, they had to do multiple redirects, and on each redirect it increased the status code. So the first response was 301, then 302, then 303, and, um, And then at the very end, they would have it go to the AWS metadata IAM endpoint. And when they did that, when they hit the cloud metadata endpoint after a bunch of those redirects, it actually worked. And, um, and they still were like really confused, just like you and I, about how that works. And I'm just going to read this paragraph because I still don't— it still doesn't really— it's still— it's not a satisfying answer, but at least made sense.
[00:56:18.19] - Justin Gardner
It says, well, why did it work? This drove us nuts. Hold up, you know, like I also just have to appreciate like whoever's writing this here. I don't know if it's Chubbs or the other people from the Asano team, but he says, this drove us nuts. This was crazy. You know, like he's saying a bunch of things in here and it's like, it makes you feel a little bit better that he's also like, what the frick is going on?
[00:56:38.13] - Joseph Thacker
Yeah. Yeah. This drove us nuts. Was there something special about the 305 status code? Even though we performed a redirect from 301 to 310, why did we only get the responses starting at 305? So they ran that script. It started at 301. It escalated up. Up to 310, and then they had it do the redirect to the Cloud Metadata. But for some reason, in the final response they got, it included all of the responses, like all of the redirect server-side responses that include like, you know, the response headers like content type, content length, connection, server location. But it only started at 305. It didn't show 301 to 304, which is super confusing. So anyways, that's why he's saying, was there something special about 305? Even though we performed a redirect from 301 to 310, why did we only get the responses from 305 and beyond? Was it an issue with libcurl? After extensive analysis, we're convinced no, we don't think so. Instead, we think that the application was happy to follow a few redirects and was failing on that JSON parsing we saw initially and was not happy about following more than max redirects configured for libcurl. However, there was an error state when it followed more than 5, not handled by libcurl, but rather by the application itself. Says this technique may sound obscure to you, and I agree, but it's now worked for us in several different situations where we would not have been able to see the full response code. I just— I'm— it's so weird. It has to be like a configuration of a tech stack that's not uncommon. That's like pretty common, right? Like lots of companies are using, because otherwise how would they have found it multiple times? But anyways, this drives me nuts, but it's so freaking cool to go from a blind SRF to a full re-SRF with this technique.
[00:58:15.53] - Justin Gardner
Dude, like, can you just imagine? Like, I know, I'm just thinking about it. Like, I know, it's like, oh my gosh, this, uh, how much of a high it would be to go from a blind SSRF. And you know, I'm thinking about it in the context of the live hack events too, where everybody's like, oh yeah, I got that blind SSRF. And you go, you know, you go there and you're like, hey, did you get anything with it? Nah. Did you get anything with it? And then freaking Shubs comes in, he's like, yeah, I got full read.
[00:58:38.69] - Joseph Thacker
And we're like, how did you do it? I don't know. The script. Anyways, all right, dude, we got like 5 minutes. Do number 1. And by the way, everyone, this is the number 1 voted Port Swigger top 10.
[00:58:52.28] - Justin Gardner
So yeah, um, really love that past research. So fun to review that. And, uh, yeah, I'll jump to number 1 now. Um, this one was really cool, and this was just really well-performed research in my opinion. Um, this is by Vladco312. And essentially is providing new techniques for exploiting SSTI. Okay? And these two techniques are called error-based exploitation and Boolean error-based blind SSTI.
[00:59:25.88] - Joseph Thacker
And he borrows a lot of the concepts for this from— Hey, sorry to cut you off.
[00:59:31.25] - Justin Gardner
He just pushed improvements yesterday to this. Oh, look at that. He did. I'm sharing my screen so we can see. Oh wow, dude, he's adding— oh my gosh, he's adding new, uh, freaking dude template. Oh my gosh, what a baller. He's adding new templates to it right away. Uh, I guess probably something to do with, um, you know, getting in the top 10. Um, that's amazing. So yeah, this research, dude, let me tell you, this was some of the most thoroughly done research I've seen in a long time. Okay. Not only is he bringing two new ways to exploit SSTI, but he's also cross-referencing this across a bunch of different templating engines, a bunch of different programming languages. This is so sick. And providing generic payloads that work across multiple programming languages that you can use from anywhere. Amazing. That's amazing, dude. So it's freaking sick, and he's integrating all of this into SSTIMAP, a tool to exploit these kind of vulnerabilities. So let me kind of go through some of these techniques. A lot of you guys that are familiar with some of the OG, you know, SQLI exploitation methods will kind of— this will make a lot of sense to you. But essentially the first one is error-based exploitation and it's essentially getting— and this is also used often in XXE, but getting an error to trigger in the templating engine and having that error displayed on the screen to the user. So, he was experimenting. He kind of goes through a pretty long intro there where he talks about how he discovered this stuff. But the way he ends up, you know, exploiting this is triggering an error using getattr in Python, which is for getting attributes, and then concatenating whatever he wants the output of. And that will trigger an error that says, you know, string has no attribute whatever, right? Whatever output you passed in. And that output then in this scenario, you know, the contents of Etsy password will then be leaked in that error, in that actual error message. And if verbose errors are on in the application, then that full output will be displayed to the user. Um, which is really, really helpful. Um, and he mentioned that he tried to do integer conversion with this as well. Um, but integer conversion truncates the data at, I think, what is it like 199 characters? Yeah. Um, but he found in Python that you can use getaddr, which does not have a limit on the amount of data that you can pass into it and leak out. Um, so he does this. I think Python is a really good example of how to do this. Um, and he provides a sort of polyglot that works. Across all tested Python-based template engines, which was amazing. But he also provides polyglot payloads for detecting these error-based oracles. And for that, he does a division by zero payload. And the way that he does that is, I guess, quotation mark? No, no, no. Parentheses, parentheses, 1 divided by 0,.zxy.zxy. And this will result in an error in any programming language that he tested here. Um, and if that error comes back, then you can use that as a way to determine, okay, wow. Um, error-based oracles for SSDI in this scenario is kind of the way to go. And then you, you pick from the variety of, uh, you know, error-based payloads he gives you.
[01:03:12.57] - Joseph Thacker
In Python, PHP, Java, Ruby, Node.js, Elixir, all of these. So can we respect the fact that he's doing a lot of this testing via some sort of like Discord bot? He knows that there's a bunch of the screens— there's a bunch of the screenshots where like, go to the— go to like the— oh yeah, he's doing like /test and then putting in some content and then it's doing some sort of test under the hood and then it's showing him the Response. He's like testing stuff from Discord.
[01:03:39.53] - Justin Gardner
I love it. What a badass, man. Yeah, dude, this is exactly how you use Cloud Code too. You just like, uh, I do just talk to it all the time in Discord, which is great. Um, so that was the error-based exploitation. We see similar stuff in XXE error-based, um, you know, leaks for XXE. Um, and then the second type he mentioned was Boolean error-based blind SSTI. Okay. And this is a similar situation. Where, um, you've got an error-based SSTI, but it's resulting only in a 500 from the server, right? Um, so what he does here is use a similar concept with, you know, 1 divided by some expression, and that expression should be cast or evaluate to 0. And if it does, then it triggers a divide by 0 exception and will cause a 500 from the server. And so using that, you have a Boolean Yes or no, did my expression resolve to zero? Uh, and that can allow you to leak valuable data in the application when you have the full set of SSTI, um, expressions at your disposal. Um, so he also provides some generic detection payloads for this. Uh, these ones actually take advantage of, uh, syntax errors for generic detection. Um, those are located at the bottom of the article. I won't read them out because it's just going to be hard to, like, explain to you that there's syntax errors here. But for any of those of you watching on YouTube, you guys can clearly see the syntax errors in the location of the parentheses in this expression, which will trigger the error message whenever you're using your scanner. So very thoroughly done research, built it into a tool, documented all of the nuances in different programming languages.
[01:05:26.42] - Joseph Thacker
There's no wonder this was number 1. Freaking crazy. No wonder. It's so good in every way.
[01:05:32.01] - Justin Gardner
Makes me want to go test for it everywhere. It's like insane. Dude, I, I very rarely find SSTI. I think that is one area where I really need to improve as a hacker is looking for SSTI because it's so valuable too. Um, all right. Shout out to Vladko for that. Um, I did want to say, I know you got to go, Joseph, so feel free to drop if you need to, but I did have one little quick story. I actually combined the ORM research. With this blind Boolean error-based attack one time. And I kind of wanted to tell them about it. So I'm going to do a quick little story here. So, um, I was testing an application where I had a secondary context path traversal. And I've talked about this on the pod before, uh, the specific instance. I won't tell you which one, but, um, we had a secondary context path traversal and I was actually able to inject Microsoft Graph, uh, OData,, filters, uh, in there. And this allowed me to do stuff like, okay, if username equals Ryan Erator, then, you know, return that in the response, right? Uh, apply filters there. So they patched that whole thing and made it so that I couldn't get the data back out. But I knew that I was still getting this OData injection, right? In the backend. Yeah. So what I ended up doing was crafting an OData query, um, where where, you know, it was like starts with a specific ID and username is equal to whatever user I wanted to target. Uh, first name is equal to this or divide by zero. Nice. Right? Divide, you know, ID by zero. Error base. And so if, you know, if the first name started with whatever, right? Then it would not hit that second divide by zero condition that divide the ID by zero. But if it didn't, then I would, I would hit it and it would trigger an error. Error, right? So, and I was able to detect that error out through the secondary context path traversal because it was reflecting error conditions back up. Sure. Yeah. Even though it blocked the JSON response.
[01:07:25.03] - Joseph Thacker
Um, so using that, we were able to also arbitrarily leak data about backend users. The fact that, the fact that you and him both converged on a divide by zero payload, like just goes to show that you're all genius. I think personally, I just think that's like really neat that both of y'all's heads, like the two people who are really intelligent, who are looking at this like similar problem.
[01:07:45.53] - Justin Gardner
Came to the same conclusion is like very cool. Yeah. Yeah. I like it too. It's very valuable for triggering error-based oracles in my opinion. So, all right, man. GG. I think that's a wrap on Port Swigger top 10. Yeah. Perfect. Yep. Peace. All right. Peace, y'all. And that's a wrap on this episode of Critical Thinking. Thanks so much for watching to the end, y'all. If you want more Critical Thinking content or if you want to support the show, head over to ctbb.show/discord. You can hop in the community There's lots of great high-level hacking discussion happening there on top of masterclasses, hackalongs, exclusive content, and a full-time Hunter's Guild if you're a full-time Hunter. It's a great time, trust me. I'll see you there.









