实际上并没有得出什么重大的结论,不过最后的总结还能看看

[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);

image-20210605012956722

可以发现,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));

image-20210605020343064

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);

image-20210605023146459

写着写着感觉不到写它的意义了,而且好费脑子(让我原本就不聪明的脑子雪上加霜)

后面目前能想到的是,负大数那边的十六进制数组赋值不是有些奇怪的现象嘛,然后用十进制赋值,再写一段,然后总结规律什么的吧,不过想不到写这些的意义,没动力写下去了,趴

下面是让我想要写这篇博客的起因,越界后发现了某种新的数据类型

界内开始,使用三方法自动下标+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);

image-20210605031029452

明白了,以上全在瞎扯,直接看总结就好

这里贴上越界后的数组表达,感觉像是越界后,在那一段数组内存外紧邻的地方也存入了数据,系统也把它当作数组了,要访问似乎只能通过这个越界的数组,试一下

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]);

image-20210605031512329

破案了,越界后,继续使用自动下标的话,是从头开始重新赋值,并不是内存外面的东西

所以说,这里唯一能用到的漏洞,就是自动下标给越界后的数组赋值时,会产生赋值失败,从而返回值为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处溢出的

该版本下的具体范围我之后再测试吧,这里先挖个坑

以上