在Windows系统中,如果保存文件的文件名是以点结尾,系统会自动将点去掉。利用这一特点在某些场景下可以绕过对应的文件后缀检查,达到任意文件上传的效果。
0x00 引言
一般针对文件上传业务,主要判断是否有检查后缀名,同时要查看配置文件是否有设置白名单或者黑名单,如果没有的话,那么攻击者利用该缺陷上传类似webshell等恶意文件。这里列举JavaWeb中一些常见的思路:
- 利用servlet单例的特点进行绕过,此外,除了servlet以外,Spring中的controller默认也是是单例的,同样会发生类似的安全问题。但是可以通过@Scope注解来指定对应controller的作用域
- 通过报错的方式进行绕过(例如JFinal的CVE-2019-17352)
实际业务中发现了一处绕过后缀安全检查进行文件上传的实例,当前漏洞已经修复完毕 。提取关键的的漏洞代码做下复盘。
0x01 业务场景
上传业务接口是基于SpringMCV实现的,主要用于文件的上传,关键代码如下:
简单总结一下当前上传接口所做的安全措施:
- 通过后缀白名单的方式限制类似jsp/jspx恶意文件的上传
- 未返回文件名/存储路径等敏感信息
查看接口具体的效果:
上传txt文件,成功上传:
尝试上传恶意JSP文件,被拦截:
可以看到设置的后缀白名单“的确”起到了一定的拦截作用。
0x02 绕过过程
查看具体的上传逻辑代码,获取后缀名的方式是通过substring进行字符串的切割:
在获取到文件后缀名后(这里的后缀名是去掉了.的),这里有个很关键的地方,如果后缀名不为空,那么进入白名单的检查,否则通过transferTo方法完成文件的上传:
//检查文件后缀名
if(extName!=null&&!extName.equals("")) {
if (!allowedExtList.contains(extName)){
result.put("code", \-1);
result.put("msg", "illegal File Type,try again!");
return result;
}
}
那么也就是说,当程序获取不到文件名的时候,即可绕过对应黑名单后缀的检查,但是单纯上传没有.xxx的文件,即使内容包含恶意的jsp内容,上传成功也无法解析造成危害。那么有没有办法进行进一步的利用呢?
首先要解决的问题是要让程序获取不到文件名,这里有师傅提到了一个思路,当上传文件名为test.jsp.
时,因为获取后缀的逻辑是通过substring最后一个.
的位置获取的,这里返回的是null,符合第一个要求。但是比较遗憾的是,直接在tomcat中访问test.jsp.
的文件是没办法解析的:
查阅相应的资料:https://docs.microsoft.com/zh-cn/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN:
Windows normally requires files to have either no extension or an extension that is at least one character long. It does not like zero length extensions (i.e. file names that end with a period). Folders can have extensions too, therefore, Windows does not let their names end with a period.
发现Windows系统中,如果保存文件的文件名是以点结尾(形如test.jsp.),系统会自动将.
去掉,上述文件名就会变成(test.jsp)。
这里做一个实验,直接在windows环境下通过代码生成以.
结尾的文件,确实无论以任意数量的.
结尾,最终实际保存的文件对应自动将.
去掉:
PS:与Windows不同,Linux并不关心文件的扩展名。它会查看文件内容,并自行解决。换句话说,Linux与扩展名无关。所以如果应用部署在Linux的话该姿势并不能产生对应的效果。
结合上述内容,因为应用部署环境刚好是windows的,这里尝试上传以jsp.结尾的文件,可以看到最终保存的文件是以.jsp结尾,并且可以成功解析,成功绕过了后缀检查:
0x03 其他
最后开发人员更换了获取后缀名的方式,通过split切割文件名获取对应的String[]数组,然后获取数组最后一个元素作为后缀名,此时类似test.jsp.
的文件名会获取到jsp后缀,避免了上述的绕过过程。
String[] splitName = fileName.split("\\.");
String extName = splitName[splitName.length - 1];
但是在window下,如果文件名+"::$DATA"
会把::$DATA
之后的数据当成文件流处理,不会检测后缀名,且保持::$DATA
之前的文件名,他的目的就是不检查后缀名,通过:$DATA
这个在黑名单场景下也是可以绕过的。
在进行黑盒测试时,通过上面的方式尝试绕过后缀安全检查进行文件上传也是一种不错的思路。白盒审计中也需要额外关注。
- 本文作者: tkswifty
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/1596
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!