Ghost Repo Campaign
Executive Summary
On February 2, a stealthy supply-chain attack campaign was identified targeting open-source software projects. The activity has been internally named “Ghost Repo” due to the way the malicious components remain invisible during normal code review.
The attackers did not exploit a traditional software vulnerability. Instead, they abused the trust-based pull request workflow used by open-source projects. Legitimate bug fixes were submitted to real repositories, but hidden within dependency metadata files was a malicious package that allowed attackers to execute code during installation or build time.
This campaign primarily impacts developers, CI/CD environments, and any downstream users who install or build affected projects.
What Happened
Attackers submitted pull requests to popular and moderately popular open-source repositories. On the surface, these pull requests appeared normal and even helpful — fixing bugs, improving stability, or addressing minor issues.
However, alongside the visible code changes, the pull requests also modified dependency-related files such as:
package-lock.jsonyarn.lock- other ecosystem-specific lock or manifest files
Inside those metadata changes, the attacker introduced a new dependency or altered an existing one. That dependency contained malicious logic designed to execute automatically during dependency installation or during the build process.
Because lockfiles often contain large diffs and are commonly auto-approved or skimmed, the malicious change went unnoticed.
How It Happened
1. Initial Access / Entry Point
- The attacker did not breach infrastructure or exploit a software flaw.
- Initial access occurred via legitimate pull requests submitted by newly created or low-profile contributor accounts.
- In some cases, the contributor account had a small but believable contribution history to appear legitimate.
This means no vulnerability was exploited — the attack relied entirely on social engineering and workflow abuse.
2. Weaponization
The malicious payload was embedded in a dependency that was:
- Newly published or very recently updated
- Named similarly to a well-known package (typosquatting or name confusion)
- Hosted on a public package registry
The malicious code was designed to run automatically via:
postinstallprepareprebuild- or equivalent lifecycle scripts
3. Delivery
Delivery occurred when:
- A maintainer merged the pull request
- A CI/CD pipeline ran
npm install,yarn install, or an equivalent command - A developer installed dependencies locally
No user interaction beyond a normal install was required.
4. Execution
Once installed, the malicious dependency executed code that could:
- Collect environment variables (including tokens and secrets)
- Identify CI/CD environments
- Write additional files to disk
- Open outbound network connections
Execution happened before the application ever ran, making it harder to associate the behavior with the affected project.
5. Persistence
Persistence was limited but effective:
- The malicious package remained locked in dependency files
- Any future installs would re-trigger execution
- CI/CD pipelines repeatedly executed the payload
No OS-level persistence mechanisms were observed (e.g., registry keys or cron jobs), as the goal was repeated execution through normal development workflows.
6. Command and Control (C2)
Observed behaviors suggest:
- Outbound HTTPS requests to attacker-controlled infrastructure
- Requests blended with normal package registry traffic patterns
- Data exfiltration performed quietly in small payloads
In some cases, connections only occurred when running inside CI environments, indicating environment-aware logic.
Payload Details
Payload Capabilities Observed
The malicious dependencies were capable of:
- Reading environment variables (
process.env) - Harvesting:
- CI secrets
- API tokens
- Cloud credentials
- Fingerprinting host environment (OS, hostname, CI provider)
- Making outbound HTTPS requests
- Dynamically loading additional JavaScript code
No destructive payloads (wipers, ransomware) were observed. This campaign appears focused on credential harvesting and long-term access, not disruption.
Anti-Malware Evasion
- Code was heavily obfuscated or minified
- Malicious logic blended with legitimate-looking helper functions
- Payload execution tied to lifecycle scripts that many security tools ignore
- Some variants delayed execution to evade sandbox detection
Traditional endpoint antivirus tools showed low or no detection, especially in developer and CI environments.
Impacted Assets
Directly Impacted
- Open-source repositories that merged affected pull requests
- CI/CD systems building those repositories
- Developers installing dependencies locally
Indirectly Impacted
- Any downstream project depending on the compromised package
- Production systems built using tainted artifacts
- Organizations trusting internal builds without dependency inspection
Impact severity depends on:
- Whether secrets were present in the environment
- Whether builds were performed in privileged CI runners
- How long the malicious dependency remained undetected
Indicators of Compromise (IOCs)
File-Level Indicators
- Unexpected changes in:
package-lock.jsonyarn.lock
- New dependencies unrelated to the stated fix
- Dependencies with install or build lifecycle scripts
Behavioral Indicators
- Outbound HTTPS traffic during dependency installation
- Network activity before application execution
- CI jobs making external calls not defined in pipeline configs
Package Characteristics
- Very recent publish date
- Low download count
- Maintainer with no ecosystem history
- Name visually similar to popular packages
CI/CD Indicators
- Secrets accessed during dependency installation
- Build logs showing unexplained script execution
- Base64 or hex-encoded strings in install scripts
Detection Rules
Dependency Change Detection
Trigger alerts when:
- Lockfiles change but application code changes are minimal
- New dependencies are added in non-feature PRs
- Lifecycle scripts appear in dependency metadata
Network Monitoring
Alert on:
- Outbound connections during
npm installor equivalent steps - Traffic to unknown domains during build stages
- Repeated small HTTPS POST requests from CI runners
CI/CD Threat Hunting Queries
Hunt for:
npm installfollowed by network calls- Access to environment variables during install steps
- Unexpected execution of shell commands during dependency resolution
Threat Hunting Guidance
For Repositories
- Review historical PRs for dependency-only changes
- Re-examine merged PRs from first-time contributors
- Diff dependency trees before and after PR merges
For CI/CD Systems
- Log and monitor network activity during build phases
- Restrict outbound access where possible
- Rotate all secrets exposed to affected builds
For Developers
- Reinstall dependencies from clean lockfiles
- Audit dependency trees using offline tools
- Treat dependency changes as first-class security events
Root Cause
The root cause is process trust, not a software flaw.
Open-source workflows assume:
- Contributors act in good faith
- Lockfile changes are mechanical
- Dependency resolution is low risk
The attackers exploited these assumptions.
Final Takeaway
Ghost Repo is a high-impact, low-noise supply-chain attack.
It does not rely on exploits, malware droppers, or obvious malicious code.
It relies on human behavior, review fatigue, and automation trust.
Any organization consuming open source without strict dependency governance should assume exposure risk.
