BLOG

The OpenSourceMalware Show #5

npm staged publishing, DPRK's Axios-linked packages, TeamPCP's biggest npm maintainer compromise yet, and how a poisoned VS Code extension led to a GitHub emplo

By cb482791-4ef1-4762-96ad-b0ca4bdd538e ·

The OpenSourceMalware Show #5

The OpenSourceMalware Show is available on YouTube, LinkedIn, and as a podcast.

In this episode we covered:

npm Staged Publishing: npm’s new staged publishing feature adds a human approval checkpoint before a package goes live. We walk through what it actually does, where it would and wouldn’t have helped in recent attacks, and the questions the docs don’t yet answer — including whether the person who stages can also approve, and what happens to the control if an account is compromised.

DPRK Axios-Linked Packages: Paul discovered three npm packages tied to the threat actor behind the March Axios compromise. These packages have been quietly harvesting developer credentials since early April. We discuss why DPRK favors multi-use, multi-flank attacks, and how these packages appear to have been built specifically to support Contagious Interview and TaskJacker campaigns running in parallel.

TeamPCP’s Biggest Maintainer Compromise Yet: Two npm maintainers were compromised — we don’t yet know how. One of them, a single developer, maintains over 540 packages. TeamPCP published over 600 malicious versions containing what appear to be Mini Shai-Hulud payloads. Just three of the affected packages account for more than 5 million weekly downloads.

GitHub Employee Device Compromised via Poisoned VS Code Extension: On May 18th, a malicious version of the Nx Console VS Code extension was published to the Visual Studio Marketplace and OpenVSX. A GitHub employee had it installed. The credential theft that made it possible happened seven days earlier, through the TanStack npm compromise. GitHub estimates around 3,800 repositories were exposed. We also discuss the CISA “private” repository that was not private, and what both incidents say about secrets management and GitHub permissions defaults.

Episode Resources

[00:00:00] Jenn Gile: All right, we’re live. Today is May 21st. It’s a sunny day in the Seattle area. I’m sure it’s sunny in Australia. In my head it’s always sunny there, even though I’ve lived there and I know that’s not how it works.

[00:00:10] Paul McCarty: Yeah, it’s been raining for weeks. My pool — I’ll take a picture and send it to you. It’s muddy. You can’t see it. It looks like a pond.

[00:00:20] Jenn Gile: Oh, no.

[00:00:22] Paul McCarty: Yeah. We just had torrential rain and the whole yard is saturated. The sun is out right now, so happy days.

[00:00:30] Jenn Gile: Lovely. Well, you’ve been in the middle of your marathon of conferences. You were at BSides Melbourne last weekend, you’re at AusCERT right now, and you’re heading to Gold Coast BSides tomorrow. What have you been hearing?

[00:01:00] Paul McCarty: I think all of these attacks have hit mainstream infosec. The conference I’m at is very traditional — AusCERT has been going for 25 years. What I’m hearing is that CISOs and mainstream infosec are now aware of these attacks, and like anything else — SBOMs three years ago — they’re looking for simplistic answers. Everybody is saying “cool down periods is gonna fix this.” If I heard cool down periods one more time this week, I’m gonna freak out.

[00:01:50] Jenn Gile: I gave a lightning talk last week about top tips on the application security side for preventing malware consumption — cool down periods, version pinning, dealing with lifecycle scripts. But none of them are a silver bullet. There are no silver bullets in malware. On that note, I want to take us slightly out of order from what we planned, because I want to start with the new npm feature that just came out for staged publishing. Have you caught up on it yet?

[00:02:50] Paul McCarty: I’ve done zero writing, so it’s your show here.

npm Staged Publishing

[00:03:00] Jenn Gile: I’ll give an overview with the caveat that I’ve reviewed the docs and poked around at the concept, but I have not implemented this. The basic idea is it creates a staging area for your package that requires an additional approval before it ships. Instead of npm publish pushing a package live immediately, you run npm stage publish, which holds it in a staging area, and a maintainer reviews and approves it with 2FA before it goes public. It’s a human checkpoint for packages hosted on npm.

[00:04:00] Jenn Gile: First off, this is a real improvement. If your CI pipeline stages a release and a second human has to interactively approve it with a different account, you’ve added a genuine barrier. The docs confirm you can’t use OIDC tokens — it requires interactive authentication, so it can’t just be automated away. From what I understand, this probably would have prevented the compromise of those SAP CAP packages about a month ago, where TeamPCP exploited a misconfigured OIDC trusted publisher workflow.

