squ1rrel CTF 2024
Squ1rrel Lottery [469 pts]
Welcome to the squ1rrel lottery! 9 winning numbers will be selected, and if any of your tickets share 3 numbers with the winning ticket you’ll win a flag!
Hint: This is a math challenge
nc 34.132.166.199 11112
Short (essentially a cheese) writeup.
Here’s the source:
import random
# user version
def input_lines():
lines = []
print("Welcome to the squ1rrel lottery! 9 winning numbers will be selected, and if any of your tickets share 3 numbers with the winning ticket you'll win! Win 1000 times in a row to win a flag")
for i in range(1, 41):
while True:
line = input(f"Ticket {i}: ").strip()
numbers = line.split()
if len(numbers) != 9:
print("Please enter 9 numbers")
continue
try:
numbers = [int(num) for num in numbers]
if not all(1 <= num <= 60 for num in numbers):
print("Numbers must be between 1 and 60")
continue
lines.append(set(numbers))
break
except ValueError:
print("Please enter only integers.")
return lines
user_tickets = input_lines()
wincount = 0
for j in range(1000):
winning_ticket = random.sample(range(1, 61), 9)
win = False
for i in user_tickets:
if len(i.intersection(set(winning_ticket))) >= 3:
print(f'Win {j}!')
win = True
wincount += 1
break
if not win:
print("99 percent of gamblers quit just before they hit it big")
break
if wincount == 1000:
print("squ1rrelctf{test_flag}")
So, essentially the gist of the problem is that we’re allowed to input 40 tickets, 9 numbers each between 1 and 60, inclusive. 1000 times in a row, we need at least one of our tickets to share at least 3 of the same numbers as the winning ticket.
I believe the intended solution was some pigeonhole principle logic that involved actual math and thinking, but, I figured why not try to just evenly distribute the numbers?
There are 40*9 = 360 inputs, so I put 6 of each number across the 40 tickets, ensuring that no one number is in the same ticket twice.
My ticket generation fails like 70% of the time, but whenever it works it gets the flag so good enough ¯\_(ツ)_/¯
from pwn import *
from random import *
sets = [[] for i in range(40)]
space = [9 for i in range(40)]
curr_indices = set()
for i in range(40):
curr_indices.add(i)
next_indices = []
for i in range(1, 61):
# print(space)
indices = sample(list(curr_indices), 6 - len(next_indices)) + next_indices
for j in indices:
sets[j].append(i)
space[j]-=1
curr_indices.remove(j)
for i in next_indices:
curr_indices.add(i)
next_indices = []
if len(curr_indices) <= 6:
for i in curr_indices:
next_indices.append(i)
for i in range(40):
if space[i] > 0 and i not in next_indices: curr_indices.add(i)
# print('\n'.join(map(str, sets)))
for i in sets:
assert len(set(i)) == len(i)
assert len(i) == 9
# exit()
p = remote('34.132.166.199', 11112)
for i in range(40):
# print(' '.join(map(str, sets[i])).split())
p.sendlineafter(b': ', ' '.join(map(str, sets[i])).encode())
p.interactive()
squ1rrelctf{your_prize_is_519225_squ1rrel_bucks}