Podcast

S2E5: Dune and Gloom Under the AppSec Tree: From Shai-Hulud to React2Shell

Supply-chain vulnerabilities are getting more frequent and dangerous, with the Shai-Hulud npm worm and React2Shell RCE vulnerability being just two of the recent ones. In this episode of AppSec Serialized, Dan Murphy and Ryan Bergquist analyze those recent threats (plus a bonus Django vulnerability) and talk about the implications of security risk shifting towards dependencies.

Hosted by:
Dan Murphy, Ryan Bergquist
,
Guests:
-
December 18, 2025

Read about the security threats discussed in this episode, namely the Shai-Hulud worm and the React2Shell vulnerability, and learn how to block them already in your CI/CD.

Transcript

Dan: Hello and welcome to another episode of AppSec Serialized, the podcast about web security and those who practice it. I’m your host Dan Murphy and with me here as always is Ryan. Ryan, do you want to say hello? How festive you look!

Ryan: I know, it is the holiday season! And whatever you are celebrating, we’re happy to be here. And we have a lot of… presents to unwrap, I guess you could say. A lot of things to talk about on this episode revolving around vulnerabilities that we’re seeing. A lot in the last few weeks that have really come out. So I’m excited to dive into this episode. I know you are!

Dan: I am. Yeah, this is our “Who’s been naughty and who’s been nice” episode. To all those AppSec practitioners out there, we know that this past couple of weeks has been one thing after another. So this episode’s kind of our grab bag. Let’s go around and find those unwanted gifts that showed up unexpectedly under the security tree in the last few weeks.

Actually the first one, Ryan, that I wanted to talk about – I’m a huge sci-fi nerd, and this is Shai-Hulud. So I love the name. But Shai-Hulud actually hit probably about a month or so from recording when this went down. It was a supply chain attack on npm. And my question to you is, what was the Shai-Hulud attack? What’s interesting about it? And what are some lessons learned for those who are the defenders of AppSec at our various organizations?

Ryan: Yeah, really interesting. I think it was beginning of September – September 8, September 9 or so – when we first heard about that attack on npm and everything. You had all the color schemes, it was the color string and everything from that perspective. And it was about, I think, close to 100 or 200 different npm packages that were compromised.

And then things start to grow, and you thought it was this tiny little worm or this tiny little infection, and next thing you know, it’s the size of one of those Dune worms. It is massive, it is huge, and it is just ready to eat you up. Because what we saw in that second wave is way, way more, right? Now we have 600 to 800 different npm packages that are compromised.

Dan: Yeah. It got bad.

Ryan: So what we’re seeing is it’s not just a malicious package where it’s put in there and you have some vulnerable components and you update them and they’re good to go with just those few components. It’s a self-propagating worm that weaponizes victims against others. Once it compromises a developer, it uses their npm tokens to publish Trojanized versions of their other packages, turning maintainers into involuntary amplifiers of this specific worm. It is quite incredible. So the scale is really staggering.

Dan: Yeah. And these aren’t sketchy npm packages that are not used. These are trusted maintainers that have good intent, but just got caught up in something that spread using their credentials.

Ryan: Yeah, it’s really – the number is just staggering, right? 600 to 800 npm packages are compromised. I think as of today, as we’re talking, we’ll see if it continues to grow, but that 600 to 800 is absolutely amazing with over 25,000 GitHub repositories affected. I mean, you could just see how many of these packages are being used and it’s just high profile ecosystems, right? Zapier, PostHog, Postman, all of them hit.

So really, the second coming introduced genuinely sophisticated techniques, hooking into that pre-install so it runs before installation even completes. I mean, it’s really quite interesting to see how this thing has been developing and how we can understand the sophisticated approach that this worm is taking.

I think perhaps the most insidious thing is if no GitHub tokens exist on the current host, the malware searches the previous victims’ exposed credentials and it looks for all those in those public repos and uses those instead. So it’s creating a web of cross-victim credentials relaying this massive worm.

I think one of the biggest lessons here is that npm packages, CI/CD workflows, and developer machines are all a part of one interconnected attack surface. So really, these engineers and the way we’re developing, we need to understand that it’s not just one little package. Everything can be infected. So once compromised, a maintainer account can cascade into thousands and thousands of affected downstream projects.

So this Shai-Hulud supply chain worm – it’s not something I’ve personally seen at such a scale. I’m interested to see where things are continuing to go and how to stay at the forefront of making sure that you are protected.

And I think something at Invicti is really interesting: having a full catalog of your packages and the packages that are using is one of the biggest aspects to make sure you are staying safe. Because when these start to come out you have to be prepared. I can’t search through all my pipelines to verify where Shai-Hulud is. So having a catalog is truly a good way to stay prepared for these massive dune-like worms that we have in security nowadays.

