A malicious version of elementary-data (0.23.3) was published to PyPI and is, at the time of writing, still listed as the latest release. elementary-data is a widely deployed Python package for dbt data observability.
The same release run also pushed a multi-arch container image to GitHub Container Registry at ghcr.io/elementary-data/elementary, tagged both 0.23.3 and latest. Every unpinned docker pull ghcr.io/elementary-data/elementary and every FROM ghcr.io/elementary-data/elementary line without a pinned tag has been pulling the trojaned image since April 24.
The attacker exploited a script injection vulnerability in one of the project's own GitHub Actions workflows, then used the workflow's GITHUB_TOKEN to forge a signed release commit and dispatch the legitimate publishing pipeline against it — without ever touching the master branch or opening a pull request.
UPDATE: The Elementary team removed elementary-data 0.23.3 from PyPI and the matching malicious image from GHCR. They have since published a clean replacement, 0.23.4, and the :latest tag now resolves to the clean image. The maintainer's full incident notice is in issue #2205.
The compromise was reported by crisperik in issue #2205 on April 25, 2026, and shortly afterwards confirmed by H-Max, who also escalated it directly on the Elementary community Slack.

The Compromised Release
elementary-data==0.23.3 was uploaded to PyPI on April 24, 2026 at 22:20:47 UTC. Both the wheel and the source distribution contain a single malicious addition compared to 0.23.2: a top-level elementary.pth file. Python automatically discovers .pth files in site-packages and execs any line beginning with import at interpreter startup — meaning the payload fires on every Python invocation in any environment where the package is installed, not only on import elementary.

The corresponding GitHub release is the giveaway. The release name, dsajdkjsajkdsajk, and body, dsakdjsakjdjsa, are gibberish - an attacker keyboard-mash that no maintainer would have signed off on. The release was created by github-actions[bot], not by a human, and is still labelled "Latest" on the project page:

The v0.23.3 Git tag points at commit b1e4b1f3. That commit changes exactly two things: it bumps pyproject.toml to 0.23.3, and it adds elementary.pth — a single line, ~245 KB of base64:

