题目

考点
本关服务端校验的是文件真实内容(用getimagesize()之类的函数读文件头判断是否为图片),改后缀和改MIME都骗不过它。思路:让文件”既是图片又是PHP”——在php代码前面加上GIF的文件签名GIF89a,校验时它是合法图片,解析时PHP引擎只认<?php标签,照样执行。
开始打靶
首先上传一张正常的图片,获取到图片存储的路径

尝试上传php文件,服务器提示文件不是有效的图像

尝试修改文件后缀为png,以及Content-Type的文件MIME类型为image/png,发现依旧提示文件不是有效图像,这里可以猜测服务器是根据文件的内容来判断的

这里我尝试在文件内容的第一行加上GIF89A,成功上传了文件
GIF89a 是 GIF 图片文件的文件签名(文件头),放在文件最开头。 很多上传校验会读取文件头部二进制判断文件类型(getimagesize ()),而不是只看后缀。 在文件最前面加上 GIF89a,就能绕过 getimagesize() 图片类型检测。

访问上传地址,成功读取到了内容

提交内容,通关

修复措施
本关能打成功是因为文件头签名(GIF89a)也可以伪造,光看文件头判断类型不够。修复要点:
- 重新编码图片:最可靠的办法——上传后用GD或ImageMagick把图片重新生成一遍,嵌入的php代码会在重绘过程中被丢弃,文件只剩下纯图片数据。
- 校验不只是文件头:别只信
getimagesize(),它只读文件头,伪造很容易;要校验整个文件是不是合法图像数据。 - 扩展名白名单+内容校验:双保险,后缀和内容都要过。
- 存储目录不可执行:老生常谈但最有效,图片目录禁脚本执行。