PHP Filter RCE 分析

PHP filter是什么

PHP filter是PHP定义的一个伪协议,用于在数据流打开时进行筛选过滤,在数据读取或者写入的时候通过过滤器对数据进行处理。PHP filter 可以使用多个过滤器进行处理。

php://filter/过滤器|过滤器/resource=待过滤的数据流

require_once和require的参数是一个文件路径,指明要包含的文件,而PHP filter 提供了接口,使得可以通过这个接口访问到指定的文件内容,require在包含文件时,只关心文件内容,而不关心文件内容来自于何处,所以可以给require 传PHP filter参数,

如果有如下代码

<?php
$file = $_GET['page'];
require($file);

则可以通过如下请求泄露敏感信息

curl "http://localhost/test.php?page=php://filter/convert.base64-encode/resource=/etc/passwd"

PHP的base64decode函数在处理base64编码的数据时,会自动规范化:去除字符串中不合法的字符并且忽略,而后尝试解码。 但PHP filter的base64解码行为和base64decode行为略有不同,PHP filter的base64-decode不能处理随即插入的等号,此时可以使用UTF-7编码规避,UTF-7编码会把等号转化为其他的base64字符。

通过编码前置字符

根据官网链接的 reference 根据文档,如果开启了iconv支持,则可以通过伪协议php://convert.iconv.*.*调用iconv函数。

convert.iconv.<input-encoding>.<output-encoding> or convert.iconv.<input-encoding>/<output-encoding>

linux中可以使用iconv函数将字符串从一个编码转为另外一个编码,在PHP://filter中可以使用iconv过滤器调用到这个函数

php -r "echo file_get_contents(\"php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7/res
ource=php://temp\");"
GyQpQw+AD0APQ-#

某些编码规定该编码会在数据之前预置一些字节,相当于签名,标识这段数据是该编码 在 RFC 2781中就说明该编码会预置0XFEFF

The Unicode Standard and ISO 10646 define the character “ZERO WIDTH NON-BREAKING SPACE” (0xFEFF), which is also known informally as “BYTE ORDER MARK” (abbreviated “BOM”).This usage, suggested by Unicode and ISO 10646 Annex F (informative), is to prepend a 0xFEFF character to a stream of Unicode characters as a “signature”; a receiver of such a serialized stream may then use the initial character both as a hint that the stream consists of Unicode characters and as a way to recognize the serialization order. In serialized UTF-16 prepended with such a signature, the order is big-endian if the first two octets are 0xFE followed by 0xFF; if they are 0xFF followed by 0xFE, the order is little-endian. Note that 0xFFFE is not a Unicode character, precisely to preserve the usefulness of 0xFEFF as a byte-order mark.

下图给出了如何在字符串前面预置8

  • 首先将UTF-8格式的字符串start转化为UTF-16格式
  • UTF-16会在字符串前面预置0xFFFE
  • 前面预置的0xFF在LATIN6表中对应于ĸ
  • 而后尝试将这个字符串以LATIN6格式转化为UTF-16格式
  • UTF-16会在字符串前面预置0xFFFE,ĸ在UNICODE表中对应于0x0138,而后逐个打印,0x38变成了8

LANTIN6表如下:

x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF
0x
1x
2x SP ! " # $ % & ' ( ) * + , - . /
3x 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4x @ A B C D E F G H I J K L M N O
5x P Q R S T U V W X Y Z [ |] ^ _
6x ` a b c d e f g h i j k l m n o
7x p q r s t u v w x y z { | } ~
8x
9x
Ax NBSP Ą Ē Ģ Ī Ĩ Ķ § Ļ Đ Š Ŧ Ž SHY Ū Ŋ
Bx ° ą ē ģ ī ĩ ķ · ļ đ š ŧ ž ū ŋ
Cx Ā Á Â Ã Ä Å Æ Į Č É Ę Ë Ė Í Î Ï
Dx Ð Ņ Ō Ó Ô Õ Ö Ũ Ø Ų Ú Û Ü Ý Þ ß
Ex ā á â ã ä å æ į č é ę ë ė í î ï
Fx ð ņ ō ó ô õ ö ũ ø ų ú û ü ý þ ĸ

UNICODE如下:

通过将恶意代码转化为base64形式,而后通过编码预置字符,最后使用convert.base64-decode尝试解码预置的数据,就可以使得PHP filter最后解码出恶意代码。

当使用require时,且路径可控,就可以利用PHP filter执行任意代码。

参考链接

https://gynvael.coldwind.pl/?id=671 https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d https://www.synacktiv.com/en/publications/php-filters-chain-what-is-it-and-how-to-use-it

Created at 2023-12-29T18:32:43+08:00

创建于:Friday, December 29,2023
最后修改于: Monday, January 8,2024