Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non json #75

Merged
merged 9 commits into from
Dec 22, 2024
16 changes: 7 additions & 9 deletions src/main/java/burp/intruder/JWSPayloadProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@
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.jose.*;
import com.blackberry.jwteditor.model.keys.Key;
import com.blackberry.jwteditor.model.keys.KeysRepository;
import com.nimbusds.jose.JWSAlgorithm;
Expand Down Expand Up @@ -57,21 +55,21 @@ public PayloadProcessingResult processPayload(PayloadData payloadData) {

if (joseObject.isPresent() && (joseObject.get() instanceof JWS jws)) {
boolean fuzzPayload = intruderConfig.fuzzLocation() == PAYLOAD;
String targetData = fuzzPayload ? jws.getPayload() : jws.getHeader();
Header header = jws.header();
JWSClaims jwsClaims = jws.claims();
String targetData = fuzzPayload ? jwsClaims.decoded() : header.decoded();
JSONObject targetJson = new JSONObject(targetData);

if (targetJson.has(intruderConfig.fuzzParameter())) {
targetJson.put(intruderConfig.fuzzParameter(), payloadData.currentPayload().toString());

Base64URL updatedHeader = fuzzPayload
? jws.getEncodedHeader()
: Base64URL.encode(targetJson.toString());
Base64URL updatedHeader = fuzzPayload ? header.encoded() : Base64URL.encode(targetJson.toString());

Base64URL updatedPayload = fuzzPayload
? Base64URL.encode(targetJson.toString())
: jws.getEncodedPayload();
: jwsClaims.encoded();

JWS updatedJws = createJWS(updatedHeader, updatedPayload, jws.getEncodedSignature());
JWS updatedJws = createJWS(updatedHeader, updatedPayload, jws.signature().encoded());
baseValue = ByteArray.byteArray(updatedJws.serialize());
}
}
Expand Down
22 changes: 20 additions & 2 deletions src/main/java/burp/scanner/JWSHeaderInsertionPoint.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
/*
Author : Dolph Flynn

Copyright 2024 Dolph Flynn

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package burp.scanner;

import burp.api.montoya.core.ByteArray;
Expand Down Expand Up @@ -40,7 +58,7 @@ class JWSHeaderInsertionPoint implements AuditInsertionPoint {
baseRequestPostfix = endOffset == baseRequestBytes.length() ? new byte[0] : baseRequestBytes.subArray(endOffset, baseRequestBytes.length()).getBytes();

try {
headerJsonObject = new JSONObject(jws.getHeader());
headerJsonObject = jws.header().json();
} catch (JSONException e) {
throw new IllegalStateException("Could not parse JWS header!", e);
}
Expand Down Expand Up @@ -78,6 +96,6 @@ private byte[] buildWeaponizedJWS(ByteArray payload) {
headerJsonObject.put(headerParameterName, payload.toString());
Base64URL headerBase64 = Base64URL.encode(headerJsonObject.toString());

return "%s.%s.%s".formatted(headerBase64, jws.getEncodedPayload(), jws.getEncodedSignature()).getBytes(UTF_8);
return "%s.%s.%s".formatted(headerBase64, jws.claims().encoded(), jws.signature().encoded()).getBytes(UTF_8);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Author : Dolph Flynn

Copyright 2024 Dolph Flynn

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package com.blackberry.jwteditor.model.jose;

import com.nimbusds.jose.util.Base64URL;

public abstract class Base64Encoded {
private final Base64URL data;

Base64Encoded(Base64URL data) {
this.data = data;
}

public Base64URL encoded() {
return data;
}

public String decoded() {
return data.decodeToString();
}

@Override
public String toString() {
return data.toString();
}
}
24 changes: 24 additions & 0 deletions src/main/java/com/blackberry/jwteditor/model/jose/ClaimsType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
Author : Dolph Flynn

Copyright 2024 Dolph Flynn

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package com.blackberry.jwteditor.model.jose;

public enum ClaimsType {
JSON,
TEXT
}
47 changes: 47 additions & 0 deletions src/main/java/com/blackberry/jwteditor/model/jose/Header.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
Author : Dolph Flynn

Copyright 2024 Dolph Flynn

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package com.blackberry.jwteditor.model.jose;

import com.nimbusds.jose.util.Base64URL;
import org.json.JSONObject;

import static com.blackberry.jwteditor.utils.JSONUtils.isJsonCompact;
import static com.blackberry.jwteditor.utils.JSONUtils.prettyPrintJSON;

public class Header extends Base64Encoded {

public Header(Base64URL header) {
super(header);
}

public boolean isCompact()
{
return isJsonCompact(decoded());
}

public String decodeAndPrettyPrint()
{
return prettyPrintJSON(decoded());
}

public JSONObject json()
{
return new JSONObject(decoded());
}
}
21 changes: 5 additions & 16 deletions src/main/java/com/blackberry/jwteditor/model/jose/JOSEObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package com.blackberry.jwteditor.model.jose;

import com.blackberry.jwteditor.presenter.Information;
import com.nimbusds.jose.util.Base64URL;

import java.util.List;
Expand All @@ -26,25 +27,13 @@
* Abstract class representing common elements of JWE/JWT
*/
public abstract class JOSEObject {
final Base64URL header;
final Header header;

JOSEObject(Base64URL header) {
this.header = header;
this.header = new Header(header);
}

/**
* Get the JOSE header
* @return the JOSE header as a string
*/
public String getHeader(){
return header.decodeToString();
}

/**
* Get the encoded JOSE header
* @return the base64 encoded header value
*/
public Base64URL getEncodedHeader(){
public Header header() {
return header;
}

Expand All @@ -54,5 +43,5 @@ public Base64URL getEncodedHeader(){
*/
public abstract String serialize();

public abstract List<TimeClaim> timeClaims();
public abstract List<Information> information();
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.blackberry.jwteditor.utils.JSONUtils.isJsonObject;
import static com.nimbusds.jose.Header.MAX_HEADER_STRING_LENGTH;
import static com.nimbusds.jose.HeaderParameterNames.ALGORITHM;

public class JOSEObjectFinder {
public static final String BASE64_REGEX = "[A-Za-z0-9-_]";

private static final String JWS_REGEX = "e%s*\\.%s+\\.%s*".formatted(BASE64_REGEX, BASE64_REGEX, BASE64_REGEX);
private static final String JWS_REGEX = "e%s*\\.%s+\\.%s*[\\.^]?".formatted(BASE64_REGEX, BASE64_REGEX, BASE64_REGEX);
private static final String JWE_REGEX = "e%s*\\.%s*\\.%s+\\.%s+\\.%s+".formatted(BASE64_REGEX, BASE64_REGEX, BASE64_REGEX, BASE64_REGEX, BASE64_REGEX);
private static final Pattern JOSE_OBJECT_PATTERN = Pattern.compile("(%s)|(%s)".formatted(JWE_REGEX, JWS_REGEX));

Expand Down Expand Up @@ -128,10 +127,6 @@ private static Optional<JOSEObject> parseJWS(String candidate) {
throw new ParseException("Missing \"alg\" in header JSON object", 0);
}

if (!isJsonObject(parts[1].decodeToString())) {
throw new ParseException("Payload is invalid JSON object", 0);
}

return Optional.of(JWSFactory.parse(candidate));
} catch (ParseException e) {
return Optional.empty();
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/blackberry/jwteditor/model/jose/JWE.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import com.blackberry.jwteditor.exceptions.DecryptionException;
import com.blackberry.jwteditor.model.keys.Key;
import com.blackberry.jwteditor.presenter.Information;
import com.nimbusds.jose.JWEDecrypter;
import com.nimbusds.jose.JWEHeader;
import com.nimbusds.jose.util.Base64URL;
Expand Down Expand Up @@ -68,7 +69,7 @@ public String serialize() {
}

@Override
public List<TimeClaim> timeClaims() {
public List<Information> information() {
return emptyList();
}

Expand Down Expand Up @@ -117,7 +118,7 @@ public byte[] getIV() {
*/
public JWS decrypt(Key key) throws DecryptionException, ParseException {
// Parse the JWE header to get the decryption algorithms
JWEHeader header = JWEHeader.parse(this.header.decodeToString());
JWEHeader header = JWEHeader.parse(this.header.decoded());

try {
// Create a new decrypter with the header algs
Expand Down
46 changes: 17 additions & 29 deletions src/main/java/com/blackberry/jwteditor/model/jose/JWS.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import com.blackberry.jwteditor.exceptions.VerificationException;
import com.blackberry.jwteditor.model.keys.Key;
import com.blackberry.jwteditor.presenter.Information;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSVerifier;
Expand All @@ -34,9 +35,8 @@
* Class representing a JWS
*/
public class JWS extends JOSEObject {
private final Base64URL payload;
private final Base64URL signature;
private final List<TimeClaim> timeClaims;
private final JWSClaims claims;
private final Signature signature;

/**
* Construct a JWS from encoded components
Expand All @@ -46,46 +46,34 @@ public class JWS extends JOSEObject {
*/
JWS(Base64URL header, Base64URL payload, Base64URL signature) {
super(header);
this.payload = payload;
this.signature = signature;
this.timeClaims = TimeClaimFactory.fromPayloadJson(payload.decodeToString());
this.claims = new JWSClaims(payload);
this.signature = new Signature(signature);
}

/**
* Get the payload as a String
*
* @return the decoded payload
*/
public String getPayload() {
return payload.decodeToString();
public JWSClaims claims() {
return claims;
}

/**
* Get the encoded payload
*
* @return the base64 encoded payload
*/
public Base64URL getEncodedPayload() { return payload; }

public byte[] getSignature() {
return signature.decode();
public Signature signature()
{
return signature;
}

public Base64URL getEncodedSignature() { return signature; }

/**
* Serialize the JWS to compact form
*
* @return the JWS in compact form
*/
@Override
public String serialize() {
return "%s.%s.%s".formatted(header.toString(), payload.toString(), signature.toString());
return "%s.%s.%s".formatted(header.toString(), claims.toString(), signature.toString());
}

@Override
public List<TimeClaim> timeClaims() {
return timeClaims;
public List<Information> information() {
return claims.timeClaims().stream()
.map(Information::from)
.toList();
}

/**
Expand Down Expand Up @@ -114,15 +102,15 @@ public boolean verify(Key key, JWSHeader verificationInfo) throws VerificationEx
// Build the signing input
// JWS signature input is the ASCII bytes of the base64 encoded header and payload concatenated with a '.'
byte[] headerBytes = header.toString().getBytes(StandardCharsets.US_ASCII);
byte[] payloadBytes = payload.toString().getBytes(StandardCharsets.US_ASCII);
byte[] payloadBytes = claims.encoded().toString().getBytes(StandardCharsets.US_ASCII);
byte[] signingInput = new byte[headerBytes.length + 1 + payloadBytes.length];
System.arraycopy(headerBytes, 0, signingInput, 0, headerBytes.length);
signingInput[headerBytes.length] = '.';
System.arraycopy(payloadBytes, 0, signingInput, headerBytes.length + 1, payloadBytes.length);

// Verify the payload with the key and the algorithm provided
try {
return verifier.verify(verificationInfo, signingInput, signature);
return verifier.verify(verificationInfo, signingInput, signature.encoded());
} catch (JOSEException e) {
throw new VerificationException(e.getMessage());
}
Expand Down
Loading
Loading