forked from carstein/burp-extensions
-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathJSONDecoder.py
165 lines (124 loc) · 4.42 KB
/
JSONDecoder.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
# Burp Extension - JSON decoder
# Copyright : Michal Melewski <[email protected]>
# Small content-type fix: Nicolas Gregoire
# Force JSON fix: Marcin 'Icewall' Noga
import json
from burp import IBurpExtender
from burp import IMessageEditorTabFactory
from burp import IMessageEditorTab
from burp import IParameter
from burp import IContextMenuFactory
# Java imports
from javax.swing import JMenuItem
from java.util import List, ArrayList
# Menu items
menuItems = {
False: "Turn JSON active detection on",
True: "Turn JSON active detection off"
}
# Content types
supportedContentTypes = [
"application/json",
"text/json",
"text/x-json",
]
# Global Switch
_forceJSON = False
class BurpExtender(IBurpExtender, IMessageEditorTabFactory, IContextMenuFactory):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName('JSON Decoder')
callbacks.registerMessageEditorTabFactory(self)
callbacks.registerContextMenuFactory(self)
return
def createNewInstance(self, controller, editable):
return JSONDecoderTab(self, controller, editable)
def createMenuItems(self, IContextMenuInvocation):
global _forceJSON
menuItemList = ArrayList()
menuItemList.add(JMenuItem(menuItems[_forceJSON], actionPerformed = self.onClick))
return menuItemList
def onClick(self, event):
global _forceJSON
_forceJSON = not _forceJSON
class JSONDecoderTab(IMessageEditorTab):
def __init__(self, extender, controller, editable):
self._extender = extender
self._helpers = extender._helpers
self._editable = editable
self._txtInput = extender._callbacks.createTextEditor()
self._txtInput.setEditable(editable)
self._jsonMagicMark = ['{"', '["', '[{']
return
def getTabCaption(self):
return "JSON Decoder"
def getUiComponent(self):
return self._txtInput.getComponent()
def isEnabled(self, content, isRequest):
global _forceJSON
if isRequest:
r = self._helpers.analyzeRequest(content)
else:
r = self._helpers.analyzeResponse(content)
msg = content[r.getBodyOffset():].tostring()
if _forceJSON and len(msg) > 2 and msg[:2] in self._jsonMagicMark:
print "Forcing JSON parsing and magic mark found: %s"%msg[:2]
return True
for header in r.getHeaders():
if header.lower().startswith("content-type:"):
content_type = header.split(":")[1].lower()
for allowedType in supportedContentTypes:
if content_type.find(allowedType) > 0:
return True
return False
def setMessage(self, content, isRequest):
if content is None:
self._txtInput.setText(None)
self._txtInput.setEditable(False)
else:
if isRequest:
r = self._helpers.analyzeRequest(content)
else:
r = self._helpers.analyzeResponse(content)
msg = content[r.getBodyOffset():].tostring()
# find garbage index
# I know, this is not bulletproof, but we have to try
try:
boundary = min(
msg.index('{') if '{' in msg else len(msg),
msg.index('[') if '[' in msg else len(msg)
)
except ValueError:
print('Sure this is JSON?')
return
garbage = msg[:boundary]
clean = msg[boundary:]
try:
pretty_msg = garbage.strip() + '\n' + json.dumps(json.loads(clean), indent=4)
except:
print "problem parsing data in setMessage"
pretty_msg = garbage + clean
self._txtInput.setText(pretty_msg)
self._txtInput.setEditable(self._editable)
self._currentMessage = content
return
def getMessage(self):
if self._txtInput.isTextModified():
try:
pre_data = self._txtInput.getText().tostring()
boundary = min(pre_data.index('{'), pre_data.index('['))
garbage = pre_data[:boundary]
clean = pre_data[boundary:]
data = garbage + json.dumps(json.loads(clean))
except:
data = self._helpers.bytesToString(self._txtInput.getText())
# Reconstruct request/response
r = self._helpers.analyzeRequest(self._currentMessage)
return self._helpers.buildHttpMessage(r.getHeaders(), self._helpers.stringToBytes(data))
else:
return self._currentMessage
def isModified(self):
return self._txtInput.isTextModified()
def getSelectedData(self):
return self._txtInput.getSelectedText()