Lucky Dice
Author: 0xIDA
Platform: HTB · Category: misc · Difficulty: Very Easy
Solvers
Summary
A speed-based dice game where we had to compute the round winner (highest dice sum) for 100 rounds with only 0.3 seconds to answer each round. Solved by pre-computing the winner while the server was still printing dice rolls.
Analysis
The challenge provided the full Python source (challenge.py). The server:
- Shows an ASCII dice banner, explains the rules, then asks if we're ready
- Runs 100 rounds
- Each round,
player_nr(random 8–13) players roll(round_nr * 2 + 2)dice each - All dice are 6-sided (random 1–6)
- Winner is the player with the highest sum; ties broken by higher player number (via Python's stable sort —
sorted(dice_sum.items(), key=lambda x:x[1])[-1]picks the last equal element, which is the player who appeared later in the dict, i.e., higher player number) - A 0.3-second timeout starts after printing
"> "
The 0.3-second window is tight, but the server prints each player's dice with time.sleep(0.1) between them. This means we have plenty of time to sum dice and track the leader as the data streams in.
The Tie-Breaker Bug/Trick
result = sorted(dice_sum.items(), key=lambda x:x[1])[-1][0][1].split('_')[1]
sorted(..., key=lambda x:x[1])sorts by sum ascending (ties keep dict order)[-1]picks the last element — highest sum, or if tied, the last tied player in dict order- Dict order is
player_1 → player_2 → … → player_N, so the highest-numbered player with the max sum wins
This matches the rule: "If there is a draw, the player who rolled the last dice wins the round."
Exploitation
The solve script uses pwntools to connect and stream-read:
- Connect and acknowledge readiness
- For each round: read lines until "Who wins this round?" appears
- Parse each
Player X: d1 d2 d3...line, sum the dice, track the running max sum + player - Consume the numbered options list +
"> "prompt - Respond with the winner's player number
- Repeat for all 100 rounds
Connecting → banner → send "1" → countdown "3...2...1...Go!"
┌── Round 1..100 ──────────────────────┐
│ Read player lines (pre-compute max) │
│ Skip options (recvuntil "> ") │
│ Send winner number │
│ Verify "Yes.. Correct!" │
└──────────────────────────────────────┘
→ "Nice job!" → flag.txt
Key Findings
- The 0.3s timeout is only triggered after
input("> ")is called — pre-computing during the player display phase bypasses the speed requirement entirely - Tie-breaking by higher player number is a subtle detail that matters in every round with 8+ players
- Round 100 has players rolling 200 dice each — the output lines are long, but parsing is still fast
Tools Used
pwntools— TCP socket interaction with serverre— regex parsing of player/dice lines
Flag
HTB{r0LL1ng-1n-t43_D33P-b0t_n3T-cRe4t10n}
Lessons Learned
- When a challenge has a tight response window, check if data streams in early enough to pre-compute
- Always read the source code carefully for tie-breaking logic —
sorted(...)[-1]with stable sort favors highest-indexed equal elements - pwntools
recvuntil()+recvline()is perfect for line-oriented streaming protocols - The "Very Easy" difficulty comes from having full source code and a straightforward computation — no crypto, no binary exploitation, just correct parsing under a timer