finfo_open函数和finfo_file函数绕过以及getimagesize函数绕过

finfo_open函数和finfo_file函数绕过以及getimagesize函数绕过

finfo_open和finfo_file

finfo_open()是创建一个fileinfo资源的,而finfo_file()是返回一个文件信息的,而一般它们两个函数结合着用。如:

1
2
$finfo = finfo_open(FILEINFO_MIME_TYPE); //创建资源类型
$type = finfo_file($finfo, $_FILES['file']['tmp_name']); //返回文件的类型

getimagesiza()

getimagesiza()函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回false,其中返回数组的信息的含义

1
2
3
4
5
6
7
8
9
10
11
12
13
索引 0 给出的是图像宽度的像素值

索引 1 给出的是图像高度的像素值

索引 2 给出的是图像的类型,返回的是数字,其中1 = GIF,2 = JPG,3 = PNG,4 = SWF,5 = PSD,6 = BMP,7 = TIFF(intel byte order),8 = TIFF(motorola byte order),9 = JPC,10 = JP2,11 = JPX,12 = JB2,13 = SWC,14 = IFF,15 = WBMP,16 = XBM

索引 3 给出的是一个宽度和高度的字符串,可以直接用于 HTML 的 <image> 标签

索引 bits 给出的是图像的每种颜色的位数,二进制格式

索引 channels 给出的是图像的通道值,RGB 图像默认是 3

索引 mime 给出的是图像的 MIME 信息,此信息可以用来在 HTTP Content-type 头信息中发送正确的信息,如:header("Content-type: image/jpeg");

实例

我们可以得到源码

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
<?php
error_reporting(0);

require_once('config.php');
require_once('lib/util.php');
require_once('lib/session.php');

$session = new SecureClientSession(CLIENT_SESSION_ID, SECRET_KEY);

// check whether file is uploaded
if (!file_exists($_FILES['file']['tmp_name']) || !is_uploaded_file($_FILES['file']['tmp_name'])) {
error('No file was uploaded.');
}

// check file size
if ($_FILES['file']['size'] > 256000) {
error('Uploaded file is too large.');
}

// check file type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$type = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
if (!in_array($type, ['image/png'])) {
error('Uploaded file is not PNG format.');
}

// check file width/height
$size = getimagesize($_FILES['file']['tmp_name']);
if ($size[0] > 256 || $size[1] > 256) {
error('Uploaded image is too large.');
}
if ($size[2] !== IMAGETYPE_PNG) {
// I hope this never happens...
error('What happened...? OK, the flag for part 1 is: <code>' . getenv('FLAG1') . '</code>');
}

// ok
$filename = bin2hex(random_bytes(4)) . '.png';
move_uploaded_file($_FILES['file']['tmp_name'], UPLOAD_DIR . '/' . $filename);

$session->set('avatar', $filename);
flash('info', 'Your avatar has been successfully updated!');
redirect('/');

通过审计源码,我们呢需要绕过一个if以及finfo_file()函数和getimagesize()函数,第一个if只要我们传小于256尺寸的图片即可,而对于finfo_file()函数,则是检测上传图片类型为image/png,同时getimagesize()则是在检查长宽时,会获取图像大小及相关信息,成功返回一个数组

1
2
3
4
5
6
7
8
9
10
Array
(
[0] => 290
[1] => 69
[2] => 3
[3] => width="290" height="69"
[bits] => 8
[mime] => image/png
)

由上面可知索引0和1是文件的长和宽,而索引2为文件类型为png,但是只要我们只留png的文件头部,则会让getimagesize()报错,然后绕过它,带出flag