1 pontos por GN⁺ 2024-01-27 | 1 comentários | Compartilhar no WhatsApp
  • O commit 0226b56 de rhboot/shim corrige o CVE-2023-40547, causado por confiar cegamente no valor de tamanho do cabeçalho HTTP durante o recebimento de arquivos
  • Se um cabeçalho manipulado indicar um tamanho menor que os dados realmente recebidos, o shim pode alocar um espaço menor que o buffer necessário
  • O código anterior usava o valor do cabeçalho para a alocação e os metadados do protocolo para a cópia, o que podia levar a uma gravação fora dos limites (out-of-bounds write)
  • O patch verifica *buf_size < rx_message.BodyLength em receive_http_response() de httpboot.c e, em caso de falha, trata como EFI_BAD_BUFFER_SIZE e Invalid Content-Length
  • O escopo da mudança é de apenas 1 arquivo, httpboot.c, com 7 linhas adicionadas e 1 removida, e o erro de digitação Content-Lenght também foi corrigido para Content-Length

Fluxo de ocorrência da vulnerabilidade

  • CVE-2023-40547 é um problema que ocorre quando o shim busca arquivos por HTTP ou protocolos relacionados
  • No processo de alocação do buffer para armazenar os dados recebidos, era usado o valor de tamanho do cabeçalho HTTP
  • O cabeçalho HTTP pode ser manipulado e indicar um tamanho menor que o dos dados realmente recebidos
  • No fluxo anterior, o valor do cabeçalho era usado para alocar o buffer, enquanto a cópia dos dados do buffer rx seguia os metadados do protocolo
  • Por causa dessa diferença, podia-se copiar mais dados do que o tamanho do buffer alocado, resultando em uma gravação fora dos limites (out-of-bounds write)

Conteúdo do patch

  • O patch adiciona uma verificação defensiva em receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size) de httpboot.c
  • Quando *buf_size == 0, corrige o erro de digitação na mensagem existente e faz goto error
    • Failed to get Content-LenghtFailed to get Content-Length
  • A nova verificação confirma a condição *buf_size < rx_message.BodyLength
    • Se a condição for verdadeira, define efi_status = EFI_BAD_BUFFER_SIZE
    • Exibe o erro Invalid Content-Length
    • Depois faz goto error

Escopo da mudança

  • O único arquivo alterado é httpboot.c
  • O volume da alteração é de 7 linhas adicionadas e 1 removida
  • O ponto central é a lógica que verifica se rx_message.BodyLength, o comprimento do corpo recebido, é maior que *buf_size, o tamanho alocado

Registros relacionados

  • Este commit está marcado como uma mudança que corrige o CVE-2023-40547
  • A mensagem do commit afirma explicitamente que o problema surgiu por confiar incorretamente em cabeçalhos HTTP
  • O registro do relatório da vulnerabilidade aponta Bill Demirkapi, do Microsoft Security Response Center, como responsável pela divulgação