[00:05:00] Jenn Gile: That said, this comes with a lot of caveats. It’s not going to do any good if you’re a maintainer group of one. The Axios compromise, for example, would not have been stopped by this. There are also a lot of things the docs don’t explicitly cover. My first question was: if you turn this on and your account gets compromised, can the attacker just turn it off? Unclear. It also has to be enabled per package — there’s no org-wide publishing option — which is a real barrier to adoption. And there’s no enforcement mechanism that prevents the person who staged the package from also being the person who approves it.

[00:06:50] Jenn Gile: I did some research earlier in the year with the Endor team where we looked at accounts that had been compromised through an ATO and whether they had turned on trusted publishing. Only about 13% of already-compromised accounts — accounts where this was a known problem — had actually turned on trusted publishing. And a lot of the accounts that hadn’t were corporate accounts. You can see it in the package names.

[00:07:30] Paul McCarty: Yeah, I think this is a great effort — good job, npm. But it falls into the bucket of GitHub and npm security features that they know almost nobody will turn on, and then they can say “hey, we did something.” And because nobody uses it, the control doesn’t actually provide comfort.

[00:08:20] Jenn Gile: They announced this was coming back in January. I would have expected a bit more around it given it’s been five months.

[00:09:00] Paul McCarty: Most of what gets pushed in npm right now is from people who’ve been seconded inside Microsoft, put on this project, told to deliver quickly, and then immediately sent back to their other Microsoft work. npm doesn’t have staff. npm and GitHub are both being absorbed into Microsoft, and you can see it everywhere.

[00:09:45] Jenn Gile: We’ve got two questions from the YouTube comments. First one is from Toaster32: “What would you say is the team size where you’d recommend enabling this by default?”

[00:10:00] Jenn Gile: It’s probably pointless if your team’s less than two — we’ll start there. If you’re a team of two, poke at it and see what the friction looks like, because this is designed to introduce a checkpoint. A friend of mine, Alex, commented on my LinkedIn post with a good framing: “Is two-stage viable if an org is publishing a boatload of packages, or looking at agentic code fixes moving really quickly? Does adding the human add security but also a bottleneck?” Absolutely it adds a bottleneck. But it’s a good bottleneck.

[00:11:00] Paul McCarty: I have to challenge this a bit. npm is not your staging branch. If you’re pushing so many changes to your public npm packages that this MFA challenge becomes an issue, then your CI and how you publish npm packages is broken. Full stop. Those highly iterative stages need to be happening earlier in your CI process. If this becomes an issue, you probably aren’t building and deploying to npm in the right way. That’s my opinion.

[00:12:15] Jenn Gile: Fair. Second question, from Josh: unique approver count required to avoid self-approve?

[00:12:20] Paul McCarty: We don’t know yet. I just looked at this. I will get you an answer, Josh.

DPRK Axios-Linked Packages

[00:12:45] Jenn Gile: Let’s go back to May 18th. You published a blog announcing you’d found three malicious npm packages connected to the March Axios compromise that have been quietly harvesting developer credentials since early April. What did you find?

[00:13:00] Paul McCarty: There are three npm packages I’ve tied back to the threat actor that published the malicious Axios dependency. DPRK loves multi-use attacks — multi-flanking attacks. Axios is one of the most popular packages on the planet. If you get a malicious dependency into it, lots of people get exposed. But in parallel, there are other groups inside DPRK, inside Lazarus, running Contagious Interview and other campaigns at the same time.

[00:13:45] Paul McCarty: My assumption is that these three packages were built to support those other campaigns — the Contagious Interview and TaskJacker campaigns. I found evidence: there are several GitHub repos calling out to these packages. They have a slightly different payload mechanism but are very similar in nature. That’s how DPRK works. They just create this whole fabric of infrastructure and compromise everywhere they can.

[00:14:30] Jenn Gile: Very highly productive. We’ll share the link to that analysis.

TeamPCP’s Largest Maintainer Compromise

[00:15:00] Jenn Gile: Next topic. You discovered earlier this week that two npm maintainers were compromised — we don’t know how yet. These maintainers, especially one of them, maintain a huge number of packages, so the threat actor was able to publish over 600 malicious versions. The payloads appear to contain the same Mini Shai-Hulud content, though we haven’t fully analyzed them yet. Paul, just three of the compromised packages account for five million downloads a week.

[00:15:50] Paul McCarty: Yeah. I’ve gotten into the guts of it. It’s very similar to previous iterations, with a few differences — TeamPCP keeps evolving the payloads and what they’re targeting, and they’re now going after Claude and some of the common agentic tools we’re all using.

[00:16:00] Paul McCarty: I want to call attention to the first part of this: one individual developer, Atul, got compromised. 540 packages under that one maintainer. And all of them are Java data visualization packages — the blast radius is enormous.

[00:17:00] Jenn Gile: Yeah. There are a lot of maintainers like this, and npm hasn’t done a good enough job enforcing security changes and best practices on existing accounts. They’re waiting for people to voluntarily do these things.

