本文为翻译文章,原文链接为:https://anotherhackerblog.com/exploiting-file-uploads-pt1/
在私有项目上寻找漏洞时,我能够能够文件上传漏洞找到存储型XSS漏洞。通过滥用IE/Edge处理文件的方式,我能够染过文件类型检查并将GIF文件转变为恶意HTML文件。我还绕过了文件上传的过滤,在利用它的时候加入了一些我自己的思考。
当我测试新的程序时,我最喜欢用来fuzz的一个功能就是文件上传。文件上传漏洞通常会带来严重问题,而且开发人员似乎很难写出一个安全的上传功能。
看这个程序我注意到有一个联系支持的功能。在联系表单中,你可以上传附件,我注意到的是当我上传文件时,它将文件上传到同一个域。
响应数据:
{"result":true,"message":"/UploadFiles/redacted/redacted/3021d74f18ddasdasd50abe934f.png,"code":0}
这引起了我的注意,通常将文件存储到同一个域内不是很好的操作。因为它可能导致一些问题,例如RCE。
接下来我们思考如何利用它,也就是怎么上传恶意文件。我尝试的第一件事是通过修改文件后缀名为.html等,但没有任何效果。
{"result":false,"message":"That file type is not supported.","code":0}
我们可以总结有一个过滤器来限制文件扩展名。我们可以通过Burp的Intruder快速找到有哪些允许的扩展名。SecLists有一个很好地文件扩展名名单,我们可以进行使用。不幸的是,端点有速率限制,十几个请求后我们的IP地址会被封禁。
切换VPN服务器,我开始重新手动测试一些扩展名。我注意到只有jpg,jpeg,png和gif是允许的。我尝试我能想到的所有扩展名绕过技巧,例如空字节,Unicode编码和其他。在第一个"."之间的文件名都被忽略,因为应用程序创建了唯一的文件名。但是我注意到扩展检查后扩展后缀名的特殊字符没有被移除,检查期间它们就被忽略了。例如文件名badfile."gif是可以被接收的,但是badfile.foo"gif不行。
发送如下请求数据:
-----------------------------6683303835495
Content-Disposition: form-data; name="upload"; filename="badfile.''gif"
Content-Type: image/png
GIF89a
@HackerOn2Wheels
-----------------------------6683303835495--
返回了响应:
{"result":true,"message":"/UploadFiles/redacted/redacted/3021d74f18f649f5ac943ff50abe934f.''gif","code":0}
我们将会慢慢研究为什么这个过程有问题。
另一个事情是web应用程序是会检查文件头,也就是常说的“magic bytes”。因此,如果我只是尝试上传一个包含随机数据的文件,例如:
-----------------------------6683303835495
Content-Disposition: form-data; name="upload"; filename="badfile.''gif"
Content-Type: image/png
foobar
@HackerOn2Wheels
-----------------------------6683303835495--
返回了响应:
{"result":false,"message":"That file type is not supported.","code":0}
但是,文件上传过滤器非常常见的一点是它们只查看文件头的前4个字节。这些是文件上载功能通常在每种图像类型中查找的字节:
JPEG - FF D8 FF DB - ÿØÿÛ
GIF - 47 49 46 38 - GIF8
PNG - 89 50 4E 47 - ‰PNG
因此,只要我在文件内容中有其中一个,我们的文件就会成功上传。但是,遗憾的是,这还不足以防范恶意文件。大多数浏览器“寻找”实际的完整文件头,而其他浏览器(IE / Edge)根本不关心。例如,对于GIF和PNG文件,签名不仅仅是4个字节。完整签名是:
GIF - 47 49 46 38 39 61 - GIF89a ( or GIF87a )
PNG - 89 50 4E 47 0D 0A 1A 0A - .PNG....
[https://en.wikipedia.org/wiki/List_of_file_signatures]:
我会在文章下面描述这个问题的细节。
怎么利用这个问题呢?我们需要做的事上传一个带错误扩展名的文件来混淆浏览器。并将GIF8字节添加到文件的开头。
最终Payload:
-----------------------------6683303835495
Content-Disposition: form-data; name="upload"; filename="badfile.''gif"
Content-Type: image/png
GIF8
<html><script>alert('XSS is easy');</script></html>
-----------------------------6683303835495--
返回了响应:
{"result":true,"message":"/UploadFiles/redacted/redacted/5060bddf6e024def9a8f5f8b9c42ba1f.''gif","code":0}
现在通过IE或者Edge浏览器访问
https[:]//redacted.com/UploadFiles/redacted/redacted/5060bddf6e024def9a8f5f8b9c42ba1f."gif
我们首先需要问自己的是:浏览器如何实际运作?
我们需要理解浏览器如何加载文件,我们做一些示例文件来测试。我创建了三个GIF文件。第一个只有GIF8作为文件头,第二个有GIF89a作为文件头,第三个没有GIF文件头但有个.gif的文件后缀名。
如果我们通过Linux下的file命令我们可以看到这些文件的属性定义。
我们可以看到前两个文件基于文件头被定义为GIF,包括那个只有4个字节文件头的。最后一个只有后缀名的文件被定义为HTML。但是当我们在一个浏览器里打开这三个文件我们可以看到处理方式是不一样的。
例如,Edge和IE不会注意GIF文件头。它会显示HTML。
但它确实关心文件扩展名。谢谢MICROSOFT!
实际上,IE和Edge对MIME欺骗/Content欺骗是默认存在“漏洞”的。简要来说,Edge和IE会监测即将打开的文件内容,并基于此设置content type。所以当我们创建badfile."gif的时候。它会先读取内容然后设置content/type为text/html因为我们在内容里有\标签。你可以在这里读到细节:
[https://en.wikipedia.org/wiki/Content_sniffing]:
这是它有趣的地方。Firefox和Chrome会关心扩展名和文件头。但是实际上只考虑完整文件头。例如在Firefox里打开只有4个字节文件头的那个文件会发生不一样的操作。
正如我们看到的一样,只有4个字节的文件头是没法让Firefox(以及Chrome)正确渲染成GIF文件的。但是Firefox和Chrome新版确实会对文件内容做pre-wrap。如上所示,这个可能会破坏html执行。现在有可能利用吗?或者改变这种行为?我现在还不知道。如果你知道请告诉我。但是我们的text是可以被展示出来的,所以我们可以进行社工让受害者在about:config关闭pre-wrap功能。这会使攻击成本增加,因为需要用户交互。我们也可以称之为self-XSS。
总结一下如果你遇到文件上传你可以通过特殊字符扰乱GIF或PNG文件后缀名或者穿件一个无需后缀名的文件的话,你可以通过滥用IE/Edge的MIME/Content欺骗来执行JS和html代码了。