Skip to content

Commit 8e1a9e8

Browse files
committedFeb 6, 2021
#7 Passing multiple inputs as single file
1 parent c62b622 commit 8e1a9e8

23 files changed

+465
-325
lines changed
 

‎README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ New context-menu-entries can be added using the "Add"-button. Each entry consist
1919
* **%Q:** will be replaced with the url query
2020
* **%C:** will be replaced with the cookies
2121
* **%M:** will be replaced with the HTTP-method
22+
* **%O:** will be replaced with the HTTP-status-code
2223
* **%S:** will be replaced with the selected text
2324
* **%F:** will be replaced with the path to a temporary file containing the selected text
2425
* **%R:** will be replaced with the path to a temporary file containing the content of the focused request/response
@@ -32,7 +33,7 @@ New context-menu-entries can be added using the "Add"-button. Each entry consist
3233
![Burp-Send-To-Extension Add-/Edit-Dialog](images/burp-send-to-extension-add-edit-dialog.png)
3334

3435
In addition it is possible to customize how placeholders behave when multiple HTTP messages are selected by clicking the "Advanced"-button.
35-
By default each selected HTTP message forms a separate command. However, it is also possible to join all values of a specific placeholder using a custom separator.
36+
By default each selected HTTP message forms a separate command. However, it is also possible to join all values of a specific placeholder using a custom separator, or to store all values of a specific placeholder within a file.
3637

3738
![Burp-Send-To-Extension Advanced-Dialog](images/burp-send-to-extension-advanced-dialog.png)
3839

‎burp-send-to-extension/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
apply plugin: 'java'
22

33
group 'net.bytebutcher'
4-
version '1.5'
4+
version '1.6'
55

