Skip to main content
PyPI Malware Hid in GitHub Actions for 72 HoursIncident
4 min readFor DevOps Leaders

PyPI Malware Hid in GitHub Actions for 72 Hours

On December 4, 2024, attackers published malicious versions of Ultralytics, a widely-used Python library for YOLO object detection models, to the Python Package Index (PyPI). The attack exploited GitHub Actions workflows to inject cryptocurrency mining malware into legitimate package releases. Google Colab's automated systems detected the compromise, but not before the malicious packages spread across the ecosystem for three days.

What Happened

Between December 4-7, 2024, attackers compromised the Ultralytics CI/CD pipeline through GitHub Actions vulnerabilities. They published altered versions of the library to PyPI containing a Monero cryptocurrency miner. The malware executed whenever users installed or updated the package, consuming CPU resources to mine cryptocurrency for the attackers.

The attack succeeded because the GitHub Actions workflow had write permissions to PyPI and lacked proper secret rotation controls. Once the attackers gained access to the workflow, they could publish packages that appeared to come from the legitimate Ultralytics maintainers.

Timeline

December 4, 2024: First malicious version published to PyPI through compromised GitHub Actions workflow.

December 4-7, 2024: Malicious packages remain available for installation; cryptocurrency mining payload executes on victim systems.

December 7, 2024: Google Colab's automated detection systems flag unusual resource consumption patterns; Ultralytics team removes malicious versions from PyPI.

Post-incident: PyPI administrators implement additional monitoring for the Ultralytics namespace.

Which Controls Failed

Workflow Permission Boundaries: The GitHub Actions workflow had persistent write access to PyPI tokens without job-level restrictions. This violated the principle of least privilege—no CI job should maintain standing write access to production package repositories.

Secret Rotation: The PyPI API token used in the workflow wasn't rotated on a schedule. Once compromised, the token remained valid indefinitely, giving attackers a persistent backdoor.

Artifact Signing: Published packages lacked cryptographic signatures that users could verify. Without signing, downstream consumers had no mechanism to validate package authenticity independent of PyPI's infrastructure.

Runtime Monitoring: The build environment didn't monitor for unexpected network connections or process spawning during package creation. The cryptocurrency miner's network activity went undetected until it hit user systems.

Dependency Pinning: Many downstream projects imported Ultralytics without version pinning, automatically pulling the latest (malicious) release when they ran pip install ultralytics.

What Standards Require

NIST 800-53 Rev 5 SA-10 (Developer Configuration Management) requires organizations to "perform configuration management during system and system component design, development, implementation, and operation." For CI/CD pipelines, this means:

  • Documenting which workflows have write access to production systems.
  • Implementing change controls before modifying deployment credentials.
  • Maintaining audit logs of all package publications.

ISO/IEC 27001:2022 Annex A.8.32 (Change Management) mandates controlled procedures for changes to information processing facilities and systems. Your CI/CD pipeline is an information processing facility. Every workflow modification should trigger a change ticket with technical review.

SOC 2 Type II CC6.6 (Logical and Physical Access Controls) requires restricting access to sensitive resources. Your PyPI tokens are sensitive resources. The control expects:

  • Time-limited credentials for automated systems.
  • Documented justification for persistent access.
  • Regular access reviews (quarterly minimum).

OWASP ASVS v4.0.3 Requirement 14.2.1 states: "All build and deployment processes are defined, repeatable, and documented." This includes:

  • Version-controlled workflow definitions.
  • Documented approval processes for workflow changes.
  • Separate credentials for development and production deployments.

Lessons and Action Items

Implement OIDC for Package Publishing

Replace long-lived PyPI API tokens with OpenID Connect (OIDC) authentication in your GitHub Actions workflows. OIDC tokens are short-lived and scoped to specific repositories and branches. Configure your workflow:

permissions:
  id-token: write
  contents: read

This limits token validity to the duration of a single workflow run. PyPI supports OIDC through Trusted Publishers—configure this in your PyPI project settings before removing the API token.

Require Manual Approval for Production Publishes

Add an environment protection rule to your deployment workflow. In your repository settings, create a "pypi-production" environment that requires approval from two maintainers before any workflow can access PyPI credentials. Your workflow references this environment:

jobs:
  publish:
    environment: pypi-production

Now even a compromised workflow can't publish without human verification.

Sign Your Packages with Sigstore

Implement cryptographic signing using Sigstore, which provides keyless signing backed by certificate transparency logs. Add the sigstore-python action to your publish workflow:

- name: Sign packages
  uses: sigstore/[email protected]

This generates verifiable signatures that downstream users can check with sigstore verify. Document the verification process in your README so security-conscious users can validate authenticity.

Monitor Build Environments for Anomalies

Deploy runtime security monitoring in your CI runners. Tools like Falco can detect unexpected behaviors:

  • Outbound network connections to cryptocurrency pools.
  • Process spawning that doesn't match your build script.
  • File modifications outside expected directories.

Configure alerts to your security team's Slack channel or PagerDuty. Detection within minutes beats discovery after user reports.

Pin Dependencies in Your Lockfiles

If you consume open-source packages, commit your requirements.txt or poetry.lock with exact versions. Don't use:

ultralytics>=8.0.0

Instead, lock to tested versions:

ultralytics==8.0.196

Run pip-audit or safety in your CI to catch known vulnerabilities in pinned versions, then update deliberately after testing.

Audit Your Workflow Permissions Quarterly

Create a spreadsheet tracking every GitHub Actions workflow with write access to external systems. Columns: workflow name, repository, destination system, credential type, last rotation date, business justification. Review this quarterly with your security team. If you can't justify persistent access, revoke it.

The Ultralytics attack succeeded because deployment credentials outlived their usefulness and workflow permissions exceeded operational needs. Your CI/CD pipeline is production infrastructure—treat it with the same rigor you apply to your application servers.

GitHub Actions

Topics:Incident

You Might Also Like