实际上并没有得出什么重大的结论,不过最后的总结还能看看
[TOC]
PHP_数组溢出分析
环境:php 7.4.0
首先,在一个64位系统中,php里面int型变量所占内存大小为64位,其中最前面的一位为符号位,0表示正数,1表示负数
本文全程以64位系统中的php着手,从比特位的角度分析,为了便于表示,本文大部分将采用0x数,即16进制数表示,即
0x7FFFFFFFFFFFFFFF(16进制) == 9223372036854775807(10进制) == 0b0111111111111111111111111111111111111111111111111111111111111111(二进制)
0x000000000000000A(16进制) == 10(10进制) == 0b0000000000000000000000000000000000000000000000000000000000001010(二进制)
int变量测试
一个int型的变量的字节表示从全0开始,每次加一,即对应的十进制大小加一,即
正,大数:
$num1=0x7FFFFFFFFFFFFFFA;
var_dump($num1);
$num1++;
var_dump($num1);
负数,十六进制:
echo(dechex(-9223372036854775806)); //该数为int型边界值减二
var_dump(-9223372036854775806);
-2测试:
echo(dechex(-2));
var_dump(0xFFFFFFFFFFFFFFFE); //对照代码执行图,php将-2十六进制解析为0xFFFFFFFFFFFFFFFE
$num2=0xFFFFFFFFFFFFFFFE; //却将0xFFFFFFFFFFFFFFFE解析为浮点型
var_dump($num2);
负数,十进制:
$num2=-9223372036854775806;
var_dump($num2);
$num2++;
var_dump($num2);

可以发现,php中,在未到达边界时,正数十六进制形式赋值正常,自增正常增加;而负数十六进制赋值时判断为浮点型,十进制赋值时正常,自增时绝对值正常减小。
而,当遇到边界时:
正数边界:
$num1=0x7FFFFFFFFFFFFFFF;
var_dump($num1);
$num1++;
echo(dechex($num1));
var_dump($num1);
负数边界:
$num2=-9223372036854775807;
echo(dechex($num2));
var_dump($num2);
$num2--;
echo(dechex($num2));
var_dump($num2);
$num2--;
echo(dechex($num2));
var_dump($num2);
$num2--;
echo(dechex($num2));
var_dump($num2);
普通浮点型数据dechex()函数测试:
$num3=1.3;
echo(dechex($num3));

int类型数据的表示范围为0x7FFFFFFFFFFFFFFF8000000000000000,即-92233720368547758089223372036854775807
数组测试
首先,php数组类型的变量有一下几种赋值方法:
$变量名=array(数组元素); 数组元素可以是任意数据类型
$变量名=[数组元素]; 数组元素的顺序以放入顺序为准,跟下标无关
$变量名[ ]=单个数组元素; 随着每次定义,下表逐渐加一
$变量名[下标]=单个数组元素值; 若前面规定了下标,则新定义的默认下标从最大值加一
下文中主要用到3,4类型的赋值方式,即自动下标+1赋值与手动定义下标赋值
正,大数:
$num2[0x7FFFFFFFFFFFFFFE]=2;
var_dump($num2);
$num2[]=2;
var_dump($num2);
负,小数,十六进制:
$num4[0xfffffffffffffc00]=2;
var_dump($num4);
$num4[]=2;
var_dump($num4);
$num5[0xfffffffffffff000]=2;
var_dump($num5);
$num5[]=2;
var_dump($num5);
echo(dechex(-4096));
负,大数,十六进制:
$num1[0x8000000000000001]=2;
var_dump($num1);
$num1[]=2;
var_dump($num1);
$num3[0x8000000FF0000001]=2;
var_dump($num3);
$num3[]=2;
var_dump($num3);

写着写着感觉不到写它的意义了,而且好费脑子(让我原本就不聪明的脑子雪上加霜)
后面目前能想到的是,负大数那边的十六进制数组赋值不是有些奇怪的现象嘛,然后用十进制赋值,再写一段,然后总结规律什么的吧,不过想不到写这些的意义,没动力写下去了,趴
下面是让我想要写这篇博客的起因,越界后发现了某种新的数据类型
界内开始,使用三方法自动下标+1;
$array[9223372036854775806]=2;
print_r($array);
var_dump($array[]=2);
print_r($array);
@var_dump($array[]=2); //这里发生越界,赋值失败
print_r($array);
unset($array);
界内开始,使用四方法手动规定下表
var_dump($array[9223372036854775806]=2);print_r($array);
var_dump($array[9223372036854775807]=2);print_r($array);
var_dump($array[9223372036854775808]=2);print_r($array);
var_dump($array[9223372036854775809]=2);print_r($array);
var_dump($array[]=2);print_r($array);
var_dump($array[]=2);print_r($array);
unset($array);
界上开始,四方法手动顶下标,未果,再使用三方法自动
var_dump($array[9223372036854775808]=2);print_r($array);
var_dump($array[9223372036854775809]=2);print_r($array);
var_dump($array[]=2);print_r($array);
var_dump($array[]=2);print_r($array);
var_dump($array[]=2);print_r($array);

明白了,以上全在瞎扯,直接看总结就好
这里贴上越界后的数组表达,感觉像是越界后,在那一段数组内存外紧邻的地方也存入了数据,系统也把它当作数组了,要访问似乎只能通过这个越界的数组,试一下
var_dump($array[9223372036854775808]=2);print_r($array);
var_dump($array[9223372036854775809]=2);print_r($array);
var_dump($array[]=3);print_r($array);
var_dump($array[]=4);print_r($array);
var_dump($array[]=5);print_r($array);
print_r('<br>'.$array[0]);
print_r('<br>'.$array[1]);
print_r('<br>'.$array[2]);

破案了,越界后,继续使用自动下标的话,是从头开始重新赋值,并不是内存外面的东西
所以说,这里唯一能用到的漏洞,就是自动下标给越界后的数组赋值时,会产生赋值失败,从而返回值为null,判断为假,用来绕过某些ctf_web题
即
$array[9223372036854775807]=2;
var_dump($array[]=3);
上一次给(2^63-1)位置赋值后,下一次自动赋值时,会给(2^63位置)赋值,从而产生错误(必须是自动赋值,即**$array[]=x;**的赋值方式)
总结
自动下标给越界位置的数组下标赋值时,会产生赋值失败,从而返回值为null,判断为假,用来绕过某些ctf_web题
用法:
$array[9223372036854775807]=2;
var_dump($array[]=3);
其中,变量名任意,赋值内容任填,上一次给(2^63-1)位置赋值后,下一次自动赋值时,会给(2^63位置)赋值,从而赋值失败,返回null
(必须是自动赋值,即【$array[]=x;】的赋值方式)
2021年6月6日新增:
今天复现一个例题的过程中,偶然间发现,5.6.40版本的php在2^63 -2的时候就已经把变量识别为浮点型float,而2^31+1时仍是整形,版本改为7.4.0后是在2^63处溢出的
该版本下的具体范围我之后再测试吧,这里先挖个坑
以上
- 本文链接:http://github.winxcloud.top/2021/06/PHP_%E6%95%B0%E7%BB%84%E6%BA%A2%E5%87%BA%E5%88%86%E6%9E%90/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。