Array Programming Rocks - GCC CTF Writeup
Introduction
This week I played a challenge from GCC CTF, Array Programming Rocks. The core concept is understanding array programming, and reversing a language that implements it.
Understanding What I’m Seeing
We where given a chall.ua
file with the below contents
|
|
The obvious response is dear me wtf. Obviously we can see some interesting things, like printable strings, big array-like collection of numbers, and even some assignments in the first two lines. However this was not enough to sit and understand the entire code, and I thought that it was probably a real language. So I decided to try and find what the language was. After some thoughts, I just decided to search for a language detection program, and found this site.
After throwing it in, it returns it’s APL, which made me understand the whole array programming connection. I then found this APL interpreter in order to do some dynamic analysis, however it produced some errors. After looking at some other APL challenge writeups, I understood that APL wasn’t exactly the language I wanted. So I decided to try and use a symbol of the language and use that to search. Through this, I found the APL Wiki. Searching inside it for the โ
character, it returned 4 search results. Unfortunately because I’m stupid, I followed the wrong page, and attempted to see if the BQN
language was what I wanted. After figuring out that wasn’t it as well, I went back, and saw that there exists a language called Uiua.
Searching for some of the symbols produced good results, and that combined with the .ua
extension, made me feel confident I had found the correct language. After looking at some general stuff about the language, like for example that it’s stack based, I tried finding an interpreter for it. Thankfully, the official website offers an interpreter in it’s home page. After attempting to run the code and reoslving some issues (Error: Maximum execution time exceeded
), I could finally run the code and start reversing it.
Reversing What I’m Seeing - Part 1
So far, we have an array programming stack-based language with it’s own unique symbols and syntax. I followed a more dynamic analysis approach rather than a static one, since learning the language seemed too much, and I had an interpreter. However, whenever I felt I needed to look at a glyph better, I consulted the docs. A very helpful symbol I found was the ?
symbol, which let me see what was on the stack at any point in the code’s execution. This means we can place this debug symbol not only in between lines, to understand a line and it’s operations as a whole, but we can also place it inside a line amongst other glyphs. This was very helpful as it let me follow every processing of data in memory. We just need to keep in mind how some instructions are syntaxed so that we don’t break them.
What followed was some hours of me just giving input to the program and trying to understand what was going on. Some things I found early on were that the first line takes the input and stores it as an array into F
, that N
holds the flag length, and that the flag is of length 36 (by seeing the length of the string in line 8). From now on, assume I’m using GCC{abcdefghijklmnopqrstuvwxyzABCDE}
for every input unless stated otherwise. If we enter a debug symbol under line 3, we get this view of the stack
|
|
So line 3 takes the flag array, and creates a rotation of it for every character, rotating from end to start. The same for line 4, it takes every one of these lines, and just converts every number to their binary representation
|
|
The next line took me some time to understand what exaclty was going on. For better comprehension, I gave a smaller input, ABCD
|
|
I also got another sample using ABCDE
, to do some differential analysis of the outputs
|
|
If we stare at these numbers for long enough, they start talking back to us, and they tell us they are the result of XORing between each bit of the number in the corresponding index. If you want to see it for yourself, look at them and talk to them, they don’t bite (a lot).
The next line is simple enough, just convert the bits to their corresponding power of 2 based on their index, and then line 7 just takes every row, and sums the numbers in it. The results of all these rows is compared in the first assertion. Throughout all of this, I implemented these steps in python to try and understand them, and also in the hopes of understanding how to reverse them
|
|
Generally looking at this, I couldn’t see how to efficiently reverse this step. I couldn’t go through an alphabet to find matches with the checked string, and the only logic I could find was maybe z3, but that would need to happen on a bit level. So maybe potentially something with sagemath? Alas, I decided to see if the next checks where enough to give me something to work with.
Reversing What I’m Seeing - Part 2
The first part was relatively easy to reverse, since the operations could be treated as a whole function with us just doing F8 over it (not literally, debugging joke). Line 10 however needed more effort, for me at least. And I made some mistakes which didn’t help me and ate away at my time and energy:
- When the assertion happens, the value that is being compared is popped from the stack after the check. So while I thought that the second part used the array that was used in the first check, it turned out it uses the original input, which makes it way more approachable than what I had initially thought
- The instructions are executed in reverse order. So while I thought that these were the first instructions that happen
:/+โ
, it’s in fact theseโ.โฏ9_4
, and in this order. So the language is read left to right. This slipped from me during the first step because I treated the lines as a whole
Line 10 can be split into two ideas, two big operations that are happening, which create the arrays which will be used in the next two checks:
- It takes our input, creates a 9x4 matrix out of it, and sums the elements of the columns. That’s how we end up with 4 elements, which are used in the first check
- Like the first part, it takes our array, and rotates it every 7 characters, starting from the beginning. It then transposes the generated matrix, so that it can take the sums of the columns, but it’s the same as if it took the sums of the rows of the matrix Below is the stack view after both operations have happened (note that I commented out the assert, so that’s why it’s working on the array generated during the first part)
|
|
This part seems much more approachable, and my instinct was telling me z3 would do the job for me. So that’s what I did.
Solving What I’m Seeing
All in all, I just tried to reproduce the two operations. However I made a big mistake while solving it. Usually in my z3 solvers I write
|
|
However for a simple sanity check, I wrote just print(s.check() == unsat)
, expecting to see True
if the model was satisfiable. This however, was a mistake of mine, since this line means that it will print False
if the model is satisfiable. However I hadn’t thought about that. As a result, I would see False
being printed, and take that as an indication that the model failed. I had to talk with a member of the Greek CTF community who had solved the challenge, Babafaba (who had also used the same z3 approach), to understand my stupidity. Thanks Babafaba :)
|
|
GCC{1_L0v3_Th15_sT4ck_ba5eD_M4dN355}
Conclusion
After the CTF ended, the organizers released a repo containing the writeup of the challenge. It tells us that for the first part, we needed to see that the flag is XORed twice. That’s true. However I don’t know how many people used that to solve this challenge. Another player seems to have used the same z3 approach, so I think we outplayed the developer bros. Jokes aside, it was a very interesting challenge, and it could help someone understand some of the core concepts of reversing, like guessing, finding out how to view the memory of a language’s runtime, guessing, how to understand a language and concepts on the go, guessing, and many more (like guessing). I made different mistakes and sacrificed a bit of my sleep, but it was an enjoyable challenge with things to teach, so kudos to the author!