漏洞简介
漏洞标题:Different arrays compare indentical due to integer key truncation
漏洞来源:https://bugs.php.net/bug.php?id=69892
影响组件: PHP
影响版本:5.4.0 - 5.4.43, 5.5.0 - 5.5.26, 5.6.0 - 5.6.10, 7.0.0alpha1
实例说明
题目
用一个 CTF 题目来说明一下上面的这个漏洞,顺便记录下 CTF 的一类题
1 | <?php |
说明:
题目中 xxoo 部分,原题是 (string)time.time(), 为了方便,我就直接改成一个字符串了。
题目分析
我们直接看后面 if 中的判断逻辑:
- $_GET[‘user’] 是一个全局的变量,我们传的是字符串,它就是字符串,传的是数组,那么它的值就是数组
- $user 是一个数组, [0 => ‘admin’, 1 => ‘xxoo’]
- === 三个等号的意思就是类型是同一类型,并且值也是相同的
- $_GET[‘user’][0] 的值不能等于 ‘admin’
也就是说,如果要使这个 if 条件成立,就必须让两个键值不相等的数组经过 === 比较后返回 true。
然后我们测试:
1 | ➜ ~ php -r "var_dump([1=>0]==[1=>0]);" |
这不扯淡嘛!
然后我们再来看我们这个漏洞:
1 | ➜ ~ php -r "var_dump([0 => 0] === [0x100000000 => 0]);" |
键名为 0 的数组与键名为 0x100000000 的数组居然相等了!
也就是说:
1 | $user : [0 =>'admin', 1=>'xxoo']; |
这样就能使题目当中的条件成立。
构造 payload :
1 | http://localhost/test.php?user[1]=xxoo&user[4294967296]=admin |
2^32 == 0x100000000 == 4294967296
由于是截断漏洞,所以 0x100000000 后面再多几个 0 也是可以的,适当转换成对应的 10 进制数就好
测试结果
发现在 32 位 PHP 上并不能成功,只能在 64 位 PHP 上测试成功。
于是我们使用 var_dump
来看一下每个变量:
代码如下:
1 | <?php |
在 32 位 PHP 上测试结果:
在 64 位 PHP 上测试结果:
结果一目了然,32位 PHP 上,会把 user[4294967296] 中的 4294967296 在接收后解析成字符串,而 64 位则会解析成整数。
扩展知识
如何判断自己的 PHP 是 32 位还是 64 位的?
1 | <?php |