Skip to content

Commit 5214f7d

Browse files
authored
Merge pull request #11 from SasanLabs/FuzzerChanges
Publicly well known secret checking attack.
2 parents 6d9e27c + 4c3022b commit 5214f7d

File tree

7 files changed

+3660
-3
lines changed

7 files changed

+3660
-3
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ All notable changes to this add-on will be documented in this file.
33

44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
55

6+
## Unreleased
7+
8+
### Added
9+
- Support for validating usage of publicly well known HMac secrets for signing JWT.
10+
611
## [1.0.0] - 2020-09-03
712

813
- First version of JWT Support.
914
- Contains scanning rules for basic JWT related vulnerabilities.
10-
- Contains JWT Fuzzer for fuzzing the JWT's present in the request.
15+
- Contains JWT Fuzzer for fuzzing the JWT's present in the request.

src/main/java/org/zaproxy/zap/extension/jwt/JWTConfiguration.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@
1919
*/
2020
package org.zaproxy.zap.extension.jwt;
2121

22+
import java.util.Arrays;
23+
import java.util.HashSet;
24+
import java.util.List;
25+
import java.util.Set;
2226
import org.apache.log4j.Logger;
2327
import org.zaproxy.zap.common.VersionedAbstractParam;
28+
import org.zaproxy.zap.extension.jwt.utils.JWTUtils;
2429

2530
/**
2631
* This class holds configuration related to JWT scanner.
@@ -53,7 +58,32 @@ public class JWTConfiguration extends VersionedAbstractParam {
5358
private boolean enableClientConfigurationScan;
5459
private static volatile JWTConfiguration jwtConfiguration;
5560

56-
private JWTConfiguration() {}
61+
/**
62+
* Files containing publicly well known JWT HMac Secrets.
63+
*
64+
* <p>There are publicly available well known JWT HMac Secrets. Special thanks to <a href=
65+
* "https://lab.wallarm.com/340-weak-jwt-secrets-you-should-check-in-your-code/">Wallarm.com</a>
66+
* for collating the list of such weak secrets and making them open-source.
67+
*/
68+
private static final List<String> FILE_NAMES_CONTAINING_PUBLICLY_KNOWN_HMAC_SECRETS =
69+
Arrays.asList("/weakKeys/wallarm_jwt_hmac_secrets_list");
70+
71+
private Set<String> publiclyKnownHMacSecrets = new HashSet<>();
72+
73+
private JWTConfiguration() {
74+
init();
75+
}
76+
77+
private void init() {
78+
FILE_NAMES_CONTAINING_PUBLICLY_KNOWN_HMAC_SECRETS.stream()
79+
.forEach(
80+
(fileName) -> {
81+
Set<String> values = JWTUtils.readFileContentsFromResources(fileName);
82+
if (values != null && values.size() != 0) {
83+
publiclyKnownHMacSecrets.addAll(values);
84+
}
85+
});
86+
}
5787

