[HarekazeCTF2019]Avatar Uploader 1

发布于:2022-12-18 ⋅ 阅读:(497) ⋅ 点赞:(0)

目录

前言:

考点:

解题:

进入正题:

总结:


前言:

仅记录我收集知识的历程

考点:

PNG 文件上传

finfo_file() 函数原理。

getimagesize() 函数原理与绕过

代码审计

解题:

进入环境:

 首先登录,进入上传页面。

提示 上传小于256kb ,小于256px*256px的PNG图片

 上传  . php 然后抓个包看看回显。

 返回了 cookie , 以点号分割,应该是 jwt 格式,复制这个cookie到jwt网页上:JSON 网络令牌 - jwt.io

提示我们上传的 不是PNG 格式,bp 改一下后缀,改成PNG,相应的,Content-Type: 也改成 image/png 

依旧返回了 cookie ,再去解码看看返回的什么。  解码后,依旧是 不为 PNG文件。

思路到这,还是不知道他过滤了什么,可能是文件头格式,但现在 上传一个正常的png文件试试,说不定还能遇到 任意文件读取,或者目录穿越。。

成功上传了,并且改了主页的头像。

但是到这就没了 后续,几番测试也没有结果。  看了下别人的文章,原来题目是给了 源码的。我没发现,  因为 是上传题,直接看upload,php 了。

<?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('/');

还是少不了代码审计啊。

关键点:

// 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.');

 finfo_file() 函数检测文件类型是否为 image/png 格式。

if ($size[2] !== IMAGETYPE_PNG) {
  // I hope this never happens...
  error('What happened...? OK, the flag for part 1 is: <code>' . getenv('FLAG1') . '</code>');
}

在检查文件长宽的时候,getimagesize() 函数用于获取图像大小及相关信息,成功将返回一个数组。 如果索引2 不是PNG  则输出flag。、

对于 getimagesize() 函数,返回的数组。测试一下此函数。

 可见他返回了 如下信息:

array(6) {
  [0]=>
  int(73)
  [1]=>
  int(100)
  [2]=>
  int(3)
  [3]=>
  string(23) "width="73" height="100""
  ["bits"]=>
  int(8)
  ["mime"]=>
  string(9) "image/png"
}

解释结果:

  • 索引 [0] 给出的是图片宽度的像素值
  • 索引 [1] 给出的是图片高度的像素值
  • 索引 [2] 给出的是图像的类型 ,返回的是数字,其中 1 = GIF2 = JPG3 = PNG4 = SWF5 = PSD6 = BMP7 = TIFF(intel byte order)8 = TIFF(motorola byte order)9 = JPC10 = JP211 = JPX12 = JB213 = SWC14 = IFF15 = WBMP16 = XBM
  • 索引 [3] 给出的是一个宽度和高度的字符串,可直接用于HTML的<image>标签。
  • 索引 [bits] 给出的是图像的每种颜色的位数,二进制格式。
  • 索引 [channels] 给出的是图像的通道值,RGB图像默认是3
  • 索引 [mime] 给出的是 图像的MIME 信息,此信息可以用来在HTTP Content-Type 头信息中发送正确的信息 如: header("Content-type: image/jpeg");

进入正题:

函数 finfo_ file() 主要是识别PNG 文件 十六进制下的第一行信息,若保留文件头信息,破坏掉 文件宽高等 其余信息,也就是说 破坏getimagesize() 函数无法正确的检验就可以了

 只要把我这里选中的文件头格式留下就可以破坏 getimagesize 的检查了,因为文件头他还是会检查是否为png 的。我们可以使用misc 常用的 winhex 来修改他的值:

使用 winhex 只留其文件头,将后面的图片内容全部删除。这样他就检查不到了。

 

 成功得到 flag 。 

总结:

对于 getimagesize() 和 finfo_file ()还是熟悉了挺多,还有索引表达的意思。

其次,还有很有意思的代码审计。


网站公告

今日签到

点亮在社区的每一天
去签到