The following description is provided:

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.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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 script succeeded in winning for 75 times in a row after about 3 hours.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
#!/usr/bin/python import socket import re # order: small ----> big games = [] save_list = True def recv_tillreturn(sock, times=1): """Receive times lines of data from a socket""" total_data=[] while times > 0: times -= 1 dat = "" while 1: char = sock.recv(1) if char == "\n": dat=dat+char;break else: dat=dat+char total_data.append(dat) return ''.join(total_data) def insert(c1, c2): """Insert a pair of ordered value in the games list, where c1 > c2.""" global games i_c1 = -1 i_c2 = -1 for i in range(len(games)): if games[i] == c1: i_c1 = i if games[i] == c2: i_c2 = i if i_c1 == i_c2 == -1: # c1 and c2 not found: insert them to the head of the list games.insert(0,c1) games.insert(0,c2) elif i_c1 != -1 and i_c2 == -1: # c1 found but not c2: insert c2 just before c1 games.insert(i_c1, c2) elif i_c1 == -1 and i_c2 != -1: # c2 found but not c1: insert c1 after c2 games.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: games.remove(c1) games.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(games)): if games[i] == c1: i_c1 = i if games[i] == c2: i_c2 = i return i_c1 > i_c2 def main(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(3) s.connect(("ec2-23-21-19-72.compute-1.amazonaws.com", 6969)) while True: junk = recv_tillreturn(s, 1) c1 = recv_tillreturn(s, 1) c2 = recv_tillreturn(s, 1) print junk, print c1, print c2, c1 = c1.split()[3] c2 = c2.split()[3] if isbigger(c1, c2): guess = 1 else: guess = 2 s.send("%s\n" % guess) answer = recv_tillreturn(s, 4) print answer if "Correct" in answer: if guess == 1: insert(c1, c2) else: insert(c2, c1) else: if guess == 1: insert(c2, c1) else: insert(c1, c2) if save_list: slist = open("/tmp/the_game_list.txt", "w") slist.write(str(games)) slist.close() if __name__ == "__main__": main() |

Key: `d03snt_3v3ry0n3_md5`

UPDATE: multi_play.py is a multithread version of the above script with other minor changes. By starting 50 parallel threads, it managed to complete the challenge in 1 minute and 30 seconds performing a total of 13000 requests. Not bad at all!

Very nice!!!

Awesome!

I do wonder though, if these are md5 sums of random integers or did the backend program have these values in an array upon which it just looked at the value of the array index and not really care about the md5? Also, I am curious how many iterations it took your script to get to 75/75?

I updated the script to run multiple threads and count the requests number ðŸ˜‰

Ehya!

To answer your questions:

1) “hex-encoded random 17-byte strings” is a quote taken from irc of one of the organizer, so no md5 hash involved in this challenge

2) this would be interesting to check. I said the script took 3h to get 75/75, but I didn’t mention that my connection has a not-so-good latency. I noticed there are other write-ups for this challenge, where authors claim that their scripts completed in <1h: it would be nice to compare all of them against a local game simulation.

Bye!

Your solution is far more elegant than mine was. I suspect overall I took longer to do the whole challenge.

Someone in #pctf was saying they’d made a multi-threaded solution (after the CTF was closed) that connected several times. They were able to solve it in 4 minutes!

It was me (lavish is my nickname) At the bottom of the post I put a link to the multi-threaded version, give it a try!