BLOG
The OpenSourceMalware Show #1
TeamPCP compromises Bitwarden, npm lifecycle scripts, OWASP's npm security cheat sheet
By cb482791-4ef1-4762-96ad-b0ca4bdd538e ·
The OpenSourceMalware Show is available on YouTube, LinkedIn, and as a podcast.
In this episode, we covered:
Bitwarden CLI Compromise: We analyze the recently discovered malicious version (2026.4.0) of the Bitwarden CLI package. We break down how this cloud-native infostealer silently executes via pre-install scripts to harvest credentials across AWS, Azure, GCP, and GitHub, as well as hoovering up AI config files like Claude. We also discuss its exfiltration tactics to a lookalike domain and explain why we are skeptical of the threat actor’s claims that this is the “third coming of Shai-Hulud”.
The Danger of npm Lifecycle Scripts: Why are pre-install and post-install scripts such a popular attack path? We discuss how threat actors exploit these convenience features to auto-install malware. We also explore the differences between package managers, noting that while these scripts are off by default in tools like pnpm and bun, they remain on by default in npm.
OWASP’s npm Security Cheat Sheet: We review a 12-point cheat sheet from OWASP covering npm security best practices. We share our thoughts on artifact governance, the realities of responsible disclosure, and why falling for dependency confusion or typo squatting attacks relies more on machine automation than just “dummy” human errors.
GenAI and Cross-Ecosystem Attacks: We wrap up with an alarming new trend we observed just this week: threat actors using Generative AI (like Claude) to rapidly translate working malware into different programming languages. This enabled them to deploy malicious packages across multiple ecosystems to target users of a specific company within a coordinated 8-hour window.
Episode Resources
NPM security cheat sheet from OWASP
[00:00:00] Jenn Gile: Super excited to be doing this. We’ve been meaning to do this for a while. So just as a means of a quick intro, my name’s Jenn, this is Paul. We’re the co-founders of OpenSourceMalware. And we’re gonna be doing this hopefully on a regular basis -- a weekly basis -- where we spend a few minutes breaking down some recent news in the malicious open source world and then pivoting to talk about something that we’ve been observing in the industry, best practices, things like that.
Bitwarden CLI Compromise
[00:00:42] Jenn Gile: So let’s start with Bitwarden. Paul, when I got up this morning -- for context, I’m in Pacific Time, Paul’s in Australia -- I saw Paul had been busy overnight, as had other people in the industry. Bitwarden CLI was compromised. There was a malicious version published, version 2026.4.0, which has already been taken down.
[00:01:05] Jenn Gile: So if you didn’t upgrade, you’re fine. But it’s always possible that it could have gotten cached, so everyone should still know about this. It was discovered by the JFrog research team, and the threat actors self-described this as a “Shai Halud the third coming.” We’re gonna circle back to that because we have thoughts.
[00:01:25] Jenn Gile: In brief, what does it do? It includes two files that are gonna steal environment variables and credentials. We’re seeing a lot of this lately. Five steps to this malware. Number one: on NPM install, the pre-install script runs a bw_setup.js file, which silently installs Bun and then executes another JavaScript file. That file runs an obfuscator bootstrap, reassembles a string table, and executes its entry point under Bun.
[00:02:00] Jenn Gile: The exciting part is it’s harvesting credentials from any developer workstation it’s installed on, as well as CI runners. It’s validating and escalating across several types of environments -- AWS, GCP, Azure, GitHub -- and then exfiltrating. Before we talk about what we think about this attack and how bad it is, let’s take a quick peek at our threat feed for it. Paul, why don’t you talk folks through what they’re looking at? I’ll share the link in the comments so anyone who wants to look at it can. This is ungated.
[00:03:00] Paul McCarty: Yeah. So this happened last night -- not quite 12 hours ago. The NPM package was up for about 14 hours. I saw somebody post a screenshot of the downloads showing only a couple hundred, but the reality is this NPM package is very popular -- 1.3 million downloads per week. I had it installed myself. I don’t have auto-updates enabled for most NPM things so I didn’t get the bad version. But a lot of people use this CLI because programmatically, if you want to connect a Bitwarden vault to something, you have to use this NPM package.
[00:03:45] Paul McCarty: What we’re looking at is the OSM open source malware threat report for this package. It’s version 2026.4.0, and it’s a single compromised version of an otherwise legitimate package. We think it’s Team PCP, which kind of vibes. I was just looking before we went live -- doesn’t look like Team PCP has taken official credit, but it does vibe with what they do.
[00:04:30] Paul McCarty: Basically it’s a Trojanized version -- drops two new files. If you diff against the last version you’re gonna see two different files, and the package.json will be different because it calls a lifecycle script that it doesn’t typically. And then those two scripts bring you onward to what I’ll call bad glory. The payload is actually a cloud-native infostealer. The first thing it does, which is really unique, is it downloads and installs four SDKs -- AWS, Azure, GCP, and GitHub -- so it can talk to those platforms programmatically, and then goes and does what you’d expect a cloud-native infostealer to do across all four environments.
[00:05:45] Paul McCarty: It also, by the way, Jenn, sucks up Claude. We’re seeing this now more and more where infostealers -- including DPRK, Invisible FARA -- hoover up Claude and other AI config files. It’s just a cloud-native infostealer, meant to run in either a CI environment or on a workstation, and it’ll suck up all those credentials. If it runs in an EC2 instance, it checks metadata to see if it can grab data out of metadata, which is classic AWS and cloud tradecraft.
[00:06:30] Jenn Gile: Let’s take a look at the threat graph. We have Bitwarden, we have this domain audit.checkmarx.cx, and then we have two C2 servers. Maybe talk through what those relationships mean.
[00:06:55] Paul McCarty: Yeah. If you deobfuscate all the way down -- turtles of deobfuscation all the way down -- it exfils out to a lookalike Checkmarx domain, checkmarx.cx. We saw something similar when Team PCP took credit for the Checkmarx Kicks attack. They used similar tradecraft there. Same thing here: lookalike domain, exfils all that hoovered-up cloud stuff to an endpoint in checkmarx.cx, and an endpoint called telemetry -- which you see quite commonly. Bad actors will use fake telemetry endpoints because it looks legit.
[00:07:55] Paul McCarty: I actually haven’t looked at this part of the payload enough, but I think the secondary exfil point is the IP address -- though that might just be what checkmarx.cx resolves to. I haven’t verified that.
[00:08:10] Jenn Gile: We walked through all of that and I just realized I never added it to the stage. User error, sorry. So clicking back here, what you can do if you come to this page is get the threat description Paul was discussing, all of the payload details. The IOCs have been updated since I looked at it this morning, which is super helpful if you ingested this. We have evidence linked and the threat graph showing the relationship between the Bitwarden CLI package, that sneaky domain, and the C2 servers.
[00:09:00] Paul McCarty: I’m literally updating that exact report right now because Tom Abai has done an amazing analysis of the whole kill chain from beginning to end. I just put that into the external evidence and references section inside the threat report. Sorry Jenn.
[00:09:15] Jenn Gile: No apologies required. It’s always great to get the fresh stuff.
Is This Actually Shai Halud?
[00:09:55] Jenn Gile: So let’s talk about why you do or don’t think this is the third Shai Halud wave, as the threat actors have claimed.
[00:10:05] Paul McCarty: I think hyperbole is just part of the culture. Team PCP was spending a lot of time on Twitter -- they’re spending less now, but I said to them publicly: you’re out there talking a lot. That’s not good. You drop an op, maybe don’t do that.
[00:10:25] Paul McCarty: This is definitely not Shai Halud. The researchers I trust don’t think this is the original threat actor. One reason: if you look at the actual commits the threat actor made in the Bitwarden CLI CI config on GitHub, it looks like they tried multiple times in public commits to get the thing to fire. Which means they weren’t staging this -- they were doing it in more or less real time. If you’re experimenting in real time, you expose your thinking, your flaws, your tradecraft. APT nation-state threat actors almost never do that.
[00:11:15] Paul McCarty: Also, the worm wasn’t very wormy. Last I checked, three people had been wormed -- and I’m not minimizing that, three people got properly poned, GitHub credentials and a bunch of other stuff exfilled publicly. That part is similar to Shai Halud. What’s happening is not only are these exfil sources being used, but this is following the Shai Halud style where when they exfil stuff, they drop it into public GitHub repos. They’re creating net new repos in the compromised user’s account with common strings so Team PCP -- or whoever it is -- can go find them easily using the GitHub API.
[00:12:20] Jenn Gile: There’s a difference between inspiration and the real thing.
[00:12:23] Paul McCarty: 100%.
NPM Lifecycle Scripts
[00:12:55] Jenn Gile: All right. Coffee break on that one. I don’t think it’s the most fascinating sample, for good reasons -- hopefully this won’t have wide impact. But I want to talk about a trend you and I have been observing for a while. I talked about it last week when I was in Phoenix for Volcon and in Denver for Snowflake -- that is NPM lifecycle scripts. What they are, and what you might want to be thoughtful about, because they do open you up to additional probability that if an account or a maintainer you trust has been compromised, and you’re using these lifecycle scripts, you’re a little more likely to install something you weren’t expecting.
[00:13:50] Jenn Gile: Here’s how I describe these scripts -- you tell me if you like this definition. When you’re a vendor, you want to make using your thing as easy as possible. The easier you make it, the less friction, the more likely people are to use it. What these lifecycle scripts do is mean that when you do NPM install, you don’t have to go through and approve each little thing to get installed. It can include a pre-install, a post-install. What we’re seeing is that this convenience factor is something threat actors take advantage of. They took advantage of it with this Bitwarden thing, with Axios. We see it in almost every ATO and lots of non-ATO situations -- they say “we’re gonna hide this benign-looking file in the package and it’s just gonna get auto installed.” That’s why we say you don’t need an attack path with malware. These lifecycle scripts create that attack path from inside the house.
[00:15:10] Paul McCarty: Oh man, we could spend an hour talking about just these scripts. NPM gets a lot of heat for still having lifecycle scripts -- post and pre-install scripts. But just to be clear, all the package managers have historically had something similar and still do.
[00:15:55] Paul McCarty: Here’s the thing. Packages are not libraries all the time. They’re not always meant to be imported. Sometimes they are, but a lot of times -- especially with NPM -- you’re building CLI tools. You’re piping stuff in and out of them, using them as tools. So you need a different instantiation mechanic. We’re expecting two totally different use cases to follow the same mechanics, and we just can’t do that.
[00:16:50] Paul McCarty: As much as I would love to see pre and post install scripts go away, I actually like them being there because they’re easy for me to detect. The first thing we look for is post and pre-install scripts. All the low-end threat actors and even some high-end ones still use them, which makes them relatively easy to find. Also, having written a lot of JavaScript CLI tools, post and pre-install scripts come in handy. There are other things you can do -- NPM supports a BIN component for CLI tools that creates a symlink and sources the path correctly. Those are really different use cases and we have to understand that’s why these things still exist. Do they cause us pain? Yes. Are they easy to detect? Also yes.
[00:18:10] Jenn Gile: You look at PNPM, Bun, and other ecosystems where these lifecycle scripts are off by default and you have to opt in. What’s different about NPM is it’s on by default and people are using these without realizing what the risk is. That’s where we have some opportunity for change.
[00:18:45] Paul McCarty: Yeah, for sure. The reason they’re on by default is because of that use case -- I get why it’s still there and why it’s enabled by default. You can make the same argument about VS Code and the tasks file, and why it’s still so easy to enable those things. But you’re right. And if you’re developing a true library, PNPM makes some sense. If you want to build a CLI tool in JavaScript, you’re gonna go to NPM -- that’s just the difference between the use cases.
OWASP NPM Security Cheat Sheet
[00:19:45] Jenn Gile: I dug up a resource this morning. OWASP -- which I know you and I are very familiar with, but not everybody watching might be -- is essentially the organization for application security. OWASP has published what they’re calling a cheat sheet of NPM security best practices.
[00:20:15] Paul McCarty: I haven’t seen it.
[00:20:17] Jenn Gile: I hadn’t either. I haven’t even looked to see when it was published. There’s 11 -- actually wait, 12. They broke their normal Top 10 list.
[00:20:35] Jenn Gile: I think there’s some very obvious stuff in here about avoiding publishing secrets to the NPM repository. Enforcing lock files. Right here they talk a bit about minimizing the attack surface by ignoring run scripts, and they mention using an allowlist for lifecycle scripts. I’m not gonna make you have a hot take on this because I’ve just seen it for the first time -- that’s unfair. But I’ll share the link in the comments so anyone who wants to take a look at it can.
[00:21:10] Paul McCarty: There’s good stuff here, Jenn. I’m only five in, but none of it is groundbreaking.
[00:21:15] Jenn Gile: But here’s the deal -- it’s not groundbreaking, but you and I go to a lot of conferences and talk to a lot of people, and a lot of these things aren’t happening.
[00:21:30] Paul McCarty: Yeah, 10-4. Number six or whatever -- artifact governance and supply chain protection. They’re basically saying use some sort of proxy: something you build yourself, a local NPM proxy, Artifactory, somewhere to store your packages. The reason that breaks down in a lot of organizations is because you implement this and then developers start complaining -- “I need this new library, this new version, and it’s not in Artifactory.” Then some team that doesn’t really understand how software sausage is made takes two or three or six weeks to okay it. That breaks the ability to build and write applications quickly.
[00:22:45] Jenn Gile: One of two things happens in that circumstance. Number one, it slows down. Or number two, they find a way around it -- approved or unapproved. The outcome is the same. You make your security processes too hard to use or not well-aligned with software development and they’re in the way. What I’ll say is it’s been interesting -- I’m hearing more people talking about wanting private registries. It will be interesting over the next year to see how many people are increasing that adoption.
[00:23:20] Paul McCarty: Agreed. Continuing to look at this list -- number seven, responsibly disclose security vulnerabilities. I don’t know if they’re aiming this at people outside the maintainer group or at the maintainer group, but the problem I run into is that when I find a vulnerability or a malicious package, reaching out to maintainers is often very difficult. And if it’s a vulnerability, you’re often gonna get a lot of pushback.
[00:23:55] Jenn Gile: They’re talking here specifically about security researchers following responsible disclosure. And I would apply exactly what I said about the last thing: if you make it too hard to responsibly disclose, the benefit goes away.
[00:24:10] Paul McCarty: Even when you do have a good pipeline -- look at the curl team, Daniel, famously the most visible and transparent open source project that accepts both bug bounty submissions and disclosures. They’ve been doing this for a long time. But they’re getting slammed by so much AI-generated noise. And now it’s not slop anymore -- it’s good, but the volume is the problem. The models -- Opus and so on -- had a lot to do with that.
[00:25:00] Jenn Gile: I see tokens in here, which honestly this may be a little dated. But PSA -- leaked tokens are a leading cause of breaches these days. If you have an option that’s not just a token, go get yourself a hardware key.
[00:25:25] Paul McCarty: The problem with RC tokens in particular -- 2FA is great and we should all use it where we can, but when you’re deploying the same NPM package three or four times a day, that 2FA push becomes an obstacle. You have to create automation. People should be using trusted publishing between GitHub and NPM, but as we saw with today’s attack, trusted publishing was enabled and the bad guys were still able to compromise the Bitwarden CLI. With a lot of these things, it’s easy to say them but really hard to do them. Why aren’t people scanning with pre-commit Git hooks? That technology’s been around for 25 or 30 years, but nobody does it because nobody does it.
[00:27:00] Jenn Gile: I think the other top question I get is why do people not do these things? You said it -- it’s certainly because it’s hard. There’s also a lot of human psychology behind it. We have biases like “nothing bad has happened so nothing bad is going to happen.” We even see with organizations that have been popped -- after the fact, they don’t necessarily enable the things that would prevent it from happening again. I don’t have a great explanation for why that is, other than it’s hard. It’s hard to do this when it’s not your full-time job and you don’t have the resources to dedicate to it.
[00:27:45] Paul McCarty: There are actually 12 things on this list, Jenn. The last one --
[00:27:48] Jenn Gile: I know. I love that it’s an OWASP page that’s not a Top 10.
[00:27:55] Jenn Gile: A couple of these things are “you should know this thing exists” rather than a genuine thing you can do -- they’re talking about typosquatting, slop squatting, dependency confusion attacks. Worth digging into more. Looks like a reasonably good resource.
[00:28:15] Paul McCarty: Yeah. And just because I have some observations about the obviousness of some of these things doesn’t mean I think this is a bad document. I’m glad it exists. I wish I had known about it before today. I just wanna double-click on preventing dependency confusion attacks. Dependency confusion attacks happen because people create internal private packages -- which they should be doing. Because of the way package managers are built, if you come up with a public version of something and somebody hasn’t taken steps to protect themselves from that, they will get the public version. We’ve known about dependency confusion for years -- even before it was made famous in 2020 or 2021. The package managers haven’t really done anything about it because that’s just the way it works.
[00:29:35] Paul McCarty: Dependency confusion exists in NPM because you can make private packages either inside of NPM or elsewhere, and if there’s a public version of that package name, you’re probably gonna get the public version before the private one. That’s something we need to address at some point.
[00:30:05] Jenn Gile: Something you’re calling out without saying it -- there’s a misconception that dependency confusion and typosquatting are obvious. Like you’re a dummy if you consume it.
[00:30:15] Paul McCarty: Oh my God, I’ve said the same thing from stage multiple times. 100%. People think -- oh, you didn’t see that was a zero instead of an O?
[00:30:25] Jenn Gile: And it’s like -- first of all, we can all get poned by social engineering. The stuff people are doing these days is gross and sophisticated. But that’s not even what we’re talking about here. With typosquatting and dependency confusion, this is more about machine automation in some cases. You may not be explicitly reading through this and picking it.
[00:31:00] Paul McCarty: 100%. Typosquatting -- people look down on organizations that get poned, like “what idiot would put a zero instead of an O?” But typosquatting is so much more effective than that. And by the way, everybody talks about the high-impact things like Bitwarden and chalk and debug and all the singularity stuff and the Shai Halud stuff. But every day, the vast majority of malicious packages hitting NPM and PyPI are typosquatting and dependency confusion attacks. They’re effective for a number of reasons. For people to look down on organizations that get poned via these is really frustrating, because they don’t understand how the software sausage is made.
[00:32:00] Paul McCarty: The most famous example I use -- there’s a famous NPM package that was only a few years old at the time it was poned. It was named Web3 -- but what it did was parse Web3 events and then sent them wherever you wanted. The bad guys created a package called Web3-parser, artificially inflated the download stats (which you can still do in NPM), and made that package look very legitimate. It was one of the best --
[00:32:55] Jenn Gile: And they made it do what it said it did. A lot of people don’t understand -- a lot of these packages actually do what they say they’re gonna do.
[00:33:00] Paul McCarty: And they don’t use lifecycle scripts. So Web3-parser is a great example. It actually parsed Web3 events. If nothing else you’re using it, you install it, import it, start using it in your application and it’s doing what you think it is. Meanwhile it’s silently exfilling all your Web3 events to the bad guy. They took advantage of the fact that the original package had inconsistent naming across the GitHub repo, the npm package, and the references -- and they just picked a better name.
Cross-Ecosystem Attacks and AI-Assisted Development
[00:33:50] Jenn Gile: Before we wrap up, I want to talk briefly about a couple of packages you came across two days ago that were targeting a specific company. I think it helps tell a story about what we’re talking about. We’re not gonna name the company -- they’re aware, we’ve talked with them. But talk about what you found.
[00:34:10] Paul McCarty: I didn’t know Jenn was gonna bring this up but I’m really glad you did because it’s super timely. The bad guys this week targeted a particular company inside an ecosystem -- we won’t talk about either of those. The attacks weren’t successful, but could have been, because they used multiple ecosystems. They built malicious packages in multiple ecosystems targeting the same users and the same company. Just to be clear, they weren’t targeting the company -- they were targeting people that were going to use this technology.
[00:34:55] Paul McCarty: We didn’t used to see this. The only time we’d ever see multiple packages in different ecosystems was rare and it would only be glassworm and a couple of other really big, well-done APT-style threats. Now, with agents and generative AI and really smart models, it’s really easy for someone to do this. What Claude won’t do is write you an infostealer in JavaScript from scratch. But if you get your hands on one and modify it yourself, you can work with Claude at the function level, make it successful -- and then say “rewrite this in Python” or “rewrite this in Go,” and it will. So the multi-ecosystem reach is getting much easier.
[00:35:55] Paul McCarty: The guardrails are getting better, but you’re not asking explicitly to write a payload. You’re saying “take this thing that works and put it in this other language.” And the immediacy of it is just -- all of those packages were deployed within an eight-hour time span across the different ecosystems. They had it all queued up. The payloads in this case were in clear text and weren’t super sophisticated, but as soon as people start perfecting this, start obfuscating and hiding it better, this will be a very successful pattern.
[00:37:10] Jenn Gile: All right. I hope people enjoy this. Leave some comments about what you like about it and what you want us to talk about. This is probably about the length and format we’ll stick with. We’re gonna try to do this about once a week, travel schedules allowing. Paul, it’s been good.
[00:37:25] Paul McCarty: I’ll try to be more energetic next time.
[00:37:28] Jenn Gile: You’re great. All right. Yeah.
[00:37:30] Paul McCarty: Ciao.
[00:37:31] Jenn Gile: Okay.