Full Report
This blog is ran by two brothers who like to hunt for bugs together. They had each found several criticals on the this target but wanted an Exceptional Vulnerability - what I'd call a super critical - on this company. They decided to look at a recent acquisition of the company; the scope was simply "anything owned by the company". Since acquisitions may not have the same security controls in place, they were hoping for some low hanging fruit. To go deeper, they were curious about supply chain vulnerabilities. If you want a super-crit, this is a good way to go. Things like, dependency confusion, artifactory access and other things are great attack methods. Using a mix of these methodologies, they were hopeful of finding a super crit. They did a bunch of recon around NPM and found what appeared to be a private NPM package. If you have a license, you can setup an organization that has private packages. They were hopeful of source code leakageor dependency confusion here but didn't find anything. With nothing on Github, they turned to Docker and found several unsecured Docker images. Once they pulled the Docker images, they found backend source code for the application. Knarly! One of the images even had the .git folder still intact, giving them access to the complete git history of it. Under .git/config, they found an authorization bearer token. After some research, they realized that this was for Github Actions! If the token was too permissive, they may be able to manipulate the pipelines or artifacts themselves. GitHub Action tokens are commonly generated automatically to allow workflows to interact with repository-pushing code but expire once the workflow completes, limiting exploitability. If the artifact containing the token is uploaded before the workflow ends, then the image can be accessed while it's still active. The Docker push was the third to last item, meaning that it may just barely be possible to use the token before it expires. A month after they did this research, some other folks used a similar method to use a Github Action token. The Dockerfile had a package.json that contained a private package from the npm organization they mentioned before. To pull these, the image would have needed an npm token within the .npmrc, but this wasn't there. This was because the Dockerfile deleted the file in the last build step. Is this file gone forever? No! Docker has layers that are used for efficient caching. It turns out that these layers can be accessed individually! They found a sick tool called dive for reviewing file system images of Docker. Using this, they found the private npm token that granted them read/write access to the packages. With the ability to write to this internal organization npm, it was game over. Developers who ran these internal packages were now compromised. The backend web service they mentioned was also compromised. This is a super-crit! Super fun blog post!
Analysis Summary
# Incident Report: Project "Exceptional Vulnerability" - Supply Chain Compromise
## Executive Summary
A security research team successfully executed a multi-stage software supply chain attack against a recently acquired subsidiary of a major organization. By exploiting exposed Docker image layers and an insecurely managed GitHub Action environment, the researchers gained unauthorized read/write access to internal NPM registries. This compromise allowed for the potential injection of malicious code into the development lifecycle, affecting developers, CI/CD pipelines, and production servers.
## Incident Details
- **Discovery Date:** 2021 (Reported as a Bug Bounty submission)
- **Incident Date:** 2021
- **Affected Organization:** Unspecified (Large organization with active acquisitions)
- **Sector:** Technology / Software Development
- **Geography:** Global
## Timeline of Events
### Initial Access
- **Date/Time:** 2021
- **Vector:** Public Reconnaissance of Cloud Assets (DockerHub)
- **Details:** Researchers identified an unsecured public Docker repository belonging to a recently acquired subsidiary. They pulled images that contained backend source code.
### Lateral Movement
- **Token Discovery:** Within one Docker image, the `.git` folder was left intact. Analysis of `.git/config` revealed an active GitHub Actions Authorization Bearer token.
- **Race Condition:** Researchers identified that the token was active during the window between the Docker push and the workflow completion.
- **Layer Analysis:** Using the tool `dive`, the researchers inspected historical Docker layers. They discovered a `.npmrc` file containing a private NPM token that had been "deleted" in a subsequent build step but remained in the image's filesystem history.
### Data Exfiltration/Impact
- **Registry Access:** The recovered NPM token provided Read/Write access to the organization's private NPM registry.
- **Scope:** Ability to poison internal packages used by company developers and production build systems.
### Detection & Response
- **Discovery:** Self-reported by researchers via the Bug Bounty program.
- **Response Actions:** The company validated the critical nature of the chain, rotated all compromised tokens, and updated build pipeline security.
## Attack Methodology
- **Initial Access:** Public Docker Image exposure.
- **Persistence:** Not applicable (Research-led), but would have been achieved via malicious package injection.
- **Privilege Escalation:** Exploiting Docker layers to recover "deleted" credentials.
- **Defense Evasion:** Using legitimate (stolen) tokens to interact with registries.
- **Credential Access:** Extraction of GitHub Action tokens and NPM automation tokens from image metadata and filesystem layers.
- **Discovery:** Asset mapping of acquired subsidiaries; DockerHub/NPM registry reconnaissance.
- **Lateral Movement:** Transitioning from container image secrets to internal package registry access.
- **Collection:** Accessing source code and private package manifests.
- **Impact:** Full Software Supply Chain Compromise (RCE potential on devs and prod).
## Impact Assessment
- **Financial:** $50,500 bounty payout; potential millions in avoided damages.
- **Data Breach:** Exposure of proprietary backend source code and internal package structures.
- **Operational:** Potential for complete takeover of the CI/CD pipeline and production environments.
- **Reputational:** High risk if a malicious actor had utilized these methods to distribute malware to customers.
## Indicators of Compromise
- **File Indicators:** `.git/` directories in production Docker images; `.npmrc` files in hidden Docker layers.
- **Behavioral Indicators:** Unexpected `npm publish` events from unauthorized accounts; GitHub Action tokens used from external IP addresses.
## Response Actions
- **Containment:** Revocation of the compromised GitHub Action and NPM tokens.
- **Eradication:** Removal of the vulnerable Docker images from public repositories.
- **Recovery:** Implementation of "Multi-stage builds" in Docker to ensure secrets are not stored in layers.
## Lessons Learned
- **Acquisition Risk:** Newly acquired companies often lack the parent company's security rigor and present a "soft" entry point.
- **Ephemeral Secret Risks:** GitHub Action tokens, though short-lived, are valid as long as the workflow is running; if a long-running step follows a secret-leaking step, the token is exploitable.
- **Docker Layer Persistence:** Deleting a file in a Dockerfile does not remove it from the image's history.
## Recommendations
1. **Secret Scanning:** Implement automated secrets scanning (e.g., TruffleHog, Gitleaks) in all CI/CD pipelines.
2. **Multi-Stage Builds:** Use Docker multi-stage builds to ensure build-time secrets (like `.npmrc`) never exist in the final production image layers.
3. **Registry Hardening:** Enforce MFA for NPM publishing and restrict IP ranges for registry interactions where possible.
4. **Asset Inventory:** Maintain a strict inventory of public DockerHub and GitHub repositories, including those belonging to subsidiaries.