bluecms 1.6 审计与渗透测试
Posted bfengj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bluecms 1.6 审计与渗透测试相关的知识,希望对你有一定的参考价值。
前言
听My0n9s师傅说bluecms比较适合审计入门,正好也到了2周审计一个CMS的时候了,就从网上下载了这个CMS的源码,开始审计与渗透测试。
源码下载:
bluecmd
安装
安装的时候主要先看看哪些目录是可写的,到时候如果可以传马或者SQL注入可以写马啥的,至少对于可用的目录知道一些。而且也大致看一下,cache是缓存,upload估计就是上传的路径了,backup是备份,说不定可能存在源码泄露啥的,complie就不清楚了,好像就是smarty?。
之后就是正常的安装,而且安装那里的用户名和密码不是有默认的,对于非默认设置用户名和密码的CMS,一般若用户名密码的可能性就会小一些。
然后我安装装不好,我吐了。整不明白,在step5:
elseif($act == 'step5')
{
define('IN_BLUE', TRUE);
include dirname(__FILE__) . '/../include/common.inc.php';
include BLUE_ROOT . 'admin/include/common.fun.php';
update_data_cache();
update_pay_cache();
if(is_writable(BLUE_ROOT.'data/'))
{
$fp = @fopen(BLUE_ROOT.'data/install.lock', 'wb+');
fwrite($fp, 'OK');
fclose($fp);
}
$install_smarty->display('step5.htm');
}
这里:include dirname(__FILE__) . '/../include/common.inc.php';
,跟进是这里:
require(BLUE_ROOT.'include/smarty/Smarty.class.php');
这里弄完程序就自动退出了,没搞懂道理哪里出了问题,就暂时放弃这里,反正CMS大致都已经安装成功了。
再看一下有没有重复安装的可能:
if(file_exists(BLUE_ROOT.'data/install.lock')){
install_showmsg('您已经安装过本系统,如果想重新安装,请删除data目录下install.lock文件', '../index.php');
}
function install_showmsg($msg,$gourl='goback', $is_write = false)
{
global $install_smarty;
$install_smarty->assign("msg",$msg);
$install_smarty->assign("gourl",$gourl);
$install_smarty->display("showmsg.htm");
exit();
}
有exit,因此没法重复安装。重复安装的话一般就是3种利用,一种是安装时候大概率会把配置写进文件里,而install的时候大部分情况开发都是不过滤的,所以有机会写马。第二种就是直接自己写管理员用户名密码,然后直接拿后台,去后台想办法getshell了。第三种就暂时不说了,因为最近刚拿这种姿势出了题,等比赛结束了再补一下,应该很多师傅都利用过。
index.php
以前从来不看这个,从极致CMS的时候开始看index.php的处理,然后xdcms也跟着学长的文章看了index.php的处理。这里也看一下bluecms的index.php的处理。具体的不分析了,看一下结果叭。最开始的这里:
跟进common.inc.php
看看,一开始是一些ip,模板,数据库啥的处理。然后这段奇奇怪怪的代码:
if(!$_SESSION['user_id'])
{
if($_COOKIE['BLUE']['user_id'] && $_COOKIE['BLUE']['user_name'] && $_COOKIE['BLUE']['user_pwd'])
{
if(check_cookie($_COOKIE['BLUE']['user_name'], $_COOKIE['BLUE']['user_pwd']))
{
update_user_info($_COOKIE['BLUE']['user_name']);
}
}
else if($_COOKIE['BLUE']['user_name'])
{
$user_name = $_COOKIE['BLUE']['user_name'];
$user = $db->query("SELECT COUNT(*) AS num FROM ".table('user')." WHERE user_name='$user_name'");
if($user['num'] == 1)
{
$active = 0;
}
else
{
$active = 1;
}
}
else
{
setcookie("BLUE[user_id]", '', -86400, $cookiepath, $cookiedomain);
setcookie("BLUE[user_name]", '', -86400, $cookiepath, $cookiedomain);
setcookie("BLUE[user_pwd]", '', -86400, $cookiepath, $cookiedomain);
}
}
直接对Cookie进行一些处理,先看一下check_cookie
:
function check_cookie($user_name, $pwd){
global $db, $_CFG;
$sql = "SELECT pwd FROM ".table('user')." WHERE user_name='$user_name'";
$user = $db->getone($sql);
if(md5($user['pwd'].$_CFG['cookie_hash']) == $pwd) return true;
else return false;
}
直接的拼接,直接一个SQL注入。。。这个CMS给我搞得人傻了,又是个SQL注入防护的近乎为0的CMS吗。。。。
但是这里是没回显的,但是如果mysql的设置没做防护的话,已经可以写shell了。跟进getone看一下,里面有个query函数:
function query($sql){
if(!$query=@mysql_query($sql, $this->linkid)){
$this->dbshow("Query error:$sql");
}else{
return $query;
}
}
用的mysql_query,没法堆叠注入。而且这里也是判断为空就直接$this->dbshow("Query error:$sql");
,mysql_query
也用的@
,怪不得我试不能报错注入。那这样的话就是0过滤的布尔注入了,可以拿sqlmap直接脱裤。而且先提前看一下用户名密码的处理,我这里的用户名和密码都是feng,数据库里是这样:
把这个pwd直接md5解一下就是的了,所以如果弱密码的话也能md5直接解,强密码的话就有些难了。不过问题不大,或许login那里也能SQL注入呢。
接下来的感觉就是一些index页面的东西了。也挺好,才index.php就拿到一个SQL注入了。
admin登录
其他的暂且不管,先看一下后台登录的那里。访问/admin/index.php肯定会转到login.php了,还是先看一下index.php,一是猜测可能这个index还是有相同的问题,二就是看看鉴权有没有问题。
还是:require_once(dirname(__FILE__) . "/include/common.inc.php");
跟进去,还是同样的问题:
if(empty($_SESSION['admin_id']) && $_REQUEST['act'] != 'login' && $_REQUEST['act'] != 'do_login' && $_REQUEST['act'] != 'logout'){
if($_COOKIE['Blue']['admin_id'] && $_COOKIE['Blue']['admin_name'] && $_COOKIE['Blue']['admin_pwd']){
if(check_cookie($_COOKIE['Blue']['admin_name'], $_COOKIE['Blue']['admin_pwd'])){
update_admin_info($_COOKIE['Blue']['admin_name']);
}
}else{
setcookie("Blue[admin_id]", '', 1, $cookiepath, $cookiedomain);
setcookie("Blue[admin_name]", '', 1, $cookiepath, $cookiedomain);
setcookie("Blue[admin_pwd]", '', 1, $cookiepath, $cookiedomain);
echo '<script type="text/javascript">top.location="login.php?act=login";</script>';
exit();
}
}elseif($_SESSION['admin_id']){
update_admin_info($_SESSION['admin_name']);
}
check_cookie
那里进行SQL注入。
但是这里不太一样了,这里我们考虑的就不是SQL注入了,是想办法进后台。check_cookie
还是老样子:
function check_cookie($user_name, $pwd){
global $db, $_CFG;
$sql = "SELECT pwd FROM ".table('user')." WHERE user_name='$user_name'";
$user = $db->getone($sql);
if(md5($user['pwd'].$_CFG['cookie_hash']) == $pwd) return true;
else return false;
}
这里还需要一层pwd的认证,能SQL注入这个肯定问题不大。但是我之前看部分处理和这里的,都似乎有些执着于代码的漏洞,没有考虑这个到底是干什么的。仔细再想想的话,这部分好像是Cookie的鉴权,也就是说似乎可以login的时候通过用户名和密码来登录,也可以通过Cookie来获得认证。
看看这个update_admin_info
干了什么:
function update_admin_info($admin_name){
global $timestamp, $online_ip, $db;
$admin = $db->getone("SELECT admin_id, admin_name, purview FROM ".table('admin')." WHERE admin_name = '$admin_name'");
$_SESSION['admin_id'] = $admin['admin_id'];
$_SESSION['admin_name'] = $admin['admin_name'];
$_SESSION['admin_purview'] = explode(',', $admin['purview']);
$user = $db->getone("SELECT user_id, user_name, money FROM ".table('user')." WHERE user_name='$admin[admin_name]'");
$_SESSION['user_id'] = $user['user_id'];
$_SESSION['user_name'] = $user['user_name'];
$_SESSION['money'] = $user['money'];
$last_login_time = $timestamp;
$last_login_ip = $online_ip;
$db->query("UPDATE ".table('user')." SET last_login_time='$last_login_time' WHERE user_id=".$user['user_id']);
$sql = "UPDATE ".table('admin')." SET last_login_time = '$last_login_time', last_login_ip = '$last_login_ip' WHERE admin_id='$_SESSION[admin_id]'";
$db->query($sql);
}
从admin表重查东西,然后赋值给$_SESSION
,因此这里可以通过SQL注入来成为admin。
目前我们的SQL注入已经可以脱裤了,admin的管理员用户名和md5加密后的密码都是知道的,比如我这里用户名是feng。如果这样:
Blue[admin_id]=1;Blue[admin_pwd]=5dfeb62681ec9e6132b9e494fba872e7;Blue[admin_name]=feng
这部分的cookie_hash
也是在数据库表里的,能爆出来,pwd
就是feng管理员的那md5加密后的密码了,同样能注出来,所以这里的结果是已知的了,可以让check_cookie
返回true。
if(md5($user['pwd'].$_CFG['cookie_hash']) == $pwd) return true;
然后update_admin_info
那里,我用的username是feng,所以信息也是feng的信息,也就是说以管理员的身份登录了:
因此后台也能拿下。
再老老实实看看正常的后台登录那里能不能SQL注入:
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;
}
}
这tm不是随便注入???然后我试试注入发现我注不了。。。一脸懵逼。。。然后手动debug了一下,发现怎么输入的变量被转义了。。。然后发现common.inc.php
的这里原来是有过滤的:
if(!get_magic_quotes_gpc())
{
$_POST = deep_addslashes($_POST);
$_GET = deep_addslashes($_GET);
$_COOKIES = deep_addslashes($_COOKIES);
$_REQUEST = deep_addslashes($_REQUEST);
}
function deep_addslashes($str)
{
if(is_array($str))
{
foreach($str as $key=>$val)
{
$str[$key] = deep_addslashes($val);
}
}
else
{
$str = addslashes($str);
}
return $str;
}
addslashes
,注锤子注。。。然后我突然想起来,Cookie不是也被转义了吗。。。。然后我就发现了这个:
$_COOKIES = deep_addslashes($_COOKIES);
开发真的牛逼。。。牛逼牛逼,我给开发点赞,真有你的。
后来看了一下别的师傅的审计文章,还能宽字节注入。主要就是因为这个CMS的数据库用的是gbk,我当时也发现了这个问题,但是去本地打开看看:
哦是utf-8就没想这事。我tm人傻了,是他CMS的mysql_connect用的gbk,关我本来的数据库是啥编码锤子事。。。正因为他mysql_connect
用的gbk,所以才能宽字节注入,所以讲道理这个CMS真的SQL注入随便日了。。。。
function __construct($dbhost, $dbuser, $dbpw, $dbname = '', $dbcharset = 'gbk', $connect = 1) {
$this -> mysql($dbhost, $dbuser, $dbpw, $dbname, $dbcharset, $connect);
}
文件上传
登录进后台第一件事肯定就是去传马,试了一下,传
以上是关于bluecms 1.6 审计与渗透测试的主要内容,如果未能解决你的问题,请参考以下文章