But that was Shai-Hulud. There’s been some more. There’s a fascinating vulnerability in Node.js.

Dan: Right. Yeah.

Ryan: In mysql, mysql2 connectors where prepared statements – something we’ve told developers for years, the gold standard for preventing SQL injections. I always have asked, “Have you looked at your prepared statements? Are they better?” That’s how you remediate. That’s been the long term standard. But can that actually introduce vulnerabilities? How does this vulnerability type work? And what does it tell us about security assumptions that we’re making?

Dan: Yeah, and this is true much in the same way that if you’re using a package that was previously trusted and it went bad, remember that whenever you’re using the appropriate tools to mitigate vulnerability, those tools themselves under the covers might have flaws.

So what’s very interesting about this particular vulnerability – this was a case where we always taught people don’t just trust what the user sends over HTTP and concatenate it to a string because that’s how you get your “; drop table users” and things like that. And instead what people are doing is they’re saying, “I’m not going to trust this. I’m gonna pass it through a bit of code that will fill in this variable.” That prepared statement will make sure that everything inside of what’s being passed in doesn’t have any hostile SQL commands. It’s the right type, all of this stuff.

That’s all well and good until you realize that the frameworks themselves, at the end of the day, somebody has to reassemble some SQL and pass that down to a driver somewhere. And the flaw here was type confusion on what was coming in. So in this case, it was assumed – and assume is always the worst possible thing when it comes to security – but it was assumed that that input was going to be a nice friendly string. And if it was a nice friendly string, it would get cleansed and all the right SQL stuff would happen.

But this backend is implemented in node, which is a JavaScript-based language. And in this case, if you passed in a dictionary – a little curly bracket, a name of key, colon and a value – you could actually do some things that were not entirely expected. So in this case, that backend, if it received something that had a key value pair, say something like “email” and another dictionary inside of that that had “foo” and “bar” as a key, what that connector would do is would actually transform that into SQL.

And because there were multiple things inside of there, it might check for something that is always going to be true. And so what you could do, much in the same way if you were doing old school SQL injection and just passing in “OR 1=1” which is one of the classic ones that you’ll see to return more than you should have gotten – you can do the same thing and you can influence what is being sent back there by filling out these things that are dictionaries and having them actually create something that does end up selecting more rows than you might want to get in your response.

So it’s classic SQL injection. But what’s very interesting about it is that if you looked at the code, anybody who code reviewed that would have been like “Oh yeah, it looks totally fine.” Now, the interesting thing about this one is that it is one of those things where it seems safe and you get this almost false sense of security by using the appropriate framework.

So it speaks in some ways to what you had said earlier about making sure you have that manifest and making sure you know what ingredients are inside of the recipes that you’re brewing up in your code. Because if you get something that has a vulnerability, that can just creep on in. So looking at that code that an end user would write, it looks totally harmless, but underneath there’s a flaw that’s not at surface level.

And the only way you can really mitigate these things is with tools, right? It’s making sure and proving that you’re not vulnerable. We added a check for this one just to be sure, because the only way that you can be sure is to actually do what an attacker does, send it out and just observe to see whether or not you can get it back. So this is kind of an exciting one.

And again, there’s another one that came out that’s very similar. And Ryan, I’m talking about CVE-2025-64459. Specifically, this is a Django bug that came out. So it’s another very interesting thing, somewhat related. Can you tell us a little bit about that gift under the tree?

Ryan: Yeah, absolutely. So the SQL injection issues – the Django ones in particular that we’re talking about – are ORM-level injection flaws, meaning that they did not come from developers writing raw SQL incorrectly. It came from edge cases in Django’s query construction logic itself. So specifically certain ORM APIs allowed user-influenced input to be interpolated into SQL identifiers – such as column aliases, expressions, filtered relations, all of that – without being safely quoted or parameterized.

So one thing that is most impactful to me is that it breaks the “Django is safe by default” assumption.

Dan: Mm-hmm.

Ryan: Django’s ORM is widely trusted to prevent SQL injections by design, but it’s so interesting because you’re going in through your pipelines and saying, “I’m gonna use this because it’s safe,” but these vulnerabilities show that the framework level abstractions can fail and that relying on an ORM alone is not complete defense. So it’s shifting the threat model from developer mistake to platform level risk that we see today.

So I think the exploitation can occur in mature, well-audited code bases. So even if you think you are safe, it very well could be living in your query construction based on everything that you are creating. So I think it’s interesting that traditional security tools may actually miss that concept of SQL injections in general because of how it’s being created. This second order, I guess you could say, of SQL injections.

