# Researching BLASTPASS: Analysing the Apple & Google WebP POC file - Part 2

URL: https://www.msuiche.com/posts/researching-blastpass-analysing-the-apple-google-webp-poc-file-part-2/
Date: 2023-12-24
Author: Matt Suiche
Tags: bug, webp, apple, google


---


More than 14 weeks pasted since [Apple Product Security team reported](https://bugs.chromium.org/p/chromium/issues/detail?id=1479274) the issue affecting WebP open source project to Google, in follow up to the BLASTPASS iOS exploit that was discovered in the wild by [CitizenLab](https://citizenlab.ca/2023/09/blastpass-nso-group-iphone-zero-click-zero-day-exploit-captured-in-the-wild/) and [discussed in September](https://www.msuiche.com/posts/researching-blastpass-detecting-the-exploit-inside-a-webp-file/). This means that the email chain is now public as of December 14, 2023.

We also learn that that Brotli compression algorithm almost got impacted by the [same issue](https://chromium.googlesource.com/chromium/src/third_party/+/refs/heads/main/brotli/dec/huffman.c#169) (c.f. `BrotliBuildHuffmanTable`) but the shape of Huffman tree is checked before actual lookup table is built so it was not vulnerable.

One of the nice thing about this email chain is that a webp poc is shared by Apple Product Security Team to Google, which I also added into [**ELEGANTBOUNCER**](https://github.com/msuiche/elegant-bouncer/commit/7e8d257128792b4a14697e43a56c1163b200b38f) repository and unit tests.

The code length counts used by NSO, as [recovered by mistymntncop](https://github.com/mistymntncop/CVE-2023-4863/blob/main/craft.c) from the Apple POC, are the following:

```cpp
   static CodeLenCountsArr code_lengths_counts = {
        //  1  2  3  4  5  6  7  8  9    10   11  12 13 14 15
        {0, 1, 0, 0, 0, 0, 0, 0, 0, 177, 154, 7,  1, 1, 1, 2}, //size = 716
        {0, 1, 0, 1, 1, 1, 0, 0, 0, 81,  85,  81, 1, 1, 1, 2}, //size = 628
        {0, 1, 0, 1, 1, 1, 0, 0, 0, 81,  85,  81, 1, 1, 1, 2}, //size = 628
        {0, 1, 0, 1, 1, 1, 0, 0, 0, 81,  85,  81, 1, 1, 1, 2}, //size = 628
        {0, 0, 0, 0, 0, 0, 3, 2, 2, 3,   12,  2,  2, 2, 0, 12} //size = 526!!!
    };
```

compared to the ones we used in the sample we re-created in the September 2023:

```cpp
    static CodeLenCountsArr code_lengths_counts = {
        //  1  2  3  4  5  6  7  8  9  10   11  12 13 14 15
        {0, 1, 1, 0, 0, 0, 0, 0, 0, 3, 229, 41,  1, 1, 1, 2},   //size = 654
        {0, 1, 1, 0, 0, 0, 0, 0, 0, 7, 241,  1,  1, 1, 1, 2},   //size = 630
        {0, 1, 1, 0, 0, 0, 0, 0, 0, 7, 241,  1,  1, 1, 1, 2},   //size = 630
        {0, 1, 1, 0, 0, 0, 0, 0, 0, 7, 241,  1,  1, 1, 1, 2},   //size = 630
        {0, 1, 1, 1, 1, 1, 0, 0, 0, 11, 5,   1, 10, 4, 2, 2},   //size = 414!!!
    };
```

The main different comes down to the fact that `color_cache_bits` is used which changes the size of the huffman tables.

Upon reading the code lengths, a specific prefix code is generated for each symbol type (A, R, G, B, distance) based on their respective alphabet sizes which depends of the color cache bits.

- G Channel: Calculated as 256 + 24 + color_cache_size
- Other Literals (A,R,B): Constant at 256
- Distance Code: Constant at 40

For `color_cache_bits == 6`, the size fo the Green channel huffman table buffer is 718 instead of 654 (for `color_cache_bits == 0`), while the other ones (A, R, B) and the distance code remain constant:

```cpp
static const uint16_t kTableSize[12] = {
  FIXED_TABLE_SIZE + 654, // For color_cache_bits (0)
  FIXED_TABLE_SIZE + 656, // (...)
  FIXED_TABLE_SIZE + 658, // (...)
  FIXED_TABLE_SIZE + 662, // (...)
  FIXED_TABLE_SIZE + 670, // (...)
  FIXED_TABLE_SIZE + 686, // (...)
  FIXED_TABLE_SIZE + 718, // For color_cache_bits (6)
  FIXED_TABLE_SIZE + 782, // (...)
  FIXED_TABLE_SIZE + 912,
  FIXED_TABLE_SIZE + 1168,
  FIXED_TABLE_SIZE + 1680,
  FIXED_TABLE_SIZE + 2704
};
```

This gives us a total size of 3018 elements instead of 2954 elements to overflow. This potential change of size [was already accounted](https://github.com/msuiche/elegant-bouncer/blob/7e8d257128792b4a14697e43a56c1163b200b38f/src/webp.rs#L527) within the `ELEGANTBOUNCER` detection code implemented in September:

```rust
    let mut alphabet_size = K_ALPHABET_SIZE[j];
    if j == 0 && color_cache_bits > 0 {
        alphabet_size += 1 << color_cache_bits;
    }

    let max_table_size = match j {
        0 => K_TABLE_SIZE[color_cache_bits as usize] - FIXED_TABLE_SIZE,
        1 | 2 | 3 => MAX_RBA_TABLE_SIZE,
        4 => MAX_DISTANCE_TABLE_SIZE,
        _ => panic!("Unhandled idx value: {}", j),
    };
```

## Conclusion
Thanks again to [mistymntncop](https://github.com/mistymntncop) for pointing out to me that the email chain was now public.

Learn more about [ELEGANTBOUNCER on GitHub](https://github.com/msuiche/elegant-bouncer).

Happy Holidays!

## References
- [Researching BLASTPASS: Detecting the exploit inside a WebP file - Part 1](https://www.msuiche.com/posts/researching-blastpass-detecting-the-exploit-inside-a-webp-file-part-1/)
- [Researching BLASTPASS: Analysing the Apple & Google WebP POC file - Part 2](https://www.msuiche.com/posts/researching-blastpass-analysing-the-apple-google-webp-poc-file-part-2/)
