QLotto
HackTheBox QLotto machine writeup — reconnaissance and enumeration walkthrough.
Challenge Information
- Name: QLotto
- Category: Quantum
- Difficulty: Easy
- Host: 83.136.252.32:31179
Challenge Description
“They call it QLotto — a dazzling new quantum lottery table provided by Qubitrix that lauders millions at the casino, where quantum draws decide your fate. If you can predict their draws, you can beat the system and clean out their coffers. Rig the jackpot, Operative. Every stolen coin funds their empire — and every coin you steal funds our fight.”
Initial Reconnaissance
Analyzing the Provided Files
The challenge provides a single file: server.py. Let’s examine its contents:
1
cat server.py
The server implements a quantum lottery system using Qiskit (IBM’s quantum computing framework). Key observations:
- Quantum Circuit: Uses 2 qubits (indices 0 and 1)
- Initial State: Qubit 0 starts in superposition via
circuit.h(0) - User Input: Players provide quantum gate instructions
- Measurement: Both qubits are measured 36 times
- Number Generation: 6 lottery numbers are extracted from the measurements
Understanding the Code Flow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def generate_circuit(self, instructions: str):
circuit = QuantumCircuit(2)
circuit.h(0) # Qubit 0 in superposition
instructions = instructions.split(";")
for instr in instructions:
parts = instr.split(":")
gate, params = parts
params = [ int(p) for p in params.split(",") ]
# CRITICAL VALIDATION
if any(p == 0 for p in params):
print("[Dealer] Hey, don't tamper with the house card — that's forbidden.")
return None
Key Restriction: The validation if any(p == 0 for p in params) prevents us from using index 0 in our gate parameters.
Available Gates
Single-qubit gates (1 parameter):
H- Hadamard gateS- Phase gateT- T gateZ- Pauli-Z gate
Two-qubit gates (3 parameters: angle, qubit1, qubit2):
RXX- Ising XX coupling gateRYY- Ising YY coupling gateRZZ- Ising ZZ coupling gate
Understanding the Lottery Mechanism
1
2
3
4
5
6
7
8
9
10
11
12
13
def extract_numbers(self, memory):
for i in range(0, len(memory), 6):
bits = memory[i : i + 6] # 6 measurements
lotto_number = ""
testing_number = ""
for testing_bit, lotto_bit in bits:
lotto_number += str(lotto_bit) # From qubit 0
testing_number += str(testing_bit) # From qubit 1
lotto_number = int(lotto_number, 2) % 42 + 1
testing_number = int(testing_number, 2) % 42 + 1
Important Details:
- Each measurement produces a 2-bit string (e.g., “01”)
- In Qiskit’s bit ordering: rightmost bit = qubit 0, leftmost bit = qubit 1
testing_numberscome from qubit 1 (shown to player)lotto_numberscome from qubit 0 (hidden, must be guessed)- 6 bits are combined to form each number:
int(binary, 2) % 42 + 1
Win Condition Analysis
1
2
3
4
5
6
7
8
9
if lotto_numbers == testing_numbers:
print("[Dealer] Trying to mirror the house's numbers, are we?")
return
print(f"[Dealer] Your draws are: {testing_numbers}")
guess_numbers = input("[Dealer] Place your six bets on the table : ")
if guess_numbers == lotto_numbers:
print(f"[Dealer] Your jackpot: {JACKPOT}")
Requirements:
lotto_numbersmust NOT equaltesting_numbers(anti-mirror check)- We see
testing_numbersafter circuit execution - We must predict
lotto_numbersto win
The Problem
We face a paradox:
- Qubit 0 (lottery) and Qubit 1 (testing) are independent by default
- We cannot use index
0in our gate parameters - We need to correlate the qubits to predict lottery numbers
- With only 2 qubits, any two-qubit gate must involve qubit 0
- But we can’t reference qubit 0… or can we?
Premium Content
The full exploitation walkthrough, privilege escalation, and flags are available exclusively for members.
Unlock Full Writeup →