当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(7) 关注此漏洞

缺陷编号: WooYun-2014-74893

漏洞标题: 74CMS 最新版二次SQL注入可越权操作两处(问题总结)

相关厂商: 74cms.com

漏洞作者: xfkxfk认证白帽子

提交时间: 2014-09-03 15:30

公开时间: 2014-12-02 15:32

漏洞类型: SQL注射漏洞

危害等级: 高

自评Rank: 20

漏洞状态: 厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 help@wooyun.org

Tags标签: 第三方不可信程序 安全意识不足 php源码审核 php源码分析 安全意识不足

1人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-09-03: 细节已通知厂商并且等待厂商处理中
2014-09-03: 厂商已经确认,细节仅向厂商公开
2014-09-06: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航无声信息
2014-10-28: 细节向核心白帽子及相关领域专家公开
2014-11-07: 细节向普通白帽子公开
2014-11-17: 细节向实习白帽子公开
2014-12-02: 细节向公众公开

简要描述:

74CMS 最新版二次SQL注入可越权操作两处

详细说明:

74CMS最新版:74cms_v3.4.20140820 官方8.20号更新



文件:/user/personal/personal_resume.php

code 区域
//创建简历 -保存基本信息
elseif ($act=='make1_save')
{
$captcha=get_cache('captcha');
$postcaptcha = trim($_POST['postcaptcha']);
if($captcha['verify_resume']=='1' && empty($postcaptcha) && intval($_REQUEST['pid'])===0)
{
showmsg("请填写验证码",1);
}
if ($captcha['verify_resume']=='1' && intval($_REQUEST['pid'])===0 && strcasecmp($_SESSION['imageCaptcha_content'],$postcaptcha)!=0)
{
showmsg("验证码错误",1);
}
$setsqlarr['uid']=intval($_SESSION['uid']);
$setsqlarr['title']=trim($_POST['title'])?trim($_POST['title']):showmsg('请填写简历名称!',1);
$setsqlarr['fullname']=trim($_POST['fullname'])?trim($_POST['fullname']):showmsg('请填写姓名!',1);
$setsqlarr['sex']=trim($_POST['sex'])?intval($_POST['sex']):showmsg('请选择性别!',1);
$setsqlarr['sex_cn']=trim($_POST['sex_cn']);
$setsqlarr['birthdate']=intval($_POST['birthdate'])>1945?intval($_POST['birthdate']):showmsg('请正确填写出生年份',1);
$setsqlarr['height']=intval($_POST['height']);
$setsqlarr['marriage']=intval($_POST['marriage']);
$setsqlarr['marriage_cn']=trim($_POST['marriage_cn']);
$setsqlarr['experience']=intval($_POST['experience']);
$setsqlarr['experience_cn']=trim($_POST['experience_cn']);
$setsqlarr['householdaddress']=trim($_POST['householdaddress'])?trim($_POST['householdaddress']):showmsg('请填写户口所在地!',1);
$setsqlarr['education']=intval($_POST['education']);
$setsqlarr['education_cn']=trim($_POST['education_cn']);
$setsqlarr['tag']=trim($_POST['tag']);
$setsqlarr['telephone']=trim($_POST['telephone'])?trim($_POST['telephone']):showmsg('请填写联系电话!',1);
$setsqlarr['email']=$user['email'];
$setsqlarr['email_notify']=$_POST['email_notify']=="1"?1:0;
$setsqlarr['address']=trim($_POST['address'])?trim($_POST['address']):showmsg('请填写通讯地址!',1);
$setsqlarr['website']=trim($_POST['website']);
$setsqlarr['qq']=trim($_POST['qq']);
$setsqlarr['refreshtime']=$timestamp;
$setsqlarr['subsite_id']=intval($_CFG['subsite_id']);
$setsqlarr['display_name']=intval($_CFG['resume_privacy']);
if (intval($_REQUEST['pid'])===0)
{
$setsqlarr['audit']=intval($_CFG['audit_resume']);
$total[0]=$db->get_total("SELECT COUNT(*) AS num FROM ".table('resume')." WHERE uid='{$_SESSION['uid']}'");
$total[1]=$db->get_total("SELECT COUNT(*) AS num FROM ".table('resume_tmp')." WHERE uid='{$_SESSION['uid']}'");
$total[2]=$total[0]+$total[1];
if ($total[2]>=intval($_CFG['resume_max']))
{
showmsg("您最多可以创建{$_CFG['resume_max']} 份简历,已经超出了最大限制!",1);
}
else
{
$setsqlarr['addtime']=$timestamp;
$pid=inserttable(table('resume'),$setsqlarr,1);
if (empty($pid))showmsg("保存失败!",0);
check_resume($_SESSION['uid'],$pid);
write_memberslog($_SESSION['uid'],2,1101,$_SESSION['username'],"创建了简历");
header("Location: ?act=make2&pid=".$pid);
}
}
else
{
$_CFG['audit_edit_resume']!="-1"?$setsqlarr['audit']=intval($_CFG['audit_edit_resume']):"";
updatetable(table('resume'),$setsqlarr," id='".intval($_REQUEST['pid'])."' AND uid='{$setsqlarr['uid']}'");
updatetable(table('resume_tmp'),$setsqlarr," id='".intval($_REQUEST['pid'])."' AND uid='{$setsqlarr['uid']}'");
check_resume($_SESSION['uid'],intval($_REQUEST['pid']));
write_memberslog($_SESSION['uid'],2,1105,$_SESSION['username'],"修改了简历({$_POST['title']})");
if ($_POST['go_resume_show'])
{
header("Location: ?act=resume_show&pid={$_REQUEST['pid']}");
}
else
{
header("Location: ?act=make2&pid={$_REQUEST['pid']}");
}
}
}



