题目

考点
本关服务端校验的是文件扩展名(只允许.jpg/.png),但保存文件名用的是用户可控的原始文件名,没有做过滤。思路:利用空字节截断(%00),让文件名在服务器眼里”到php就结束了”,把后面的.jpg丢掉。
开始打靶
首先还是上传一张正常的图片,获取到图片存储的路径

接下来尝试上传php文件,发现服务器提示只能上传jpg和png文件

尝试使用00截断,在文件名php后加上%00,截断后面的.jpg文件后缀,看到可以成功上传

%00是URL编码的空字节。在老版本PHP(5.3之前)里,字符串处理函数遇到空字节就认为字符串到此为止,所以文件名shell.php%00.jpg:校验时看后缀是.jpg能通过,但真正保存拼接路径时被截断成shell.php,后面的.jpg被丢弃,文件最终以.php落盘。这个漏洞在现代PHP已经修复,属于历史遗留手法,但靶场里依然能考到。
访问上传的文件路径,成功读取到了服务器文件内容

提交,完美通关!

修复措施
本关能打成功靠的是老版本PHP的空字节截断缺陷,修复要点:
- 升级PHP:空字节截断在PHP 5.3之后已经修复,现代版本不会再截断字符串,升级就堵住了这个洞。
- 过滤空字节:对文件名做消毒,遇到
%00、\0直接拒绝,别让它进入文件系统。 - 服务端重命名:不用用户文件名拼路径,随机生成文件名,
%00想截断也无从下手。 - 白名单+内容校验:扩展名白名单和真实内容校验双保险,多层防御总没错。