4 ms·
Zizmor would have caught the Ultralytics workflow vulnerability
- HeWhoLurksLate 2y agoWow, I had no clue about how many ways it was possible to get burned with Actions - as an ME nerd, I've set up a few CI/CD workflows, and if I recall correctly, while I was reading through the documentation for GitHub Actions (circa 2022) there wasn't any mention of cybersecurity best practices in the general docs. Is that generally considered best practice, or at least acceptable? I'm not a programmer by trade- I generally write one-off or two-off code, but that's changing as I get deeper into simulation land. For me, reading the entirety of the docs is something that generally happens only when I'm troubleshooting something or an LLM dragged me significantly further than my understanding and I have to go learn how a library or API works.
- woodruffw 2y ago(Author of this post.) If you’re interested in how this went down, the timeline section[1] in particular is worth jumping to: my key takeaway is that this vulnerability was reintroduced, and that there’s only limited evidence that the Ultralytics team have done a full revocation and rotation of all accounts and credentials that the attacker may have had access to. Given that, it’s not inconceivable that a third round of backdoored packages will occur. I would recommend that people exercise extreme caution when installing the current versions; most users would probably be best served by pinning to an older version from before any indicators of compromise. [1]: https://blog.yossarian.net/2024/12/06/zizmor-ultralytics-injection#appendix-rough-timeline-of-events https://blog.yossarian.net/2024/12/06/zizmor-ultralytics-inj...
- the_mitsuhiko 2y agoOne quite annoying element is that as a third party you cannot access the attestations of the deleted releases any more. I really wanted to see if the attestations would help here to figure out what happened. But maybe I’m just not informed enough about where to look. Another element here is that the releases seemingly were deleted and re-created? I thought that was prevented by PyPI?
- woodruffw 2y agoThe attestations are checked into the public transparency log, so they’re still accessible — that’s how I did a decent amount of the triage in the write up. You can find them in the write up by searching for “Sigstore” (I would direct link them, but I’m on mobile). > Another element here is that the releases seemingly were deleted and re-created? I thought that was prevented by PyPI? Hmm, where do you see this? The release history on PyPI doesn’t show any recreations[1]. [1]: https://pypi.org/project/ultralytics/ https://pypi.org/project/ultralytics/
- the_mitsuhiko 2y ago> You can find them in the write up by searching for “Sigstore” (I would direct link them, but I’m on mobile). Yeah, I know they are in sigstore, I just did not know how to find them. Is there an interface for this I missed? > Hmm, where do you see this? The release history on PyPI doesn’t show any recreations[1]. Then I completely misunderstood what happened. Was this in fact completely made up releases that were not even intended to be triggered? Eg: a bot released .41 without there being an intent of being an actual .41 release? I thought that UltralyticsAssistant was the developer, not the attacker. Do they also control that thing?
- woodruffw 2y ago> Is there an interface for this I missed? That would be search.sigstore.dev, unless I'm misunderstanding what you mean. > Was this in fact completely made up releases that were not even intended to be triggered? Eg: a bot released .41 without there being an intent of being an actual .41 release? I thought that UltralyticsAssistant was the developer, not the attacker. Do they also control that thing? .41 and .42 were triggered directly from the repository. One was triggered by the UltralyticsAssistant account and included a human bypass, which strongly suggests that the attacker controlled (and maybe still controls) that bot account. The last two compromised releases were published directly via API token, not via the source repo, which strongly suggests that the attacker either exfil’d an old API token from CI/CD or that they’re in control of the developer’s account on PyPI. Those ones don’t have attestations, while the first two releases do (two each, one per dist per release).
- IshKebab 2y ago[flagged]
- woodruffw 2y agoThe vulnerability has nothing to do with bash. It’s a template injection in GitHub Actions, which bypasses the interpolation of any shell or interpreter.
- tigereyeTO 2y agoThis post has left me wondering: what is zizmor? What is ultralytics? Are these words actually real or is someone having a stroke? Not all nerds know all projects so I decided to educate myself and followed OP’s links to learn about Ultralytics: > Ultralytics YOLO11 is a cutting-edge, state-of-the-art (SOTA) model that builds upon the success of previous YOLO versions and introduces new features and improvements to further boost performance and flexibility. Ultralytics’ readme doesn’t explain what ultralytics is or does. Thankfully Zizmor’s readme describes itself clearly: > zizmor is a static analysis tool for GitHub Actions. It can find many common security issues in typical GitHub Actions CI/CD setups. This isn’t a critique on OP: I enjoyed reading about the vulnerability(ies!) you found and I learned a lot. I’m just generally frustrated that so many readme files on GitHub fail to describe what the project actually does, Ultralytics being just one example. Have fun and keep hacking
- bobthepanda 2y agoI wonder if Zizmor has anything to do with this NYC local notable: https://en.wikipedia.org/wiki/Jonathan_Zizmor https://en.wikipedia.org/wiki/Jonathan_Zizmor
- hardwaregeek 2y agoI was legitimately wondering if Dr Zizmor made a pivot into cybersecurity
- woodruffw 2y agoI named it explicitly for Dr. Zizmor :-) https://github.com/woodruffw/zizmor#the-name https://github.com/woodruffw/zizmor#the-name
- Eisenstein 2y agoYOLO is an ML architecture used for object detection and recognition and Ultralytics develops a version of YOLO.
- xign 2y agoUltralytics' README gives me a headache to read lol, for similar reasons you gave. But then the package is called YOLO and the author abbreviated state-of-the-art to SOTA (wat…). It's exactly the kind of "modern" GitHub repository that I like to stay away from lol. My only critique of this README was that it didn't have enough emojis. If you want to truly YOLO, may as well fill half your text with emojis. This hilariously tech bro optimistic auto-response (made by a bot) from the linked issue (https://github.com/ultralytics/ultralytics/issues/18027#issuecomment-2519321742 https://github.com/ultralytics/ultralytics/issues/18027#issu...) also gave me a laugh in how out of touch it was with what the issue was.
- throw4847285 2y agoThank you Doctor Zizmor!
- IncreasePosts 2y agoSpotted the straphanger
- RainyDayTmrw 2y agoWhy has CI for open-source projects become so difficult to secure? Where did we, collectively, go wrong? I suppose, it's probably some combination of: CI is configured in-band in the repo, PRs are potentially untrusted, CI uses the latest state of config on a potentially untrusted branch, we still want CI on untrusted branches, CI needs to run arbitrary code, CI has access to secrets and privileged operations. Maybe it's too many degrees-of-freedom creating too much surface area. Maybe we could get by with a much more limited subset, at least by default. I've been doing CI stuff in my last two day jobs. In contrast, we worked only on private repos with private collaborators, and we explicitly designated CI as trusted.
- woodruffw 2y ago> Maybe it's too many degrees-of-freedom creating too much surface area. I think this is essentially it: there's extraordinary demand for "publicly dispatchable and yet safe" CI/CD, despite those requirements being fundamentally in tension with each other. All things considered, I don't think GitHub has done the worst job here: the security model for GitHub Actions is mostly intuitive, so long as you stick to triggers like `push`, `pull_request`, etc. The problems only really begin when people begin to use triggers that (IMO) GitHub should never have added in the first place, like `pull_request_target` -- those triggers break the basic "in repo privileged, out of repo unprivileged" security assumption and cause the kinds of problems we're seeing here.
- RainyDayTmrw 2y agoI wonder about an alternative history where the default feature set is much smaller and much safer, and everything else is opt-in behind a flag, and the flags are prefixed with "unsafe_" or something. That would hopefully encourage people to look up the "unsafe_allow_foobar" docs before using it.
- rzimmerman 2y agoIt's a web of danger for sure. Configuring CI in-repo is popular (especially in the Gitlab world) and it's admittedly a low-friction way to at least get people to use config control for CI (or use CI for builds at all). I think the number of degrees of freedom is really a footgun. I remember early Gitlab runner use when I had a (seemingly) standard build for a docker image. There wasn't any obvious standard way to do that. There were recommendations for dind, just giving shell access, etc. There's so much customization that it's hard to decide what's safe for a protected/main branch vs. user branches. I don't have a solution. But I think it would be better if, by default, CI engines were a lot less configurable and forced users to adjust their repo and build to match some standard configurations, like: - Run `make` in a Debian docker image and extract this binary file/.deb after installing some apt packages - Run docker build . and push the image somewhere - Run go build in a standard golang container And really made you dance a little more to do things like "just run this bash script in the repo". Restrict those kinds of builds to protected branches/special setups. Having the CI config in the same source control tree is dangerous and hard to secure. It would probably be better to have some kind of headless branch like Github pages that is just for CI config.