Tags: misc

Rating:

# SCRAMBLE

This challenge was kinda like Wordle, with the twist that you had to guess a completely random string. The game gives you a wordlist of 12000 words and the requirement to get the flag was, to solve 100 games in less than 500 total guesses. After failing 5 times for one game, we get a new game but without getting a new solve.

The server lets us guess 7 times per word and it sends us a string back, which tells us how close we are.

example:

secret: SEINR

guess: EEONE

comparison: \*\+\-\+\*

\* means that that character in the guess is also in the secret word, + means that the position is right and - tells us that the character is not inside the secret.

I wrote a function that removes wrong words from the wordlist by using the comparison we got back:

py
def eliminate(res, guess, wl):
result = []
for e in wl:
flg = True
for i in range(5):
if res[i] == "*" or res[i] == "+":
if guess[i] not in e:
flg = False
if res[i] == "+":
if guess[i] != e[i]:
flg = False
if res[i] == "-":
if guess[i] in e:
flg = False
if flg and e != guess:
result.append(e)
return result


And then also some code to communicate with the server (of course I also tested it locally):

py
def pwn():
conn = remote("scamble.rumble.host", 2568)

old_wordlist = conn.recvuntil(b'>').decode().split("\n[")[1].split("\n")[1:]

while True:
wordlist = old_wordlist
while True:
choice = ""
if len(wordlist) == 12000:
choice = rng.choice(wordlist)
else:
choice = rng.choice(wordlist)
conn.sendline(choice)
res = conn.recvuntil(b'> ').decode().strip()
if "avg" in res:
conn.interactive()
if "[guess correct]" in res or "[number of guesses exceeded]" in res:
conn.recvline()
r = conn.recvline().decode()
if "avg" in r:
print(r)
try:
print(conn.recvline())
except:
pass
conn.close()
return
conn.recvuntil(b'> ').decode()
break
res = res.split("\n")[0]
wordlist = getpossible(res, choice, wordlist)

while True:
pwn()


It fetches the wordlist, then randomly chooses a word each time and eliminates using the feedback from the server.

But that was sadly not enough. I optimized my code a bit by trying to find good guesses. I tried some things like e.g. choosing guesses that are as far apart from the previous guess as possible but that did not really improve my score. Luckily, choosing a good starting guess, which includes one of the words with the least amount of duplicates possible.

py
def getstartinggoodguess(wl):
scores = {1: [], 2: [], 3: [], 4: [], 5: []}
for e in wl:
scores[max([e.count(x) for x in e])].append(e)
for j in [1, 2, 3, 4, 5]:
if len(scores[j]) != 0:
return scores[j][0]


With some additional luck and a few tries later I finally got the flag! (With a score of 4.82)

Original writeup (https://github.com/xXLeoXxOne/writeups/blob/main/CyberSecurityRumble%202022/SCRAMBLE.md).