Dan: Yeah, right. It’s not in the code you write, but it’s the code that you write that’s using something else that ends up being vulnerable.

Ryan: Right. Second order, I think, is a good word for it. So that injection where the framework’s own features become the attack vector. So we really have to be able to take that lesson and bring that into security by saying – again, I always say trust but verify. We’re trusting that this is secure. We’re trusting that this doesn’t have any vulnerabilities in it.

There are major consequences. So scanning, whether it’s pen testers, automated scanners – I mean, we’re doing some pretty cool things when it comes to pen testing as well.

Dan: Hey, we’ve got a check for this one.

Ryan: Yeah, we definitely have a check for this one. So I would just say the broader lesson that we are learning is that framework abstraction layer can hide both complexity and risk. So security researchers and developers need to understand what’s happening under the hood. Not just that ORMs are automatically safe, but it’s a good place to start. You still have to be vigilant. You still have to be curious what’s out there.

And at end of the day, have fun seeing if you can get past it in a good way. In a good way. We stay white hat hackers for a reason. We don’t go on the dark side. We stay in that mode so we can make sure that not only we are safe at the companies that we are at, but that we protect everyone else out there.

On to the next present. So I want to talk about one that came out a few days ago, actually. It was the React2Shell.

Dan: Oh yeah. This is the big present to the back of the tree. This is the big one that you open last.

Ryan: You thought you may have been getting cold, but this one may be a little bit better than that. So yeah, React2Shell. A critical, critical vulnerability. It’s making waves right now. So it’s affecting that React Server Components and Next.js. It’s got a perfect CVSS score – 10. All the judges are holding up there 10.0. What’s happening here? And why is this potentially one of the biggest web app vulnerabilities of 2025?

Dan: Yeah. So this one is huge for a number of different reasons. First off is just the sheer popularity of React. Nowadays, if you were to go in and ask an LLM, “Hey, I want to make a web app,” generally speaking, chances are it’s going to be a React app. Next.js is also super popular.

And in this case, what was super interesting about this is that we’re not talking about a complex chain of statefully driven attacks, taking advantage of timing. It’s one shot. One very highly mutable, difficult to detect with signatures, but it’s just one HTTP request. And that one HTTP request is one done drive-by and suddenly you’ve got full remote code execution. So it’s scary.

And this one was actually really interesting on the inside because it had a lot of things that made it – there was a little bit of a smoke screen, which I’ll get into in just a minute. But to kind of go down with what it does, I would recommend – this is actually a pretty cool exploit. The original researcher who found this published a PoC and it’s a great thing to take a look at, because it’s actually pretty clever.

Ryan: Hmm.

Dan: It’s a series of chained gadgets. If you don’t speak native JavaScript stuff, running it through an LLM to explain it is a great way to get started, but it’s pretty clever. But effectively you can do anything you want. So you can evaluate arbitrary code, which means for an attacker, this is the holy grail. This is the type of thing that not only does an attacker want to exploit, but got actively exploited.

So this shot up in the rankings in terms of actively exploited vulnerabilities out there. It was so bad that on our internal research channel somebody found that someone went and built a browser plugin so that as you visited sites, it would just exploit as you went through about your daily browsing. Created a blog, exploited.

Ryan: Hmph.

Dan: Check this web e-commerce thing, exploit it. It would just go through. And that’s how bad it was.

The thing that’s interesting with this one is that it’s vulnerable by default. So any sort of React stuff that’s using a server side component – which not all React apps do, but it’s very, very common to be able to get fast updates and kind of cooperation between front end and back end – you’d get it right out of the box if you just use the basic command line, the create-next-app. And so this was very, very prevalent all over the place. There’s been some pretty good numbers of this.

I mean, hey, it’s called React2Shell. This is speaking back to some of the big ones – the Log4j’s and things like that. This is, if it’s got its own domain that’s registered, that’s a pretty big deal. But pretty cool attack.

Ryan: Log4j’s, Spring4Shells. I saw that actually recently. I went on and I was obviously doing a little bit of research on all the different CVEs that are coming out and I saw, yeah, has its own website. I was wary to click on it though, because you never know what’s behind there. What could possibly go wrong of looking at a web URL with that?

Dan: Yeah. What could possibly go wrong?

So actually Ryan, that’s something I want to share – a story from the inside on this one. Oftentimes when things drop, there’s PoCs – proofs of concept – that are out there. This one was interesting because there was this smoke screen of fake exploits for the vulnerability that were purporting to be proofs of it, but were either fake and just pure crap that was wasting people’s time or malicious.

