蹭一下某CMS 5.X版本GETSHELL漏洞合集的热点。
在 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 行开始是发送重置密码链接邮箱的处理过程:
在发送阶段,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
。
因为使用curl_post
中使用的是http协议,默认端口为80。因此在vps上监听80端口:
nc -lvv 80
在填入管理员密码或邮箱后,抓包修改数据:
forward掉后,
在6.0版本中,官方直接删除了该文件,简单粗暴。