Security Digest · Dec 4, 2024 · Supply-chain compromise · AI / LLM
Ultralytics PyPI Releases Shipped a Crypto Miner
ultralytics 8.3.41, 8.3.42, 8.3.45 and 8.3.46 on PyPI carried an XMRig downloader after a GitHub Actions compromise. All removed. Verdict: most can relax.
Our verdict
Most can relax
The four bad versions were pulled from PyPI within hours to about half a day in December 2024 and carried a coin miner, not a credential stealer; unless something still pins one of them, there is nothing to do but learn the CI lessons.
Who must act: Anyone with a lockfile, image, cache or mirror that still contains ultralytics 8.3.41, 8.3.42, 8.3.45 or 8.3.46, or who ran one of them in December 2024 and never rebuilt that host.
Who can relax: Users on 8.3.40 or earlier, 8.3.43, or any other release installed from PyPI after the bad versions were removed.
- Affected
- ultralytics 8.3.41
ultralytics 8.3.42
ultralytics 8.3.45
ultralytics 8.3.46 - Fixed in
- ultralytics 8.3.43 (clean, Dec 5, 2024)
any later release other than 8.3.45 and 8.3.46 - CVSS
- Not scored
- EPSS
- n/a
- CISA KEV
- Not listed
- Exploitation
- Exploited in the wild
- Severity
- High
What to do
- Check every environment and lockfile: pip show ultralytics and grep -rn 'ultralytics==8.3.4[1256]' .
- Purge the bad versions from internal mirrors (Artifactory, Nexus, devpi) and from Docker image layers and pip caches.
- Rebuild any host or container that ran an affected version instead of trying to clean it.
- If you publish to PyPI: use Trusted Publishing, revoke leftover long-lived API tokens, and gate releases with a protected GitHub Environment.
- Audit your workflows for pull_request_target and template injection (for example with zizmor), and keep release jobs off shared caches.
How to check if you are affected or compromised
- pip show ultralytics, or pip freeze | grep ultralytics, in every venv, image and notebook environment
- Look for unexplained sustained CPU load on hosts that ran ultralytics in early December 2024 (users first noticed CPU spikes).
- Compare the installed package's utils/downloads.py, model.py and __init__.py against the matching GitHub tag, or against the SHA1 hashes in ReversingLabs' IoC list.
What happened#
On December 4, 2024, version 8.3.41 of ultralytics was published to PyPI with downloader code that fetched the XMRig cryptocurrency miner. ultralytics is the widely used YOLO computer-vision library, which ReversingLabs puts at about 60 million downloads. A user found the problem by diffing the wheel against the Git tag and reported it in issue #18027. According to ReversingLabs, the malicious code lived in downloads.py and model.py, where it picked a payload to match the host's platform. Version 8.3.42, released on December 5 as the fix, carried the same code, because the source of the compromise had not been found yet. A clean 8.3.43 followed the same day. On December 7, versions 8.3.45 and 8.3.46 appeared with the downloader in __init__.py.
The GitHub source code was never modified. William Woodruff's analysis traces how the attack worked. Pull requests from the openimbot account had crafted branch names that triggered a pull_request_target workflow. A composite action in ultralytics/actions pasted github.head_ref straight into a shell script. That was a template-injection bug that had been reported, fixed, and then reintroduced. The resulting code execution most likely let the attacker poison the GitHub Actions cache that the release build later used, so a legitimate release job built a malicious wheel. The PyPI analysis confirms that 8.3.41 and 8.3.42 went out through the project's Trusted Publishing workflow. The second round, 8.3.45 and 8.3.46, was uploaded directly with an unrevoked PyPI API token, with no matching CI run and no attestations. PyPI says no PyPI flaw was involved.
Who is affected#
Only installations of 8.3.41, 8.3.42, 8.3.45 or 8.3.46. Downstream tools that floated to the latest version during the windows were also pulled in. The issue thread links reports from ComfyUI-Impact-Pack and comic-translate users. PyPI removed all four versions. Woodruff's timeline puts each version's exposure at roughly 1 to 12 hours. There is no CVE and no KEV entry, because this was a malicious release rather than a code flaw.
Do you really need to act?#
Most people do not. The bad versions have not been installable from PyPI since December 2024, and the payload that sources describe was a coin miner. That costs CPU and money, but it is not a credential stealer. The remaining risk is stale artifacts: a pinned lockfile, a Docker image built that week, or a private mirror that cached the wheel. If you find one, rebuild. You should not trust a host that ran attacker-chosen code. The bigger lesson is for maintainers. Trusted Publishing worked, because its attestations are what made the forged releases stand out. But pull_request_target, shared caches in release jobs and a forgotten API token undid it.
What to do now#
- Run
pip show ultralyticsin every environment, andgrep -rn "ultralytics==8.3.4[1256]"across requirements and lock files. - Purge the four versions from internal package mirrors, pip caches and container images.
- Rebuild any machine or container that ran an affected version.
- If you publish packages, follow PyPI's advice. Use Trusted Publishing, revoke long-lived API tokens, require a protected GitHub Environment for releases, and audit workflows with a tool such as zizmor.
- Keep release jobs off caches that untrusted workflows can write to, and pin dependencies by hash.
How to tell if you were hit#
Check installed versions first. On hosts that ran an affected version, look for unexplained sustained CPU usage, which is how users first noticed the miner, and for unknown binaries or processes started by Python. To verify a wheel, compare utils/downloads.py, model.py and __init__.py against the matching GitHub tag, or match file hashes against the SHA1 IoCs ReversingLabs published. Specific network indicators for the miner were not in the sources reviewed for this brief.
Timeline#
- — Malicious 8.3.41 is published to PyPI via the project's own release workflow.
- — Issue #18027 is opened. 8.3.41 is removed. 8.3.42 ships with the same payload and is pulled about an hour later. Clean 8.3.43 is released.
- — 8.3.45 and 8.3.46 are uploaded with a stolen API token and removed hours later.
- — PyPI publishes its attack analysis and planned token and Environment warnings.
Sources
Original sources first: vendor and project advisories, CVE and government records, the discoverers’ write-ups. Every claim above links to one of these. See how the Security Desk works.
Original and primary sources
- PyPI Blog — Supply-chain attack analysis: Ultralytics blog.pypi.org
- ultralytics/ultralytics issue #18027 — Discrepancy between GitHub and PyPI for v8.3.41 github.com
- William Woodruff — Ultralytics attack analysis (zizmor) blog.yossarian.net
- ReversingLabs — Compromised ultralytics PyPI package delivers crypto coinminer reversinglabs.com