影风博客

代码审计的艺术系列 第十二篇

2019-03-23

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-32768md5值,然后使用burpFuzz就能重置任意用户密码了。

生成1-32768md5脚本如下:

$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可控,最终就可以构造找回密码链接来重置任意账户密码了。

使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章