CTF: CSA20/Tricky Guess (or why not to use expect)

Orel Damari
5 min readOct 31, 2020

tl;dr 1 Do not use expect when you need logic. Use python instead.

tl;dr 2 Code solution to Tricky Guess at the end of this post.

Photo by Kyle Hanson on Unsplash

CSA

Well, I spent a few hours trying to solve this checkpoint challenge. It was fun, you should try it yourself. Checkpoint creates a CTF challenge every year. The winners gets an offer from checkpoint academy, Here you can read more about it.

Anyways, With their approval- I can write about it. Lets go.

Photo by Markus Spiske on Unsplash. Cyber Cyber Cyber.

Tcl/Tk and Expect

A while ago, when I started working as an automation developer I got exposed to tcl/tk, specifically to expect.

The only book I read

Tcl/Tk

Maybe when it started it was good. Back in 1990. Before google was born. The days when Java was cool and windows had a better gui. In my opinion Tcl/Tk is a pure mess. For those of you who are crying about handling your legacy PHP code- just try to write a code that will increment a counter when a button is pressed using tcl. Here is the best solution:

#! /usr/local/bin/wish8.5
button .b -text 0 -command {.b config -text [expr {[.b cget -text]+1}]}
pack .b ;#RS

Expect

For those of you who are not familiar with expect- its a program that “talks” to other interactive programs according to a script. The program can be used inside tcl/tk script- although there are some extension to other languages such as Java, Go , Python, Shell and many more. Even though it is possible to use expect in a lot of languages- from my attempt using Java, Python and Shell, its not the optimal solution. There are a lot of “sync” problems to solve between the interactive program to our script. I found tcl/tk the easiest language to use.

windows 95. The good old days. I was a baby

Tricky Guess Solution

This is how the challenge starts:

All you need to know is: netcat tricky-guess.csa-challenge.com 2222Good luck!

After telneting to the address that’s what I’ve got:

Writing the letter “a” gave me this output:

Writing a word from the dictionary output:

Which means that in the word osfznmlerixw there are 4 good letters.

First attempt

As you could understand- I tried to use expect. I wrote a tcl/tk script that will use the list of words and will always isolate only the possible words after the last input. Well, I was a kid.

  • Synchronizing between tcl/tk expect to netcat is possible.
  • Writing the logic in tcl/tk- it was not fun- but its possible.
  • Reading a file using tcl- also possible.
  • All I have now is to integrate everything and make it work! “All I have”.

Sometimes it took time for netcat to answer, sometimes expect decided to close the session with netcat atm it closed it with the file, and most of the time tcl/tk used too much energy to calculate the logic.

There must be another way… Better way…

Second Attempt

Well, there is. I’m not sure why it wasn’t the first thing that came to my mind. Python Socket Library. Maybe because one year ago I used java and trying to synchrone between System.in to System.out and it was too hard. I don’t know.

Code & Logic

Logic

Find matching items using this algorithm.

  • create a 1 dimensional array with the length of 24 (“a” would be index 0, “b” index 1, etc..)
  • Iterate over the dictionary and add ++ to every location we have a letter.
  • Delete all the words that does not have the same amount of common letters as the output shows

Anyways... Here is the final solution using a bad bad python code :)

import socket
import time

from NextNumber import NextNumber

s = socket.socket()


try:
n = NextNumber()
port = 2222

s.connect(('tricky-guess.csa-challenge.com', port))

start_time = time.time()
while True:
msg = (s.recv(1024)).decode("utf-8")
print(msg)

if "GO" in msg:
print(n.nextWord)
s.send(str.encode(n.nextWord))
else:
try:
val = int(msg)
n.findMatchingWords(n.nextWord,val)
print(n.nextWord)
s.send(str.encode(n.nextWord))
except ValueError:
abc=1
print("--- %s seconds ---" % (time.time() - start_time))


except:
raise
finally:
s.close()
import copy
class NextNumber:

def __init__(self):
self.nextWord = "tdaxjkvyczuo"; #the first word
self.list =[]
f = open("words.txt", "r")
for x in f:
x=x.replace("\n","")
#print(x)
self.list.append(x)

def findMatchingWords(self, word, number):
newArray = copy.deepcopy(self.list)
for str in newArray:
if not self.isMatchingWord(word,str,number):
self.list.remove(str)
self.nextWord = self.list[0]
return self.nextWord

def isMatchingWord(self,word1 , word2, number):
freq1 = [0] * 26;
freq2 = [0] * 26;
count = 0;
for i in range(len(word1)):
freq1[ord(word1[i]) - ord('a')] += 1;

for i in range(len(word2)):
freq2[ord(word2[i]) - ord('a')] += 1;

for i in range(26):
count += min(freq1[i], freq2[i]);

return count==number;

And the output:

--

--

Orel Damari

Senior Staff Software Engineer at Palo Alto Networks. I love security, computer networks, and pictures of dogs in front of a computer.