Lazarus - Rewind 2
Lazarus - Rewind 2
Platform: CUSTOM | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: deterministic_beacon_replay, probability_sampling_reconstruction, elliptic_curve_public_key_verification
Summary
Walkthrough of the CUSTOM Crypto Lazarus - Rewind 2. Task: A secp256k1 wallet key comes from a supposedly device-independent CHSH quantum beacon whose public generation block seeds every trial. Solution: replay the hash-derived sampling, hash all 1024 o
Description
A supposedly unbreakable device-independent quantum RNG generated a secp256k1 wallet key; spot the implementation's lazy corner and recover the wallet.
The challenge exposed an HTTP documentation site and a TCP wallet service. The goal was to recover the private scalar corresponding to the advertised secp256k1 public point, then provide it to the wallet service.
Analysis
Service surface
The two services had distinct roles:
http://154.57.164.74:31041/docsdisclosed the complete deterministic CHSH beacon algorithm. The same web application exposed an encrypted/onionarchive.154.57.164.74:30958acceptedcase,observe <nonce>, andseize <hexkey>commands.casereturned the wallet generation block and its public point.
The public wallet data was:
text read-only • copy allowedgen_block = 41a2ab27e654e682142d5bc4fcfd876001d891333b5ee81fc5623b8092cc5790 pubkey_x = 5521380e6e7c2181e33baaf28a0836eea739df2676d0e36c2186a3b19368a48b pubkey_y = dc40f3cc07c63e2833e6631082d20a227300702bd86ef4e449d3d8c328e6889c
The lazy corner: deterministic "quantum" sampling
The documentation defines the 32-byte generation block directly as the beacon
seed H. For every trial i from 0 through 1023, it computes
text read-only • copy allowedtb = SHA256(H || "|trial|" || i.to_bytes(4, "big")) a = tb[0] & 1 b = (tb[0] >> 1) & 1 lambda = int(tb[8:16], "big") / 2^64
The CHSH correlation is +1/sqrt(2) for settings (0,0), (0,1), and
(1,0), and -1/sqrt(2) for (1,1). For each candidate outcome (x,y), its
probability is
text read-only • copy allowedP(x,y | a,b) = (1 + (-1)^(x+y) * E(a,b)) / 4.
The implementation visits outcomes in the fixed order (0,0), (0,1),
(1,0), (1,1) and selects the first one whose cumulative probability exceeds
lambda.
This models the desired CHSH distribution, but it does not obtain a fresh random
draw from a quantum device. Both the settings and lambda come from SHA-256 of
the public H and trial number. Therefore every one of the purportedly quantum
outcomes is publicly reproducible. The private scalar is then simply
text read-only • copy allowedW = SHA256(concatenated ASCII x/y outcome bits for all 1024 trials).
There is no unknown entropy to brute-force. There is also no reason to solve the secp256k1 discrete-log problem: replaying the published generation procedure produces the scalar directly.
Solution
1. Replay the beacon
The following solver is the working task script. It reproduces the server's
floating-point cumulative sampling exactly, derives W, and implements
secp256k1 point arithmetic without external packages.
python read-only • copy allowed#!/usr/bin/env python3 import hashlib import math import sys P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 EXPECTED_X = 0x5521380E6E7C2181E33BAAF28A0836EEA739DF2676D0E36C2186A3B19368A48B EXPECTED_Y = 0xDC40F3CC07C63E2833E6631082D20A227300702BD86EF4E449D3D8C328E6889C def beacon(seed: bytes): outcomes = [] counts = {(a, b): [0, 0] for a in range(2) for b in range(2)} pairs = [(0, 0), (0, 1), (1, 0), (1, 1)] for i in range(1024): tb = hashlib.sha256( seed + b"|trial|" + i.to_bytes(4, "big") ).digest() a, b = tb[0] & 1, (tb[0] >> 1) & 1 lam = int.from_bytes(tb[8:16], "big") / 2**64 correlation = (-1 if (a, b) == (1, 1) else 1) / math.sqrt(2) cumulative = 0.0 selected = pairs[-1] for x, y in pairs: cumulative += ( 1 + ((-1) ** (x + y)) * correlation ) / 4 if lam < cumulative: selected = (x, y) break x, y = selected outcomes.append(f"{x}{y}") counts[(a, b)][0] += 1 if x == y else -1 counts[(a, b)][1] += 1 private_key = hashlib.sha256("".join(outcomes).encode()).hexdigest() observed = { ab: total / number for ab, (total, number) in counts.items() } chsh = ( observed[(0, 0)] + observed[(0, 1)] + observed[(1, 0)] - observed[(1, 1)] ) return private_key, chsh def add(q, r): if q is None: return r if r is None: return q x1, y1 = q x2, y2 = r if x1 == x2 and (y1 + y2) % P == 0: return None if q == r: slope = (3 * x1 * x1) * pow(2 * y1, -1, P) % P else: slope = (y2 - y1) * pow(x2 - x1, -1, P) % P x3 = (slope * slope - x1 - x2) % P return x3, (slope * (x1 - x3) - y1) % P def mul(k, q=(GX, GY)): result = None while k: if k & 1: result = add(result, q) q = add(q, q) k >>= 1 return result if __name__ == "__main__": seed_hex = ( sys.argv[1] if len(sys.argv) > 1 else "41a2ab27e654e682142d5bc4fcfd876001d891333b5ee81fc5623b8092cc5790" ) key, chsh = beacon(bytes.fromhex(seed_hex)) point = mul(int(key, 16) % N) assert point == (EXPECTED_X, EXPECTED_Y) print(f"chsh={chsh:.6f}") print(f"private_key={key}") print(f"pubkey_x={point[0]:064x}") print(f"pubkey_y={point[1]:064x}")
Running it against the public generation block recovered this private scalar:
text read-only • copy allowed8741d975bc3cf1e0a7e5d92d7d0f3c4c1cecc52e119bb0da6a87ac2972b85aa9
2. Verify the seed interpretation and key
Scalar multiplication of the recovered value by the secp256k1 generator produced
the exact advertised (pubkey_x, pubkey_y). This equality independently confirms
both important implementation details:
gen_blockis the exact 32-byteHused for wallet generation.- The outcome serialization and SHA-256 derivation match the wallet service.
An incorrect seed, probability order, comparison boundary, or outcome encoding would produce an unrelated scalar and public point.
3. Submit the recovered key
Connect to the TCP service and use the hexadecimal scalar returned by the solver:
text read-only • copy allowed$ nc 154.57.164.74 30958 seize <RECOVERED_PRIVATE_KEY>
The service accepted the key and returned the challenge flag. The same key also
unlocked /onion; its archived chat explicitly stated that the operator rebuilt
the wallet key from public beacon data whenever it was needed, corroborating the
intended flaw.
Key Takeaways / Lessons
- Mastering deterministic_beacon_replay, probability_sampling_reconstruction, elliptic_curve_public_key_verification was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-08-14 • CUSTOM • Lazarus - Rewind 2 • Difficulty: Medium