-
Notifications
You must be signed in to change notification settings - Fork 117
/
Copy pathSophosUTM_plxDecrypter.py
112 lines (104 loc) · 3.86 KB
/
SophosUTM_plxDecrypter.py
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
#Python3
import os
import sys
import struct
import zlib
def yank(path):
with open(path, 'r+b') as f:
f.seek(-12, 2)
magic_chunk = f.read(12)
pointer_header = struct.unpack('<III', magic_chunk)
assert(pointer_header[0] == 0xab2155bc)
f.seek(-12 - pointer_header[2], 2)
data = f.read(pointer_header[2])
outpath1 = "Stage1-" + path
print("[*] Try to generate " + outpath1)
with open(outpath1, 'wb') as outf:
outf.write(data)
def bfs(path):
outpath1 = "Stage1-" + path
outpath2 = "Stage2-" + path
print("[*] Try to generate " + outpath2)
with open(outpath1, 'rb') as f, open(outpath2, 'wb') as f2:
header = f.read(4 * 4)
sig, unk, size, data_offset = struct.unpack('<IIII', header)
size_data = size - data_offset
f.seek(data_offset, os.SEEK_SET)
while True:
b = f.read(4)
if len(b) != 4:
break
v = struct.unpack('<I', b)[0]
v ^= size
f2.write(struct.pack('<I', v))
def snap_dword(pos):
if pos % 4 != 0:
return 4 - (pos & 3)
else:
return 0
def bfs_extract(path):
outpath2 = "Stage2-" + path
outpath3 = "Export-" + path
with open(outpath2, 'rb') as f:
paths = []
pos = 0x20
f.seek(pos, os.SEEK_SET)
try:
while True:
path_len = struct.unpack('<H', f.read(2))[0]
pos += 2
if path_len == 0:
break
path = bytes([b ^ 0xea for b in f.read(path_len)]).decode('ascii')
print(f'Found file {path}')
pos += path_len
pos_delta = snap_dword(pos)
pos += pos_delta
f.seek(pos_delta, os.SEEK_CUR)
offset = struct.unpack('<I', f.read(4))[0]
pos += 4
print(f' Offset: {offset:x}')
paths.append((path, offset))
except Exception as e:
pass
print("[*] Try to generate " + outpath3)
for path_offset in paths:
path, offset = path_offset
f.seek(offset, os.SEEK_SET)
header = f.read(4 * 3)
len_decompressed, len_data, unk = struct.unpack('<III', header)
print(f'Extracting {path}')
print(f' Offset: {pos:x}')
print(f' Length (decompressed): {len_decompressed:x}')
print(f' Length (data): {len_data:x}')
print(f' Unknown: {unk:x}')
path_components = path.split('/')
dir_path = os.path.join(outpath3, *path_components[:-1])
file_path = os.path.join(outpath3, *path_components)
if not os.path.exists(dir_path):
os.makedirs(dir_path)
data = f.read(len_data)
with open(file_path, 'wb') as f2:
if unk & 1 == 0:
print(' obfuscated data')
f2.write(bytes([b ^ 0xea for b in data]))
else:
print(' compressed data')
f2.write(bytes([b ^ 0xea for b in zlib.decompress(data)]))
if __name__ == '__main__':
if len(sys.argv)!=2:
print('SophosUTM_plxDecrypter')
print('Use to decrypt the .plx file of Sophos UTM')
print('Reference:')
print('https://www.atredis.com/blog/2021/8/18/sophos-utm-cve-2020-25223')
print('https://github.com/the6p4c/bfs_extract')
print('Usage:')
print('%s <plx file>'%(sys.argv[0]))
print('Eg.')
print('%s confd.plx'%(sys.argv[0]))
sys.exit(0)
else:
yank(sys.argv[1])
bfs(sys.argv[1])
bfs_extract(sys.argv[1])
print("[*] All done.")