5888
public static JWTConfiguration getInstance() {
5989
if (jwtConfiguration == null) {
@@ -130,4 +160,14 @@ protected void parseImpl() {
130160

131161
@Override
132162
protected void updateConfigsImpl(int fileVersion) {}
163+
164+
/**
165+
* There are publicly available well known JWT HMac Secrets. This API provides all those secrets
166+
* and help in finding JWT's signed using these weak secrets.
167+
*
168+
* @return publicly known HMac Secrets
169+
*/
170+
public Set<String> getPubliclyKnownHMacSecrets() {
171+
return publiclyKnownHMacSecrets;
172+
}
133173
}

src/main/java/org/zaproxy/zap/extension/jwt/attacks/SignatureAttack.java

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,20 @@
5353
import java.security.NoSuchAlgorithmException;
5454
import java.security.cert.Certificate;
5555
import java.security.cert.CertificateException;
56+
import java.text.MessageFormat;
5657
import java.text.ParseException;
58+
import java.util.Set;
5759
import org.apache.commons.lang3.StringUtils;
5860
import org.apache.log4j.Logger;
5961
import org.json.JSONObject;
6062
import org.parosproxy.paros.Constant;
6163
import org.parosproxy.paros.core.scanner.Alert;
64+
import org.parosproxy.paros.core.scanner.Plugin.AttackStrength;
6265
import org.zaproxy.zap.extension.jwt.JWTConfiguration;
6366
import org.zaproxy.zap.extension.jwt.JWTHolder;
67+
import org.zaproxy.zap.extension.jwt.JWTI18n;
6468
import org.zaproxy.zap.extension.jwt.exception.JWTException;
69+
import org.zaproxy.zap.extension.jwt.utils.JWTConstants;
6570
import org.zaproxy.zap.extension.jwt.utils.JWTUtils;
6671
import org.zaproxy.zap.extension.jwt.utils.VulnerabilityType;
6772

@@ -79,6 +84,71 @@ public class SignatureAttack implements JWTAttack {
7984
"jwt.scanner.server.vulnerability.signatureAttack.";
8085
private ServerSideAttack serverSideAttack;
8186

87+
/**
88+
* There are publicly available well known JWT HMac Secrets and This attack checks if JWT is
89+
* signed using weak well known secret.
90+
*
91+
* <p>Special thanks to <a href=
92+
* "https://lab.wallarm.com/340-weak-jwt-secrets-you-should-check-in-your-code/">Wallarm.com</a>
93+
* for collating the list of such weak secrets and making them opensource.
94+
*
95+
* <p>For knowing all the secrets please visit: <a href=
96+
* "https://raw.githubusercontent.com/wallarm/jwt-secrets/master/jwt.secrets.list">Weak Publicly
97+
* known HMac Secrets</a>
98+
*
99+
* @return {@code true} if found any publicly well known secret used to sign JWT else {@code
100+
* false}
101+
*/
102+
private boolean executePubliclyWellKnownHMacSecretAttack() {
103+
// will only run for high or insane strength
104+
if (serverSideAttack.getJwtActiveScanRule().getAttackStrength().equals(AttackStrength.LOW)
105+
|| serverSideAttack
106+
.getJwtActiveScanRule()
107+
.getAttackStrength()
108+
.equals(AttackStrength.MEDIUM)) {
109+
return false;
110+
}
111+
Set<String> secrets = JWTConfiguration.getInstance().getPubliclyKnownHMacSecrets();
112+
// Checks if HMac is the actual algorithm for the JWT
113+
if (JWTConstants.JWT_HMAC_ALGO_TO_JAVA_ALGORITHM_MAPPING.containsKey(
114+
serverSideAttack.getJwtHolder().getAlgorithm())) {
115+
for (String secret : secrets) {
116+
if (serverSideAttack.getJwtActiveScanRule().isStop()) {
117+
return false;
118+
}
119+
JWTHolder jwtHolder = serverSideAttack.getJwtHolder();
120+
try {
121+
String tokenSignedWithWeakSecret =
122+
JWTUtils.getBase64EncodedHMACSignedToken(
123+
JWTUtils.getBytes(
124+
jwtHolder.getBase64EncodedTokenWithoutSignature()),
125+
JWTUtils.getBytes(secret),
126+
jwtHolder.getAlgorithm());
127+
if (tokenSignedWithWeakSecret.equals(jwtHolder.getBase64EncodedToken())) {
128+
this.raiseAlert(
129+
MESSAGE_PREFIX,
130+
VulnerabilityType.PUBLICLY_KNOWN_SECRETS,
131+
Alert.RISK_HIGH,
132+
Alert.CONFIDENCE_HIGH,
133+
MessageFormat.format(
134+
JWTI18n.getMessage(
135+
MESSAGE_PREFIX
136+
+ VulnerabilityType.PUBLICLY_KNOWN_SECRETS
137+
.getMessageKey()
138+
+ ".param"),
139+
jwtHolder.getBase64EncodedToken(),
140+
secret),
141+
serverSideAttack);
142+
return true;
143+
}
144+
} catch (JWTException e) {
145+
LOGGER.warn("An error occurred while getting signed manipulated tokens", e);
146+
}
147+
}
148+
}
149+
return false;
150+
}
151+
82152
/**
83153
* Adds Null Byte to the signature to checks if JWT is vulnerable to Null Byte injection. Main
84154
* gist of attack is say validator is vulnerable to null byte hence if anything is appended
@@ -297,7 +367,8 @@ public boolean executeAttack(ServerSideAttack serverSideAttack) {
297367
try {
298368
return this.executeCustomPrivateKeySignedJWTTokenAttack()
299369
|| this.executeAlgoKeyConfusionAttack()
300-
|| this.executeNullByteAttack();
370+
|| this.executeNullByteAttack()
371+
|| this.executePubliclyWellKnownHMacSecretAttack();
301372
} catch (JWTException e) {
302373
LOGGER.error("An error occurred while getting signed manipulated tokens", e);
303374
}

src/main/java/org/zaproxy/zap/extension/jwt/utils/JWTUtils.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@
3131
import com.nimbusds.jose.JWSHeader;
3232
import com.nimbusds.jose.crypto.RSASSASigner;
3333
import com.nimbusds.jose.util.Base64URL;
34+
import java.io.BufferedReader;
3435
import java.io.File;
3536
import java.io.IOException;
37+
import java.io.InputStreamReader;
3638
import java.nio.ByteBuffer;
3739
import java.nio.CharBuffer;
3840
import java.nio.charset.StandardCharsets;
@@ -45,11 +47,15 @@
4547
import java.security.spec.PKCS8EncodedKeySpec;
4648
import java.text.ParseException;
4749
import java.util.Base64;
50+
import java.util.HashSet;
4851
import java.util.Objects;
52+
import java.util.Set;
4953
import java.util.regex.Pattern;
5054
import javax.crypto.Mac;
5155
import javax.crypto.spec.SecretKeySpec;
5256
import org.apache.commons.io.FileUtils;
57+
import org.apache.commons.lang3.StringUtils;
58+
import org.apache.log4j.Logger;
5359
import org.json.JSONException;
5460
import org.json.JSONObject;
5561
import org.zaproxy.zap.extension.dynssl.SslCertificateUtils;
@@ -64,6 +70,8 @@
6470
*/
6571
public class JWTUtils {
6672

73+
private static final Logger LOGGER = Logger.getLogger(JWTUtils.class);
74+
6775
/**
6876
* Converts string to bytes. This method assumes that token is in UTF-8 charset which is as per
6977
* the JWT specifications.
@@ -291,4 +299,28 @@ public static boolean isValidJson(String value) {
291299
}
292300
return true;
293301
}
302+
303+
/**
304+
* Generic utility to read contents from the file and returning the provided content as list of
305+
* Strings.
306+
*
307+
* @param fileName
308+
* @return content from file as list of strings
309+
*/
310+
public static Set<String> readFileContentsFromResources(String fileName) {
311+
Set<String> values = new HashSet<>();
312+
try (BufferedReader bufferedReader =
313+
new BufferedReader(
314+
new InputStreamReader(JWTUtils.class.getResourceAsStream(fileName)))) {
315+
String inputLine;
316+
while ((inputLine = bufferedReader.readLine()) != null) {
317+
if (StringUtils.isNotBlank(inputLine)) {
318+
values.add(inputLine);
319+
}
320+
}
321+
} catch (Exception ex) {
322+
LOGGER.warn("Unable to read publicly known secrets from: " + fileName, ex);
323+
}
324+
return values;
325+
}
294326
}

src/main/java/org/zaproxy/zap/extension/jwt/utils/VulnerabilityType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public enum VulnerabilityType {
3030
EMPTY_TOKENS("emptyTokens"),
3131
JWK_CUSTOM_KEY("jwkCustomKey"),
3232
ALGORITHM_CONFUSION("algorithmConfusion"),
33+
PUBLICLY_KNOWN_SECRETS("publiclyKnownSecrets"),
3334
// JWT token client side storage related vulnerability type
3435
SECURE_COOKIE("cookiesecureflag"),
3536
HTTPONLY_COOKIE("cookiehttponly"),

src/main/resources/org/zaproxy/zap/extension/jwt/resources/Messages.properties

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ jwt.scanner.server.vulnerability.signatureAttack.jwkCustomKey.desc=JWT library i
8484
jwt.scanner.server.vulnerability.signatureAttack.jwkCustomKey.refs=https://nvd.nist.gov/vuln/detail/CVE-2018-0114
8585
jwt.scanner.server.vulnerability.signatureAttack.jwkCustomKey.soln=Validating Library should not depend on user provided input
8686

87+
jwt.scanner.server.vulnerability.signatureAttack.publiclyKnownSecrets.name=Publicly Well Known HMac Secret Attack
88+
jwt.scanner.server.vulnerability.signatureAttack.publiclyKnownSecrets.desc=JSON web tokens signed using HMac algorithm requires secret key and there are publicly well known secret keys which should not be used for signing the JSON web token as it can cause various attacks like identity theft, user impersonation etc.
89+
jwt.scanner.server.vulnerability.signatureAttack.publiclyKnownSecrets.refs=https://lab.wallarm.com/340-weak-jwt-secrets-you-should-check-in-your-code
90+
jwt.scanner.server.vulnerability.signatureAttack.publiclyKnownSecrets.soln=Secret keys used for signing should not be publicly well known or easy to guess.
91+
jwt.scanner.server.vulnerability.signatureAttack.publiclyKnownSecrets.param=JWT: \"{0}\" is signed by: \"{1}\"
92+
8793
jwt.scanner.server.vulnerability.payloadAttack.nullByte.name=Null Byte Injection Attack
8894
jwt.scanner.server.vulnerability.payloadAttack.nullByte.desc=Payload bytes after null byte are ignored ie not included in validation of JWT hence JWT validator is vulnerable to null byte injection
8995
jwt.scanner.server.vulnerability.payloadAttack.nullByte.refs=http://projects.webappsec.org/w/page/13246949/Null%20Byte%20Injection

0 commit comments

Comments
 (0)