This repository has been archived by the owner on Jun 7, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
/
rtf.py
173 lines (149 loc) · 7.1 KB
/
rtf.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
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
# -*- coding: utf-8 -*-
'''
Code based on the python-oletools package by Philippe Lagadec 2012-10-18
http://www.decalage.info/python/oletools
'''
import os
import tempfile
from viper.common.abstracts import Module
from viper.core.session import __sessions__
try:
from oletools.rtfobj import RtfObjParser
from oletools import oleobj
HAVE_RTF = True
except ImportError:
HAVE_RTF = False
class Rtf(Module):
cmd = 'rtf'
description = 'RTF Parser'
authors = ['xorhex']
categories = ["document"]
def __init__(self):
super(Rtf, self).__init__()
self.parser.add_argument('-l', "--list", action='store_true', help='List of ')
self.parser.add_argument('-s', "--save", metavar='item_index', help='Save object')
def parse_rtf(self, filename, data):
'''
The bulk of this fuction is taken from python-oletools: https://github.com/decalage2/oletools/blob/master/oletools/rtfobj.py
See link for license
'''
self.log('success', 'File: {name} - size: {size} bytes'.format(name=filename, size=hex(len(data))))
table = []
h = ['id', 'index', 'OLE Object']
rtfp = RtfObjParser(data)
rtfp.parse()
for rtfobj in rtfp.objects:
row = []
obj_col = []
if rtfobj.is_ole:
obj_col.append('format_id: {id} '.format(id=rtfobj.format_id))
if rtfobj.format_id == oleobj.OleObject.TYPE_EMBEDDED:
obj_col.append('(Embedded)')
elif rtfobj.format_id == oleobj.OleObject.TYPE_LINKED:
obj_col.append('(Linked)')
else:
obj_col.append('(Unknown)')
obj_col.append('class name: {cls}'.format(cls=rtfobj.class_name))
# if the object is linked and not embedded, data_size=None:
if rtfobj.oledata_size is None:
obj_col.append('data size: N/A')
else:
obj_col.append('data size: %d' % rtfobj.oledata_size)
if rtfobj.is_package:
obj_col.append('OLE Package object:')
obj_col.append('Filename: {name}'.format(name=rtfobj.filename))
obj_col.append('Source path: {path}'.format(path=rtfobj.src_path))
obj_col.append('Temp path = {path}'.format(path=rtfobj.temp_path))
obj_col.append('MD5 = {md5}'.format(md5=rtfobj.olepkgdata_md5))
# check if the file extension is executable:
_, temp_ext = os.path.splitext(rtfobj.temp_path)
self.log('debug', 'Temp path extension: {ext}'.format(ext=temp_ext))
_, file_ext = os.path.splitext(rtfobj.filename)
self.log('debug', 'File extension: %r' % file_ext)
if temp_ext != file_ext:
obj_col.append("MODIFIED FILE EXTENSION")
else:
obj_col.append('MD5 = {md5}'.format(md5=rtfobj.oledata_md5))
if rtfobj.clsid is not None:
obj_col.append('CLSID: {clsid}'.format(clsid=rtfobj.clsid))
obj_col.append(rtfobj.clsid_desc)
# Detect OLE2Link exploit
# http://www.kb.cert.org/vuls/id/921560
if rtfobj.class_name == b'OLE2Link':
obj_col.append('Possibly an exploit for the OLE2Link vulnerability (VU#921560, CVE-2017-0199)')
# Detect Equation Editor exploit
# https://www.kb.cert.org/vuls/id/421280/
elif rtfobj.class_name.lower() == b'equation.3':
obj_col.append('Possibly an exploit for the Equation Editor vulnerability (VU#421280, CVE-2017-11882)')
else:
obj_col.append('Not a well-formed OLE object')
row.append(rtfp.objects.index(rtfobj))
row.append('%08Xh' % rtfobj.start)
row.append('\n'.join(obj_col))
table.append(row)
self.log('table', dict(rows=table, header=h))
def list(self):
self.parse_rtf(__sessions__.current.file.name, __sessions__.current.file.data)
def save_ole_objects(self, data, save_object, filename):
'''
The bulk of this fuction is taken from python-oletools: https://github.com/decalage2/oletools/blob/master/oletools/rtfobj.py
See link for license
'''
rtfp = RtfObjParser(data)
rtfp.parse()
try:
i = int(save_object)
objects = [rtfp.objects[i]]
except Exception as ex:
self.log('error', 'The -s option must be followed by an object index, such as "-s 2"\n{ex}'.format(ex=ex))
return
for rtfobj in objects:
i = objects.index(rtfobj)
tmp = tempfile.NamedTemporaryFile(delete=False)
if rtfobj.is_package:
self.log('info', 'Saving file from OLE Package in object #%d:' % i)
self.log('info', ' Filename = %r' % rtfobj.filename)
self.log('info', ' Source path = %r' % rtfobj.src_path)
self.log('info', ' Temp path = %r' % rtfobj.temp_path)
self.log('info', ' saving to file %s' % tmp.name)
self.log('info', ' md5 %s' % rtfobj.olepkgdata_md5)
tmp.write(rtfobj.olepkgdata)
tmp.close()
# When format_id=TYPE_LINKED, oledata_size=None
elif rtfobj.is_ole and rtfobj.oledata_size is not None:
self.log('info', 'Saving file embedded in OLE object #%d:' % i)
self.log('info', ' format_id = %d' % rtfobj.format_id)
self.log('info', ' class name = %r' % rtfobj.class_name)
self.log('info', ' data size = %d' % rtfobj.oledata_size)
# set a file extension according to the class name:
self.log('info', ' saving to file %s' % tmp.name)
self.log('info', ' md5 %s' % rtfobj.oledata_md5)
tmp.write(rtfobj.oledata)
tmp.close()
else:
self.log('info', 'Saving raw data in object #%d:' % i)
self.log('info', ' saving object to file %s' % tmp.name)
self.log('info', ' md5 %s' % rtfobj.rawdata_md5)
tmp.write(rtfobj.rawdata)
tmp.close()
if not save_object == 'all':
__sessions__.new(tmp.name)
def save(self, idx):
self.save_ole_objects(__sessions__.current.file.data, idx, __sessions__.current.file.name)
# Main starts here
def run(self):
super(Rtf, self).run()
if self.args is None:
return
if not __sessions__.is_set():
self.log('error', 'No open session. This command expects a file to be open.')
return
if not HAVE_RTF:
self.log('error', 'Missing dependancy. install oletools (pip install oletools)')
return
if self.args.list:
self.list()
elif self.args.save:
self.save(self.args.save)
else:
self.parser.print_usage()