Skip to content

Commit

Permalink
Merge branch 'DolphFlynn-main'
Browse files Browse the repository at this point in the history
  • Loading branch information
PortSwiggerSupport committed Feb 26, 2024
2 parents 7bfa376 + 6065d71 commit 44aea9a
Show file tree
Hide file tree
Showing 32 changed files with 1,872 additions and 494 deletions.
4 changes: 2 additions & 2 deletions BappManifest.bmf
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ Uuid: 26aaa5ded2f74beea19e2ed8345a93dd
ExtensionType: 1
Name: JWT Editor
RepoName: jwt-editor
ScreenVersion: 2.1.1
ScreenVersion: 2.2
SerialVersion: 10
MinPlatformVersion: 8
ProOnly: False
Author: Fraser Winterborn and Dolph Flynn.
ShortDescription: Edit, sign, verify, encrypt and decrypt JSON Web Tokens (JWTs).
EntryPoint: build/libs/jwt-editor-2.1.1.jar
EntryPoint: build/libs/jwt-editor-2.2.jar
BuildCommand: ./gradlew jar
SupportedProducts: Pro, Community
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ Additionally it facilitates several well-known attacks against JWT implementatio

## Changelog

**2.2 2024-02-29**
- Allow resigning of JWS tokens during fuzzing (Thanks to [@BafDyce](https://github.com/BafDyce)).

**2.1.1 2024-01-22**
- Use split panes to improve JWT editor with small screens or large font sizes (Thanks to [@eldstal](https://github.com/eldstal)).

Expand Down Expand Up @@ -149,7 +152,7 @@ This option is automatically enabled if it is detected that the original JWT did
*JWT Editor* can be built from source.
* Ensure that Java JDK 17 or newer is installed
* From root of project, run the command `./gradlew jar`
* This should place the JAR file `jwt-editor-2.1.1.jar` within the `build/libs` directory
* This should place the JAR file `jwt-editor-2.2.jar` within the `build/libs` directory
* This can be loaded into Burp Suite by navigating to the `Extensions` tab, `Installed` sub-tab, clicking `Add` and loading the JAR file
* This BApp is using the newer Montoya API so it's best to use the latest version of Burp Suite (try the earlier adopter channel if there are issues with the latest stable release)

Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}

group = 'com.blackberry'
version = '2.1.1'
version = '2.2'
description = 'jwt-editor'

repositories {
Expand Down Expand Up @@ -36,7 +36,7 @@ dependencies {
'com.nimbusds:nimbus-jose-jwt:9.21',
'org.exbin.deltahex:deltahex-swing:0.1.2',
'com.fifesoft:rsyntaxtextarea:3.3.4',
'org.json:json:20231013',
'org.json:json:20240205',
'org.apache.commons:commons-lang3:3.14.0'
)
testImplementation(
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
bouncycastle_version=1.77
gui_designer_version=233.13135.104
gui_designer_version=233.14475.38
extender_version=2023.5
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
2 changes: 1 addition & 1 deletion src/main/java/burp/JWTEditorExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public void initialize(MontoyaApi api) {
);

Intruder intruder = api.intruder();
intruder.registerPayloadProcessor(new JWSPayloadProcessor(burpConfig.intruderConfig()));
intruder.registerPayloadProcessor(new JWSPayloadProcessor(burpConfig.intruderConfig(), api.logging(), keysModel));

if (api.burpSuite().version().edition() != COMMUNITY_EDITION) {
api.scanner().registerInsertionPointProvider(new JWSHeaderInsertionPointProvider(burpConfig.scannerConfig()));
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/burp/config/BurpConfigPersistence.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import burp.proxy.HighlightColor;
import burp.proxy.ProxyConfig;
import burp.scanner.ScannerConfig;
import com.nimbusds.jose.JWSAlgorithm;
import org.json.JSONException;
import org.json.JSONObject;

Expand All @@ -36,6 +37,7 @@ public class BurpConfigPersistence {
private static final String INTRUDER_FUZZ_PARAMETER_NAME = "intruder_payload_processor_parameter_name";
private static final String INTRUDER_FUZZ_RESIGNING = "intruder_payload_processor_resign";
private static final String INTRUDER_FUZZ_SIGNING_KEY_ID = "intruder_payload_processor_signing_key_id";
private static final String INTRUDER_FUZZ_SIGNING_ALGORITHM = "intruder_payload_processor_signing_algorithm";
private static final String SCANNER_INSERTION_POINT_PROVIDER_ENABLED_KEY = "scanner_insertion_point_provider_enabled";
private static final String SCANNER_INSERTION_PARAMETER_NAME = "scanner_insertion_point_provider_parameter_name";

Expand Down Expand Up @@ -77,6 +79,11 @@ public BurpConfig loadOrCreateNew() {
intruderConfig.setSigningKeyId(keyId);
}

if (parsedObject.has(INTRUDER_FUZZ_SIGNING_ALGORITHM) && parsedObject.get(INTRUDER_FUZZ_SIGNING_ALGORITHM) instanceof String algorithm) {
JWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(algorithm);
intruderConfig.setSigningAlgorithm(jwsAlgorithm);
}

if (parsedObject.has(INTRUDER_FUZZ_RESIGNING) && parsedObject.get(INTRUDER_FUZZ_RESIGNING) instanceof Boolean resign) {
intruderConfig.setResign(resign);
}
Expand Down Expand Up @@ -107,6 +114,11 @@ public void save(BurpConfig model) {
burpConfigJson.put(INTRUDER_FUZZ_PARAMETER_TYPE, model.intruderConfig().fuzzLocation());
burpConfigJson.put(INTRUDER_FUZZ_RESIGNING, model.intruderConfig().resign());
burpConfigJson.put(INTRUDER_FUZZ_SIGNING_KEY_ID, model.intruderConfig().signingKeyId());

JWSAlgorithm signingAlgorithm = model.intruderConfig().signingAlgorithm();
String signingAlgorithmName = signingAlgorithm == null ? null : signingAlgorithm.getName();
burpConfigJson.put(INTRUDER_FUZZ_SIGNING_ALGORITHM, signingAlgorithmName);

burpConfigJson.put(SCANNER_INSERTION_POINT_PROVIDER_ENABLED_KEY, model.scannerConfig().enableHeaderJWSInsertionPointLocation());
burpConfigJson.put(SCANNER_INSERTION_PARAMETER_NAME, model.scannerConfig().insertionPointLocationParameterName());

Expand Down
20 changes: 19 additions & 1 deletion src/main/java/burp/intruder/IntruderConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@

package burp.intruder;

import com.nimbusds.jose.JWSAlgorithm;

import static burp.intruder.FuzzLocation.PAYLOAD;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;

public class IntruderConfig {
private String fuzzParameter;
private FuzzLocation fuzzLocation;
private String signingKeyId;
private JWSAlgorithm signingAlgorithm;
private boolean resign;

public IntruderConfig() {
Expand Down Expand Up @@ -53,13 +57,27 @@ public String signingKeyId() {

public void setSigningKeyId(String signingKeyId) {
this.signingKeyId = signingKeyId;
this.resign = resign && canSign();
}

public boolean resign() {
return resign;
}

public void setResign(boolean resign) {
this.resign = resign;
this.resign = resign && canSign();
}

public JWSAlgorithm signingAlgorithm() {
return signingAlgorithm;
}

public void setSigningAlgorithm(JWSAlgorithm signingAlgorithm) {
this.signingAlgorithm = signingAlgorithm;
this.resign = resign && canSign();
}

private boolean canSign() {
return isNotEmpty(signingKeyId) && signingAlgorithm != null;
}
}
57 changes: 55 additions & 2 deletions src/main/java/burp/intruder/JWSPayloadProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,32 @@
import burp.api.montoya.intruder.PayloadData;
import burp.api.montoya.intruder.PayloadProcessingResult;
import burp.api.montoya.intruder.PayloadProcessor;
import burp.api.montoya.logging.Logging;
import com.blackberry.jwteditor.exceptions.SigningException;
import com.blackberry.jwteditor.model.jose.JOSEObject;
import com.blackberry.jwteditor.model.jose.JWS;
import com.blackberry.jwteditor.model.jose.JWSFactory;
import com.blackberry.jwteditor.model.keys.Key;
import com.blackberry.jwteditor.model.keys.KeysModel;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.util.Base64URL;
import org.json.JSONObject;

import java.util.Optional;

import static burp.intruder.FuzzLocation.PAYLOAD;
import static com.blackberry.jwteditor.model.jose.JOSEObjectFinder.parseJOSEObject;
import static com.nimbusds.jose.HeaderParameterNames.ALGORITHM;

public class JWSPayloadProcessor implements PayloadProcessor {
private final Logging logging;
private final IntruderConfig intruderConfig;
private final KeysModel keysModel;

public JWSPayloadProcessor(IntruderConfig intruderConfig) {
public JWSPayloadProcessor(IntruderConfig intruderConfig, Logging logging, KeysModel keysModel) {
this.logging = logging;
this.intruderConfig = intruderConfig;
this.keysModel = keysModel;
}

@Override
Expand All @@ -43,16 +53,59 @@ public PayloadProcessingResult processPayload(PayloadData payloadData) {
? Base64URL.encode(targetJson.toString())
: jws.getEncodedPayload();

JWS updatedJws = JWSFactory.jwsFromParts(updatedHeader, updatedPayload, jws.getEncodedSignature());
JWS updatedJws = createJWS(updatedHeader, updatedPayload, jws.getEncodedSignature());
baseValue = ByteArray.byteArray(updatedJws.serialize());
}
}

return PayloadProcessingResult.usePayload(baseValue);
}

private Optional<Key> loadKey() {
if (!intruderConfig.resign()) {
return Optional.empty();
}

Key key = keysModel.getKey(intruderConfig.signingKeyId());

if (key == null) {
logging.logToError("Key with ID " + intruderConfig.signingKeyId() + " not found.");
}

return Optional.ofNullable(key);
}

@Override
public String displayName() {
return "JWS payload processor";
}

// Creates a JWS object from the given attributes. Signs the JWS if possible (i.e., available key selected in Intruder settings)
private JWS createJWS(Base64URL header, Base64URL payload, Base64URL originalSignature) {
return loadKey()
.flatMap(key -> {
try {
JWSAlgorithm algorithm = intruderConfig.signingAlgorithm();

String headerJson = header.decodeToString();
JSONObject headerJsonObject = new JSONObject(headerJson);

Object originalAlgorithm = headerJsonObject.get(ALGORITHM);
headerJsonObject.put(ALGORITHM, algorithm.getName());

// Only update when alg different to preserve key order
Base64URL updatedHeader = originalAlgorithm instanceof JWSAlgorithm alg && alg.equals(algorithm)
? header
: Base64URL.encode(headerJsonObject.toString());

return Optional.of(JWSFactory.sign(key, algorithm, updatedHeader, payload));
} catch (SigningException ex) {
logging.logToError("Failed to sign JWS: " + ex);
return Optional.empty();
}
})
.orElseGet(
() -> JWSFactory.jwsFromParts(header, payload, originalSignature)
);
}
}
23 changes: 15 additions & 8 deletions src/main/java/com/blackberry/jwteditor/model/keys/KeysModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
package com.blackberry.jwteditor.model.keys;

import com.blackberry.jwteditor.exceptions.UnsupportedKeyException;
import com.blackberry.jwteditor.model.keys.KeysModelListener.InertKeyModelListener;
import org.json.JSONArray;
import org.json.JSONObject;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -37,12 +37,12 @@
public class KeysModel {
private final Map<String, Key> keys;
private final Object lock;
private KeysModelListener modelListener;

private final List<KeysModelListener> modelListeners;

public KeysModel() {
this.keys = new LinkedHashMap<>();
this.modelListener = new InertKeyModelListener();
this.modelListeners = new ArrayList<>();
this.lock = new Object();
}

Expand All @@ -53,7 +53,7 @@ public Iterable<Key> keys() {
}

public void addKeyModelListener(KeysModelListener modelListener) {
this.modelListener = modelListener;
this.modelListeners.add(modelListener);
}

/**
Expand Down Expand Up @@ -131,8 +131,13 @@ public void addKey(Key key) {
oldKey = keys.put(key.getID(), key);
}

modelListener.notifyKeyDeleted(oldKey);
modelListener.notifyKeyInserted(key);
for (KeysModelListener modelListener : modelListeners) {
if (oldKey != null) {
modelListener.notifyKeyDeleted(oldKey);
}

modelListener.notifyKeyInserted(key);
}
}

private int findIndexOfKeyWithId(String id) {
Expand Down Expand Up @@ -163,7 +168,9 @@ public void deleteKey(String keyId) {
}

if (rowIndex >= 0) {
modelListener.notifyKeyDeleted(rowIndex);
for (KeysModelListener modelListener : this.modelListeners) {
modelListener.notifyKeyDeleted(rowIndex);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public interface KeysModelListener {

void notifyKeyDeleted(Key key);

class InertKeyModelListener implements KeysModelListener {
class InertKeysModelListener implements KeysModelListener {
@Override
public void notifyKeyInserted(Key key) {
}
Expand All @@ -20,4 +20,27 @@ public void notifyKeyDeleted(int rowIndex) {
public void notifyKeyDeleted(Key key) {
}
}

class SimpleKeysModelListener implements KeysModelListener {
private final Runnable action;

public SimpleKeysModelListener(Runnable action) {
this.action = action;
}

@Override
public void notifyKeyInserted(Key key) {
action.run();
}

@Override
public void notifyKeyDeleted(int rowIndex) {
action.run();
}

@Override
public void notifyKeyDeleted(Key key) {
action.run();
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/blackberry/jwteditor/view/SuiteView.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,6 @@ private void createUIComponents() {
keysModel,
rstaFactory
);
configView = new ConfigView(burpConfig, userInterface, isProVersion);
configView = new ConfigView(burpConfig, userInterface, isProVersion, keysModel);
}
}
Loading

0 comments on commit 44aea9a

Please sign in to comment.