【PHP数组key溢出】2021.6.1萌新赛 easy_web WriteUp

本文最后更新于:2021年8月18日下午1点46分

题目描述:

image-20210603192836796

前景知识:

PHP数组key溢出:

通过PHP创建关联数组的时候,键值Key如果是数值型(可通过is_numeric()判断),则会在int有效范围内被自动转换为int型,如果超过int有效范围就会有问题,这就涉及到数组键值Key作为int型时的有效范围判断。

PHP的int型数据取值范围,与操作系统相关,32位系统上为2的31次方,即-2147483648到2147483647,64位系统上为2的63次方,即-9223372036854775808到9223372036854775807。

这里给一段测试代码,测试环境如下

image-20210603201210058

首先测试一个普通情况,利用array函数创建一个数组

1
2
3
php > $arr = array( 1 => 'test1');
php > var_dump($arr[]=1);
int(1)

image-20210603201333128

这个时候往数组里插入一个值,然后使用var_dump一下,发现是可以正常返回的

但是,当我们的数组下标key足够大的时候,例如以下情况

1
2
3
4
php > $arr = array( 9223372036854775807 => 'test1' , 9223372036854775808 =>'test2');
php > var_dump($arr[]=1);
PHP Warning: Cannot add element to the array as the next element is already occupied in php shell code on line 1
NULL

image-20210603201600147

可以看到,这个时候php报warning了,因为数组下标达到了9223372036854775807,这个时候想要再往里面插入元素,就会报错,var_dump之后返回一个NULL

而这一点正是这道题最内层if语句的考点

1
2
3
4
5
6
7
8
9
if($array[++$c]=1){
if($array[]=1){
echo "nonono";
}
else{
require_once 'flag.php';
echo $flag;
}
}

这时候往里c传参9223372036854775806,要比上面提到的那个数字小1

因为题目是++$c,先加上1,变成9223372036854775807,然后执行最内层的if时,因为没有地方可以开数组了,就返回NULL,即false,这样一来就可以执行else里的语句

WriteUp:

打开题目,是一道php+html的代码审计:

image-20210603193218996

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>EasyWeb</title>

<style>
html, body {
margin: 0;
padding: 0;

width: 100%;
height: 100%;

text-align: center;
}

body {
background-image: url(backImg.jpg);
background-size: contain;
}

.content {
width: 80%;
text-align: left;
padding : 10px;
margin: 0 auto;
background-color: rgb(255, 255, 255, 0.7);
}
</style>
</head>
<body>
<h1>EasyWeb</h1>
<div class="content">
<?php highlight_file("index.php") ?>
</div>
<div class="content">
<?php
$six_number = $_POST['webp'];
$a = $_POST['a'];
$b = $_POST['b'];
$c = $_POST['c'];
if (md5($six_number) == 'e10adc3949ba59abbe56e057f20f883e' && md5($a) === md5($b) && $a !== $b) {
if($array[++$c]=1){
if($array[]=1){
echo "nonono";
}
else{
require_once 'flag.php';
echo $flag;
}
}
}
?>
</div>
</body>
</html>

其中php部分的关键代码先拎出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$six_number = $_POST['webp'];
$a = $_POST['a'];
$b = $_POST['b'];
$c = $_POST['c'];
if (md5($six_number) == 'e10adc3949ba59abbe56e057f20f883e' && md5($a) === md5($b) && $a !== $b) {
if($array[++$c]=1){
if($array[]=1){
echo "nonono";
}
else{
require_once 'flag.php';
echo $flag;
}
}
}
?>

可以看到,该php代码中,要求我们POST传递4个参数:webpabc

先看第一层if语句,首先一个变量md5加密后为e10adc3949ba59abbe56e057f20f883e

image-20210603193642736

所以对webp传参123456

再看$a$b,是常见的md5绕过,可以用数组绕过,也可以用0e\d+的形式绕过

因此对aa[]=1,对bb[]=2

根据前景知识,对c参数传9223372036854775806即可绕过最内层if语句

整一个传参如下:

image-20210603200634610

然而题目并没有直接给我们flag,只是给了一个密码:xluoyyds123456@@@

结合题目给的hint,说是背景图片上可能有我们想要的东西

于是我们下载图片,给了一个密码,猜测是一个压缩包

image-20210603200830606

.jpg后缀改成.zip后缀

image-20210603200853202

可以看到,压缩包可以正常被打开

输入网页中给的密码xluoyyds123456@@@

image-20210603200938528

得到flagnewsctf{this_1s_veryveryveryeasyweb}

我愿称之为套神,web+misc杂交的鼻祖


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!