There was – some might call it a concerted effort, but there was a very curious effort of lots of stuff that was trying to throw people off the trail. And some of those things were themselves harmful and saying, “Hey, download this PoC!” One of the things we always say internally here – you gotta be very careful when you’re dealing with anything that deals with a live exploitation. You never want to be running some random script kiddie from the internet’s code off of GitHub that says, “Yeah, this Python is totally legit.” You always got to read the code, right? You always got to understand it.

And as we kind of showed earlier, just reading the code sometimes isn’t enough to know that it’s vulnerable because it could be deep down in one of the dependencies. So you always got to execute this stuff in a nice quarantined area that’s not going to spread. But that smoke screen was kind of new and it was worse this time.

And what it ended up doing is it sowed a lot of confusion because there was a little bit of a question, “Wait, is this real? No, no, no.” Some of the original PoCs were fake. So internal security chatter was, “Wait, wait, wait, is this just more hype? That’s not real.” But it ended up being extremely real.

And there was so much confusion that in this case, the race to kind of get out a working PoC was pretty intense. I’m super happy by the way – we shipped this one pretty quickly and it was a real win. It’s also wonderful to work with smart people who are able to kind of crush something out and do all the right things and get stuff shipped.

Because at the end of the day, you want to make sure that people have the surety of a check to make sure they’re not vulnerable because this is scary. And I’m sure that there are a ton of CISOs last week or the week before that got asked that question. “Hey, are we vulnerable?” And sometimes you’re in a position where you’re guessing. And when it’s security, you gotta be sure.

So there’s nothing quite like mapping one of these checks across everything inside of your organization and just proving, “Yeah, hey, we’re good.” Or as more often is the case, “Hey, yeah, we’ve got some work to do.” But I’m sure that many of our listeners spent a lot of time patching. But definitely check it out there. We published a good blog on what we did, the mitigations that are available, how to help yourselves out.

But this was interesting. I think this is probably the biggest one of ’25 – came right at the end of the year. And it was a surprise that was waiting under the tree.

Ryan: Yeah, and the React2Shell, I want to say again, props to the team. We had a custom security check very quickly. And so what I mean by custom security check for everyone listening is within our solution, we give the ability to create custom payloads, custom security checks, because we know it’s fun. We have, as security engineers, sometimes we like to play, create our own levels of zero days or attacks. We were able to get this into our customers’ hands very quickly.

Dan: Mm-hmm.

Ryan: So not only can we create a custom check, but we applied it to so many different customers to run scans immediately. And it was quick, right? So it was just looking at one specific attack. Because we’ll also already identify those React vulnerabilities, Next versions that you’re potentially using. So adding on an extra layer of that RCE is quite incredible – just being able to do that and then scanning your entire asset.

I think it’s very, very interesting from that perspective. There’s been a lot coming out in 2025. Those are some big ones recently that have come out. 2026 is going to be... crazier? I don’t know. It’s just what’s ahead of us. It’s going to be really interesting to see how life plays in the level of security.

Dan: Yeah. And I actually think the pace of both attack and defense have just been multiplied. Maybe a couple of years ago now, I did a little talk at Black Hat where we were taking a look at the number of CVEs over time and just how that curve is going up exponentially.

Now with the power of AI, I kind of feel like it has been accelerated even more. We’ve always had the problem of asymmetric resources to the attacker and the defender, right? Anybody who’s done AppSec for a big company knows that you never feel like there’s enough of you and there’s a lot of bad guys out in the world.

Now that there’s AI tools, we’ve seen a lot of sophisticated attacks that are really being able to be done. Take a look at Shai-Hulud. Take a look at some of the stuff that’s happened recently. A bunch of smoke screens that went out at the same time that a zero day dropped. The tools are enabling a lot more offensive action than we’ve seen before.

It’s also being mirrored by defensive actors who are starting to embrace these things as well. But on both sides, I think that this is fuel for the fire. We’re pouring gasoline on it and we’re just gonna see it burn so much quicker in 2026 for both offense and defense. But it is without question – it’s gonna be some interesting times.

Ryan: Yes, it will be.

Dan: All right. Excellent. Well, I think that brings us to about time. Ryan, I just want to say happy holidays to you.

Ryan: Happy holidays, everyone. Happy holidays to you, Dan. You’re the best co-host and main host of this podcast. It’s a time to celebrate, a time to smile and be with everyone, including all of our lovely vulnerabilities that come out within the year.

Dan: That’s right. That’s right. So hey, stay happy and stay safe. And to everyone, this has been another episode of AppSec Serialized. Thank you all. Good night.

Credits