0x00 背景
最近国外漏洞披露平台hackerone
上,一位白帽子发现Uber
一修改任意账户密码的逻辑漏洞,获得了$10000
美元的奖励。
所以如何通过代码审计来挖掘逻辑漏洞是安全研究人员和开发人员必须熟悉的技能。本篇将主要讲解代码审计中的任意用户密码重置的安全问题。
找回密码的原理:验证auth
,在找回密码的时候生成一个auth
,然后存储到数据库中,然后把找回密码的地址发到邮箱中url
中就含有auth
,由用户点开后就能修改密码。
0x01 rand函数生成auth
Windows
环境下rand()
最大值为32768
,所以可被穷举,使用不当就会出现漏洞。之前出现过的漏洞代码如下:
<?php
...
$resetpwd = md5(rand());
$new['user']->update('user',array(
'email'=>$email,
),array(
'resetpwd'=>$resetpwd,
));
//发送邮件
$subject = $TS_SITE['base']['site_title'].'会员密码找回';
$content = '您的登陆信息:<br />Email:'.$email.
'<br />重设密码链接:<br /><a href="'.$TS_SITE['base']['site_url'].
'index.php?app=user&ac=resetpwd&mail='.$email.'&set='.$resetpwd.'">'.$TS_SITE['base']['site_url'].'index.php?app=user&ac=resetpwd&mail='.$email.'&set='.$resetpwd.'</a>';
echo $content;
可以看到重置密码链接就两个参数,一个是邮箱另外一个就是auth
,然而auth
是使用了rand()
函数生成的,所以我们可以写个脚本生成1-32768
的md5
值,然后使用burp
来Fuzz
就能重置任意用户密码了。
生成1-32768
的md5
脚本如下:
$a=0;
for ($a=0;$a<=32768;$a){
$b = md5(++$a);
echo "\r\n";
echo $b;
}
之后我们以重置管理员admin@admin.com
的密码为例,首先抓重置密码的包并设置好变量如下:
然后爆破resetpwd
这个auth
字段,发现爆破成功!
0x02 auth过于简单可被猜解
跟与rand
函数生成auth
类似,这里讲另外一个auth
生成的算法,算法中的Key
没初始化导致的可被枚举,缺陷代码如下:
else{
$timetemp=date("Y-m-d H:i:s",$this->time);
$auth = util::strcode($timetemp, 'ENCODE');
$verification= rand(1000,9999);
$encryptstring=md5($this->time.$verification.$auth);
$reseturl=WIKI_URL."/index.php?user-getpass-".$user['uid'].'-'.$encryptstring;
$_ENV['user']->update_getpass($user['uid'],$encryptstring);
$mail_subject = $this->setting['site_name'].$this->view->lang['getPass'];
$mail_message = $this->view->lang['resetPassMs1'].$user['username'].$this->view->lang['resetPassMs2'].$timetemp.$this->view->lang['resetPassMs3']."<a href='".$reseturl."' target='_blank'>".$reseturl."</a>".$this->view->lang['resetPassMs4'].$this->setting['site_name'].$this->view->lang['resetPassMs5'].$this->setting['site_name'].$this->view->lang['resetPassMs6'];
$this->load('mail');
$_ENV['mail']->add(array(), array($email), $mail_subject, $mail_message, '', 1, 0);
$this->message($this->view->lang['emailSucess'],'index.php?user-login',0);
}
简单分析可知这里重置密码的auth
是由
$encryptstring=md5($this->time.$verification.$auth);
这段代码生成的,而$this->time
和$vertification=rand(1000,9999)
都是可控的变量(一个是当前时间的时间戳、一个是1000-9999
随机的数值,这两个可以生成一个字典),所以关键点就是$auth
,我们跟进strcode
函数
function strcode($string,$action='ENCODE'){
$key = substr(md5($_SERVER["HTTP_USER_AGENT"].PP_KEY),8,18);
$string = $action == 'ENCODE' ? $string : base64_decode($string);
$len = strlen($key);
$code = '';
for($i=0; $i < strlen($string); $i++){
$k = $i % $len;
$code .= $string[$i] ^ $key[$k];
}
$code = $action == 'DECODE' ? $code : base64_encode($code);
return $code;
}
函数里有个$key
是生成$auth
的关键,而$key
是对一个字符串进行了截取操作,里面$_SERVER["HTTP_USER_AGENT"]
是我们请求header
里的user_agent
变量,用户可控,后面有个PP_KEY
居然没有初始化,至此$key
也可控了即$auth
可控,最终就可以构造找回密码链接来重置任意账户密码了。
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
扫描二维码,分享此文章