1 pontos por GN⁺ 3 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • Desde o Linux 6.9, uma ferramenta que bloqueia o drive durante o suspend de notebooks vinha falhando silenciosamente, deixando na memória as chaves de criptografia de disco completo do LUKS
  • A causa foi uma interação inesperada entre uma refatoração do acesso a dispositivos de bloco, incluída no Linux 6.9 em maio de 2024, e o código de criptografia; a correção proposta é um patch de uma linha
  • O problema não aparecia no desligamento completo, mas no suspend-to-RAM as chaves permaneciam, criando uma situação em que um invasor que obtivesse um notebook ligado poderia extrair as chaves da RAM
  • A descoberta começou ao observar entradas em /proc/keys durante a limpeza do port para NixOS do cryptsetup-suspend do Debian; um dump de memória no QEMU confirmou que a volume key que deveria ter sido apagada continuava presente
  • Foram propostos testes de integração no NixOS e um patch de alerta para o cryptsetup; recursos de segurança como apagar chaves logo antes do suspend podem parecer estar funcionando, mas é fácil deixar a falha passar sem verificação real da memória

Problema de chaves LUKS remanescentes durante suspend desde o Linux 6.9

  • Desde o Linux 6.9, ou seja, desde maio de 2024, uma ferramenta que bloqueia o drive durante o suspend de notebooks vinha falhando silenciosamente
  • A criptografia de disco completo com LUKS é usada para proteger dados em caso de perda, apreensão ou roubo de notebooks, mas, neste caso, as chaves de criptografia ficavam na memória durante o suspend
  • No desligamento completo, ela ainda funcionava, mas o impacto é maior porque é comum deixar o notebook em suspend-to-RAM em vez de desligá-lo completamente
  • Se alguém obtivesse o notebook ainda ligado, as chaves remanescentes na memória poderiam ficar expostas
  • No Windows, o VeraCrypt foi mencionado como software para o mesmo propósito, mas depois um comentário corrigiu que “canonical software” não significava o software mais usado, e sim uma recomendação representativa na área de segurança de TI

Causa e patch de uma linha

  • A causa foi o commit de refatoração do kernel Linux md: port block device access to file
    • A mudança em si era uma refatoração razoável e útil, mas gerou uma interação de longa distância com o código de criptografia
  • A correção proposta é um patch de uma linha
  • O autor do patch afirmou que, sem verificação formal, não é possível dizer que esse patch está correto nem que não há outras interações de longa distância
  • Também surgiram trabalhos posteriores para evitar recorrência

Processo de descoberta

  • O ponto de partida foi um trabalho de limpeza do port para NixOS do cryptsetup-suspend do Debian
  • Tanto o original do Debian quanto o port para NixOS tinham uma condição de corrida incômoda, embora não prejudicial, que às vezes impedia o notebook de entrar em sleep
  • Para resolver isso, a ideia foi ressuscitar o patch de kernel não mesclado de Pali Rohár, patch para apagar chaves no suspend/hibernation do dm-crypt
  • Nesse processo, ao examinar o código-fonte do cryptsetup e do kernel, foi confirmado que, segundo a documentação, o keyring fica associado à thread chamadora e é removido quando a thread termina
  • Porém, apareceram entradas em /proc/keys, algo desconhecido até então, e isso aumentou a suspeita
  • Por fim, uma máquina virtual QEMU foi iniciada e sua memória foi despejada, confirmando que a LUKS volume key que deveria ter sido apagada ainda estava presente

Projeto de secure suspend-to-RAM no NixOS

  • O projeto secure-suspend, divulgado separadamente, fornece um secure suspend-to-RAM experimental no NixOS
  • Na criptografia de disco completo comum, quando o notebook está em suspend, as chaves permanecem na memória, podendo ficar vulneráveis a cold boot attacks ou métodos de vazamento de RAM
  • O projeto ressuscita o patch antigo de kernel de Pali Rohár para apagar as chaves de criptografia LUKS durante o suspend
  • Ele foi inspirado no cryptsetup-suspend do Debian, mas usa um patch de kernel para evitar a condição de corrida que às vezes impede o notebook de dormir e acrescenta medidas preventivas adicionais
  • Oferece suporte completo a root filesystem criptografado e também fornece testes de integração
  • O patch de kernel e as ferramentas de espaço de usuário podem ser adaptados também para outras distribuições Linux
  • O projeto está disponível como secure-suspend

