Hackers Injecting Malicious Code into GitHub Actions to Steal PyPI Tokens CyberDudeBivash — Threat Brief & Defensive Playbook

 


SUMMARY 

Attackers are modifying GitHub Actions workflows (via compromised accounts, malicious PRs, or forged commits) to add steps that exfiltrate PyPI (or other package registry) publishing tokens. With those tokens they publish backdoored packages or download private packages — a supply-chain multiplier. Defend by eliminating long-lived secrets, switching to OIDC/ephemeral credentials, hardening workflow controls, monitoring workflow changes and secret usage, and rotating tokens immediately if you suspect compromise.


How attackers typically operate 

  • Gain write access to repo or CI/CD config (via compromised developer account, stolen token, privilege abuse, or merge of malicious PR).

  • Modify or add a workflow file that runs in the repo’s runner context (self-hosted or GitHub-hosted).

  • Add steps that read repository secrets (e.g., PYPI_TOKEN) or create credentials files (~/.pypirc), then send the token to attacker-controlled endpoints or push a malicious package.

  • Use obfuscation (multi-stage steps, encoded payloads) or leverage trusted third-party actions to hide the behavior.

  • Reuse stolen token immediately (publish backdoored package or exfiltrate private packages).


High-value Indicators of Compromise

Watch for these signs in repos, org settings, and pipeline telemetry:

  1. Unexpected workflow changes

    • New/modified .github/workflows/*.yml commits by unfamiliar accounts or from external forks.

  2. Secrets added or changed

    • Creation of secrets named PYPI_TOKEN, TWINE_PASSWORD, DOCKER_PASSWORD by non-admins or via CI.

  3. Workflows using secrets then calling external endpoints

    • Steps that POST/GET to unfamiliar domains after referencing secrets.

  4. Base64, eval, curl/wget, or arbitrary shell code inside workflows

    • Encoded blobs, inline scripts, or one-liners that write to files or call python -c / sh -c.

  5. Workflow runs triggered from forks with write to secret usage

    • Fork PRs that somehow result in workflows that access secrets (check pull_request_target misuse).

  6. New files writing ~/.pypirc, or creating environment files with credentials

  7. Unusual publishing activity

    • New package versions published outside normal release windows or by unexpected accounts.

  8. Outbound network calls from self-hosted runners to suspect domains right after a workflow step uses secrets.


Immediate containment checklist 

  1. Pause publishing: disable automated publish workflows (temporarily block actions that push to PyPI).

  2. Rotate tokens: immediately rotate all PyPI/twine tokens used by CI for affected repos. Treat all tokens as compromised if a workflow was tampered with.

  3. Revoke suspicious secrets & keys in GitHub Org and cloud providers; require reauthorization via admins.

  4. Block affected runners: isolate or remove any self-hosted runners involved.

  5. Freeze merges: enable branch protections (require review, block force pushes) on all release branches.

  6. Snapshot evidence: collect workflow history, commit logs, workflow run logs, audit logs, and runner logs for forensics.

  7. Notify package registries: inform PyPI (and other registries) to block suspicious package names/versions and to assist with takedowns.


Short hunts & SIEM rules

1) GitHub audit / event stream: detect workflow file modifications

  • What to watch: repository_dispatch, push, workflow_dispatch events modifying .github/workflows/* by uncommon actors.

  • Splunk/ELK pseudo query:

index=github_audit event_name=push OR event_name=workflow_dispatch | where tostring(files_changed) contains ".github/workflows/" | where actor NOT IN ("trusted_release_bot","ci-bot","list_of_approved_committers")

2) Secret creation/change alerts

  • Watch: secret creation in org or repo by non-admins.

index=github_audit event_name=create_secret OR event_name=update_secret | stats count by actor, repo, secret_name | where secret_name IN ("PYPI_TOKEN","TWINE_PASSWORD","PUBLISH_TOKEN")

3) Workflow run calling external hosts immediately after secret use

  • Detect curl|wget|nc usage in run logs or outbound connections from runner IPs.

index=runner_logs source=actions_run | rex "curl|wget|nc|curl -s" | stats count by run_id, actor, dest_host | where dest_host NOT IN ([trusted_cdns])

4) Unusual PyPI publishing events

  • Monitor PyPI / internal registry events for publish by unexpected identities or at odd hours.

index=package_registry logs action=publish | where publisher NOT IN (approved_ci_service_accounts) | table package_name, version, publisher, time

Hardening: short and long term actions

Immediate (0–7 days)

  • Remove long-lived registry tokens from repo secrets. Use short-lived tokens or ephemeral credentials.

  • Switch to OIDC and short-lived cloud creds (GitHub Actions → cloud provider via OIDC; for PyPI, use scoped tokens and rotate often).

  • Lock down workflows: require code owners and at least 1–2 approvers for changes to workflow files; disallow direct merges to main.

  • Disable pull_request_target unless carefully reviewed — it runs in the base repo context and can access secrets. Prefer pull_request and explicit review gates.

  • Block untrusted third-party actions or pin to verified commit SHAs; prefer actions from GitHub Verified creators.

  • Enable secret scanning & push protection in GitHub; scan workflow files for suspicious commands.

  • Enforce branch protection: required status checks, signed commits, enforce linear history.

Short/medium term (1–3 months)

  • Require artifact and release signing (GPG/cosign) so published packages can be validated.

  • Implement SBOM/Provenance for published packages — include build metadata, runner IDs, and signatures.

  • Use ephemeral publishing tokens created dynamically by secure processes (e.g., a short-lived token issued at release time and revoked immediately after publishing).

  • Harden runners: run ephemeral, single-job self-hosted runners that are destroyed after use; restrict egress for runners.

  • Shift publish actions into a dedicated, highly audited release service (not arbitrary repo workflows).

  • Audit GitHub App/Integration permissions — remove excessive write scopes.

Long term (3–12 months)

  • Centralize publish pipeline: only one hardened, auditable pipeline can publish to PyPI.

  • Require step-level attestations & SBOMs produced during build and attached to published artifacts.

  • Continuous monitoring of package registry and typosquatting for your packages.

  • Company policy: never store plaintext credentials in repo variables or logs; scan build logs for secrets leakage automatically.


Dev & release best practices 

  • Pin third-party actions to commit SHAs (not @latest).

  • Require Signed Commits (GPG) for release branches.

  • Enforce least privilege for service accounts that interact with registry APIs.

  • Add workflow rules that fail if runner logs contain printenv, env, or cat ~/.pypirc.

  • Build with reproducible flags and verify digests of published artifacts post-release.


Post-incident recovery checklist

  1. Revoke and rotate all affected tokens and secrets.

  2. Rebuild release artifacts from trusted source commits and republish if needed (only after forensic validation).

  3. Revoke compromised runner credentials and rotate host keys.

  4. Conduct code/CI audit to ensure no further backdoors.

  5. Notify customers, repos, and registries if packages were published maliciously.

  6. Run a postmortem and enforce policy & automation fixes to prevent recurrence.


Communication & policy notes

  • Treat any workflow modification touching release/publish steps as a high-severity incident.

  • Make publishing a multi-person operation with approval workflow (no single-person autopublish).

  • For open-source projects: prefer requiring maintainers to publish via a separate operator-controlled account, not via repo secrets.


Quick checklist for execs 

  • Mandate OIDC-based ephemeral credentials for CI → cloud.

  • Fund ephemeral runner architecture and secrets manager (Vault).

  • Require branch protection for workflows and enforce code owner reviews.

  • Approve dedicated, centralized release pipeline with audit logging.


#CyberDudeBivash #SupplyChainSecurity #GitHubActions #PyPI #DevSecOps #CI_CD #ArtifactSecurity #SecretsManagement #ThreatIntel

Comments

Popular posts from this blog

CyberDudeBivash Rapid Advisory — WordPress Plugin: Social-Login Authentication Bypass (Threat Summary & Emergency Playbook)

Exchange Hybrid Warning: CVE-2025-53786 can cascade into domain compromise (on-prem ↔ M365) By CyberDudeBivash — Cybersecurity & AI