66
sourceCompatibility = 1.8
77

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package net.bytebutcher.burpsendtoextension.builder;
2+
3+
import com.google.common.collect.Lists;
4+
import com.google.common.collect.Maps;
5+
import net.bytebutcher.burpsendtoextension.models.CommandObject;
6+
import net.bytebutcher.burpsendtoextension.models.Context;
7+
import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser;
8+
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour;
9+
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour;
10+
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour;
11+
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour;
12+
13+
import java.io.File;
14+
import java.io.PrintWriter;
15+
import java.util.Comparator;
16+
import java.util.List;
17+
import java.util.Map;
18+
import java.util.Objects;
19+
import java.util.stream.Collectors;
20+
21+
public class CommandBuilder {
22+
23+
// The model of the context menu entry with the format string and various options.
24+
private final CommandObject commandObject;
25+
26+
// List of selected messages containing the placeholders and their values.
27+
private final List<Map<String, IPlaceholderParser>> placeholderMap;
28+
29+
// List of placeholders and merged values.
30+
private final Map<Placeholder, String> placeholderValues;
31+
32+
private final Context context;
33+
34+
private static class Placeholder {
35+
36+
private final String name;
37+
private final IPlaceholderBehaviour behaviour;
38+
39+
public Placeholder(CommandObject.Placeholder placeholder) {
40+
this.name = placeholder.getName();
41+
this.behaviour = placeholder.getBehaviour();
42+
}
43+
44+
@Override
45+
public boolean equals(Object o) {
46+
if (this == o) return true;
47+
if (o == null || getClass() != o.getClass()) return false;
48+
Placeholder that = (Placeholder) o;
49+
return Objects.equals(name, that.name) && Objects.equals(behaviour, that.behaviour);
50+
}
51+
52+
@Override
53+
public int hashCode() {
54+
return Objects.hash(name, behaviour);
55+
}
56+
}
57+
58+
public CommandBuilder(CommandObject commandObject, List<Map<String, IPlaceholderParser>> placeholderMap, Context context) throws Exception {
59+
this.commandObject = commandObject;
60+
this.placeholderMap = placeholderMap;
61+
this.context = context;
62+
this.placeholderValues = initPlaceholderValues();
63+
}
64+
65+
public Map<Placeholder, String> initPlaceholderValues() throws Exception {
66+
Map<Placeholder, String> placeholderValues = Maps.newHashMap();
67+
for (CommandObject.Placeholder coPlaceholder : commandObject.getPlaceholders()) {
68+
Placeholder cbPlaceholder = new Placeholder(coPlaceholder);
69+
if (!placeholderValues.containsKey(cbPlaceholder)) {
70+
if (coPlaceholder.getBehaviour() instanceof StringSeparatedPlaceholderBehaviour){
71+
// combine the values of all messages using the defined placeholder separator
72+
placeholderValues.put(cbPlaceholder, commandObject.getValid(placeholderMap, context).stream()
73+
.map(m -> m.get(coPlaceholder.getName()))
74+
.map(iPlaceholder -> iPlaceholder.getValue(context))
75+
.collect(Collectors.joining(((StringSeparatedPlaceholderBehaviour) coPlaceholder.getBehaviour()).getSeparator())));
76+
} else if (coPlaceholder.getBehaviour() instanceof FileSeparatedPlaceholderBehaviour){
77+
// combine the values of all messages and write them into a file.
78+
placeholderValues.put(cbPlaceholder, writeToFile(commandObject.getValid(placeholderMap, context).stream()
79+
.map(m -> m.get(coPlaceholder.getName()))
80+
.map(iPlaceholder -> iPlaceholder.getValue(context))
81+
.collect(Collectors.joining("\n"))));
82+
}
83+
}
84+
}
85+
return placeholderValues;
86+
}
87+
88+
/**
89+
* Returns the command while all placeholders are replaced with their associated value as String.
90+
* @throws Exception when retrieving/replacing a placeholder failed.
91+
*/
92+
public String build() throws Exception {
93+
try {
94+
List<String> result = Lists.newArrayList();
95+
boolean containsCommandSeparatedPlaceholderBehaviour = commandObject.getPlaceholders().stream()
96+
.map(CommandObject.Placeholder::getBehaviour)
97+
.anyMatch(c -> c instanceof CommandSeparatedPlaceholderBehaviour);
98+
if (containsCommandSeparatedPlaceholderBehaviour) {
99+
for (int messageIndex = 0; messageIndex < placeholderMap.size(); messageIndex++) {
100+
result.add(buildByMessage(messageIndex));
101+
}
102+
} else {
103+
result.add(buildByMessage(0));
104+
}
105+
return String.join("\n", result);
106+
} catch (RuntimeException e) {
107+
// Rethrow from unchecked to checked exception. We only deal with RuntimeException here, since streams
108+
// (here: placeholderMap.stream()) does not handle checked exceptions well.
109+
throw new Exception(e);
110+
}
111+
}
112+
113+
private String buildByMessage(int messageIndex) throws Exception {
114+
StringBuffer format = new StringBuffer(commandObject.getFormat());
115+
// For each placeholder, starting from the placeholder at the very end, replace it with the value from the message.
116+
List<CommandObject.Placeholder> placeholders = commandObject.getPlaceholders().stream().sorted(
117+
Comparator.comparing(CommandObject.Placeholder::getEnd).reversed()
118+
).collect(Collectors.toList());
119+
for (CommandObject.Placeholder placeholder : placeholders) {
120+
replaceCommandPlaceholder(placeholder, placeholderMap, messageIndex, format, context);
121+
}
122+
return format.toString();
123+
}
124+
125+
private void replaceCommandPlaceholder(CommandObject.Placeholder placeholder, List<Map<String, IPlaceholderParser>> placeholderMap, int messageIndex, StringBuffer command, Context context) throws Exception {
126+
String value;
127+
if (placeholderValues.containsKey(new Placeholder(placeholder))) {
128+
// merged values
129+
value = placeholderValues.get(new Placeholder(placeholder));
130+
} else {
131+
// command separated - use the value from the actual message
132+
value = placeholderMap.get(messageIndex).get(placeholder.getName()).getValue(context);
133+
}
134+
command.replace(placeholder.getStart(), placeholder.getEnd(), value);
135+
}
136+
137+
private String writeToFile(String value) throws Exception {
138+
try {
139+
File tmp = File.createTempFile("burp_", ".snd");
140+
PrintWriter out = new PrintWriter(tmp.getPath());
141+
out.write(value);
142+
out.flush();
143+
return tmp.getAbsolutePath();
144+
} catch (RuntimeException e) {
145+
throw new Exception(this.getClass().getSimpleName() + ": Error writing to temporary file!", e);
146+
}
147+
}
148+
}

‎burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedDialog.java

+10-19
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
package net.bytebutcher.burpsendtoextension.gui;
22

3-
import burp.BurpExtender;
43
import com.google.common.collect.Lists;
54
import com.intellij.uiDesigner.core.GridConstraints;
65
import com.intellij.uiDesigner.core.GridLayoutManager;
76
import com.intellij.uiDesigner.core.Spacer;
87
import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil;
9-
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour;
10-
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.PlaceholderBehaviour;
11-
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour;
8+
import net.bytebutcher.burpsendtoextension.models.CommandObject;
129

1310
import javax.swing.*;
1411
import java.awt.*;
@@ -17,17 +14,17 @@
1714

