CVE-2023-40547 – vulnerabilidade causada porque o shim confiava incorretamente em cabeçalhos HTTP
(github.com/rhboot)- O commit
0226b56de 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.BodyLengthemreceive_http_response()dehttpboot.ce, em caso de falha, trata comoEFI_BAD_BUFFER_SIZEeInvalid 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çãoContent-Lenghttambém foi corrigido paraContent-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)dehttpboot.c - Quando
*buf_size == 0, corrige o erro de digitação na mensagem existente e fazgoto errorFailed to get Content-Lenght→Failed 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
- Se a condição for verdadeira, define
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
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
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 HTTPIsso 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
Fonte: https://techcommunity.microsoft.com/t5/hardware-dev-center/u...
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
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 timeoutEm qualquer um dos casos, se não há garantia de que
rx_message.BodyLengthé igual a Content-Length, é um valor incorretoSe quisesse tratar isso de forma mais permissiva, não haveria motivo para olhar o cabeçalho Content-Length; bastaria usar
rx_message.BodyLengthcomo tamanho do buffer e interpretar todos os dados no fio como a mensagem recebidaO código atual é desnecessariamente complexo, e é assim que bugs acabam entrando
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-LengthSe 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
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
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
Cabeçalhos inválidos podem ser enviados de qualquer forma
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
Parece que você não entendeu bem o problema
Content-lengthnão é o tamanho real do corpo, mas o tamanho após Content-encodingEsses 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
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:closee não verifiquem o Content-LengthEssa 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
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