Skip to content

Commit 70a9f48

Browse files
Add files via upload
1 parent 34dab2d commit 70a9f48

File tree

1 file changed

+284
-0
lines changed

1 file changed

+284
-0
lines changed

Obsidan_BurpSuite.py

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
import os
2+
from threading import Thread
3+
from burp import IBurpExtender, ITab
4+
from javax.swing import JPanel, JButton, JTextField, JFileChooser, JLabel, JOptionPane, JCheckBox, BorderFactory
5+
from java.awt import GridBagLayout, GridBagConstraints, Insets, Color, Dimension, Font
6+
from java.net import URL
7+
8+
class BurpExtender(IBurpExtender, ITab):
9+
def registerExtenderCallbacks(self, callbacks):
10+
self._callbacks = callbacks
11+
self._helpers = callbacks.getHelpers()
12+
13+
# Set the extension name
14+
callbacks.setExtensionName("Obsidian")
15+
16+
# Create and add the custom tab
17+
self._tab = JPanel(GridBagLayout())
18+
self._tab.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10))
19+
20+
constraints = GridBagConstraints()
21+
constraints.fill = GridBagConstraints.HORIZONTAL
22+
constraints.insets = Insets(5, 5, 5, 5)
23+
24+
# Add description label
25+
constraints.gridx = 0
26+
constraints.gridy = 0
27+
constraints.gridwidth = 3
28+
self._descriptionLabel = JLabel("Request to Import to Obsidian")
29+
self._descriptionLabel.setForeground(Color.WHITE)
30+
self._descriptionLabel.setFont(Font("Arial", Font.BOLD, 16))
31+
self._tab.add(self._descriptionLabel, constraints)
32+
33+
# Add vertical space (empty label)
34+
constraints.gridx = 0
35+
constraints.gridy = 1
36+
constraints.gridwidth = 2
37+
self._verticalSpace = JLabel(" ")
38+
self._tab.add(self._verticalSpace, constraints)
39+
40+
# Save folder location label
41+
constraints.gridx = 0
42+
constraints.gridy = 2
43+
constraints.gridwidth = 1
44+
self._label = JLabel("Save folder location:")
45+
self._tab.add(self._label, constraints)
46+
47+
# Folder path field
48+
constraints.gridx = 1
49+
constraints.gridy = 2
50+
self._folderPathField = JTextField(20)
51+
self._tab.add(self._folderPathField, constraints)
52+
53+
# Browse button
54+
constraints.gridx = 2
55+
constraints.gridy = 2
56+
self._browseButton = JButton("Browse")
57+
self._browseButton.addActionListener(self._browse)
58+
self._tab.add(self._browseButton, constraints)
59+
60+
# Create checkboxes for HTTP, HTTPS, and both
61+
constraints.gridx = 0
62+
constraints.gridy = 3
63+
self._httpCheckBox = JCheckBox("HTTP")
64+
self._httpCheckBox.addActionListener(self._updateCheckboxes)
65+
self._tab.add(self._httpCheckBox, constraints)
66+
67+
constraints.gridx = 1
68+
constraints.gridy = 3
69+
self._httpsCheckBox = JCheckBox("HTTPS", True) # Default to checked
70+
self._httpsCheckBox.addActionListener(self._updateCheckboxes)
71+
self._tab.add(self._httpsCheckBox, constraints)
72+
73+
constraints.gridx = 2
74+
constraints.gridy = 3
75+
self._httpAndHttpsCheckBox = JCheckBox("HTTP & HTTPS")
76+
self._httpAndHttpsCheckBox.addActionListener(self._updateCheckboxes)
77+
self._tab.add(self._httpAndHttpsCheckBox, constraints)
78+
79+
# Add vertical space (empty label)
80+
constraints.gridx = 0
81+
constraints.gridy = 1
82+
constraints.gridwidth = 2
83+
self._verticalSpace = JLabel(" ")
84+
self._tab.add(self._verticalSpace, constraints)
85+
86+
# Generate button positioned below the checkboxes
87+
constraints.gridx = 0
88+
constraints.gridy = 4
89+
constraints.gridwidth = 1
90+
self._generateButton = JButton("Generate")
91+
self._generateButton.setPreferredSize(Dimension(100, 30))
92+
self._descriptionLabel.setForeground(Color(255, 255, 255))
93+
self._generateButton.setBackground(Color(238, 103, 0)) # Orange color
94+
self._generateButton.setOpaque(True)
95+
self._generateButton.setBorderPainted(False)
96+
self._generateButton.addActionListener(self._generate)
97+
self._tab.add(self._generateButton, constraints)
98+
99+
# Loading label
100+
constraints.gridx = 1
101+
constraints.gridy = 4
102+
constraints.gridwidth = 2
103+
self._loadingLabel = JLabel("")
104+
self._tab.add(self._loadingLabel, constraints)
105+
106+
# Message label
107+
constraints.gridx = 0
108+
constraints.gridy = 5
109+
constraints.gridwidth = 3
110+
self._messageLabel = JLabel("")
111+
self._messageLabel.setForeground(Color(0, 128, 0))
112+
self._tab.add(self._messageLabel, constraints)
113+
114+
# Add the custom tab to Burp Suite
115+
callbacks.addSuiteTab(self)
116+
117+
118+
def getTabCaption(self):
119+
return "Obsidian"
120+
121+
def getUiComponent(self):
122+
return self._tab
123+
124+
def _browse(self, event):
125+
# Create a JFileChooser configured for directory selection
126+
file_chooser = JFileChooser()
127+
file_chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
128+
result = file_chooser.showSaveDialog(None)
129+
if result == JFileChooser.APPROVE_OPTION:
130+
folder = file_chooser.getSelectedFile()
131+
self._folderPathField.setText(folder.getAbsolutePath())
132+
133+
def _updateCheckboxes(self, event):
134+
source = event.getSource()
135+
if source == self._httpCheckBox:
136+
if self._httpCheckBox.isSelected():
137+
self._httpsCheckBox.setSelected(False)
138+
self._httpAndHttpsCheckBox.setSelected(False)
139+
elif source == self._httpsCheckBox:
140+
if self._httpsCheckBox.isSelected():
141+
self._httpCheckBox.setSelected(False)
142+
self._httpAndHttpsCheckBox.setSelected(False)
143+
elif source == self._httpAndHttpsCheckBox:
144+
if self._httpAndHttpsCheckBox.isSelected():
145+
self._httpCheckBox.setSelected(False)
146+
self._httpsCheckBox.setSelected(False)
147+
148+
def _generate(self, event):
149+
# Show loading message
150+
self._loadingLabel.setText("Loading...")
151+
152+
# Run the generation process in a new thread
153+
thread = Thread(target=self._generate_in_thread)
154+
thread.start()
155+
156+
def _generate_in_thread(self):
157+
folder_path = self._folderPathField.getText()
158+
if not folder_path:
159+
JOptionPane.showMessageDialog(None, "Please specify a folder path.", "Error", JOptionPane.ERROR_MESSAGE)
160+
self._loadingLabel.setText("")
161+
return
162+
163+
# Determine selected protocols
164+
selected_protocols = set()
165+
if self._httpCheckBox.isSelected():
166+
selected_protocols.add("http")
167+
if self._httpsCheckBox.isSelected():
168+
selected_protocols.add("https")
169+
if self._httpAndHttpsCheckBox.isSelected():
170+
selected_protocols.add("http")
171+
selected_protocols.add("https")
172+
173+
# Ensure at least one protocol is selected
174+
if not selected_protocols:
175+
JOptionPane.showMessageDialog(None, "Please select at least one protocol.", "Error", JOptionPane.ERROR_MESSAGE)
176+
self._loadingLabel.setText("")
177+
return
178+
179+
# Fetch URLs based on the selected protocols
180+
if "http" in selected_protocols and "https" in selected_protocols:
181+
urls = self._get_unique_urls_in_scope()
182+
else:
183+
urls = self._get_filtered_urls_in_scope("http" in selected_protocols, "https" in selected_protocols)
184+
185+
if not urls:
186+
self._messageLabel.setText("No URLs in scope.")
187+
self._loadingLabel.setText("")
188+
return
189+
190+
# Process URLs to create folder structure and include request details
191+
try:
192+
self._create_folder_structure(folder_path, urls)
193+
self._messageLabel.setText("Folder structure created in {}".format(folder_path))
194+
except Exception as e:
195+
JOptionPane.showMessageDialog(None, "An error occurred: {}".format(str(e)), "Error", JOptionPane.ERROR_MESSAGE)
196+
self._messageLabel.setText("Failed to create folder structure.")
197+
198+
self._loadingLabel.setText("")
199+
200+
def _get_filtered_urls_in_scope(self, http_selected, https_selected):
201+
urls = set()
202+
http_protocol = "http"
203+
https_protocol = "https"
204+
205+
# Get the list of IHttpRequestResponse objects in the target scope
206+
http_messages = self._callbacks.getProxyHistory()
207+
for message in http_messages:
208+
url = self._helpers.analyzeRequest(message).getUrl()
209+
url_str = url.toString()
210+
211+
if self._callbacks.isInScope(url):
212+
if (http_selected and url.getProtocol() == http_protocol and not https_selected) or \
213+
(https_selected and url.getProtocol() == https_protocol):
214+
urls.add(url_str)
215+
216+
return urls
217+
218+
def _get_unique_urls_in_scope(self):
219+
urls = set()
220+
# Get the list of IHttpRequestResponse objects in the target scope
221+
http_messages = self._callbacks.getProxyHistory()
222+
for message in http_messages:
223+
url = self._helpers.analyzeRequest(message).getUrl()
224+
if self._callbacks.isInScope(url):
225+
urls.add(url.toString())
226+
return urls
227+
228+
def _create_folder_structure(self, base_folder, urls):
229+
for url_str in urls:
230+
try:
231+
url = URL(url_str)
232+
# Get the path and clean it
233+
path = url.getPath().strip("/")
234+
# Replace ':' with empty string in the path
235+
path = path.replace(":", "")
236+
237+
# Construct folder paths
238+
tld_folder = url.getHost().split('.')[-2] + '.' + url.getHost().split('.')[-1]
239+
subfolders = [url.getHost()] + path.split('/')
240+
241+
current_path = os.path.join(base_folder, tld_folder)
242+
folder_exists = os.path.exists(current_path)
243+
244+
for subfolder in subfolders:
245+
current_path = os.path.join(current_path, subfolder)
246+
if not os.path.exists(current_path):
247+
os.makedirs(current_path)
248+
folder_exists = False
249+
250+
# Always create root.md in the subdomain folder
251+
subdomain_path = os.path.join(base_folder, tld_folder, url.getHost())
252+
root_md_path = os.path.join(subdomain_path, "root.md")
253+
if not os.path.exists(root_md_path):
254+
with open(root_md_path, 'w') as md_file:
255+
md_file.write(self._get_request(url_str))
256+
257+
# Only create .md file if the folder did not exist
258+
if not folder_exists:
259+
if not path:
260+
md_file_path = os.path.join(current_path, "root.md")
261+
else:
262+
last_segment = subfolders[-1]
263+
md_file_path = os.path.join(current_path, "{}.md".format(last_segment))
264+
265+
if not os.path.exists(md_file_path):
266+
with open(md_file_path, 'w') as md_file:
267+
md_file.write(self._get_request(url_str))
268+
except Exception as e:
269+
raise RuntimeError("Failed to create folder structure for URL '{}': {}".format(url_str, str(e)))
270+
271+
def _get_request(self, url_str):
272+
# Get the list of IHttpRequestResponse objects in the target scope
273+
http_messages = self._callbacks.getProxyHistory()
274+
for message in http_messages:
275+
url = self._helpers.analyzeRequest(message).getUrl()
276+
if url.toString() == url_str:
277+
request_info = self._helpers.analyzeRequest(message)
278+
headers = request_info.getHeaders()
279+
body = message.getRequest()[request_info.getBodyOffset():]
280+
281+
# Format request data
282+
request_data = "## Request" + "\n\n" + "```\n" + "\n".join(headers) + "\n\n" + self._helpers.bytesToString(body) + "\n" + "```" + "\n\n" + "## Information"
283+
return request_data
284+
return "Request data not found for URL: {}".format(url_str)

0 commit comments

Comments
 (0)