什么是文件上传漏洞?
文件上传漏洞是指网络服务器允许用户上传文件到其文件系统,而不对文件名、类型、内容或大小等进行充分验证。如果不能正确执行这些限制,即使是基本的图片上传功能也可能被用来上传任意的、具有潜在危险的文件。这甚至可能包括支持远程代码执行的服务器端脚本文件。
在某些情况下,上传文件本身就足以造成破坏。其他攻击可能涉及对文件的后续 HTTP 请求,通常是为了触发服务器执行文件。
文件上传漏洞有什么影响
文件上传漏洞的影响通常取决于两个关键因素:网站未能正确验证文件的哪个方面,无论是文件大小、类型还是内容等。文件上传成功后,对文件施加了哪些限制。
在最坏的情况下,文件类型没有得到正确验证,服务器配置允许某些类型的文件(如 .php 和 .jsp)作为代码执行。在这种情况下,攻击者有可能上传一个服务器端代码文件,该文件可作为 web shell 使用,从而有效地完全控制服务器。
如果文件名没有得到正确验证,攻击者只需上传同名文件,就能覆盖关键文件。如果服务器还存在目录遍历漏洞,这就意味着攻击者甚至可以将文件上传到意想不到的位置。
如果不能确保文件大小在预期的阈值范围内,还可能导致某种形式的拒绝服务(DoS)攻击,即攻击者填满可用磁盘空间。
文件上传漏洞是如何产生的
因为文件上传漏洞巨大的危害性,网站很少会对允许用户上传的文件不加任何限制。更常见的情况是,开发人员实施他们认为强大的验证,但这种验证要么存在固有缺陷,要么很容易被绕过。
例如,它们可能试图将危险文件类型列入黑名单,但在检查文件扩展名时却没有考虑到解析差异。与任何黑名单一样,黑名单也很容易不小心遗漏更隐蔽的文件类型,而这些文件类型可能仍然具有危险性。
在其他情况下,网站可能会尝试通过验证属性来检查文件类型,而这些属性很容易被使用 Burp Proxy 或 Repeater 等工具的攻击者操纵。
最后,即使是强大的验证措施,在构成网站的主机和目录网络中的应用也可能不一致,从而导致可被利用的差异。
网络服务器如何处理静态文件请求
在了解如何利用文件上传漏洞之前,您必须对服务器如何处理静态文件请求有一个基本的了解。
过去,网站几乎完全由静态文件组成,当用户提出请求时,网站就会向用户提供静态文件。因此,每个请求的路径都可以与服务器文件系统中的目录和文件层次结构进行 1:1 的映射。如今,网站越来越动态化,请求的路径往往与文件系统没有任何直接关系。不过,网络服务器仍然要处理一些静态文件的请求,包括样式表、图像等。
处理这些静态文件的流程大致相同。服务器会解析请求中的路径,以确定文件扩展名。然后,服务器根据扩展名和 MIME 类型之间的预配置映射列表来确定请求的文件类型。接下来会发生什么取决于文件类型和服务器配置。
如果该文件类型不可执行,如图像或静态 HTML 页面,服务器可能只会在 HTTP 响应中向客户端发送文件内容。
如果文件类型是可执行的,如 PHP 文件,且服务器被配置为执行此类文件,那么在运行脚本之前,服务器会根据 HTTP 请求中的头和参数来分配变量。由此产生的输出结果会在 HTTP 响应中发送给客户端。
如果文件类型是可执行的,但服务器未配置为执行该类型的文件,则通常会响应错误。不过,在某些情况下,文件内容仍会以纯文本形式提供给客户端。这种错误配置偶尔会被利用来泄露源代码和其他敏感信息。你可以在我们的信息披露学习材料中看到这方面的例子。
Content-Type
响应头可提供服务器认为它已提供的文件类型的线索。如果应用程序代码没有明确设置该标头,它通常包含文件扩展名/MIME 类型映射的结果。
利用不受限制的文件上传部署网络外壳
Lab: 通过Web shell 上传远程执行代码
从安全角度来看,最糟糕的情况是网站允许您上传服务器端脚本(如 PHP、Java 或 Python 文件),并将其配置为代码执行。这样,在服务器上创建自己的网络外壳就变得轻而易举了。
Web shell
Web shell是一种恶意脚本,攻击者只需向正确的端点发送 HTTP 请求,就能在远程网络服务器上执行任意命令
如果你能成功上传Web shell,你实际上就拥有了对服务器的完全控制权。这意味着你可以读写任意文件、外泄敏感数据,甚至利用服务器对内部基础架构和网络外的其他服务器进行透视攻击。例如,可以使用下面的 PHP 单行程序从服务器的文件系统中读取任意文件:
1 | echo file_get_contents('/path/to/target/file'); |
一旦上传,发送对该恶意文件的请求就会在响应中返回目标文件的内容。
本实验室包含一个易受攻击的图片上传功能。在将用户上传的文件存储到服务器文件系统之前,它没有对这些文件执行任何验证。
要解决该实验问题,请上传一个基本的PHP web shell
,并使用它来外泄文件 /home/carlos/secret
的内容。使用实验室横幅中提供的按钮提交此秘密。
要解决该实验问题,请上传一个基本的 PHP web shell
,并使用它来外泄文件 /home/carlos/secret
的内容。使用实验室横幅中提供的按钮提交此秘密。
您可以使用以下凭据登录自己的账户: wiener:peter
登录题目给的账户后,可以上传头像
上传1.php文件
1 | echo file_get_contents('/home/carlos/secret'); |
打开图像
总结:前端无防护、后端无防护、服务器具备执行条件。
利用文件上传验证的缺陷
在网络中,你不太可能发现一个网站像我们在上一个实验室中看到的那样,对文件上传攻击没有任何防护措施。但是,有了防御措施,并不意味着它们就是强大的。有时,你仍然可以利用这些机制中的漏洞来获取网络外壳,从而远程执行代码。
文件类型验证存在缺陷
在提交 HTML 表单时,浏览器通常会在 POST
请求中发送所提供的数据,内容类型为 application/x-www-form-url-encoded
。这对于发送简单的文本(如姓名或地址)很合适。但它不适合发送大量二进制数据,如整个图像文件或 PDF 文档。在这种情况下,首选内容类型为 multipart/form-data
。
考虑一个包含上传图片、提供图片说明和输入用户名等字段的表单。提交这样的表单后,可能会得到类似下面的请求:
1 | POST /images HTTP/1.1 |
正如你所看到的,消息正文被分割成不同的部分,分别用于表单的每个输入。每个部分都包含一个 Content-Disposition
标头,它提供了与输入字段相关的一些基本信息。这些单独的部分还可能包含自己的 Content-Type
标头,告诉服务器使用此输入提交的数据的 MIME 类型。
网站尝试验证文件上传的一种方法是检查特定于输入的 Content-Type 标头是否与预期的 MIME 类型相匹配。例如,如果服务器只接收图像文件,它可能只允许图像/jpeg 和图像/png 等类型。当服务器默认信任该标头的值时,就会出现问题。如果不执行进一步的验证来检查文件内容是否真的与假定的 MIME 类型相匹配,就可以使用 Burp Repeater 等工具轻松绕过这一防御。
Lab: 绕过内容类型限制上传网络外壳
本实验室包含一个易受攻击的图片上传功能。它试图防止用户上传意外的文件类型,但需要依靠检查用户可控输入来验证。
要解决该实验问题,请上传一个基本的 PHP web shell
,并使用它来外泄文件 /home/carlos/secret
的内容。使用实验室横幅中提供的按钮提交此秘密。
您可以使用以下凭据登录自己的账户: wiener:peter
上传PHP代码发现进行了前端防护
将Content-Type修改为符合规定的内容,php代码不变,即可上传成功
访问账户的头像通关
总结:前端防护,限制上传的文件类型,通过Burp Suitte 修改文件名绕过检测。后端无防护,服务器具备执行条件。
防止在用户可访问的目录中执行文件
虽然从一开始就防止上传危险文件类型显然更好,但第二道防线阻止服务器执行任何漏网脚本。
为谨慎起见,服务器通常只运行其 MIME 类型已明确配置为可执行的脚本。否则,它们可能只会返回某种错误信息,或者在某些情况下,以纯文本形式提供文件内容:
1 | GET /static/exploit.php?command=id HTTP/1.1 |
这种行为本身可能很有趣,因为它提供了一种泄漏源代码的方法,但它使任何创建网络 shell 的尝试都化为乌有。
这种配置通常因目录而异。上传用户提供文件的目录可能比文件系统中其他位置的控制要严格得多,因为其他位置被认为是最终用户无法访问的。如果你能找到一种方法,将脚本上传到另一个不应该包含用户提供文件的目录,那么服务器终究可能会执行你的脚本。
注意:网络服务器通常使用multipart/form-data
请求中的文件名字段来确定文件的名称和保存位置。
Lab:通过路径遍历上传网络 shell
该实验室包含一个易受攻击的图片上传功能。服务器被配置为防止执行用户提供的文件,但可通过利用次要漏洞(文件路径遍历)绕过此限制。要解决该实验问题,请上传一个基本的 PHP web shell
,并使用它来外泄文件 /home/carlos/secret
的内容。使用实验室横幅中提供的按钮提交此秘密。您可以使用以下凭据登录自己的账户: wiener:peter
按照以往的方式访问自己上传的php代码,服务器直接将php代码以文本方式返回
修改
filename
目录遍历语句被服务器清理
使用url编码上传成功
访问即可
您还应注意,尽管您可能会将所有请求发送到同一域名,但这通常指向某种反向代理服务器,如负载平衡器。您的请求通常会由其他服务器在幕后处理,这些服务器的配置也可能不同。
危险文件类型黑名单不足
防止用户上传恶意脚本的一个比较明显的方法是将 .php
等潜在危险的文件扩展名列入黑名单。黑名单的做法本质上是有缺陷的,因为很难明确阻止每一个可能用于执行代码的文件扩展名。有时可以通过使用较不知名的、仍可执行的替代文件扩展名(如 .php5
、.shtml
等)来绕过此类黑名单。
覆盖服务器配置
正如我们在上一节所讨论的,除非经过配置,否则服务器通常不会执行文件。例如,若允许 Apache 服务器执行客户端请求的 PHP 文件,开发人员需要在 /etc/apache2/apache2.conf
文件中添加以下指令:
1 | LoadModule php_module /usr/lib/apache2/modules/libphp.so |
许多服务器还允许开发人员在个别目录中创建特殊配置文件,以覆盖或添加一个或多个全局设置。例如,Apache 服务器会从名为 .htaccess
的文件中加载特定目录的配置(如果有的话)。
同样,开发人员可以使用 web.config
文件对 IIS 服务器进行特定目录配置。这可能包括以下指令,在本例中,这些指令允许向用户提供 JSON 文件:
1 | <staticContent> |
网络服务器会使用这类配置文件,但通常不允许使用 HTTP 请求访问它们。不过,你偶尔会发现服务器无法阻止你上传自己的恶意配置文件。在这种情况下,即使你需要的文件扩展名被列入了黑名单,你也可以欺骗服务器将任意的自定义文件扩展名映射到可执行的 MIME 类型。
Lab: Web shell upload via extension blacklist bypass
本文参考: