-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathOfficeOpenXMLEditor.py
217 lines (171 loc) · 7.89 KB
/
OfficeOpenXMLEditor.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
from burp import IBurpExtender
from burp import IMessageEditorTabFactory
from burp import IMessageEditorTab
import json
import tempfile
import os
import re
from UpdateableZipFile import UpdateableZipFile
import multipart as mp
try:
from io import BytesIO as IO
except ImportError:
from StringIO import StringIO as IO
from array import array
from zipfile import ZipFile, ZIP_STORED, ZipInfo
class BurpExtender(IBurpExtender, IMessageEditorTabFactory):
#
# implement IBurpExtender
#
def registerExtenderCallbacks(self, callbacks):
# keep a reference to our callbacks object
self._callbacks = callbacks
# obtain an extension helpers object
self._helpers = callbacks.getHelpers()
# set our extension name
callbacks.setExtensionName("Office Open XML Editor")
# register ourselves as a message editor tab factory
callbacks.registerMessageEditorTabFactory(self)
return
#
# implement IMessageEditorTabFactory
#
def createNewInstance(self, controller, editable):
# create a new instance of our custom editor tab
return OfficeOpenXMLInputTab(self, controller, editable)
#
# class implementing IMessageEditorTab
#
class OfficeOpenXMLInputTab(IMessageEditorTab):
#Parsing multipart request do verify Content-Type of parameter inside multipart/form-data
def parseMultipart(self,request):
reqInfo = self._extender._helpers.analyzeRequest(request)
if reqInfo.getMethod() == "POST" and reqInfo.getContentType() == reqInfo.CONTENT_TYPE_MULTIPART:
#Going thru headers to find Content-Type header
headers = reqInfo.getHeaders()
for header in headers:
#Reading boundary
res = re.search(r'Content-Type: multipart/.*; boundary=(.*)', header)
if res != None :
boundary = res.group(1)
try:
env = {'REQUEST_METHOD': 'POST','CONTENT_TYPE': 'multipart/form-data; boundary='+boundary,'wsgi.input': IO(request[reqInfo.getBodyOffset():])}
rforms, rfiles = mp.parse_form_data(env, strict=True, charset='utf8')
for files in rfiles:
for file in rfiles.getall(files):
print "file:"+file.name+":"+file.content_type
if file.content_type in self._listOfOOXMLContentType:
parameter = self._extender._helpers.getRequestParameter(request, file.name)
#Saving zip content and the span where the zip is
self._zip = parameter.getValue()
self._valueStart = parameter.getValueStart()
self._valueEnd = parameter.getValueEnd()
print "OOXML Document detected in the following parameter:" + file.name
break;
except Exception as e:
print("Error: {0}".format(e))
def readConfig(self):
file_name = os.getcwd() + os.sep + "conf" + os.sep + "conf.json"
with open(file_name) as data_file:
data = json.load(data_file)
self._listOfOOXMLContentType = data["Content-Types"]
self._fileToOpen = data["FileToOpen"]
self._tryToFindZip = data["tryToFindZip"]
print "Extension enabled for following Content-Types:" + str(self._listOfOOXMLContentType)
print "File to open in OOXML:" + self._fileToOpen
print "tryToFindZip value:" + str(self._tryToFindZip)
def __init__(self, extender, controller, editable):
try:
self._tryToFindZip = False
self.readConfig()
self._extender = extender
self._editable = editable
self._content = ""
self._tempName = ""
self._txtInput = extender._callbacks.createTextEditor()
self._txtInput.setEditable(editable)
self._zip = None
self._valueStart = 0
self._valueEnd = 0
except Exception as e:
print("Error: {0}".format(e))
return
#
# implement IMessageEditorTab
#
def getTabCaption(self):
return "Office Open XML Editor"
def getUiComponent(self):
return self._txtInput.getComponent()
def isEnabled(self, content, isRequest):
if isRequest :
#Try to find parameter with good content-Type in multipart/form
self.parseMultipart(content)
if self._zip is not None:
return True
#If tryTofindZip enabled
else :
if self._tryToFindZip is True:
try:
#Try to find zip with this regex
m = re.search(r"^.+\n(PK.+)\r\n.+$", content, re.MULTILINE|re.DOTALL)
if m :
print 'ZIP detected'
#Saving zip content and the span where the zip is
self._zip = m.group(1)
self._valueStart = m.start(1)
self._valueEnd = m.end(1)
return True
return False
except Exception as e:
print("Error: {0}".format(e))
return False
def setMessage(self, content, isRequest):
if (content is None):
# clear our display
self._txtInput.setText(None)
self._txtInput.setEditable(False)
else:
try:
#Creating temporary file
with tempfile.NamedTemporaryFile('wb',delete=False) as temp:
temp.write(self._extender._helpers.stringToBytes(self._zip))
self._tempName=temp.name
#Reading the file inside zip
with ZipFile(self._tempName,'r') as myzip:
data = myzip.read(self._fileToOpen)
self._txtInput.setText(data)
self._txtInput.setEditable(self._editable)
self._content=content
except Exception as e:
print("Error: {0}".format(e))
return
def getMessage(self):
# determine whether the user modified data
if (self._txtInput.isTextModified()):
try:
#Modifying content with the input of the tab
with UpdateableZipFile(self._tempName, 'a') as o:
o.writestr(self._fileToOpen, self._txtInput.getText())
#Reading the modified document
with open(self._tempName, mode='rb') as file:
modifiedContent = file.read()
#reading Request data in order to rebuild it
request=self._extender._helpers.analyzeRequest(self._content)
headers=request.getHeaders()
#Replacing with new zip
self._content[self._valueStart:self._valueEnd]=array('b',modifiedContent)
#Reinitialize value
self._zip = None
self._valueStart = 0
self._valueEnd = 0
#Rebuild request(calculate Content-length)
return self._extender._helpers.buildHttpMessage(headers,self._content[request.getBodyOffset():])
except Exception as e:
print("Error: {0}".format(e))
else:
return self._content
def isModified(self):
return self._txtInput.isTextModified()
def getSelectedData(self):
return self._txtInput.getSelectedText()