The Docker Image Is Compromised Too Including :latest
The same Release package workflow that uploads to PyPI also has a build-and-push-docker-image job. Both jobs ran successfully against the orphan-tagged commit and finished in the same workflow run. The result is a multi-arch (linux/amd64 + linux/arm64) image at ghcr.io/elementary-data/elementary, tagged with both 0.23.3 and latest, that carries the same payload as the wheel:
- Compromised:
ghcr.io/elementary-data/elementary:0.23.3→ digestsha256:31ecc5939de6...634255 - Compromised (same digest):
ghcr.io/elementary-data/elementary:latest→ digestsha256:31ecc5939de6...634255 - Clean:
ghcr.io/elementary-data/elementary:0.23.2→ digestsha256:b3bbfafde1a0...35d3d9
The image landing at :latest is the consequential part. Many teams pin Python package versions in requirements.txt or lockfiles but use latest (or no tag at all, which defaults to latest) for container images. Anyone running a Kubernetes deployment, an Argo CD application, a Docker Compose stack, or a Dockerfile FROM line that does not pin the image by digest has been pulling the trojaned build since April 24.
The Payload: A Three-Stage Credential Stealer
The .pth file decodes a base64 wrapper, which then walks two more layers of XOR-with-MD5-keystream encryption before reaching the actual collector. Two cipher seeds are baked into the payload as cleartext strings:
swabag— seed for stage 1 → stage 2for any questions: contact 050afbe046d7545f5af1a0d3fcfbaf6e993fd93d487b431f09bc9e963c7220a135 on session— seed for stage 2 → stage 3 (the string also functions as the actor's contact channel)
Stage 3 is a comprehensive credential and secret harvester. It reads from disk and from live cloud APIs:
- Identity: all SSH private keys,
authorized_keys,known_hosts,~/.git-credentials,gh auth token - Cloud (file + live API): AWS credentials and IMDSv2 role lookup, then SigV4-signed direct calls to AWS Secrets Manager (
ListSecrets/GetSecretValue) and SSM Parameter Store; GCPapplication_default_credentials.json; Azure~/.azure - Container & orchestration:
~/.docker/config.json,~/.kube/config, all/etc/kubernetes/*.conf, ServiceAccount tokens,kubectl get secrets --all-namespaces - Secrets at rest: every
.env*reachable to depth 6,~/.npmrc,~/.pypirc,~/.cargo/credentials.toml,~/.vault-token,~/.netrc,~/.pgpass,~/.my.cnf - Crypto wallets: Bitcoin, Litecoin, Dogecoin, Zcash, Dash, Monero, Ripple wallet configs and
wallet*.dat; Ethereum keystores; Cardano keys; Solana validator and identity keypairs (validator-keypair.json,id.json, etc.); Anchortarget/deploykeys - System:
/etc/passwd,/etc/shadow, shell histories,/var/log/auth.log
The collected output is tar-gzipped into trin.tar.gz and POSTed in a single request via curl --data-binary to:
https://igotnofriendsonlineorirl-imgonnakmslmao.skyhanni.cloud/
Header: X-Rise-To-The-Trinny: agree
How the Forged Release Reached PyPI and GHCR
Step 1: A comment that became code
The first move was a single comment on an open pull request. PR #2147 is a long-lived master → docs sync PR opened automatically by the project's release pipeline back in March, sitting open in the queue. The attacker dropped a single line into the comment box:

The vulnerable workflow is .github/workflows/update_pylon_issue.yml. Its first step interpolates the comment body directly into a run: block:

This is a textbook GitHub Actions script injection vulnerability. The same class of bug appears across github.event.issue.title, github.event.pull_request.title, github.head_ref, and similar fields. When the comment fired the workflow at 22:10:14, the runner started a handle_comment job with the repository's GITHUB_TOKEN in scope, then immediately executed the attacker's curl | bash stager:

Step 2: Forging a release commit, then keeping the runner alive
The forged commit's message, release/v0.23.2 (#2188), is a verbatim copy of an unrelated, legitimate PR title from nine days earlier:

The original handle_comment job did not finish quickly. It kept running for over two and a half hours before finally exiting with status failure — long enough for the rest of the attack to play out under the cover of an "in-progress" workflow run:

Step 3: Dispatching the legitimate publishing pipeline
With the malicious commit in place and the v0.23.3 tag pointing at it, the attacker called the GitHub API to dispatch the Release package workflow with input tag=v0.23.3. The workflow's checkout step uses ref: ${{ inputs.tag || github.ref }}, so it built straight from the orphan-tagged commit:


How StepSecurity Detects and Prevents This
Threat Intelligence: 24x7 SOC and Threat Center
StepSecurity operates a 24x7 Security Operations Center that continuously monitors npm, PyPI, GitHub Actions, and the wider open-source ecosystem for supply chain attacks. When the SOC confirms a compromise, the StepSecurity Threat Center publishes a real-time advisory to our customers with "Am I Affected?" links pre-wired to that tenant's own codebase, CI baselines, and developer-machine inventory and the relevant indicators are pushed to downstream protections (Harden-Runner global block list, Compromised Package Check, Developer MDM, etc.) so they take effect across every workflow without any customer configuration change.

Block C2 traffic with Harden-Runner
Harden-Runner monitors every outbound connection from your GitHub Actions runners. The C2 domain has been added to the Harden-Runner global block list, so every workflow using Harden-Runner now refuses the curl POST to igotnofriendsonlineorirl-imgonnakmslmao.skyhanni.cloud before any credential leaves the runner.

Block known-compromised packages at PR time
The PyPI Compromised Package Check blocks pull requests that introduce a known-malicious version. elementary-data==0.23.3 has been added to the list. The PyPI Package Cooldown Check would also have flagged a PR that adopted 0.23.3 for being too newly published to trust, holding it for review until the cooldown window elapsed.

Discover affected developer machines
StepSecurity Developer MDM inventories Python packages installed across enrolled developer machines so you can identify exposure within minutes of a disclosure.
Indicators of Compromise
- Compromised PyPI package:
elementary-data==0.23.3 - Last clean PyPI version:
0.23.2 - Compromised container image:
ghcr.io/elementary-data/elementary:0.23.3and:latest(both at digestsha256:31ecc5939de6d24cf60c50d4ca26cf7a8c322db82a8ce4bd122ebd89cf634255; multi-arch linux/amd64 + linux/arm64) - Last clean container image:
ghcr.io/elementary-data/elementary:0.23.2(digestsha256:b3bbfafde1a0db3a4d47e70eb0eb2ca19daef4a19410154a71abee567b35d3d9) - Injection file:
elementary.pthat the package root (single base64-wrapped line, ~245 KB) - Git tag:
v0.23.3→ commitb1e4b1f3aad0d489ab0e9208031c67402bbb8480(orphan; not reachable from any branch) - C2 / exfiltration domain:
igotnofriendsonlineorirl-imgonnakmslmao.skyhanni.cloud - Exfiltration header:
X-Rise-To-The-Trinny: agree - Exfiltration archive:
trin.tar.gz(created in temp dir; auto-cleaned post-upload) - Persistent execution marker:
$TMPDIR/.trinny-security-update - Stager URL (expired):
https://litter.catbox.moe/iqesmbhukgd2c7hq.sh - Attacker GitHub account:
realtungtungtungsahur(created April 22, 2026) - Actor contact (Session ID):
050afbe046d7545f5af1a0d3fcfbaf6e993fd93d487b431f09bc9e963c7220a135
Acknowledgements
Credit for this disclosure belongs to two members of the elementary-data community whose quick action shortened the exposure window:
crisperik— identified that thev0.23.3release contained malicious base64-encoded code, recognised the similarity to the recent litellm compromises, and opened issue #2205 at 06:18 UTC on April 25.H-Max— independently confirmed the report on the issue minutes later and posted to the Elementary community Slack so a maintainer would see it directly, accelerating the response.
We also recognise the Elementary team for their fast investigation and remediation: the malicious artifacts were removed from PyPI and GHCR within hours of the report, a clean replacement (0.23.4) was published the same day, and a transparent public incident notice was posted on issue #2205.