1 comentários

 
GN⁺ 2024-01-27
Opiniões no Hacker News
  • shim é um bootloader EFI comumente usado por distribuições Linux que querem ativar o Secure Boot
    Do ponto de vista das distribuições, em vez de fazer o usuário registrar chaves manualmente, elas querem facilitar a ativação do Secure Boot usando a chave de assinatura da Microsoft já incluída por padrão
    Mas, como a Microsoft em geral não assina bootloaders GPL como o GRUB, foi criado o shim, que pode ser assinado com a chave da Microsoft; o shim verifica a assinatura do que vai inicializar usando uma chave separada chamada Machine Owner Key, ou MOK
    Ao especificar para o shim o binário EFI a ser inicializado, é possível fornecer uma URL HTTP; nesse caso, se o servidor HTTP for malicioso, ele pode provocar uma escrita fora dos limites
    No entanto, como normalmente isso é usado para inicializar um bootloader local de segundo estágio, como o GRUB, na maioria das instalações parece pouco provável que isso seja um problema
    O Secure Boot foi projetado desde o início para permitir que até binários já assinados sejam revogados por meio da lista DBX; quando essa lista é inserida no UEFI, o binário é rejeitado mesmo que tenha uma assinatura válida
    Se as assinaturas de binários shim antigos com esse bug forem adicionadas à lista, cada pessoa poderá atualizar a lista em seu próprio equipamento; ela também pode ser distribuída por atualizações em cápsula como o LVFS, e, se você gerencia diretamente as chaves e variáveis do Secure Boot, também pode baixar a lista em https://uefi.org/revocationlistfile e registrá-la

    • Sou quem encontrou o bug do texto original, e é um mal-entendido comum achar que esse problema só pode ser explorado quando se usa boot por HTTP
      Se fosse assim, ele não teria classificação Critical
      Esse bug pode ser explorado quando um malware local com privilégios sobrescreve a partição EFI, em um ataque man-in-the-middle em uma rede adjacente com boot PXE ativado, ou como ataque man-in-the-middle remoto quando se usa boot por HTTP
      Um atacante remoto sem privilégios que esteja em posição de man-in-the-middle, se a máquina vítima usa boot por HTTP, pode explorá-lo sem acesso direto
      Um atacante remoto que tenha obtido privilégios e execução de código na máquina vítima pode contornar o Secure Boot se o firmware oferecer suporte a HTTP, mesmo que a vítima não use boot por HTTP
      Por exemplo, ele pode alterar as variáveis da ordem de boot para apontar para um servidor controlado pelo atacante, ou sobrescrever o bootloader da partição EFI com imagens legítimas do shim e do GRUB2 e, em grub.cfg, fazer chainload de um novo shim via HTTP
      Isso porque a sintaxe de dispositivos do GRUB2 permite especificar dispositivos compatíveis, incluindo HTTP
      Além disso, se um atacante adjacente sem privilégios estiver em posição de man-in-the-middle e a máquina vítima usar boot PXE, ele pode explorá-lo encadeando algo como shim via PXE → GRUB2 via PXE → shim via HTTP
    • A equipe jurídica da Microsoft entende que, se a Microsoft assinar o GRUB, um bootloader licenciado sob GPLv3, a GPLv3 poderia dar aos desenvolvedores o direito de exigir o fornecimento da chave de assinatura
      Fonte: https://techcommunity.microsoft.com/t5/hardware-dev-center/u...
    • Eu estava me perguntando por que um bootloader faria comunicação de rede, mas a explicação de que é possível especificar um binário EFI por meio de uma URL HTTP esclareceu
    • Fico curioso para saber como o shim evita o problema que preocupa a Microsoft
      Se as cláusulas anti-Tivoization da GPLv3 podem exigir o fornecimento das chaves de assinatura do Secure Boot, então as chaves de assinatura MOK também não teriam de ser fornecidas mediante solicitação?
      Nesse caso, qualquer pessoa poderia obter uma chave capaz de assinar código arbitrário que seria inicializado indiretamente via Secure Boot; não sei se isso é significativamente diferente de a Microsoft simplesmente emitir uma chave de assinatura para projetos GPLv3 como o GRUB
    • Se o objetivo é inicializar um bootloader local de segundo estágio, me pergunto se o Windows Boot Manager também não poderia cumprir esse papel
      Em máquinas BIOS antigas, eu já configurei o WBM para fazer chainload do GRUB, mas ainda não tentei em máquinas UEFI, então não sei se existe algum ponto em que isso trava
  • Você pode se perguntar “por que inicializar a partir de um servidor não confiável ou comprometido?” ou “se o servidor foi comprometido, não bastaria ele enviar um binário malicioso, tornando isso irrelevante?”; em resumo, o binário que o shim inicializa no fim precisa estar assinado com a MOK
    Portanto, a mesma garantia de segurança deveria ser mantida, independentemente de você inicializar dentro de uma rede comprometida, via HTTP, ou a partir de um servidor comprometido, usando HTTPS ou não
    Como o Secure Boot não impede downgrade, o fato de um servidor comprometido poder ser usado em um ataque de downgrade é uma questão separada desta vulnerabilidade
    A proteção contra ataques de downgrade, de todo modo, precisa ser implementada separadamente de uma forma mais robusta
    Dito isso, não sei por que o shim precisa oferecer suporte direto a boot por HTTP
    Isso poderia ser tratado por um segundo binário EFI local assinado com a MOK; talvez tenham considerado que era um recurso relativamente simples de implementar

  • Não entendo por que esse código trata o tamanho do corpo com dois critérios diferentes
    Pelas RFCs, no HTTP/1.1, Content-Length é a informação autoritativa sobre o tamanho do corpo de uma requisição/resposta HTTP
    Dados no fio além desse tamanho são, por definição, parte de outra mensagem
    Por outro lado, se Content-Length for maior que rx_message.BodyLength, isso significa que a mensagem inteira ainda não foi recebida, então deveria esperar mais ou gerar um erro de timeout
    Em qualquer um dos casos, se não há garantia de que rx_message.BodyLength é igual a Content-Length, é um valor incorreto
    Se quisesse tratar isso de forma mais permissiva, não haveria motivo para olhar o cabeçalho Content-Length; bastaria usar rx_message.BodyLength como tamanho do buffer e interpretar todos os dados no fio como a mensagem recebida
    O código atual é desnecessariamente complexo, e é assim que bugs acabam entrando

    • Olhando só esse commit isoladamente, é fácil entender errado
      Vendo o código ao redor https://github.com/rhboot/shim/blob/0226b56513b2b8bd5fd281bc..., ele recebe pedaços de dados dentro de um loop e, a cada vez, verifica se os novos dados não ultrapassam a capacidade do buffer definida pelo Content-Length
      Mas antes ele não fazia essa verificação para a primeira leitura fora do loop, e esse era o bug
      Dito isso, não vejo um código que faça uma verificação final de que o tamanho baixado é igual a *buf_size, isto é, ao Content-Length
      Se essa condição for violada, pode ser um sinal de que a conexão foi fechada cedo demais
  • Isso claramente é um bug, e ainda bem que foi corrigido, mas fico pensando: quem inicializa seu próprio equipamento a partir de um host não confiável?
    Se um invasor controla o serviço HTTP a ponto de enviar cabeçalhos maliciosos, evitar esse overflow é o menor dos problemas; o certificado também foi comprometido, e ele também poderia enviar um payload válido pela especificação contendo código malicioso
    É um bug, sim, mas não tenho certeza se é Critical

    • Quem força o uso de Secure Boot também é de um tipo parecido
      Eles acreditam seriamente em uma estratégia de segurança na qual tudo que possa vir a ser comprometido não deve ser assinado pelo Secure Boot
      Se houver qualquer coisa assinada com uma vulnerabilidade, ela pode ser usada para descriptografar os discos criptografados com Secure Boot+TPM de todo mundo
      É difícil entender por que essa abordagem foi considerada um modelo de segurança válido, e já existem vulnerabilidades desse tipo aos montes
      Além disso, ignoram completamente o elefante na sala: o Windows
      Um exemplo desse modo de pensar: https://lkml.org/lkml/2018/4/3/767
      Apesar das preocupações do Linus, em muitas distribuições, inicializar com Secure Boot realmente ativa o modo de integridade
      Provavelmente por causa das políticas da Microsoft e da situação em que as distribuições são obrigadas a seguir o procedimento descrito naquela thread para obter a assinatura UEFI da Microsoft
      Como resultado, ao ativar o Secure Boot, em geral os recursos da distribuição ficam limitados; por exemplo, não dá para usar hibernação
    • Uma boa defesa só existe como defesa em profundidade, com várias camadas de proteção, e esse bug abre um buraco em uma dessas camadas
    • Também pode ser usado para invadir alguns dispositivos bloqueados
    • Pela excelente explicação em outra thread https://news.ycombinator.com/item?id=39135275, o vetor de ataque não se limita a HTTP, então dá para considerá-lo Critical
  • Quando algo importante está acontecendo, deve-se usar o S do HTTP
    Inicialização de dispositivos se inclui nisso, e cabeçalhos HTTPS sempre foram criptografados
    Ainda assim, foi um bug bem encontrado

    • Aqui, HTTPS é irrelevante
      Cabeçalhos inválidos podem ser enviados de qualquer forma
    • Não sei bem se HTTPS é viável para esse uso
      Porque criptografia exige hora e data corretas
      O RTC pode estar válido, mas não sei se lida bem com fusos horários e, de qualquer modo, a hora pode estar errada
    • O problema não tem relação com haver ou não o S
      Parece que você não entendeu bem o problema
  • Content-length não é o tamanho real do corpo, mas o tamanho após Content-encoding

    • “HTTP/1.1 é um protocolo maravilhosamente simples quando você ignora a maior parte dele”
  • Esses builds do shim incluem httpboot?
    Até onde sei, o shim serve apenas para executar outros binários EFI no disco, e acho que nunca vi o recurso de boot pela rede do shim sendo usado de fato

  • Talvez eu esteja enganado, mas eu achava que a maioria dos clientes HTTP só lia até o Content-Length especificado e tratava como erro se os bytes lidos fossem menores que o Content-Length

    • O cliente HTTP é fornecido pela UEFI como um driver EFI
      Pelo que vejo, a especificação UEFI não parece definir em detalhes o comportamento quando o cabeçalho Content-Length e o tamanho do corpo da resposta não batem
      Então é bem possível que algumas implementações simplesmente façam uma requisição com connection:close e não verifiquem o Content-Length
      Essa vulnerabilidade foi reportada pelo MSRC, e a descrição do CVE não fala de exploração real
      Pode ser que isso seja divulgado mais tarde, ou pode ser uma questão teórica
  • Você pode explicar por que ler menos do que o tamanho real do corpo é perigoso?
    Eu esperaria que o contrário fosse perigoso

    • Ao buscar um arquivo via HTTP ou protocolo relacionado, o shim tenta alocar um buffer para armazenar os dados recebidos
      Só que o tamanho vem de um cabeçalho HTTP manipulável, e o invasor pode especificar um tamanho menor que os dados recebidos
      Nesse caso, o código usa o valor do cabeçalho para a alocação e, ao copiar a partir do buffer de recebimento, usa o tamanho dos metadados do protocolo, causando uma escrita fora dos limites
    • Pela descrição, ele aloca o buffer com base no Content-Length, mas copia a quantidade correspondente ao tamanho do buffer realmente recebido, escrevendo fora dos limites alocados