Robots enjoy some strange games and we just can’t quite figure this one out. Maybe you will have better luck than us. 23.22.16.34:6969

The game offers a choice between two hex strings asking for the “bigger” one. Anyway, it doesn’t appear to be a way to determine which one is bigger just by looking at the values.

The challenge consists in winning at this game for 75 times in a row.

0 $ nc ec2-23-21-19-72.compute-1.amazonaws.com 6969
You have gotten 0 of 75
Choice 1 = 68af489d11366a85e755d95f516c48c50f
Choice 2 = 9795b347f0ceb9aa35f6127cdfb7145810
Which one is bigger? (1 or 2)
2
2
Correct!
--------------------
You have gotten 1 of 75
Choice 1 = 77c99d06baea4f703316e683a25264e3ba
Choice 2 = 2dcccbe6467658ed37b35167eb9f6e3318
Which one is bigger? (1 or 2)
1
1
Wrong :(

After several attempts, it’s possible to see some recurrent values. Indeed, it turns out to be a finite set of hex-encoded random 17-byte strings with a strict total ordering on them. We counted 500 different strings during our experiments.

The strategy for winning is to play while keeping track of the values and sorting them on a list. The following multithread script succeeded in winning for 75 times in a row in just 1 minute and 30 seconds performing a total of 13000 requests.

#!/usr/bin/python

import re
import sys
import time
import socket
import threading

strings_file = "/tmp/the_game_list.txt"

# order: small ----> big
strings   = []
run       = True
lock      = threading.Lock()
flag 	  = ""
requests  = 0

def recv_tillreturn(sock, times=1):
	"""Receive times lines of data from a socket"""

	total_data = []
	while times > 0:
		times -= 1
		data = ""
		while 1:
			char = sock.recv(1)
			data += char
			if char == "\n":
				break
		total_data.append(data)
	return ''.join(total_data)

def insert(c1, c2):
	"""Insert a pair of ordered value in the strings list, where c1 > c2."""
	global strings 

	i_c1 = -1
	i_c2 = -1
	for i in range(len(strings)):
		if strings[i] == c1:
			i_c1 = i
		if strings[i] == c2:
			i_c2 = i
	if i_c1 == i_c2 == -1:
		# c1 and c2 not found: insert c2 in front of the list and append c1
		strings.insert(0,c2)
		strings.append(c1)
	elif i_c1 != -1 and i_c2 == -1:
		# c1 found but not c2: insert c2 just before c1
		strings.insert(i_c1, c2)
	elif i_c1 == -1 and i_c2 != -1:
		# c2 found but not c1: insert c1 after c2
		strings.insert(i_c2 + 1, c1)
	else:
		# both c1 and c2 found: if they are already ordered in the list leave
		# them where they are, otherwise remove the old location of c1 and put
		# c1 after c2
		if i_c2 > i_c1:
			strings.remove(c1)
			strings.insert(i_c2, c1)

def isbigger(c1, c2):
	"""Check whether c1 is bigger than c2."""

	i_c1 = -1
	i_c2 = -1
	for i in range(len(strings)):
		if strings[i] == c1:
			i_c1 = i
		if strings[i] == c2:
			i_c2 = i
	return i_c1 > i_c2

def play():
	"""Play until victory."""

	global run, requests, flag

	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.settimeout(3)
	s.connect(("ec2-23-21-19-72.compute-1.amazonaws.com", 6969))
	wins = 0

	while run:
		junk = recv_tillreturn(s, 1)
		choices = range(2)
		c1 = recv_tillreturn(s, 1)
		c2 = recv_tillreturn(s, 1)
		print("{}{}{}".format(junk, c1, c2))
		c1 = c1.split()[3]
		c2 = c2.split()[3]
		with lock:
			if isbigger(c1, c2):
				guess = 1
			else:
				guess = 2
		s.send("%s\n" % guess)
		answer = recv_tillreturn(s, 4)
		print(answer)
		with lock:
			requests += 1
			if "Correct" in answer:
				if guess == 1:
					insert(c1, c2)
				else:
					insert(c2, c1)
				wins += 1
			else:
				if guess == 1:
					insert(c2, c1)
				else:
					insert(c1, c2)
				wins = 0
			if wins > 74:
				run  = False
				flag = recv_tillreturn(s, 2)
	s.close()

def main():
	global strings, run

	if len(sys.argv) < 2:
		sys.stderr.write('Specify the number of threads\n')
		sys.exit(1)

	try:
		f = open(strings_file, 'r')
		strings = eval(f.read())
		f.close()
	except IOError:
		strings = []

	threads = []
	for i in range(int(sys.argv[1])):
		t = threading.Thread(target=play)
		threads.append(t)
		t.start()

	main_thread = threading.currentThread()
	for t in threading.enumerate():
		if t is main_thread:
			continue
		t.join()

	f = open(strings_file, "w")
	f.write(str(strings))
	f.close()
	print("Flag: {}\nCompleted after {} requests.".format(flag, requests))

if __name__ == "__main__":
	main()

Key: d03snt_3v3ry0n3_md5