在创建建立第一步时:

将fullname,education_cn等信息通过函数inserttable插入数据库

在进入数据库时,通过转义,但是进入数据库存储时,依然可带入单引号等而已SQL语句



在inserttable进入数据库后

通过check_resume函数检测简历完成程度:

code 区域
//检查简历的完成程度
function check_resume($uid,$pid)
{
global $db,$timestamp,$_CFG;
$uid=intval($uid);
$pid=intval($pid);
$percent=0;
$resume_basic=get_resume_basic($uid,$pid);
$resume_intention=$resume_basic['intention_jobs'];
$resume_specialty=$resume_basic['specialty'];
$resume_education=get_resume_education($uid,$pid);
if (!empty($resume_basic))$percent=$percent+15;
if (!empty($resume_intention))$percent=$percent+15;
if (!empty($resume_specialty))$percent=$percent+15;
if (!empty($resume_education))$percent=$percent+15;
if ($resume_basic['photo_img'] && $resume_basic['photo_audit']=="1" && $resume_basic['photo_display']=="1")
{
$setsqlarr['photo']=1;
}
else
{
$setsqlarr['photo']=0;
}
if ($percent<60)
{
$setsqlarr['complete_percent']=$percent;
$setsqlarr['complete']=2;
}
else
{
$resume_work=get_resume_work($uid,$pid);
$resume_training=get_resume_training($uid,$pid);
$resume_photo=$resume_basic['photo_img'];
if (!empty($resume_work))$percent=$percent+13;
if (!empty($resume_training))$percent=$percent+13;
if (!empty($resume_photo))$percent=$percent+14;
$setsqlarr['complete']=1;
$setsqlarr['complete_percent']=$percent;
require_once(QISHI_ROOT_PATH.'include/splitword.class.php');
$sp = new SPWord();
$setsqlarr['key']=$resume_basic['intention_jobs'].$resume_basic['recentjobs'].$resume_basic['specialty'];
$setsqlarr['key']="{$resume_basic['fullname']} ".$sp->extracttag($setsqlarr['key']);
$setsqlarr['key']=str_replace(","," ",$resume_basic['intention_jobs'])." {$setsqlarr['key']} {$resume_basic['education_cn']}";
$setsqlarr['key']=$sp->pad($setsqlarr['key']);
if (!empty($resume_education))
{
foreach($resume_education as $li)
{
$setsqlarr['key']="{$li['school']} {$setsqlarr['key']} {$li['speciality']}";
}
}
$setsqlarr['refreshtime']=$timestamp;
}



通过:

code 区域
$resume_basic=get_resume_basic($uid,$pid);



取出已经进入简历中的信息

看这里:

code 区域
$setsqlarr['key']="{$resume_basic['fullname']} ".$sp->extracttag($setsqlarr['key']);
$setsqlarr['key']=str_replace(","," ",$resume_basic['intention_jobs'])." {$setsqlarr['key']} {$resume_basic['education_cn']}";



fullname,education_cn两个参数被取出来后进入了setsqlarr变量

最后进入了updatetable函数:

code 区域
updatetable(table('resume'),$setsqlarr,"uid='{$uid}' AND id='{$pid}'");



进入updatetable函数:

code 区域
function updatetable($tablename, $setsqlarr, $wheresqlarr, $silent=0) {
global $db;
$setsql = $comma = '';
foreach ($setsqlarr as $set_key => $set_value) {
if(is_array($set_value)) {
$setsql .= $comma.'`'.$set_key.'`'.'='.$set_value[0];
} else {
$setsql .= $comma.'`'.$set_key.'`'.'=\''.$set_value.'\'';
}
$comma = ', ';
}
$where = $comma = '';
if(empty($wheresqlarr)) {
$where = '1';
} elseif(is_array($wheresqlarr)) {
foreach ($wheresqlarr as $key => $value) {
$where .= $comma.'`'.$key.'`'.'=\''.$value.'\'';
$comma = ' AND ';
}
} else {
$where = $wheresqlarr;
}
return $db->query("UPDATE ".($tablename)." SET ".$setsql." WHERE ".$where, $silent?"SILENT":"");
}



在updatetable中,没有对数据进行任何的处理专业过滤等

到这里我们第一步进入数据库的恶意SQL,在这里再次进去了数据库,导致SQL注入



因为在update中,所以我们想更新简历中那个信息都可以



================================================================================

我们来总结下:

前面发了很多二次注入的漏洞,都是在建立简历这里,基本都在updatetable这个函数里面产生的

1、首先74cms在获取内容后通过转义过滤处理,进入了数据库

2、进入书库时使用inserttable,这里没有任何过滤处理,导致单引号进入数据库

3、在其他地方取出简历数据,通过get_resume_***函数,也没有处理,此时单引号已经产生了

4、在其他地方进行修复内容时,再次使用inserttable和updatetable,由于没有过滤,导致二次SQL注入

code 区域
foreach ($setsqlarr as $set_key => $set_value) {
if(is_array($set_value)) {
$setsql .= $comma.'`'.$set_key.'`'.'='.$set_value[0];
} else {
$setsql .= $comma.'`'.$set_key.'`'.'=\''.$set_value.'\'';
}
$comma = ', ';
}



当进入updatetable是数组时,这里直接去数组的元素,连引号都没有,直接注入,如:

http://**.**.**.**/bugs/wooyun-2014-071571

当非数组是,使用单引号保护,这里产生了二次注入,如:

http://**.**.**.**/bugs/wooyun-2014-074521

http://**.**.**.**/bugs/wooyun-2014-068362

等,所以这里修复已经很简单了

当然还有inserttable函数里面也需要处理

漏洞证明:

这里存在两个参数fullname,education_cn

由于fullname在数据库中限制长度为15,太短了

这里我们使用education_cn为测试

因为在update中,所以我们想更新简历中那个信息都可以,这里选择最短的QQ

在第一步,创建基本信息时:

在学历处填写:',qq=concat(user(),version())#

111.png



然后继续完善简历信息,直到第四步,填写完教育信息时,返回预览自己的建立

qq联系方式即被修改为user和version的值

222.png





由于这里可以任意截断,所以可以修改任意用户的联系方式等简历信息

这里我们没有添加where条件,那么即可修改全部用户的QQ联系方式

修复方案:

进入数据库时,再次转义过滤

版权声明:转载请注明来源 xfkxfk@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:20

确认时间:2014-09-03 16:02

厂商回复:

感谢反馈!

最新状态:

暂无


漏洞评价:

对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值

漏洞评价(共0人评价):
登陆后才能进行评分

评价

  1. 2014-12-02 15:41 | no.thinking ( 路人 | Rank:4 漏洞数:3 | no.thinking)
    2

    传说中的审计?

  2. 2014-12-02 17:09 | 带我玩 ( 路人 | Rank:16 漏洞数:8 | 带我玩)
    2

    好黑..表示看的云里雾里

登录后才能发表评论,请先 登录