← Back to Writeups
CUSTOMMediumCrypto

The Coin That Wont Land

XESXOR9/2/20265 min read
#crypto#custom#medium#epr_attack#bell_state_preparation#basis_correlation_cheat#deferred_measurement

The Coin That Wont Land

Platform: CUSTOM | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: epr_attack, bell_state_preparation, basis_correlation_cheat, deferred_measurement

Summary

Walkthrough of the CUSTOM Crypto The Coin That Wont Land. Task: quantum bit-commitment (Oathbinding) game exposed over an HTTP JSON API; the committer keeps qubit a while the Warden seals qubit b, and must survive 32 rounds with a per-round challenge basis (

Description

After the realm cracked apart, nobody trusted a word anyone said, so the Registry built the Oathbinding to settle disputes for good. You swear a vow before the court, the Warden seals it in a box nobody can open, and later they call you back and ask what you swore. Lie about it and the box catches you. Lands, titles, old debts, it is all decided this way now, because it is the one thing nobody can argue with. Or so they say. The court throws question after question at you, round after round, waiting for you to fold. Get through every round without a slip and the confession you drag out is your flag. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: A quantum bit-commitment ("Oathbinding") game is exposed over an HTTP JSON API. Each round you prepare 8 two-qubit strands, keep one qubit (a) and let the Warden seal the other (b). After committing, the court reveals a challenge basis (Z or X); you must "open" with values that match what the Warden measures on the sealed qubits in that basis. All 8 strands must match, for all 32 rounds. Honest play wins each round with probability 1/2 per round (2^-32 total), so brute luck is hopeless. The goal is to pass all 32 rounds deterministically to extract the flag. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Recon

The target is 154.57.164.70:32173. Raw TCP probing is a red herring:

  1. nc 154.57.164.70 32173 returns nothing (silent, no banner).
  2. nmap -sV -p 32173 -Pn 154.57.164.70 identifies the service as HTTP / Gunicorn.
  3. curl http://154.57.164.70:32173/ returns an HTML app titled "Oathbinding · The Coin That Won't Land"; its app.js references /api/new, /api/commit, /api/peek, /api/open, /api/oath.
  4. curl http://154.57.164.70:32173/api/oath returns a self-documenting JSON API reference. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

API (from GET /api/oath)

  • POST /api/new -> {token, strands:8, rounds:32}
  • POST /api/commit {token, slots:[<circuit> x 8 strands]} -> {challenge: c, round}. Each strand is a 2-qubit circuit over qubits a and b applied to |00>. You keep qubit a; the Warden seals qubit b. Gates: single-qubit I X Y Z H S SDG on a or b, e.g. ['H','a']; and CX with control/target among a,b, e.g. ['CX','a','b'].
  • POST /api/peek {token, basis} -> {a_outcomes:[...]} measures YOUR kept a-qubits in basis Z or X.
  • POST /api/open {token, values:[0/1 x 8]} -> the Warden measures each b in basis c (c=0 -> Z, c=1 -> X); the oath holds only if every value matches.
  • Rule: 32 rounds, every round must hold (all 8 strands match) or the court walks. Complete all 32 rounds to get the flag. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Analysis

This is a textbook quantum bit-commitment scheme. The security promise is that the sealed qubit b binds the committer (they cannot change their sworn value) while hiding it from the court until "opening" time. The homepage teaser "2^-32 by luck alone" makes the soundness bound explicit: 32 independent rounds, each with a random binary challenge basis, so an honest-or-guessing committer wins with probability 2^-32.

The decisive observation is the classic no-go theorem: unconditionally secure quantum bit commitment is impossible. A scheme cannot be simultaneously binding and hiding against a committer who keeps half of an entangled pair and defers the choice of what they committed to until after seeing the challenge. That is exactly the escape hatch here — peek lets us measure the kept qubit a in any basis after the challenge is revealed, and the committed circuit is fully attacker-chosen. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key primitive: the Bell state Phi+ = (|00> + |11>)/sqrt(2), prepared by:

[["H","a"], ["CX","a","b"]]

Phi+ is maximally correlated in BOTH the computational (Z) basis and the Hadamard (X) basis:

  • Z-basis: measuring a and b always yields equal bits (ZZ correlation +1).
  • X-basis: measuring a and b always yields equal bits (XX correlation +1).

So whatever basis c the court challenges with, the outcome of measuring our kept qubit a in that basis is identical to what the Warden will get measuring the sealed qubit b. The flag text epr_0ath_0p3ns_b0th_w4ys ("opens both ways") confirms the intended EPR/Bell attack: the commitment opens correctly in both Z and X. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Exploitation

Per round, for each of the 8 strands:

  1. Commit the Bell state Phi+ ([["H","a"],["CX","a","b"]]) — this entangles kept qubit a with sealed qubit b instead of committing a definite basis state.
  2. Read the challenge bit c from the commit response (c=0 -> Z, c=1 -> X).
  3. Peek the kept a-qubits in the challenged basis. Because a and b are maximally correlated in that basis, the a-outcomes exactly equal what the Warden will measure on b.
  4. Open with those peeked outcomes -> every value matches deterministically. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

This holds all 8 strands every round with probability 1, defeating the 2^-32 soundness bound. Running the loop over all 32 rounds yields the flag on the final round.

Solution

python
#!/usr/bin/env python3
import requests, json

BASE = "http://154.57.164.70:32173"

def new():
    return requests.post(BASE+"/api/new", timeout=10).json()

def commit(token, slots):
    return requests.post(BASE+"/api/commit", json={"token": token, "slots": slots}, timeout=10).json()

def peek(token, basis):
    return requests.post(BASE+"/api/peek", json={"token": token, "basis": basis}, timeout=10).json()
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

def open_(token, values):
    return requests.post(BASE+"/api/open", json={"token": token, "values": values}, timeout=10).json()

t = new()
token = t["token"]; strands = t["strands"]; rounds = t["rounds"]

# Bell state per strand: H on a, then CX a,b -> Phi+ = (|00>+|11>)/sqrt(2)
bell = [["H", "a"], ["CX", "a", "b"]]

for r in range(rounds):
    slots = [list(bell) for _ in range(strands)]
    ch = commit(token, slots)["challenge"]
    basis = "Z" if ch == 0 else "X"          # match the Warden's measurement basis
    outs = peek(token, basis)["a_outcomes"]  # a-qubits equal b-qubits in this basis
    res = open_(token, outs)                 # every value matches
    print(f"round {r}: challenge={ch} basis={basis} -> {res}")
    if "flag" in json.dumps(res).lower():
        print("FLAG:", res["flag"]); break

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

Sample winning output (final round):

round 31: challenge=0 basis=Z ... -> {'flag': 'HTB{REDACTED}', 'passes': 32, 'round_held': True, 'rounds_done': 32, 'total': 32}

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

Key Takeaways / Lessons

  • Mastering epr_attack, bell_state_preparation, basis_correlation_cheat, deferred_measurement was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-07-28 • CUSTOM • The Coin That Wont Land • Difficulty: Medium