Por que é difícil verificar a segurança do suspend

  • Ver uma tela de bloqueio após o suspend não significa que o dispositivo de armazenamento tenha sido realmente bloqueado
  • Se o disco puder ser acessado imediatamente após sair do suspend, isso indica que o armazenamento não estava bloqueado desde o início
    • Por exemplo, é possível verificar o acesso ao disco com um script que continua rodando atrás da tela de bloqueio
    • Se for possível conectar via chave pública SSH após o suspend sem primeiro desbloquear o armazenamento criptografado, é fácil confirmar que o armazenamento não foi bloqueado
  • Também houve comentários dizendo que as configurações padrão do Ubuntu ou do Debian sequer tentavam oferecer esse tipo de proteção
  • É preciso verificar separadamente se a tentativa de bloquear o armazenamento realmente funcionou corretamente
    • Timestamps de logs podem ter sido gerados antes do suspend, mas gravados após o wake
    • Por outro lado, logs criados logo após o wake, antes de o horário do sistema ser ajustado, também podem parecer ter o horário do momento do suspend
  • Do ponto de vista da experiência do usuário, pode parecer igual se o bloqueio do armazenamento aconteceu logo antes do suspend ou logo após o resume, mas em termos de segurança a diferença é decisiva
  • O teste de integração do NixOS inicializa o sistema em uma máquina virtual e faz dump da memória para verificar se as chaves foram realmente apagadas durante o suspend

