FCSC Prequals 2020 - Bestiary
Web challenge of the annual CTF organized by the ANSSI, where PHP sessions can be used to perform a LFI.
This CTF is run yearly by ANSSI, in order to get 30 people qualified for another (shorter) CTF. The winners will play the ECSC CTF in Viennes as part of the official French team.
I didn’t manage to be qualified, but I played this CTF because the challenges are always very fun and their goal is to learn you something.
Here is my writeup for a web challenge quite easy to solve, but which taught me a really nice technique to perform PHP code injection from a local file inclusion vulnerability.
We just ask you to find the flag.
When we land on the index page, we see a very minimalist website.
Let’s play its game and select a monster.
Nice! A Dungeons and Dragons theme. But that doesn’t make us omit this GET parameter up there:
As I always do first, I replace the legit value
mind_flayer by a single quote
A PHP error betrays the presence of an
include() function called on the
monster parameter value. So this is probably a LFI here.
Before doing more tests, let’s open up Burp Suite which is more convenient for injecting dynamic parameters thanks to its Repeater tab. Then, we try the classic
The fact that it works instantly tells us that there isn’t anything appended at the beginning or the end of the
monster value, like “file_” or “.php” for example. Therefore we can maybe inject a PHP filter, very useful for obtaining the server’s source code. This payload
php://filter/convert.base64-encode/resource=index.php is a classic one which will output the base64-encoded
The goal is to get the the
flag.php file’s content. We could use the exact same technique as for the
index.php file, but here we are facing a constraint: the word
flag cannot appear in URL. Additionally, I tried the wrappers
input but without success.
LFI exploitation via PHP sessions
By scrolling down PayloadAllTheThings’s file inclusion page, I found something that could be applicable in our case, as it involves PHP sessions to execute PHP code. Maybe not a coincidence, there is a suspicious line
session_save_path("./sessions/"); at the very beginning of the
Indeed, in PHP,
$_SESSION variables rely on files to store their content. By default those files are located in the
/var/lib/php5/sessions directories, with permissions like
drwx-wx-wt root root. So ordinarily, with a
PHPSESSID=3a717e2a84b5a538f9d78251b49506c cookie, there would be a
/var/lib/php/sessions/sess_3a717e2a84b5a538f9d78251b49506c file, but unreadable using LFI.
session_save_path line indicates that the sessions files are located alongside the source code. Maybe we can even read them. Besides, according to the line
$_SESSION['monster'] = $monster;, we fully control what is contained in our session file: it’s what we put previously in the
Let’s test this by putting a dumb value like
?monster=bouzygouloum id the URL, send the request, and then try to include our session file, and see its content.
So we effectively can include our session file, and control what is inside, which is passed into an
include() function. Now, the PHP documentation tells us “The include statement includes and evaluates the specified file” => PHP code will be executed if it goes through it.
By knowing that, this PHP code should do the trick.
So we first send a request containing our URL-encoded payload:
And then include it:
And this is a flag
This challenge wasn’t too hard and had a lot of solves, but I learnt that trick which is kind of cool.
system function was disabled. But if you have access to it or
passthru, etc., you can even achieve RCE from this LFI!