Lua/PHP哈希碰撞攻击浅析

4,010 views

Published on

0 Comments
14 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,010
On SlideShare
0
From Embeds
0
Number of Embeds
21
Actions
Shares
0
Downloads
65
Comments
0
Likes
14
Embeds 0
No embeds

No notes for slide

Lua/PHP哈希碰撞攻击浅析

  1. 1. Lua/PHP 哈希碰撞攻击浅析 chaoslawful
  2. 2. 哈希表的构成● 哈希函数:H:key->int● 数据存储结构 ○ 线性分桶 ○ 平衡树 ○ ...● 碰撞处理策略 ○ 二次探查 ○ 拉链 ○ ...
  3. 3. 哈希碰撞攻击● 哈希表插入/查找效率同哈希值碰撞概率直接相关● 攻击方式: ○ 针对哈希函数H构造特殊key序列,使序列中所有key的哈 希值相同,即碰撞概率p=1 ○ 对于拉链法,相当于每次插入都要全量遍历链表,循环次 数为0+1+2+...+(n-1)=n(n-1)/2,时间复杂度O(n^2) ○ 插入64K个哈希值相同的key-val对的总循环次数为4G次
  4. 4. PHP中哈希表实现 ● PHP数组为纯哈希表,无通常意义上的数组部分,所有下标 均作为key看待,使用按哈希值取模分桶方式保存键值对,靠 外部拉链解决冲突 ● 初始分桶数为2^3=8个(_zend_hash_init@zend_hash.c:140) ● 插入哈希表的键值对数量超过分桶数后,分桶数量倍增并将 所有键值对重新哈希(zend_hash_do_resize@zend_hash.c:418) ● 数值(或数值字符串)下标的key被转换成整数后直接当作哈 希值使用,即H(n)=n(爆破点!)zend_fetch_dimension_address (zend_execute.c:903)->zend_fetch_dimension_address_inner (zend_execute.c:798) ->zend_symtable_update (zend_hash.h:343)->(由 ZEND_HANDLE_NUMERIC 判定为类数值字符串后)->_zend_hash_index_update_or_next_insert (zend_hash.c:350)
  5. 5. PHP中哈希表实现 ● 字符串哈希算法为DJBX33A,全部键内容均参与哈希值计算 (zend_inline_hash_func@zend_hash.h:261) ● 制造哈希碰撞稍微复杂一些,但并非不可能!zend_fetch_dimension_address (zend_execute.c:903)->zend_fetch_dimension_address_inner (zend_execute.c:798) ->zend_symtable_update (zend_hash.h:343)->(由 ZEND_HANDLE_NUMERIC 判定 为非数值字符串后)->_zend_hash_add_or_update (zend_hash.c:203)
  6. 6. PHP哈希碰撞攻击原理● PHP处理请求时会将uri参数、POST请求体和cookie的全部内 容分别解析到特殊PHP数组$_GET、$_POST和$_COOKIE 中 (php_default_treat_data@php_variables.c:292、php_content_types.c:30、 ) php_std_post_handler@php_variables.c:249● 在uri参数、POST请求体或cookie中构造从x开始的n个整数 key,依次递增步长m(m为2^N,N>=log2(n)),即可保证解 析时n个key都落在一个桶内,实现哈希值完全碰撞● 例如:n=64k,m=2^16
  7. 7. PHP哈希碰撞示例<?php$k = pow(2, 16);$max = $k*($k-1);$array = array();for($i=0; $i<=$max; $i+=$k) {    $array[$i] = 0;}?>
  8. 8. Lua中哈希表实现● Lua table同时包含数组和哈希表部分,数值下标先在数组部 分查找再到哈希表中查找● 以哈希值取模分桶方式保存键值对,靠内部拉链解决冲突● 哈希表部分初始分桶数为0(luaH_new@ltable.c:358)● 插入哈希表的键值对数量超过分桶数后,分桶数量倍增并将 所有键值对重新哈希(rehash@ltable.c:333)● 数值、userdata、table等下标对(n-1)取模,字符串、 boolean等下标对n取模(n为分桶数,总是2^k形式) (mainposition@ltable.c:100)● 数值下标(通常为double型)按32bit分为多段后累加得到哈 希值(hashnum@ltable.c:84)(结合之前的取模规则较难攻击)
  9. 9. Lua中哈希● 由于Lua中字符串是常量,故字符串下标创建时即计算出了对 应的哈希值(luaS_newlstr@lstring.c:75)● 计算字符串哈希值时,并非字符串全部内容都参与计算,而 是从后向前按步长k抽出字符计算,这里k=(L>>5)+1(爆破 点!)● 为节约存储空间,Lua将所有字符串加入全局字符串哈希表, 新建字符串若已在全局表中存在则不再创建新实例。全局字 符串表为分桶外部拉链结构,分桶数量按需倍增,按字符串哈 希值对分桶数量取模进行分桶(另一个爆破点!)
  10. 10. Lua潜在的哈希碰撞攻击● 利用字符串哈希值计算只挑选特定位置字符的特点,保证这 些位置上的字符不变构造不同的字符串,使其哈希值完全相 同,实现完全碰撞。● 例如:固定字符串长度为32,保证从尾部向前每间隔1个字符 都相同。 ○ x00 x11 x00 ... x00 x11 x00 ○ x00 x22 x00 ... x00 x22 x00 ○ x00 x33 x00 ... x00 x33 x00 ○ ...
  11. 11. Lua哈希碰撞示例local t = {}                                                                    local n = 10000                                                                 for i = 1, n do                                                                     local s = (0):rep(28) .. string.char(i/255) .. 0 ..string.char(i%255) .. 0    -- global string table colliding                                                t[#t+1] = s                                                                     -- global string table and table hash colliding                            --t[s] = 1                                                                  end                                                                             
  12. 12. LuaJIT中哈希表实现● 基本哈希表实现同Lua一致● 所有类型下标哈希值的取模范围都是分桶数量(总是2^k形 式)● 数值下标分为高低32bit两段后进行旋转移位计算哈希值 (hashnum@lj_tab.c:30)● 字符串下标按照其长度范围选择不同部分内容进行计算,对 于长度>=4的字符串,只有4部分内容参与计算: (lj_str_new@lj_str.c:106)(爆破点!) ○ 头部4字节 ○ 1/4处4字节(以(L>>2)-1为起始位置)  ○ 中间4字节(以(L>>1)-2为起始位置)  ○ 尾部4字节
  13. 13. LuaJIT中哈希表实现● 全局字符串查找表的构造同Lua类似(另一个爆破点!)● 碰撞攻击原理同Lua一样:保证字符串下标参与哈希值计算的 部分相同● 例如:固定字符串长度为32,则参与哈希值运算的位置范围为 [0,3]、[7,10]、[14,17]、[28,31],保证该范围内字符一致 即可
  14. 14. LuaJIT哈希碰撞示例local t = {}                                                                    local n = 10000                                                                 for i = 1, n do                                                                     local s = (0):rep(18) .. string.char(i/255) .. string.char(i%255) .. (0):rep(12)    -- global string table colliding                                                t[#t+1] = s                                                                     -- global string table and table hash colliding                            --t[s] = 1                                                                  end
  15. 15. 解决方案● 绝不要无条件解析完整的用户请求!最好以迭代方式惰性解 析● 限制HTTP请求中出现的参数数量为较小的值(PHP>=5.3.9)● 熟悉自己所用语言的数据结构实现细节!
  16. 16. Thanks! Q&A

×