[00:17:30] Paul McCarty: TeamPCP did another interview, by the way. More OpSec details slipping out. I don’t know — stop talking. Actually wait, keep talking.

[00:18:00] Jenn Gile: Dig that hole. Okay, last topic, and it is still TeamPCP, sorry.

GitHub Employee Device Compromised via Poisoned VS Code Extension

[00:18:10] Jenn Gile: On Monday, GitHub announced they had “detected and contained a compromise on an employee device involving a poisoned VS Code extension published by a third party.” That’s a bummer. You knew pretty quickly which extension it was.

[00:18:35] Jenn Gile: Unfortunately, this extension comes from Nx, which was also compromised in the Singularity attack last year. Nx is used for monorepo management. I will give Nx some credit — they write really good postmortems. They learn from their mistakes.

[00:19:00] Jenn Gile: What happened this time: one of their developers was compromised through the TanStack compromise that happened a couple of weeks ago. The TanStack malware silently exfiltrated the maintainer’s GitHub CLI OAuth token seven days before the malicious extension was published. So the credential theft happened on May 11th. The marketplace publish happened on May 18th. The threat actors had access to Nx’s GitHub repos for seven days without detection.

[00:20:00] Jenn Gile: The Visual Studio Marketplace took the extension down within 11 minutes. OpenVSX took it down within 36 minutes. It was only up for a maximum of 36 minutes, and in that time, someone from GitHub had consumed one of those poisoned versions — and that’s how TeamPCP got access to what GitHub is saying is approximately 3,800 to 5,000 repositories.

[00:21:00] Paul McCarty: Last number I saw was 3,800. I was looking through some of the repo names late last night. The naming conventions check out — it looks like what you’d expect. And on the question of how long it was actually available: they say it was only up for 18 to 31 minutes, but I grabbed it outside of that window. I’ve heard from others that it was available via CDN or other means beyond the stated window. Just because it’s gone doesn’t mean it’s gone.

[00:22:00] Jenn Gile: You’ve been saying that for a while.

[00:22:10] Paul McCarty: Yeah, both in the Python world and the JavaScript world. And VS Code extensions are just JavaScript — you can run the same analysis on them that you do on npm packages. Tools like GuardDog and MuadDib work on these. The level of inception here just blows my mind: a Microsoft employee using a Microsoft product compromises another Microsoft employee, which then leaks a bunch of GitHub repositories. Pull your head out.

[00:23:00] Jenn Gile: Not a whole lot of information from GitHub. The statement is about three paragraphs. They’re focused on saying no customer data was affected, but the threat actors almost certainly got ahold of something useful.

[00:23:30] Paul McCarty: They were asking for bids — starting at $50K, which I thought was “way to set low expectations.” Last I saw, the bid was up to $95,000. Looking at the files, I’m not surprised. It’s not like the CISA breach.

[00:24:00] Jenn Gile: Oh yeah. Speaking of CISA — there was a “private” repository that was not private. It had a whole bunch of keys in it. This goes back to what we’ve talked about before: you need visibility into which keys have been compromised. Turn on whatever free tooling you have to scan for this. More and more, I’m seeing the need for endpoint products that can tell you what’s on your developer endpoint so you can rotate quickly.

[00:24:45] Paul McCarty: We need to start talking about managing GitHub repo sprawl. That private repo was inside the CISA GitHub organization. Why are you letting people create public repos inside an org like that? It’s the same thing we dealt with AWS and cloud sprawl, and then IAM just blew up. A lot of these permissions are opt-in, not opt-out.

[00:25:45] Jenn Gile: Many of those GitHub permissions are opt-in by default. There are reasons products make these conscious-decision-to-turn-on calls. We may not agree with them, but reasons exist.

[00:26:00] Paul McCarty: I got a response back on my Supabase VDB disclosure, and it’s not good.

[00:26:10] Jenn Gile: That’s a good teaser for next week.

[00:26:30] Jenn Gile: All right, I think we’ve hit everything we wanted to cover. Good job keeping it to about 30 minutes. Anything to add before we wrap up?

[00:26:45] Paul McCarty: Gold Coast BSides is tomorrow. If you’re in Australia and in the area, there are still some tickets available. We’d love to see people come out.

[00:27:00] Jenn Gile: I will be in San Francisco next week. If anyone is looking for content on AI agent security, I’m giving a talk on malicious skills at Zenity’s AI Agent Summit. I’ve heard great things about it — check it out. All right. Enjoy AusCERT and BSides.

[00:27:30] Paul McCarty: Thanks everybody for listening. We appreciate it. Cheers.

[00:27:35] Jenn Gile: Cheers.