This is a write-up of the The ASCII Ruler challenge (150 points) from BSides Canberra 2017.
You can find a copy of the challenge on GitHub (https://github.com/OJ/bsides-2017-ctf-docker/tree/master/misc-ruler) if you want to give this a go.
We are given a server to netcat to, and are presented with this:
We are given a Yes/No option regarding whether we want a sample. If we enter Y, we are given a generated ruler like above. Otherwise, we are asked to give a rule of random sections and rows (sections are marked on the bottom in Figure 1, rows on the side).
We decided to take a mathematical approach to solving this as we saw a couple of patterns to begin with. In Figure 1, the first row contained 3 pipes. The second, 5. The third, 9. The fourth, 17. The fifth, 33. Here’s another ruler we made observations off:
In Figure 2, we noticed that the first row had 5 pipes, second had 9, third had 17, and the fourth row had 33. This seemed odd, as there were the exact same number of pipes as above for rows 2–5.
After examining more rulers, we came to the following conclusions:
- The first row always contained n sections + 1 pipes
- The first row always contained (2^rows) – rows spaces
- The second row contained 2*sections – 1 pipes, and (2^(row – 1)) * (2*sections – 1) spaces
- The third row contained 2*(2*sections – 1) – 1 pipes
- The fourth row contained 2*(2*(2*sections – 1) – 1) – 1 pipes
From these observations, we created a recursive function that calculated the number of pipes (in Python):
The base case for the above is when the current row is equal to one (it would be two, but we’re programmers, right?!).
The next step was to find the number of spaces that should occur between each pipeline. We discovered the equation for figuring this out was to take 2to the power of the total number of rows and subtract the current row from that result.
The final ruler generation function looked like this:
The next step was sending our generated ruler to the server. We used pwntools to do this. We originally tried copy and pasting the rulers, but we discovered there was a time limit for sending the rulers.
We used Regular Expressions to find out the size of the ruler, then we sent the ruler. We keep doing this until the line does not being with “Give me a ruler”, which either means we failed, or we got the flag. The code was as below:
Issues and Takeaways
We came across a couple of issues while doing the challenge. The first was that our ruler generation didn’t seem to work for rulers of pretty big sections and rows. Another issue was that we weren’t too sure how on earth we were supposed capture inputs until EOF, since the script would just throw an exception.
But in the end, we managed to get the flag outputted which was
Looking back, it was an interesting challenge, and I really enjoyed it 🙂
A big thank you to the BSidesCBR CTF organisers and also for their encouragement for us to solve the challenge.