1 comentários

 
GN⁺ 3 시간 전
Opiniões no Hacker News
  • É, de fato, um bug interessante, mas o título parece um pouco clickbait
    Pelo que entendi, cryptsetup luksSuspend é mais uma extensão criada pelo Debian do que um recurso oficialmente suportado, então acho que essa regressão afetou apenas o Debian
    Não sei bem se dá para culpar o kernel por um recurso que não é suportado nem amplamente testado
    Ainda assim, é impressionante, e é bom que tenham criado testes para impedir que essa regressão volte a acontecer. Também concordo com a opinião do OP de que os NixOSTests são realmente excelentes
    Só que, pelo título, parece um problema disseminado, não algo restrito a uma distribuição específica

    • A intenção era ter um título tecnicamente correto, não atrair cliques
      Sim. Isso não afeta pessoas que usam a configuração padrão, porque elas nem esperariam que a chave do volume estivesse segura durante a suspensão
      A solução do Debian foi portada para várias, provavelmente a maioria, das outras distribuições, e imagino que também houvesse bastante gente mantendo ports próprios
      A página de manual thread-keyring(7) promete que “o keyring da thread é destruído quando a thread que o referencia termina”
      O projeto cryptsetup dependia dessa propriedade no mecanismo que leva a chave do espaço de usuário para o espaço do kernel, mas o kernel 6.9 introduziu uma regressão que quebrou essa propriedade
    • Fico confuso com a afirmação de que isso seria exclusivo do Debian. luksSuspend é um recurso upstream e foi adicionado no lançamento v1.1.0, em 2009
      Eu já o usei algumas vezes no Arch e no openSUSE no passado, e ele claramente existe em distribuições que não são Debian
      Talvez a pessoa esteja pensando na integração automática com a suspensão do sistema, mas isso foge do ponto principal. luksSuspend é documentado como algo que remove a chave da memória do sistema, e esse comportamento parou de funcionar por causa daquele patch de refatoração no Linux 6.9
      Dito isso, na prática também dá para ver como um bug do lado do cryptsetup. Ele dependia de um comportamento de ciclo de vida muito específico das chaves do keyring do kernel, e é possível argumentar que deveria tê-las apagado de forma mais explícita no espaço de usuário
      [1]: https://gitlab.com/cryptsetup/cryptsetup/-/commit/3cea5dcc7b...
      [2]: https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/docs/v1...
      [3]: https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/93...
    • O subcomando está no repositório oficial do cryptsetup, e a descrição também parece correta: https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/man/cry...
    • Já usei esse recurso no Arch, e ele pode ser usado com LUKS comum. Só que, até onde sei, ele não é usado por padrão ao suspender
      Imagino que estejam falando de dispositivos que, depois de luksSuspend, realmente executam a suspensão para RAM de uma forma útil; isso inicialmente mirava o Debian e depois também apareceu no Arch, mas em ambos os casos não era o padrão
    • Fico curioso para saber em qual versão o Debian distribuiu 6.9 pela primeira vez
  • Não vejo muito outro jeito. Ao entrar em modo de economia de energia, ou seja, suspender para RAM, tudo fica armazenado na RAM e criptografado, mas, pelo que lembro, a chave mestra permanece na memória do kernel
    Já ao hibernar, ou seja, suspender para disco, todo o conteúdo da RAM é gravado no disco, incluindo a chave mestra, e fica criptografado; a RAM é apagada
    Ao acordar de novo, é preciso digitar novamente a frase-senha para descriptografar a chave mestra e carregar o conteúdo do disco de volta para a memória

    • Exato. Na maioria das distribuições Linux padrão, se você simplesmente suspende o notebook, tudo fica na memória, incluindo a chave mestra
      Mas o Debian criou antes um complemento opcional chamado cryptsetup-suspend, que executa o comando luksSuspend, feito para apagar a chave da memória, e depois pede a frase-senha novamente ao retomar
      Até o kernel 6.8, funcionava como descrito; a partir do kernel 6.9, deixou de funcionar silenciosamente
    • CPUs Intel/AMD dos últimos 5 anos mais ou menos oferecem suporte a criptografia de memória completa, transparente para o sistema operacional
      Com esse recurso ativado, ataques de cold boot viram coisa do passado. Ele costuma vir desativado por padrão apenas porque reduz a velocidade da RAM em cerca de 0,5%
  • Como não é preciso digitar novamente a senha de boot depois do Sleep, a chave de criptografia claramente ainda está na memória

    • A distribuição claramente não está usando cryptsetup-luksSuspend
  • Isso não é um problema que me preocupe muito
    A única razão pela qual uso criptografia de disco é para, ao vender o notebook, não precisar me preocupar com alguém fuçando meus documentos fiscais ou dados de cartão de crédito
    Claro que eu também apago o notebook, mas, se os dados estão criptografados no nível do drive, considero muito pequeno o risco de alguém recuperar os dados com ferramentas forenses ou algo do tipo

    • Como um meio-termo razoável, basta apagar o cabeçalho LUKS
      O LUKS usa um algoritmo antiforense que exige a chave de volume inteira para abrir o disco. Ele combina blocos da chave com um algoritmo de difusão e faz XOR para gerar a chave mestra real, então, em teoria, apagar apenas um setor da chave de volume já deveria tornar tudo irrecuperável
      Ou seja, se faltar até mesmo um bloco da chave, não dá para adivinhar facilmente o restante
    • Supondo que a chave de criptografia seja forte, apagar é teoricamente um trabalho redundante
  • Não sou nem de longe especialista em segurança, mas vendo esses bugs críticos de segurança que ultimamente são descobertos regularmente por “terem deixado passar uma única linha de verificação em C que atravessava arquivos durante uma refatoração”, a própria premissa de uma base de código C open source enorme e segura me parece suspeita
    Não é um problema exclusivo de C, mas acho que em C, em especial, é mais difícil impor e rastrear invariantes de forma consistente, ainda mais quando o código muda
    Também não sei se programação funcional, codificando invariantes nos tipos, é uma solução realisticamente escalável. Verificação de modelos? Fuzzing com LLM? Menos primitivas, com fronteiras mais claras? O seLinux foi “verificado” desse jeito?

    • As desvantagens de C são visíveis, e eu geralmente também não recomendaria C para projetos novos, mas não vejo esse bug específico como um bom exemplo de algo que o borrow checker do Rust, ou o sistema de tipos de outra linguagem, pegaria. Acho que nem analisadores estáticos pegariam
      Essencialmente é algo assim:
      original: DoTheThing()
      new: DoTheThingSlightlyDifferentButKeepMyCredentialsAlive()
      fix: DoTheThingSlightlyDifferentButDoInFactNOTKeepMyCredentialsAlive()
      Pela minha experiência, uma boa parte dos bugs difíceis vem de violações de invariantes de sistema de nível mais alto, e isso não parece ser o tipo de coisa que dê para automatizar
      Mesmo com algo como Lean, dá para provar que um programa satisfaz certa propriedade, mas você precisava ter pensado nessa propriedade antes. A prova não descobre a invariante por você
      Se tivessem pensado na propriedade de segurança relevante, não teria sido difícil escrever um teste de regressão. Acho que a parte realmente difícil não é expressar a implementação de forma segura, mas perceber que existe uma propriedade que a implementação precisa preservar
    • A premissa de uma base de código pública segura em si é aceitável
      O problema é que uma maior auditabilidade não significa automaticamente que ela será mais auditada
      Pessoas com habilidade suficiente precisam dedicar tempo suficiente a esse trabalho
    • Mesmo que fosse traduzido para Rust, teria virado “deixaram passar uma linha de verificação em Rust”
      Esse bug surgiu de preocupações que se cruzam e da falta de conhecimento entre domínios. Provavelmente teria sido igual em Lisp ou assembly
    • A lição aqui é que, se uma funcionalidade não tem pelo menos casos de teste relacionados, então ela não é uma funcionalidade de verdade
    • A premissa de uma “base de código C open source enorme e segura” parece suspeita porque code review às vezes não é muito diferente de uma versão idealizada do problema da parada, com acesso a uma versão formalizada da especificação
      Em outras palavras, não existe uma definição rigorosa do que é um problema de segurança
  • Será que uma agência federal precisava desesperadamente de uma forma de obter a chave? Isso é um bugdoor? O commit foi rastreado?
    Tenho visto muito esse padrão recentemente, então estou começando a ficar um pouco desconfiado. Pode ser também que as pessoas estejam mais sensíveis a isso e postando mais

    • Isso é uma regressão. O aplicativo em espaço de usuário também deve ter falhado silenciosamente, e foi o resultado de uma sequência de descuidos
      O fato de a chave de criptografia estar na memória não significa que ela possa ser extraída. É mais como algo que foi deixado desnecessariamente, por tempo indefinido, em um lugar onde não deveria estar
  • Regressões assim são fáceis de deixar passar porque tudo continua “funcionando”. Bugs de segurança muitas vezes não se denunciam

    • Exato. É por isso que testes de integração são ainda mais importantes para funcionalidades assim
      Também foi divertido escrevê-los, e eles permitiram rodar git-bisect para encontrar a refatoração específica do kernel que introduziu esse bug: https://github.com/NixOS/nixpkgs/pull/532499
  • No meu notebook Fedora, configurei o Linux para hibernar no disco 15 minutos depois de entrar em suspend. Se você corta a energia da memória, esse bug específico do Debian deixa de ser um problema
    A extensão das ferramentas Linux do Debian é boa em teoria, mas, se você realmente se preocupa com ataques de cold boot, não só a chave LUKS, como todas as chaves e documentos importantes precisam ser apagados da memória
    Então a única forma correta de impedir cold boot acaba sendo hibernar

  • Basta imaginar como seria esta thread do HN se essa vulnerabilidade estivesse em um sistema operacional comercial
    O comentário principal com certeza diria que a Applosoft não se importa mais com qualidade de software, ou que “é isso que acontece quando se permite lixo de vibe coding no OS”
    O comentário logo abaixo seria alguma teoria da conspiração sobre o complexo industrial da vigilância e a NSA; em outros lugares isso soaria maluco, mas no HN não

  • Não entendo por que algo tão importante não é testado a cada build