Windows 模式下 PHP 文件写入特性
0x00 前言
最近看到一篇审计的文章讲了如何巧妙的运用Windows 模式下 PHP 文件写入特性来绕过文件上传的安全校验。虽然提及的技术早在2018年就已经研究过了,但这些知识如今仍然广泛使用,值得我们花一些时间了解一下。
0x01 正文
1) 我们先来看效果
这是一个白名单正则校验文件后缀的文件上传接口,后缀必须包含图片格式且以图片格式开头,如png
或者pngbac
。
我们将正常的文件名picture.png
修改成了picture.png.php:.png
提示上传成功。
此时我们将文件写入进去了,但文件内容为空。
下一步,我们将文件名修改成picture.png<<<
,再把文件内容修改成<?= phpinfo();
上传成功后,我们访问路径uploads/picture.png.php
发现已经代码已经执行,说明通过这样的方式可以写入内容。
2)为什么会有这样的效果?
我们在很早以前就已经知道Windows下文件命名是有规则的。
在windows环境中,当创建的文件名中存在:
时,文件名会被截断并生成空文件。这也就解释了我们通过修改文件名为picture.png.php:.png
上传后,保存的文件名为picture.png.php
但文件为0KB的原因。
我们后续也通过文件名修改picture.png<<<
来达到文件写入目的而不被规则拦截的动作,为什么可以这样?我们直接说结论。
通过阅读PHP源码调试之Windows文件通配符分析 - 先知社区这篇文章可以知道,以下通配符可以在模式字符串中使用:
● 大于号>
相等于通配符问号?
● 小于号<<
相当于通配符星号*
● 双引号"
相等于通配符匹配字符点.
所以这个上传分为两个步骤:
- 通过
:
上传后缀为php
的文件,确保文件名后缀是PHP可以被解析。 - 通过
<<<
作为通配符来匹配picture.png*
(开头为picture.png的文件)写入恶意代码。
3)举一反三?
主要是通过改变输入的符号转换成通配符来判断匹配的效果。
编写如下代码文件读取代码进行测试。
|
在测试代码的同目录下创建六个内容为文件名称的文件。
使用test.jpg<<<
进行匹配,果然先匹配到了test.jpg
和test.jpg.jpg
,从而导致给php文件写入失败。<<
效果测试:
需要注意单个<
无法正常匹配到结果,只有连着使用两个或两个以上<
才有通配符*
的效果。
删除test.jpg
再次使用相同的参数请求,匹配到test.jpg.aaa
删除test.jpg.aaa
再次使用相同的参数请求,匹配到test.jpg.jpg
删除test.jpg.jpg
文件后再次使用test.jpg<<<
进行匹配,成功获得.php
文件
对其他符号同样进行测试>
效果测试:
单独>
可以匹配零个或一个字符,效果类似于通配符?
,缺失多少拼接多少>
就可以匹配到对应的文件。
但经过测试无法代替文件名中的字符.
使用"
效果测试:
符号"
可以匹配到字符.
。
4)如何自行验证?
新建文件夹,在文件夹内新建index.php
,内容如下:
<form action="/uploadImg.php" method="post" enctype="multipart/form-data"> |
在相同目录下新建uploadImg.php
,内容如下:
<?php |
这是一个经典的文件上传,校验文件合法共有两处。
- 校验
Content-Type
是否为image/gif
、image/jpeg
、image/jpg
等 - 校验文件后缀是否包含且以
jpg
、jpeg
、gif
等
然后再目录下面启动服务php -S 127.0.0.1:8889
后,访问http://127.0.0.1:8889
即可。
5)底层原因?
根据StackOverflow
上面的一个相关问题和MSDN的解释,这是NtQueryDirectoryFile
/ ZwQueryDirectoryFile
通过FsRtlIsNameInExpression
的一个功能特性,对于FsRtlIsNameInExpression
有如下描述:
The following wildcard characters can be used in the pattern string. |
另外,MSDN的解释并没有提到DOC-*
具体指哪些字符,但根据ntfs.h
,我们发现了如下的定义:
// The following constants provide addition meta characters to fully |
总结:
- 问题的产生的根本原因PHP调用了Windows API里的FindFirstFileExW()/FindFirstFile()方法
- 该Windows API方法对于这个三个字符做了特别的对待和处理
- 任何调用该Windows API方法的语言都有可能存在以上这个问题,比如:Python
Windows 模式下 PHP 文件写入特性