1815
public class SendToAddAdvancedDialog extends JDialog {
1916
private final Component parent;
20-
private final List<PlaceholderBehaviour> placeholderBehaviourList;
17+
private final List<CommandObject.Placeholder> placeholders;
2118
private JPanel contentPane;
2219
private JButton buttonOK;
2320
private JButton buttonCancel;
2421
private JPanel pnlPlaceholderBehaviour;
2522

2623
private boolean success = false;
2724

28-
public SendToAddAdvancedDialog(Component parent, List<PlaceholderBehaviour> placeholderBehaviourList) {
25+
public SendToAddAdvancedDialog(Component parent, List<CommandObject.Placeholder> placeholders) {
2926
this.parent = parent;
30-
this.placeholderBehaviourList = placeholderBehaviourList;
27+
this.placeholders = placeholders;
3128
$$$setupUI$$$();
3229

3330
setContentPane(contentPane);
@@ -63,12 +60,12 @@ public void actionPerformed(ActionEvent e) {
6360
}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
6461
}
6562

66-
public List<PlaceholderBehaviour> getPlaceholderBehaviourList() {
67-
List<PlaceholderBehaviour> placeholderBehaviourList = Lists.newArrayList();
63+
public List<CommandObject.Placeholder> getPlaceholders() {
64+
List<CommandObject.Placeholder> placeholders = Lists.newArrayList();
6865
for (Component component : pnlPlaceholderBehaviour.getComponents()) {
69-
placeholderBehaviourList.add(((SendToAddAdvancedPlaceholderBehaviourPanel) component).getPlaceholderBehaviour());
66+
placeholders.add(((SendToAddAdvancedPlaceholderBehaviourPanel) component).getPlaceholder());
7067
}
71-
return placeholderBehaviourList;
68+
return placeholders;
7269
}
7370

7471
private void onOK() {
@@ -82,14 +79,8 @@ private void onCancel() {
8279
}
8380

8481
public boolean run() {
85-
for (PlaceholderBehaviour placeholderBehaviour : placeholderBehaviourList) {
86-
if (placeholderBehaviour instanceof StringSeparatedPlaceholderBehaviour) {
87-
pnlPlaceholderBehaviour.add(new SendToAddAdvancedPlaceholderBehaviourPanel((StringSeparatedPlaceholderBehaviour) placeholderBehaviour));
88-
} else if (placeholderBehaviour instanceof CommandSeparatedPlaceholderBehaviour) {
89-
pnlPlaceholderBehaviour.add(new SendToAddAdvancedPlaceholderBehaviourPanel((CommandSeparatedPlaceholderBehaviour) placeholderBehaviour));
90-
} else {
91-
BurpExtender.printErr("Unknown placeholder behaviour! This is a programming error and should not happen!");
92-
}
82+
for (CommandObject.Placeholder placeholder : placeholders) {
83+
pnlPlaceholderBehaviour.add(new SendToAddAdvancedPlaceholderBehaviourPanel(placeholder));
9384
}
9485
this.setSize(450, 250);
9586
int x = DialogUtil.getX(parent, this);

‎burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedPlaceholderBehaviourPanel.form

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
<properties>
2424
<model>
2525
<item value="split into separate commands"/>
26-
<item value="separated by string"/>
26+
<item value="separate by string"/>
27+
<item value="merge into file"/>
2728
</model>
2829
</properties>
2930
</component>

‎burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedPlaceholderBehaviourPanel.java

+42-30
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package net.bytebutcher.burpsendtoextension.gui;
22

3+
import burp.BurpExtender;
34
import com.intellij.uiDesigner.core.GridConstraints;
45
import com.intellij.uiDesigner.core.GridLayoutManager;
6+
import net.bytebutcher.burpsendtoextension.models.CommandObject;
57
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour;
6-
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.PlaceholderBehaviour;
8+
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour;
9+
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour;
710
import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour;
811

912
import javax.swing.*;
@@ -13,57 +16,65 @@
1316

1417
public class SendToAddAdvancedPlaceholderBehaviourPanel extends JPanel {
1518

19+
private final CommandObject.Placeholder placeholder;
20+
1621
private JTextField txtSeparator;
1722
private JPanel pnlMain;
1823
private JComboBox cmbSeperator;
1924
private JLabel lblPlaceholder;
2025

21-
public SendToAddAdvancedPlaceholderBehaviourPanel(StringSeparatedPlaceholderBehaviour placeholderBehaviour) {
26+
public SendToAddAdvancedPlaceholderBehaviourPanel(CommandObject.Placeholder placeholder) {
27+
this.placeholder = placeholder;
2228
this.add(pnlMain);
23-
initFields(placeholderBehaviour);
29+
this.lblPlaceholder.setText(placeholder.getName());
30+
initFields();
2431
initEventListener();
2532
}
2633

27-
public SendToAddAdvancedPlaceholderBehaviourPanel(CommandSeparatedPlaceholderBehaviour placeholderBehaviour) {
28-
this.add(pnlMain);
29-
initFields(placeholderBehaviour);
30-
initEventListener();
31-
}
32-
33-
private void initFields(CommandSeparatedPlaceholderBehaviour placeholderBehaviour) {
34-
this.lblPlaceholder.setText(placeholderBehaviour.getPlaceholder());
35-
this.txtSeparator.setText("");
36-
this.txtSeparator.setEnabled(false);
37-
this.cmbSeperator.setSelectedIndex(0);
38-
}
39-
40-
private void initFields(StringSeparatedPlaceholderBehaviour placeholderBehaviour) {
41-
this.lblPlaceholder.setText(placeholderBehaviour.getPlaceholder());
42-
this.txtSeparator.setText(placeholderBehaviour.getSeparator());
43-
this.txtSeparator.setEnabled(true);
44-
this.cmbSeperator.setSelectedIndex(1);
34+
private void initFields() {
35+
IPlaceholderBehaviour placeholderBehaviour = placeholder.getBehaviour();
36+
if (placeholderBehaviour instanceof StringSeparatedPlaceholderBehaviour) {
37+
this.txtSeparator.setText(((StringSeparatedPlaceholderBehaviour) placeholderBehaviour).getSeparator());
38+
this.txtSeparator.setEnabled(true);
39+
this.cmbSeperator.setSelectedIndex(1);
40+
} else if (placeholderBehaviour instanceof FileSeparatedPlaceholderBehaviour) {
41+
this.txtSeparator.setText("");
42+
this.txtSeparator.setEnabled(false);
43+
this.cmbSeperator.setSelectedIndex(2);
44+
} else {
45+
this.txtSeparator.setText("");
46+
this.txtSeparator.setEnabled(false);
47+
this.cmbSeperator.setSelectedIndex(0);
48+
}
4549
}
4650

4751
private void initEventListener() {
4852
this.cmbSeperator.addActionListener(new ActionListener() {
4953
public void actionPerformed(ActionEvent e) {
50-
if (cmbSeperator.getSelectedIndex() == 0) {
51-
txtSeparator.setEnabled(false);
54+
if (cmbSeperator.getSelectedIndex() == 1) {
55+
txtSeparator.setEnabled(true);
5256
txtSeparator.setText("");
5357
} else {
54-
txtSeparator.setEnabled(true);
58+
txtSeparator.setEnabled(false);
5559
txtSeparator.setText("");
5660
}
5761
}
5862
});
5963
}
6064

61-
public PlaceholderBehaviour getPlaceholderBehaviour() {
62-
if (cmbSeperator.getSelectedIndex() == 0) {
63-
return new CommandSeparatedPlaceholderBehaviour(lblPlaceholder.getText());
64-
} else {
65-
return new StringSeparatedPlaceholderBehaviour(lblPlaceholder.getText(), txtSeparator.getText());
65+
public CommandObject.Placeholder getPlaceholder() {
66+
switch (cmbSeperator.getSelectedIndex()) {
67+
case 1:
68+
placeholder.setBehaviour(new StringSeparatedPlaceholderBehaviour(txtSeparator.getText()));
69+
break;
70+
case 2:
71+
placeholder.setBehaviour(new FileSeparatedPlaceholderBehaviour());
72+
break;
73+
default:
74+
placeholder.setBehaviour(new CommandSeparatedPlaceholderBehaviour());
75+
break;
6676
}
77+
return placeholder;
6778
}
6879

6980
{
@@ -88,7 +99,8 @@ public PlaceholderBehaviour getPlaceholderBehaviour() {
8899
cmbSeperator = new JComboBox();
89100
final DefaultComboBoxModel defaultComboBoxModel1 = new DefaultComboBoxModel();
90101
defaultComboBoxModel1.addElement("split into separate commands");
91-
defaultComboBoxModel1.addElement("separated by string");
102+
defaultComboBoxModel1.addElement("separate by string");
103+
defaultComboBoxModel1.addElement("merge into file");
92104
cmbSeperator.setModel(defaultComboBoxModel1);
93105
pnlMain.add(cmbSeperator, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
94106
lblPlaceholder = new JLabel();

0 commit comments

Comments
 (0)
Please sign in to comment.