0x00 前言

蹭一下某CMS 5.X版本GETSHELL漏洞合集的热点。

0x01 漏洞分析

在 admin/admin/getpassword.php 中包含了 admin/include/common.inc.php 。跟入common.inc.php,第77行:

foreach(array('_COOKIE', '_POST', '_GET') as $_request) {
    foreach($$_request as $_key => $_value) {
        $_key{0} != '_' && $$_key = daddslashes($_value,0,0,1);
        $_M['form'][$_key]=daddslashes($_value,0,0,1);
    }
}

这里存在变量覆盖漏洞。

回到 admin/admin/getpassword.php ,从第 94 行开始是发送重置密码链接邮箱的处理过程:

  1. 从数据库中取出管理员的信息
  2. 生成密码重置链接
  3. 发送密码重置链接

在发送阶段,admin/admin/getpassword.php 第 143 行:

require_once ROOTPATH.'include/jmail.php';
$sendMail=jmailsend($from,$fromname,$to,$title,$body,$usename,$usepassword,$smtp);
if($sendMail==0){
    require_once ROOTPATH.'include/export.func.php';
    $post=array('to'=>$to,'title'=>$title,'body'=>$body);
    $met_file='/passwordmail.php';
    $sendMail=curl_post($post,30);
    if($sendMail=='nohost')$sendMail=0;
}

先来看看当$sendMail==0时的情况,此时会将所需信息整合为变量$post,之后调用curl_post发送。curl_post定义在 include/export.func.php:

function curl_post($post,$timeout){
global $met_weburl,$met_host,$met_file;
$host=$met_host;
$file=$met_file;
    if(get_extension_funcs('curl')&&function_exists('curl_init')&&function_exists('curl_setopt')&&function_exists('curl_exec')&&function_exists('curl_close')){
        $curlHandle=curl_init(); 
        curl_setopt($curlHandle,CURLOPT_URL,'http://'.$host.$file); 
        curl_setopt($curlHandle,CURLOPT_REFERER,$met_weburl);
        ....
        $result=curl_exec($curlHandle); 
        curl_close($curlHandle); 
    }
    else{
        if(function_exists('fsockopen')||function_exists('pfsockopen')){
            $post_data=$post;
            $post='';
            @ini_set("default_socket_timeout",$timeout);
            while (list($k,$v) = each($post_data)) {
                $post .= rawurlencode($k)."=".rawurlencode($v)."&";
            }
            $post = substr( $post , 0 , -1 );
            $len = strlen($post);
            if(function_exists(fsockopen)){
                $fp = @fsockopen($host,80,$errno,$errstr,$timeout);
            }
            else{
                $fp = @pfsockopen($host,80,$errno,$errstr,$timeout);
            }
            if (!$fp) {
                $result='';
            }
            else {
                $result = '';
                $out = "POST $file HTTP/1.0\r\n";
                $out .= "Host: $host\r\n";
                $out .= "Referer: $met_weburl\r\n";
                $out .= "Content-type: application/x-www-form-urlencoded\r\n";
                $out .= "Connection: Close\r\n";
                $out .= "Content-Length: $len\r\n";
                $out .="\r\n";
                $out .= $post."\r\n";
                fwrite($fp, $out);

可以看到这里的$met_host操控了邮件内容的发送地点,而该参数可以利用前面的变量覆盖漏洞来进行直接的控制。

接着考虑如何让$sendMail==0。跟入jmailsend,include/jmail.php 第7行:

function jmailsend($from,$fromname,$to,$title,$body,$usename,$usepassword,$smtp,$repto,$repname)
{
    global $met_fd_port,$met_fd_way;
    ...
    if(stripos($smtp,'.gmail.com')===false){
        $mail->Port       = $met_fd_port;
        ...
    }
    else{
        $mail->Port       = 465;
        ...
    }

    ...

    if(!$mail->Send()) {
        $mail->SmtpClose();
        //return "Mailer Error: " . $mail->ErrorInfo;
        return false;
    } else {
        $mail->SmtpClose();
        //return "Message sent!";
        return true;
    }
}

met_fd_port指定了邮件发送端口,属于系统配置。因此倘若我们利用前面的变量覆盖漏洞修改端口,即可导致邮件发送失败,进入到curl_post

0x02 漏洞复现

因为使用curl_post中使用的是http协议,默认端口为80。因此在vps上监听80端口:

nc -lvv 80

在填入管理员密码或邮箱后,抓包修改数据:

forward掉后,

0x03 漏洞补丁

在6.0版本中,官方直接删除了该文件,简单粗暴。

源链接

Hacking more

...