├── .gitignore
├── README.md
├── TODO.txt
├── pom.xml
└── src
├── main
├── java
│ └── burp
│ │ ├── BurpExtender.java
│ │ └── FormHelp.java
└── resources
│ ├── Burplay.properties
│ ├── FormHelp.html
│ └── define_session.png
└── test
└── java
└── burp
└── BurplayTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | work
3 | .DS*
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # burplay
2 | Burplay is a Burp Extension allowing for replaying any number of requests using same modifications definition. Its main purpose is to aid in searching for Privilege Escalation issues.
3 |
4 | Build:
5 | $ mvn package
6 |
--------------------------------------------------------------------------------
/TODO.txt:
--------------------------------------------------------------------------------
1 |
2 | - min width for right panel
3 | - handle burp connection problems (FIXED?)
4 | - add manual session definition
5 | - reset modifications
6 | - max width for session define dialog box
7 | - allow for defining a regex triggering priv escalation alert
8 | - multi-threading
9 |
10 |
11 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | UTF-8
6 |
7 |
8 |
9 |
10 | org.apache.maven.plugins
11 | maven-compiler-plugin
12 | 3.6.1
13 |
14 | 1.8
15 | 1.8
16 |
17 |
18 |
19 | exec-maven-plugin
20 | org.codehaus.mojo
21 | 1.6.0
22 |
23 |
24 | Version Calculation
25 | package
26 |
27 | exec
28 |
29 |
30 | ln
31 |
32 | -fnsv
33 | Burplay-${project.version}.jar
34 | target/Burplay.jar
35 |
36 |
37 |
38 |
39 |
40 |
41 | org.apache.maven.plugins
42 | maven-jar-plugin
43 | 3.0.2
44 |
45 |
46 |
47 | true
48 | true
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | src/main/resources
57 | true
58 |
59 |
60 | Burplay-${project.version}
61 |
62 | burp
63 | Burplay
64 | jar
65 | 0.6.2-beta
66 | Burplay
67 |
68 |
69 |
70 | junit
71 | junit
72 | 3.8.1
73 | test
74 |
75 |
76 | net.portswigger.burp.extender
77 | burp-extender-api
78 | 1.7.22
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/src/main/java/burp/BurpExtender.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | import javax.swing.*;
4 | import javax.swing.border.TitledBorder;
5 | import javax.swing.event.ListSelectionEvent;
6 | import javax.swing.event.ListSelectionListener;
7 | import javax.swing.table.AbstractTableModel;
8 | import javax.swing.table.TableColumn;
9 | import javax.swing.table.TableModel;
10 | import java.awt.*;
11 | import java.awt.event.ActionEvent;
12 | import java.awt.event.ActionListener;
13 | import java.awt.event.MouseEvent;
14 | import java.awt.event.MouseListener;
15 | import java.io.OutputStream;
16 | import java.io.PrintStream;
17 | import java.lang.reflect.Array;
18 | import java.util.*;
19 | import java.util.List;
20 |
21 | /**
22 | * Created by mtalecki on 05/07/2017.
23 | */
24 |
25 |
26 |
27 | public class BurpExtender implements IBurpExtender, IContextMenuFactory, ITab {
28 |
29 | private IBurpExtenderCallbacks callbacks;
30 | private IExtensionHelpers helpers;
31 |
32 | private PrintStream printStream;
33 | private PrintStream errorStream;
34 |
35 | // main panel
36 | private final JPanel panelMain = new JPanel();
37 |
38 | private String version;
39 |
40 | // progress panel
41 | private final JPanel panelProgress = new JPanel();
42 | private final JProgressBar barProgress = new JProgressBar();
43 | private final JButton buttonReplay = new JButton();
44 | private final JButton buttonReset = new JButton();
45 |
46 | // replay table and controller
47 | private final ReplayTableController replayTableController = new ReplayTableController();
48 | private final ReplayTable replayTable = new ReplayTable(replayTableController);
49 |
50 | // modification table and controller
51 | private final ModificationTableController modificationTableController = new ModificationTableController();
52 | private final ModificationTable modificationTable = new ModificationTable(modificationTableController);
53 | private final ModificationConfig modificationConfig = new ModificationConfig();
54 |
55 | // session table and controller
56 | private final SessionTableController sessionTableController = new SessionTableController();
57 | private final SessionTable sessionTable = new SessionTable(sessionTableController);
58 |
59 | // details tabbed pane
60 | private ReplayDetailsTabbedPane tabbedReplayDetails;
61 |
62 | public void registerExtenderCallbacks (IBurpExtenderCallbacks callbacks) {
63 |
64 | // keep a reference to our callbacks object
65 | this.callbacks = callbacks;
66 |
67 | // obtain an extension helpers object
68 | this.helpers = callbacks.getHelpers();
69 |
70 | // set our extension name
71 | this.callbacks.setExtensionName("Burplay");
72 |
73 | // context menu
74 | callbacks.registerContextMenuFactory(this);
75 |
76 | // stdout / stderr
77 | OutputStream stdOut = callbacks.getStdout();
78 | OutputStream stdErr = callbacks.getStderr();
79 | printStream = new PrintStream(stdOut);
80 | errorStream = new PrintStream(stdErr);
81 |
82 | Properties properties = new Properties();
83 |
84 | try {
85 | properties.load(this.getClass().getResourceAsStream("/Burplay.properties"));
86 | version = "v." + properties.getProperty("version");
87 | } catch (Exception e) {
88 | version = "";
89 | }
90 |
91 | // welcome message
92 | log(String.format("Burplay %s\nMichal Talecki ", version));
93 |
94 | SwingUtilities.invokeLater(new Runnable() {
95 | @Override
96 | public void run() {
97 | setUI();
98 | }
99 | });
100 | }
101 |
102 | private void setUI() {
103 | JPanel panelLeft = new JPanel();
104 | JPanel panelRight = new JPanel();
105 |
106 | // mainSplitPane
107 | JSplitPane mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
108 | panelLeft, panelRight);
109 | mainSplitPane.setResizeWeight(0);
110 | panelMain.setLayout(new BorderLayout());
111 | panelMain.add(mainSplitPane, BorderLayout.CENTER);
112 |
113 | // panelLeft
114 | panelLeft.setLayout(new BorderLayout());
115 | panelLeft.setMinimumSize(new Dimension(500,200));
116 |
117 | // panel progress
118 | panelProgress.setLayout(new BoxLayout(panelProgress, BoxLayout.X_AXIS));
119 | panelProgress.add(barProgress);
120 | panelProgress.add(buttonReplay);
121 |
122 | // button reset
123 | buttonReset.setAction(new ResetAction());
124 | buttonReset.setText("Reset");
125 |
126 | // button replay
127 | buttonReplay.setAction(new ReplayAction());
128 | buttonReplay.setActionCommand("replay");
129 | buttonReplay.setText("REPLAY!");
130 |
131 | // panel top (box layout)
132 | JPanel panelTop = new JPanel();
133 | panelTop.setLayout(new BoxLayout(panelTop, BoxLayout.X_AXIS));
134 |
135 |
136 | JButton buttonDoc = new JButton();
137 | buttonDoc.addActionListener(new HelpAction());
138 | buttonDoc.setText("?");
139 | panelTop.add(buttonDoc);
140 | panelTop.add(buttonReset);
141 | panelTop.add(panelProgress);
142 |
143 | // panelTop is PAGE START in panelLeft's BorderLayout
144 | panelLeft.add(panelTop, BorderLayout.PAGE_START);
145 |
146 |
147 | JScrollPane replayScrollPane = new JScrollPane(replayTable);
148 | panelLeft.add(replayScrollPane, BorderLayout.CENTER);
149 |
150 | JScrollPane modificationScrollPane = new JScrollPane(modificationTable);
151 | JScrollPane sessionScrollPane = new JScrollPane(sessionTable);
152 |
153 |
154 | JPanel panelConfig = new JPanel();
155 | panelConfig.setLayout(new BoxLayout(panelConfig, BoxLayout.Y_AXIS));
156 |
157 |
158 | // panel modifications
159 | JPanel panelModificationsAll = new JPanel();
160 | TitledBorder titleModifications;
161 | titleModifications = BorderFactory.createTitledBorder("Modifications");
162 | panelModificationsAll.setBorder(titleModifications);
163 | panelModificationsAll.setLayout(new BoxLayout(panelModificationsAll, BoxLayout.Y_AXIS));
164 |
165 | JPanel panelModifications = new JPanel();
166 | panelModifications.setLayout(new BoxLayout(panelModifications, BoxLayout.X_AXIS));
167 |
168 | // button remove modification
169 | JButton buttonRemoveModification = new JButton();
170 | buttonRemoveModification.setAction(new AddRemoveAction());
171 | buttonRemoveModification.setText("Remove");
172 | buttonRemoveModification.setActionCommand("removemod");
173 |
174 | panelModifications.add(modificationScrollPane);
175 | panelModifications.add(buttonRemoveModification);
176 |
177 | Dimension dimMod = modificationTable.getPreferredSize();
178 | modificationScrollPane.setPreferredSize(
179 | new Dimension(400,modificationTable.getRowHeight()*7+1));
180 | modificationScrollPane.setMaximumSize(
181 | new Dimension(400, modificationTable.getRowHeight()*7+1));
182 |
183 | panelModificationsAll.add(panelModifications);
184 | panelModificationsAll.add(modificationConfig);
185 |
186 |
187 |
188 |
189 | // panel sessions
190 | JPanel panelSessionsAll = new JPanel();
191 | TitledBorder titleSessions;
192 | titleSessions = BorderFactory.createTitledBorder("Sessions");
193 | panelSessionsAll.setBorder(titleSessions);
194 | panelSessionsAll.setLayout(new BoxLayout(panelSessionsAll, BoxLayout.Y_AXIS));
195 |
196 | JPanel panelSessions = new JPanel();
197 | panelSessions.setLayout(new BoxLayout(panelSessions, BoxLayout.X_AXIS));
198 |
199 | // button remove session
200 | JPanel panelSessionButtons = new JPanel();
201 | panelSessionButtons.setLayout(new BoxLayout(panelSessionButtons, BoxLayout.Y_AXIS));
202 |
203 | JButton buttonRemoveSession = new JButton();
204 | buttonRemoveSession.setAction(new AddRemoveAction());
205 | buttonRemoveSession.setText("Remove");
206 | buttonRemoveSession.setActionCommand("removeses");
207 |
208 | JButton buttonApplySession = new JButton();
209 | buttonApplySession.setAction(new AddRemoveAction());
210 | buttonApplySession.setText("Apply");
211 | buttonApplySession.setActionCommand("applyses");
212 |
213 | panelSessionButtons.add(buttonApplySession);
214 | panelSessionButtons.add(buttonRemoveSession);
215 |
216 |
217 | panelSessions.add(sessionScrollPane);
218 | panelSessions.add(panelSessionButtons);
219 |
220 | Dimension dimSes = sessionTable.getPreferredSize();
221 | sessionScrollPane.setPreferredSize(
222 | new Dimension(500,sessionTable.getRowHeight()*7+1));
223 | sessionScrollPane.setMaximumSize(
224 | new Dimension(500, sessionTable.getRowHeight()*7+1));
225 |
226 | panelSessions.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
227 | panelSessionsAll.add(panelSessions);
228 | panelSessionsAll.add(new JPanel()); // sizing hack
229 |
230 |
231 | // panel config
232 | panelConfig.add(new JSeparator(SwingConstants.HORIZONTAL));
233 | panelConfig.add(panelModificationsAll);
234 |
235 | panelConfig.add(new JSeparator(SwingConstants.HORIZONTAL));
236 | panelConfig.add(panelSessionsAll);
237 |
238 | //panelConfig.setBorder(BorderFactory.createEmptyBorder(30,30,30,30));
239 | panelLeft.add(panelConfig, BorderLayout.PAGE_END);
240 |
241 |
242 |
243 |
244 |
245 | // panelRight
246 | panelRight.setLayout(new BorderLayout());
247 | tabbedReplayDetails = new ReplayDetailsTabbedPane();
248 | tabbedReplayDetails.addReplayTab("Original");
249 | panelRight.add(tabbedReplayDetails);
250 |
251 |
252 |
253 | callbacks.customizeUiComponent(panelMain);
254 | // these should not be customized explicitly (should propagate from panelMain)
255 | //callbacks.customizeUiComponent(panelLeft);
256 | //callbacks.customizeUiComponent(panelRight);
257 | //callbacks.customizeUiComponent(panelRequest);
258 | //callbacks.customizeUiComponent(panelResponse);
259 | //callbacks.customizeUiComponent(replayScrollPane);
260 |
261 | callbacks.addSuiteTab(BurpExtender.this);
262 | }
263 |
264 | public class ReplayWorker extends SwingWorker {
265 |
266 | private List inputRequestResponseList;
267 | private List outputRequestResponseList;
268 |
269 | public ReplayWorker(List inp, List outp) {
270 | inputRequestResponseList = inp;
271 | outputRequestResponseList = outp;
272 | }
273 |
274 | private IHttpRequestResponse makeRequest(IHttpService s, byte[] req) {
275 | IHttpRequestResponse rr = null;
276 | try {
277 | rr = callbacks.makeHttpRequest(s, req);
278 | } catch (Exception e) {
279 | logError(e.toString() +": Error while requesting from " + s.getHost());
280 | }
281 | return rr;
282 | }
283 |
284 | @Override
285 | protected void process(List progress) {
286 | barProgress.setValue(progress.get(progress.size() - 1));
287 | }
288 |
289 | @Override
290 | protected Void doInBackground() throws Exception {
291 |
292 | //log("ReplayWorker thread started...");
293 |
294 | List listMods = modificationTableController.listModifications;
295 |
296 | for (int i = 0; i < inputRequestResponseList.size(); i++) {
297 | if (this.isCancelled()) {
298 | return null;
299 | }
300 | publish(i + 1);
301 |
302 | byte[] newReq = inputRequestResponseList.get(i).getRequest();
303 |
304 | for (int m = 0; m= 0 && selected < numRows) {
330 | replayTable.addRowSelectionInterval(selected, selected);
331 | }
332 | }
333 |
334 | }
335 |
336 | private class ReplayAction extends AbstractAction {
337 |
338 | private ReplayWorker replayWorker;
339 |
340 |
341 | @Override
342 | public void actionPerformed(ActionEvent e) {
343 | //log("replay action");
344 |
345 | if (e.getActionCommand().equals("cancel")) {
346 | replayWorker.cancel(true);
347 | replayWorker = null;
348 | return;
349 | }
350 |
351 | // base requests
352 | ReplayDetailsPanel panelCurrentDetails = tabbedReplayDetails.getOriginalDetailsPanel();
353 | List replayList = panelCurrentDetails.listHTTPRequestsResponses;
354 |
355 | // don't proceed with empty list
356 | if (replayList.size() == 0) {
357 | return;
358 | }
359 |
360 | // new tab
361 | // TODO give more specific name to a new tab
362 | ReplayDetailsPanel newDetailsPanel = tabbedReplayDetails.addReplayTab(null);
363 | tabbedReplayDetails.setSelectedIndex(tabbedReplayDetails.getTabCount()-1);
364 | List outputReplayList = newDetailsPanel.listHTTPRequestsResponses;
365 |
366 | // disable button so next replay can't be requested until the current one is done
367 | buttonReplay.setActionCommand("cancel");
368 | buttonReplay.setText("Cancel");
369 |
370 |
371 | buttonReset.setEnabled(false);
372 | barProgress.setMinimum(0);
373 | barProgress.setMaximum(replayList.size());
374 | barProgress.setStringPainted(true);
375 | replayWorker = new ReplayWorker(replayList, outputReplayList);
376 | replayTable.lastSelectedRow = replayTable.getSelectedRow();
377 | replayWorker.execute();
378 |
379 | //buttonReplay.setEnabled(true);
380 | //log("Requests done, updating model...");
381 | replayTableController.updateTableModel();
382 | //log ("Model updated.");
383 | }
384 | }
385 |
386 |
387 | private void log(String text) {
388 | printStream.println(text);
389 | printStream.flush();
390 | }
391 |
392 | private void logError(String text) {
393 | errorStream.println(text);
394 | errorStream.flush();
395 | }
396 |
397 | private class ModificationConfig extends JPanel implements ActionListener {
398 |
399 | private JComboBox comboParamType;
400 | private JTextField fieldParamName;
401 | private JCheckBox chkboxRemove;
402 | private JTextField fieldParamValue;
403 | private JButton buttonAddModification;
404 |
405 | public ModificationConfig() {
406 | comboParamType = new JComboBox<>();
407 | comboParamType.addItem("Header");
408 | comboParamType.addItem("Cookie");
409 | comboParamType.addItem("GET");
410 | comboParamType.addItem("POST");
411 | fieldParamName = new JTextField(10);
412 | fieldParamName.setToolTipText("Header/cookie/param name");
413 | chkboxRemove = new JCheckBox();
414 | chkboxRemove.setToolTipText("Remove this header/cookie/param");
415 | chkboxRemove.setSelected(false);
416 | chkboxRemove.addActionListener(this);
417 | fieldParamValue = new JTextField(10);
418 | fieldParamValue.setToolTipText("Header/cookie/param value");
419 | buttonAddModification = new JButton();
420 | buttonAddModification.setAction(new AddRemoveAction());
421 | buttonAddModification.setText("Add");
422 | buttonAddModification.setActionCommand("addmod");
423 |
424 |
425 | this.add(comboParamType);
426 | this.add(fieldParamName);
427 | this.add(chkboxRemove);
428 | this.add(fieldParamValue);
429 | this.add(buttonAddModification);
430 |
431 | }
432 |
433 | @Override
434 | public void actionPerformed(ActionEvent e) {
435 | fieldParamValue.setEnabled(!chkboxRemove.isSelected());
436 | }
437 | }
438 |
439 | private class ModificationTableController extends AbstractTableModel {
440 |
441 | public final List listModifications = new ArrayList<>();
442 | private final List data = new ArrayList<>();
443 | private String[] columnNames = {"Type", "Name", "Value"};
444 |
445 | public void addModification(ReplayModification mod) {
446 | listModifications.add(mod);
447 | updateTableModel();
448 | }
449 |
450 | public void removeModification(int index) {
451 | listModifications.remove(index);
452 | updateTableModel();
453 | }
454 |
455 | public void updateTableModel() {
456 | data.clear();
457 |
458 | for (int i = 0; i < listModifications.size(); i++) {
459 | ReplayModification mod = listModifications.get(i);
460 | String[] row = new String[3];
461 | row[0] = mod.modTypeString;
462 | row[1] = mod.modName;
463 | row[2] = mod.modValue;
464 | data.add(row);
465 | }
466 | //log("updating model...");
467 | fireTableDataChanged();
468 | }
469 |
470 | @Override
471 | public int getRowCount() {
472 | return data.size();
473 | }
474 |
475 | @Override
476 | public int getColumnCount() {
477 | return 3;
478 | }
479 |
480 | public String getColumnName(int column) {
481 | return columnNames[column];
482 | }
483 |
484 | @Override
485 | public Object getValueAt(int rowIndex, int columnIndex) {
486 | String[] row = data.get(rowIndex);
487 | if (columnIndex == 2 && row[columnIndex] == null) {
488 | return "-- REMOVE --";
489 | }
490 | return row[columnIndex];
491 | }
492 | }
493 |
494 | private class ModificationTable extends JTable {
495 | public ModificationTable(ModificationTableController modificationTableController) {
496 | super(modificationTableController);
497 | this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
498 | }
499 | }
500 |
501 | private class BurplaySession {
502 | String sessionName;
503 | String cookieName;
504 | String cookieValue;
505 |
506 | public BurplaySession (String sName, String cName, String cValue) {
507 | sessionName = sName;
508 | cookieName = cName;
509 | cookieValue = cValue;
510 | }
511 | }
512 |
513 | private class SessionTableController extends AbstractTableModel {
514 |
515 | public final List listSessions = new ArrayList<>();
516 | private final List data = new ArrayList<>();
517 | private String[] columnNames = {"Session Name", "Cookie Name", "Cookie Value"};
518 |
519 | public void addSession(BurplaySession s) {
520 | listSessions.add(s);
521 | updateTableModel();
522 | }
523 |
524 | public void removeSession(int index) {
525 | listSessions.remove(index);
526 | updateTableModel();
527 | }
528 |
529 | public void updateTableModel() {
530 | data.clear();
531 |
532 | for (int i = 0; i < listSessions.size(); i++) {
533 | BurplaySession s = listSessions.get(i);
534 | String[] row = new String[3];
535 | row[0] = s.sessionName;
536 | row[1] = s.cookieName;
537 | row[2] = s.cookieValue;
538 | data.add(row);
539 | }
540 | //log("updating model...");
541 | fireTableDataChanged();
542 | }
543 |
544 | @Override
545 | public int getRowCount() {
546 | return data.size();
547 | }
548 |
549 | @Override
550 | public int getColumnCount() {
551 | return 3;
552 | }
553 |
554 | public String getColumnName(int column) {
555 | return columnNames[column];
556 | }
557 |
558 | @Override
559 | public Object getValueAt(int rowIndex, int columnIndex) {
560 | String[] row = data.get(rowIndex);
561 | return row[columnIndex];
562 | }
563 | }
564 |
565 | private class SessionTable extends JTable {
566 | public SessionTable(SessionTableController sessionTableController) {
567 | super(sessionTableController);
568 | this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
569 | }
570 | }
571 |
572 |
573 | private class ReplayContextMenu extends JPopupMenu implements ActionListener {
574 | JMenuItem miRemoveRequests;
575 | ReplayContextMenu() {
576 | super();
577 | miRemoveRequests = new JMenuItem("Remove request(s)");
578 | miRemoveRequests.setActionCommand("removeRequests");
579 | miRemoveRequests.addActionListener(this);
580 | this.add(miRemoveRequests);
581 | }
582 |
583 | @Override
584 | public void actionPerformed(ActionEvent e) {
585 | String actCommand = e.getActionCommand();
586 | if (actCommand == "removeRequests") {
587 | replayTableController.removeRequests(replayTable.getSelectedRows());
588 | }
589 | }
590 | }
591 |
592 | private class ReplayTable extends JTable implements MouseListener{
593 | private int lastSelectedRow = 0;
594 | public ReplayTable(TableModel tableModel) {
595 | super(tableModel);
596 | this.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
597 | this.getTableHeader().setReorderingAllowed(true);
598 | //this.getTableHeader().setResizingAllowed(false);
599 | // this.setRowSelectionAllowed(true);
600 | // this.setCellSelectionEnabled(false);
601 | // this.setColumnSelectionAllowed(false);
602 | this.selectionModel.addListSelectionListener((ReplayTableController)tableModel);
603 | TableColumn indexColumn = this.getColumnModel().getColumn(0);
604 | indexColumn.setMaxWidth(40);
605 | TableColumn methodColumn = this.getColumnModel().getColumn(1);
606 | methodColumn.setMaxWidth(60);
607 | this.addMouseListener(this);
608 | }
609 |
610 | @Override
611 | public void mouseClicked(MouseEvent e) {
612 |
613 | }
614 |
615 | @Override
616 | public void mousePressed(MouseEvent e) {
617 | if (e.isPopupTrigger()) {
618 | ReplayContextMenu cm = new ReplayContextMenu();
619 | cm.show(e.getComponent(), e.getX(), e.getY());
620 | }
621 | }
622 |
623 | @Override
624 | public void mouseReleased(MouseEvent e) {
625 |
626 | }
627 |
628 | @Override
629 | public void mouseEntered(MouseEvent e) {
630 |
631 | }
632 |
633 | @Override
634 | public void mouseExited(MouseEvent e) {
635 |
636 | }
637 | }
638 |
639 | // JPanel
640 | @Override
641 | public String getTabCaption() {
642 | return "Replay";
643 | }
644 |
645 | @Override
646 | public Component getUiComponent() {
647 | return panelMain;
648 | }
649 |
650 | // IContextMenuFactory
651 | @Override
652 | public List createMenuItems(IContextMenuInvocation invocation) {
653 |
654 | byte invocationContext = invocation.getInvocationContext();
655 |
656 | if (invocationContext == IContextMenuInvocation.CONTEXT_PROXY_HISTORY ||
657 | invocationContext == IContextMenuInvocation.CONTEXT_TARGET_SITE_MAP_TABLE ) {
658 | List menu = new ArrayList<>();
659 | Action sendToReplayAction = new SendToReplayAction("Send to Replay", invocation);
660 | JMenuItem sendToReplayMenuItem = new JMenuItem(sendToReplayAction);
661 | menu.add(sendToReplayMenuItem);
662 | return menu;
663 | }
664 |
665 | if (invocationContext == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST ||
666 | invocationContext == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST ||
667 | invocationContext == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_RESPONSE ||
668 | invocationContext == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_RESPONSE) {
669 | List menu = new ArrayList<>();
670 | Action defineSessionAction = new DefineSessionAction("Define Burplay session", invocation);
671 | JMenuItem defineSessionMenuItem = new JMenuItem(defineSessionAction);
672 | menu.add(defineSessionMenuItem);
673 | return menu;
674 | }
675 | return null;
676 | }
677 |
678 | class DefineSessionAction extends AbstractAction {
679 |
680 | IContextMenuInvocation invocation;
681 |
682 | public DefineSessionAction(String text, IContextMenuInvocation invocation) {
683 | super(text);
684 | this.invocation = invocation;
685 | }
686 |
687 | // this handles defining session
688 | @Override
689 | public void actionPerformed(ActionEvent e) {
690 | //log("defining session");
691 | IHttpRequestResponse[] reqsResps = invocation.getSelectedMessages();
692 | if (reqsResps.length != 1) { return; }
693 | int[] bounds = invocation.getSelectionBounds();
694 | if (bounds.length != 2 || bounds[0] == bounds[1]) { return; }
695 |
696 | byte context = invocation.getInvocationContext();
697 | byte[] messageContent;
698 | if (context == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST ||
699 | context == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST) {
700 | messageContent = reqsResps[0].getRequest();
701 | } else {
702 | messageContent = reqsResps[0].getResponse();
703 | }
704 |
705 | String selected = helpers.bytesToString(messageContent).substring(bounds[0], bounds[1]);
706 |
707 | String[] cookieData = selected.split("=");
708 | if (cookieData.length != 2) { return; }
709 |
710 | String dialogMessage = String.format("Enter name for the following session cookie details:\n\n%s\n\n", selected);
711 |
712 | String sesName = JOptionPane.showInputDialog((Component)e.getSource(), dialogMessage,"Define Burplay session", JOptionPane.QUESTION_MESSAGE);
713 | if (sesName == null) {
714 | return;
715 | }
716 |
717 | BurplaySession newSession = new BurplaySession(sesName, cookieData[0], cookieData[1]);
718 | sessionTableController.addSession(newSession);
719 | }
720 | }
721 |
722 |
723 | // this is a table model but also a controller for details tabs
724 | class ReplayTableController extends AbstractTableModel implements ListSelectionListener {
725 | //private final String[] columnNames = {"Host", "Method", "URL"};
726 | private final String[] columnNames = {"#","Method", "URL"};
727 | //public final List replayTable = new ArrayList<>();
728 |
729 | // list for table
730 | private final List data = new ArrayList<>();
731 |
732 | public void addRequestsResponses(IHttpRequestResponse[] rrs) {
733 | // update tabbed details
734 | for (IHttpRequestResponse rr: rrs) {
735 | tabbedReplayDetails.addRequestResponse(rr);
736 | }
737 | // update table model
738 | updateTableModel();
739 | }
740 |
741 | public void resetTable() {
742 | data.clear();
743 | fireTableDataChanged();
744 | tabbedReplayDetails.resetTabs();
745 | }
746 |
747 | // TODO make it better
748 | public void removeRequests(int[] rows) {
749 | tabbedReplayDetails.removeRequests(rows);
750 | this.updateTableModel();
751 | }
752 |
753 |
754 | public void updateTableModel() {
755 | data.clear();
756 | // this is safe because we update this one first
757 | List rrs = tabbedReplayDetails.getOriginalDetailsPanel().listHTTPRequestsResponses;
758 |
759 | for (int i = 0; i < rrs.size(); i++) {
760 | IHttpRequestResponse reqres = rrs.get(i);
761 | //IHttpService httpService = reqres.getHttpService();
762 | IRequestInfo reqInfo = helpers.analyzeRequest(reqres);
763 | String method = reqInfo.getMethod();
764 | String url = reqInfo.getUrl().toString();
765 | String[] row = new String[3];
766 | row[0] = Integer.toString(i+1);
767 | row[1] = method;
768 | row[2] = url;
769 | data.add(row);
770 | }
771 | //log("updating model...");
772 | fireTableDataChanged();
773 | }
774 |
775 | @Override
776 | public int getRowCount() {
777 | if (data == null) {
778 | return 0;
779 | }
780 | return data.size();
781 | }
782 |
783 | public String getColumnName(int c) {
784 | return columnNames[c];
785 | }
786 |
787 | @Override
788 | public int getColumnCount() {
789 | return 3;
790 | }
791 |
792 |
793 | @Override
794 | public Object getValueAt(int rowIndex, int columnIndex) {
795 | return data.get(rowIndex)[columnIndex];
796 | }
797 |
798 | public void valueChanged(ListSelectionEvent e) {
799 | ListSelectionModel lsm = (ListSelectionModel) e.getSource();
800 | int selected = lsm.getMinSelectionIndex();
801 | if (selected >= 0) {
802 | tabbedReplayDetails.chooseRequestNo(selected);
803 |
804 | }
805 | }
806 |
807 | }
808 |
809 | class SendToReplayAction extends AbstractAction {
810 |
811 | IContextMenuInvocation invocation;
812 |
813 | public SendToReplayAction(String text, IContextMenuInvocation invocation) {
814 | super(text);
815 | this.invocation = invocation;
816 | }
817 |
818 | // this handles adding requests to the extension
819 | @Override
820 | public void actionPerformed(ActionEvent e) {
821 | IHttpRequestResponse[] selectedReqsResps = this.invocation.getSelectedMessages();
822 | replayTableController.addRequestsResponses(selectedReqsResps);
823 | }
824 | }
825 |
826 | class ResetAction extends AbstractAction {
827 |
828 | @Override
829 | public void actionPerformed(ActionEvent e) {
830 | int yesno = JOptionPane.showConfirmDialog(panelMain, "Remove all requests?",
831 | "Reset Confirmation", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
832 | if (yesno == JOptionPane.NO_OPTION) {
833 | return;
834 | }
835 | replayTableController.resetTable();
836 | }
837 | }
838 |
839 | class HelpAction extends AbstractAction {
840 |
841 | @Override
842 | public void actionPerformed(ActionEvent e) {
843 | FormHelp formHelp = new FormHelp();
844 | formHelp.pack();
845 | formHelp.setVisible(true);
846 | }
847 | }
848 |
849 | class AddRemoveAction extends AbstractAction {
850 |
851 | @Override
852 | public void actionPerformed(ActionEvent e) {
853 | String actionCommand = e.getActionCommand();
854 | switch(actionCommand) {
855 | case "addmod":
856 | String t = modificationConfig.comboParamType.getSelectedItem().toString();
857 | String n = modificationConfig.fieldParamName.getText();
858 | String v = (modificationConfig.chkboxRemove.isSelected())?null:modificationConfig.fieldParamValue.getText();
859 | ReplayModification modAdd = new ReplayModification(t, n, v);
860 | modificationTableController.addModification(modAdd);
861 | modificationConfig.fieldParamName.setText("");
862 | modificationConfig.chkboxRemove.setSelected(false);
863 | modificationConfig.fieldParamValue.setEnabled(true);
864 | modificationConfig.fieldParamValue.setText("");
865 | break;
866 | case "removemod":
867 | int rowMod = modificationTable.getSelectedRow();
868 | if (rowMod >= 0 ) {
869 | modificationTableController.removeModification(rowMod);
870 | }
871 | break;
872 | case "removeses":
873 | int rowSes = sessionTable.getSelectedRow();
874 | if (rowSes >= 0) {
875 | sessionTableController.removeSession(rowSes);
876 | }
877 | break;
878 | case "applyses":
879 | int rowSesApply = sessionTable.getSelectedRow();
880 | BurplaySession ses;
881 | if (rowSesApply >= 0) {
882 | ses = sessionTableController.listSessions.get(rowSesApply);
883 | } else {
884 | return;
885 | }
886 | ReplayModification modApply = new ReplayModification("Cookie", ses.cookieName, ses.cookieValue);
887 | modificationTableController.addModification(modApply);
888 | break;
889 | }
890 |
891 | }
892 | }
893 |
894 | class ReplayDetailsTabbedPane extends JTabbedPane {
895 |
896 | public int currentTab = 1;
897 |
898 | public ReplayDetailsTabbedPane() {
899 | super();
900 | }
901 |
902 | public ReplayDetailsPanel addReplayTab(String name) {
903 |
904 | ReplayDetailsPanel panel = new ReplayDetailsPanel();
905 | if (name == null) {
906 | name = Integer.toString(currentTab);
907 | currentTab++;
908 | }
909 |
910 | // if (parentName != null) {
911 | // parentName = parentName.replaceAll("\\(.*\\)", "");
912 | // name += String.format("(%s)", parentName);
913 | // }
914 |
915 | this.addTab(name, panel);
916 | return panel;
917 | }
918 |
919 | public void removeRequests(int[] rows) {
920 | int tabCount = this.getTabCount();
921 | for (int i = 0; i0 ; i--) {
931 | this.remove(i);
932 | }
933 | this.updateUI();
934 | this.getOriginalDetailsPanel().listHTTPRequestsResponses.clear();
935 | this.getOriginalDetailsPanel().updateMessages(-1);
936 | currentTab = 1;
937 | }
938 |
939 | // TODO this should add request and response to the original reqres tab
940 | // TODO should also add the request to other tabs, so they can issue them and get responses
941 | public void addRequestResponse(IHttpRequestResponse reqres) {
942 | ReplayDetailsPanel panelOriginalDetails = (ReplayDetailsPanel) this.getComponentAt(0);
943 | panelOriginalDetails.addRequestResponse(reqres);
944 | }
945 |
946 |
947 | public ReplayDetailsPanel getOriginalDetailsPanel() {
948 | ReplayDetailsPanel panelDetails = (ReplayDetailsPanel) this.getComponentAt(0);
949 | return panelDetails;
950 | }
951 |
952 | public ReplayDetailsPanel getCurrentDetailsPanel() {
953 | ReplayDetailsPanel panelDetails = (ReplayDetailsPanel) this.getSelectedComponent();
954 | return panelDetails;
955 | }
956 |
957 |
958 | public void chooseRequestNo(int n) {
959 | for (int i = 0; i listHTTPRequestsResponses = new ArrayList<>();
998 | private IMessageEditor messageEditorRequest;
999 | private IMessageEditor messageEditorResponse;
1000 | private MessageEditorController messageEditorController = new MessageEditorController();
1001 |
1002 | public ReplayDetailsPanel() {
1003 | super();
1004 | messageEditorRequest = callbacks.createMessageEditor(messageEditorController, false);
1005 | messageEditorResponse = callbacks.createMessageEditor(messageEditorController, false);
1006 | JPanel panelRequest = new JPanel();
1007 | JPanel panelResponse = new JPanel();
1008 |
1009 | JSplitPane detailsSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
1010 | panelRequest, panelResponse);
1011 | detailsSplitPane.setResizeWeight(0.5);
1012 | panelRequest.setLayout(new BorderLayout());
1013 | panelResponse.setLayout(new BorderLayout());
1014 |
1015 | panelRequest.add(messageEditorRequest.getComponent());
1016 | panelResponse.add(messageEditorResponse.getComponent());
1017 |
1018 | this.setLayout(new BorderLayout());
1019 | this.add(detailsSplitPane, BorderLayout.CENTER);
1020 | }
1021 |
1022 | public void addRequestResponse(IHttpRequestResponse reqres) {
1023 | this.listHTTPRequestsResponses.add(reqres);
1024 | }
1025 |
1026 | public void removeRequest(int row) {
1027 | listHTTPRequestsResponses.remove(row);
1028 | }
1029 |
1030 | // TODO fixit
1031 | public void removeRequests(int[] rows) {
1032 | List newList = new ArrayList<>();
1033 | for (int i=0; i headers = requestInfo.getHeaders();
1138 | Iterator iterator = headers.iterator();
1139 | // TODO collection.removeIf
1140 | while (iterator.hasNext()) {
1141 | if (iterator.next().startsWith(modName + ":")) {
1142 | iterator.remove();
1143 | }
1144 | }
1145 | // modValue == null means remove
1146 | if (modValue != null) {
1147 | headers.add(String.format("%s: %s", modName, modValue));
1148 | }
1149 | byte[] body = Arrays.copyOfRange(inputRequest, requestInfo.getBodyOffset(), inputRequest.length);
1150 | newReq = helpers.buildHttpMessage(headers, body);
1151 | } else {
1152 | // this is Cookie, GET or POST
1153 | newReq = inputRequest.clone();
1154 | List params = requestInfo.getParameters();
1155 | for (IParameter p: params) {
1156 | if (p.getName().equals(modName) && this.isOfType(p.getType())) {
1157 | newReq = helpers.removeParameter(newReq,p);
1158 | }
1159 | }
1160 | // modValue == null means remove
1161 | if (modValue != null) {
1162 | newParam = helpers.buildParameter(modName, modValue, paramType);
1163 | newReq = helpers.addParameter(newReq, newParam);
1164 | }
1165 | }
1166 | return newReq;
1167 | }
1168 |
1169 | }
1170 | }
1171 |
1172 |
1173 |
--------------------------------------------------------------------------------
/src/main/java/burp/FormHelp.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 | import java.net.URL;
6 |
7 | /**
8 | * Created by mike on 7/24/17.
9 | */
10 |
11 | public class FormHelp extends JFrame {
12 |
13 | public FormHelp() {
14 | URL htmlFile = FormHelp.class.getResource("/FormHelp.html");
15 |
16 | JEditorPane editorPane;
17 | try {
18 | editorPane = new JEditorPane(htmlFile);
19 | } catch (Exception e) {
20 | editorPane = new JEditorPane();
21 | }
22 | this.setTitle("Burplay Manual");
23 | editorPane.setEditable(false);
24 | JScrollPane editorScroll = new JScrollPane(editorPane);
25 | editorScroll.setPreferredSize(new Dimension(600,400));
26 | this.add(editorScroll);
27 |
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/resources/Burplay.properties:
--------------------------------------------------------------------------------
1 | version=${project.version}
2 |
--------------------------------------------------------------------------------
/src/main/resources/FormHelp.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Burplay Help
6 |
7 |
8 |
9 | Burplay
10 | Burplay is a Burp Extension allowing for replaying any number of requests using same modifications definition.
11 | Its main purpose is to aid in searching for Privilege Escalation issues.
12 |
13 |
14 | Request List
15 |
16 | Add requests to Burplay from Proxy -> HTTP History or Target -> Site map ->Contents table by choosing "Send to Replay" from the context menu.
17 |
18 |
19 | Modifications
20 |
21 | Define any number of modifications by adding them to the modifications table.
22 | They will be applied to the base requests ("Original" tab).
23 | Four types of modifications are available:
24 |
25 | Header
26 | Cookie
27 | GET
28 | POST
29 |
30 | For each modification, if no Header/Cookie/parameter exists in the base request, it will be added, otherwise modified.
31 |
32 |
33 | Sessions
34 | You can define a Burplay session in any Request/Response Editor within Burp by selecting text
35 | (typically "SESSIONID=sessionid") and choosing "Define Burplay session" from the context menu.
36 |
37 |
38 |
39 |
40 | The session will be added to the Sessions table and can be applied as a modification with "Apply" button.
41 | Only cookie-based session identifiers are currently supported.
42 |
43 |
44 | Details
45 | Details tabs on the right hand side show the original set of requests (base requests) and each replay round
46 | in numbered tabs. You can compare responses manually or use Burp Comparer.
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/main/resources/define_session.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpiderLabs/burplay/2fdcea6b39ed470e4211ec157b55f483535e9e56/src/main/resources/define_session.png
--------------------------------------------------------------------------------
/src/test/java/burp/BurplayTest.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | import junit.framework.Test;
4 | import junit.framework.TestCase;
5 | import junit.framework.TestSuite;
6 |
7 | /**
8 | * Unit test for simple App.
9 | */
10 | public class BurplayTest extends TestCase
11 | {
12 | /**
13 | * Create the test case
14 | *
15 | * @param testName name of the test case
16 | */
17 | public BurplayTest( String testName )
18 | {
19 | super( testName );
20 | }
21 |
22 | /**
23 | * @return the suite of tests being tested
24 | */
25 | public static Test suite()
26 | {
27 | return new TestSuite( BurplayTest.class );
28 | }
29 |
30 | /**
31 | * Rigourous Test :-)
32 | */
33 | public void testApp()
34 | {
35 | assertTrue( true );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------