LakeCTF: So What?

Misc, 50 Points, (but used to be 500). Follow my thoughts on this journey.

The description already taunts us.

Are you a shellcoding pro? If not, so what? (salt guaranteed once you know the solution)

The author is playing a game against us. They want to see us suffer. For them, the greatest happiness is to scatter their enemy, to drive us before them, to see our cities reduced to ashes and to see those who love us shrouded in tears. But to beat them, all we need to to is play along in their game of gleeful malice, with intent. Obviously, the obvious path is wrong.


The Dockerfile

We received two files and start with the obviously wrong one to look at. However, it does not seem to be that interesting, apart from needing special options to be built and launched.

Importantly, it does not contain the flag.


The Flag's Residence

So where is it? We find it in the challenge.py file. Or rather, a placeholder.

For the reader new to playing CTF: Oftentimes, challenges have a server that runs the exact same program as we receive to look at, except that it has a different flag. That makes local testing more reliable but keeps the flag securely hidden in its grove.

But actually, it is not ... really to be found inside the challenge.py. Because the challenge.py file itself is not accessible inside the jail in the docker where the challenge server is running at. And the code writes the part with the flag placeholder to a file.

This snippet is only the end of the code, but we can already see the obvious solution - which must be a trap, otherwise there would be no salt at the end. We clearly can provide some your_input library that the main.c is linked against.

The print("Stage") and os.system("cat libflag.so") were added by me, to debug. Since the flag is inside main.c it is also in libflag.so, and being able to cat any of these would give the flag.


The Start of the File

Oh btw, this is the first part of the file: Some annoying filters but basically we provide input and it first runs it through as, then ld, and then as we have already seen through gcc to create both libflag.so and an executable called main, which is then run.

There was more code, but it is useless to understanding so I removed it for brevity.


Excurse

We recall that there was some quirky behaviour where invalid files are treated as linker scripts. Since the description clearly stated that the obvious path is not a fun one, let us verify this idea.

Indeed. It says "treating as linker script". So we try a few things with that.


The First Flag

Let us spend hours reading up, to no avail, about setting a custom entrypoint, dynamically including, and skimming through the whole manual of the linker and assembler.

Somewhere pretty soon along this way, an announcement was made in the LakeCTF Discord:

We have fixed so what ? and will therefore release so what? revenge in order to let you play it in the intended way.

After a quick look at the diff and then an support ticket to make sure they did not just forget to actually update their files, I knew: The files for the revenge challenge were the same. Except for the trailing newline, but I could not imagine how that would have any impact...

This made no sense so I decided to just solve the revenge first and then get the "easier" challenge for free. I spent another hour reading documentation, then got bored of thinking and decided not to think for a moment. Handing in this youtube link actually congratulated me!


Revenge Flag

After continuing the mentioned reading for a day, interspersed with looking at other challenges, I finally gave up on the linker idea and instead attempted with low motivation to create a win symbol without the i character by escaping hex digits in as, and to use pwnlib encoders to generate an input that would get past the filters, crash the assembler, and then be linked to anyway. Or actually just writing a piece of assembly that can print the files in the working directory.

Eventually, I returned to the linker idea and read the manual again.

INCLUDE filename Include the linker script filename at this point. The file will be searched for in the current directory,

But this does not support globbing, and I still can not insert the lowercase character i.

INPUT (file , file , ...) INPUT (file file ...) The INPUT command directs the linker to include the named files in the link, as though they were named on the command line. [...]

  • If you use INPUT (-l file ) , ld will transform the name to lib file.a, as with the command line argument -l.

I had tried this before. At the very start of my journey. It did not work. Still not.

INPUT (-l flag ) gives

Huh. So what if I do once more what is obviously wrong and deviate from the manual website by omitting whitespace?

INPUT(-lflag) gives


Useless Conclusion

Hopefully, this kind of writeup is entertaining and shows also how one might go about a challenge. Even if I had not known that the linker sometimes does weird stuff, simply submitting garbage was enough to be informed about that in a warning. If you don't know who to be, be a human fuzzer.

I was right, the challenge was fun. The author was right, the challenge solutions (both!) made me salty.

And the manual was wrong.

Lucid, 26.09.2022