Como o Espectro e o Colapso Hacks Realmente Funcionou

Por Nael Abu-Ghazaleh, Dmitry Ponomarev e Dmitry Evtyushkin

Postado 2019-02-28 16:00 GMT

Um olhar em profundidade essas perigosas explorações de microprocessador vulnerabilidades e porque pode haver mais deles lá fora

Ilustração: Erik Vrielink
/image/MzIzODU1Mw.jpeg
Ilustração: Erik Vrielink

we’re used to thinking of computer processors as ordered machines that proceed from one simple instruction to the next with complete regularity. Mas a verdade é que, há décadas, eles têm feito as suas tarefas fora de ordem e apenas adivinhando o que deve vir a seguir. São muito bons nisso, claro. Tão bom de fato, que esta habilidade, chamada de execução especulativa, tem apoiado grande parte da melhoria no poder computacional durante os últimos 25 anos ou mais. Mas em 3 de janeiro de 2018, o mundo aprendeu que este truque, que tinha feito tanto pela computação moderna, era agora uma de suas maiores vulnerabilidades.Ao longo de 2017, pesquisadores da Cyberus Technology, Google Project Zero, Universidade de tecnologia de Graz, Rambus, Universidade de Adelaide e Universidade da Pensilvânia, bem como pesquisadores independentes, como o criptógrafo Paul Kocher, realizaram ataques que se aproveitaram da execução especulativa. Nosso próprio grupo havia descoberto a vulnerabilidade original por trás de um desses ataques em 2016, mas não juntamos todas as peças.

estes tipos de ataques, chamados de fusão e espectro, não eram bugs comuns. Na época em que foi descoberto, a fusão poderia hackear todos os microprocessadores Intel x86 e processadores IBM Power, bem como alguns processadores ARM-based. Spectre e suas muitas variações adicionaram processadores de Micro dispositivos avançados (AMD) a essa lista. Em outras palavras, quase todo o mundo da computação era vulnerável.

e porque a execução especulativa é em grande parte cozida em hardware do processador, corrigir essas vulnerabilidades não tem sido um trabalho fácil. Fazê-lo sem fazer com que as velocidades computacionais se transformem em velocidades baixas tornou-o ainda mais difícil. De facto, um ano depois, o trabalho está longe de ter terminado. Patches de segurança eram necessários não apenas dos fabricantes de processadores, mas daqueles mais abaixo na cadeia de suprimentos, como a Apple, Dell, Linux e Microsoft. Os primeiros computadores alimentados por chips que são intencionalmente projetados para serem resistentes até mesmo a algumas dessas vulnerabilidades chegaram recentemente.

Spectre and Meltdown are the result of the difference between what software is supposed to do and the processor’s microarchitecture—the details of how it really does those things. Estas duas classes de guardas descobriram uma forma de informação escapar através dessa diferença. E há todas as razões para acreditar que mais maneiras serão descobertas. Ajudámos a encontrar dois, o Branchscope e o Espectrersb, no ano passado.Se vamos manter o ritmo das melhorias de computação indo sem sacrificar a segurança, vamos ter que entender como essas vulnerabilidades de hardware acontecem. E isso começa com a compreensão do espectro e a fusão.

em sistemas de computação modernos, programas de software escritos em linguagens compreensíveis para humanos como C++ são compilados em instruções de linguagem de montagem-operações fundamentais que o processador de computador pode executar. Para acelerar a execução, processadores modernos usam uma abordagem chamada pipelining. Como uma linha de montagem, o gasoduto é uma série de etapas, cada uma das quais é um passo necessário para completar uma instrução. Algumas etapas típicas para um processador Intel x86 incluem aqueles que trazem a instrução da memória e descodificá-la para entender o que a instrução significa. Pipelining basicamente traz o paralelismo para o nível de execução da instrução: quando uma instrução é feita usando um estágio, a próxima instrução é livre de usá-la.

desde a década de 1990, os microprocessadores têm se baseado em dois truques para acelerar o processo de pipeline: execução fora de ordem e especulação. Se duas instruções são independentes uma da outra—isto é, a saída de uma não afeta a entrada de outra—elas podem ser reordenadas e seu resultado ainda será correto. Isso é útil, porque permite que o processador continue a trabalhar se uma instrução parar no oleoduto. Por exemplo, se uma instrução requer dados que estão fora na memória principal DRAM ao invés da memória cache localizada na própria CPU, pode levar algumas centenas de ciclos de clock para obter esses dados. Em vez de esperar, o processador pode mover outra instrução através do pipeline.O segundo truque é a especulação. Para compreendê-lo, Comece com o fato de que algumas instruções necessariamente levam a uma mudança em que as instruções vêm a seguir. Considere um programa contendo uma declaração “se”: ele verifica para uma condição, e se a condição é verdadeira, o processador salta para um local diferente no programa. Este é um exemplo de uma instrução de ramo condicional, mas existem outras instruções que também levam a mudanças no fluxo de instruções.

agora considere o que acontece quando tal instrução de ramo entra em um pipeline. É uma situação que leva a um enigma. Quando a instrução chega ao início do oleoduto, não sabemos o seu resultado até que tenha progredido bastante fundo no oleoduto. E sem conhecer este resultado, não podemos obter a próxima instrução. Uma solução simples mas ingênua é impedir que novas instruções entrem no pipeline até que a instrução do branch chegue a um ponto em que sabemos de onde virá a próxima instrução. Muitos ciclos de relógio são desperdiçados neste processo, porque pipelines tipicamente têm 15 a 25 estágios. Ainda pior, as instruções do ramo surgem muitas vezes, representando mais de 20 por cento de todas as instruções em muitos programas.

para evitar o custo de alto desempenho de empatar o gasoduto, os processadores modernos usam uma unidade arquitetônica chamada um predictor de ramo para adivinhar de onde virá a próxima instrução, depois de um ramo. O objetivo deste predictor é especular sobre alguns pontos-chave. Primeiro, será tomado um ramo condicional, fazendo com que o programa se desvie para uma seção diferente do programa, ou continuará no caminho existente? E segundo, se o ramo for tomado, para onde irá o programa-qual será a próxima instrução? Armado com estas previsões, o oleoduto do processador pode ser mantido cheio.

porque a execução da instrução é baseada em uma predição, ela está sendo executada “especulativamente”: se a predição é correta, o desempenho melhora substancialmente. Mas se a previsão se provar incorreta, o processador deve ser capaz de desfazer os efeitos de quaisquer instruções executadas especulativamente relativamente rapidamente.

o design do predictor do ramo tem sido robustamente pesquisado na comunidade de arquitetura de computador por muitos anos. Os predictors modernos usam o history da execução dentro de um programa como a base para seus resultados. Este esquema atinge precisões superiores a 95% em muitos tipos diferentes de programas, levando a melhorias dramáticas de desempenho, em comparação com um microprocessador que não especula. No entanto, é possível uma interpretação errada. E, infelizmente, é um equívoco que os ataques Spectre exploram.

outra forma de especulação que tem levado a problemas é a especulação dentro de uma única instrução no oleoduto. É um conceito muito abstruso, por isso vamos desempacotar. Suponha que uma instrução requer permissão para executar. Por exemplo, uma instrução poderia direcionar o computador para escrever um pedaço de dados para a porção de memória reservada para o núcleo do sistema operacional. Não ias querer que isso acontecesse, a não ser que fosse sancionado pelo próprio sistema operativo, ou arriscavas estragar o computador. Antes da descoberta da fusão e da Spectre, a sabedoria convencional era que não faz mal começar a executar a instrução de forma especulativa mesmo antes do processador ter chegado ao ponto de verificar se a instrução tem ou não Permissão para fazer o seu trabalho.

no final, se a permissão não for satisfeita-em nosso exemplo, o sistema operacional não sancionou esta tentativa de mexer com sua memória—os resultados são jogados fora e o programa indica um erro. Em geral, o processador pode especular em torno de qualquer parte de uma instrução que poderia fazê-lo esperar, desde que a condição seja finalmente resolvida e quaisquer resultados de suposições ruins sejam, efetivamente, desfeitos. É este tipo de especulação intra-instrução que está por trás de todas as variantes do bug derretido, incluindo a sua versão indiscutivelmente mais perigosa, prenúncio.

a visão que permite ataques de especulação é esta: Durante a má observação, nenhuma mudança ocorre que um programa pode observar diretamente. Em outras palavras, não há nenhum programa que você poderia escrever que simplesmente exibiria quaisquer dados gerados durante a execução especulativa. No entanto, o fato de que a especulação está ocorrendo deixa vestígios, afetando quanto tempo leva instruções para executar. E, infelizmente, agora está claro que podemos detectar esses sinais de tempo e extrair dados secretos deles.

What is this timing information, and how does a hacker get held of it? Para entender isso, você precisa entender o conceito de canais paralelos. Um canal lateral é uma via não intencional que vaza informações de uma entidade para outra (geralmente ambos são programas de software), tipicamente através de um recurso compartilhado, como um disco rígido ou memória.

como um exemplo de um ataque de canal lateral, considere um dispositivo que está programado para ouvir o som emanando de uma impressora e, em seguida, use esse som para deduzir o que está sendo impresso. O som, neste caso, é um canal lateral.

em microprocessadores, qualquer recurso de hardware compartilhado pode, em princípio, ser usado como um canal lateral que vaza informações de um programa vítima para um programa atacante. Em um ataque de canal lateral comumente usado, o recurso compartilhado é o cache da CPU. O cache é uma memória relativamente pequena e de acesso rápido no chip do processador usado para armazenar os dados mais frequentemente necessários por um programa. Quando UM programa acessa a memória, o primeiro processador verifica o cache; se os dados estão lá (um acerto de cache), ele é recuperado rapidamente. Se os dados não estão no cache (uma falha), o processador tem que esperar até que seja obtido da memória principal, que pode levar várias centenas de ciclos de clock. Mas uma vez que os dados chegam da memória principal, ele é adicionado ao cache, o que pode exigir lançar alguns outros dados para criar espaço. O cache é dividido em segmentos chamados conjuntos de cache, e cada localização na memória principal tem um conjunto correspondente no cache. Esta organização faz com que seja fácil verificar se algo está no cache sem ter que pesquisar toda a coisa.

ataques baseados em Cache tinham sido extensivamente pesquisados mesmo antes de Spectre e Meltdown aparecerem na cena. Embora o atacante não possa ler diretamente os dados da vítima—mesmo quando esses dados se encontram em um recurso compartilhado como o cache—O atacante pode obter informações sobre os endereços de memória acessados pela vítima. Esses endereços podem depender de dados sensíveis, permitindo que um atacante inteligente recupere esses dados secretos.Como é que o atacante faz isto? Há várias maneiras possíveis. Uma variação, chamada descarga e recarga, começa com o atacante removendo dados compartilhados do cache usando a instrução “flush”. O atacante então espera que a vítima acesse esses dados. Como já não está no cache, os dados que a vítima pede devem ser trazidos da memória principal. Mais tarde, o atacante acessa os dados compartilhados enquanto cronometra quanto tempo isso leva. Um sucesso de cache—o que significa que os dados estão de volta no cache-indica que a vítima acessou os dados. Uma falha de cache indica que os dados não foram acessados. Então, simplesmente medindo quanto tempo levou para acessar os dados, o atacante pode determinar quais conjuntos de cache foram acessados pela vítima. É preciso um pouco de magia algorítmica, mas este conhecimento de quais conjuntos de cache foram acessados e que não foram Pode levar à descoberta de chaves de criptografia e outros segredos.

fusão, espectro e suas variantes seguem o mesmo padrão. Primeiro, eles desencadeiam especulação para executar o código desejado pelo atacante. Este código lê dados secretos sem permissão. Em seguida, os ataques comunicam o segredo usando descarga e recarga ou um canal paralelo similar. Esta última parte é bem compreendida e semelhante em todas as variações de ataque. Assim, os ataques diferem apenas no primeiro componente, que é como desencadear e explorar a especulação.

img

ataques de fusão exploram a especulação dentro de uma única instrução. Embora as instruções assembly-language são tipicamente simples, uma única instrução muitas vezes consiste de múltiplas operações que podem depender umas das outras. Por exemplo, as operações de leitura de memória são muitas vezes dependentes da instrução que satisfaz as permissões associadas com o endereço de memória sendo lido. Uma aplicação geralmente tem permissão para ler apenas a partir da memória que foi atribuída a ela, não a partir da memória alocada para, digamos, o sistema operacional ou algum outro programa de usuário. Logicamente, devemos verificar as permissões antes de permitir que a leitura prossiga, que é o que alguns microprocessadores fazem, notavelmente os da AMD. No entanto, desde que o resultado final seja correto, os designers da CPU assumiram que eles eram livres para executar especulativamente essas operações fora de ordem. Portanto, microprocessadores Intel ler a localização da memória antes de verificar as permissões, mas apenas “commit” a instrução—tornando os resultados visíveis para o programa—quando as permissões são satisfeitas. Mas como os dados secretos foram recuperados especulativamente, pode ser descoberto usando um canal lateral, tornando os processadores Intel vulneráveis a este ataque.

o ataque Foreshadow é uma variação da vulnerabilidade de fusão. Este ataque afeta microprocessadores Intel por causa de uma fraqueza que Intel se refere como falha Terminal L1 (L1TF). Enquanto o ataque de fusão original dependia de um atraso na verificação de permissões, Foreshadow se baseia em especulações que ocorrem durante uma fase do pipeline chamada tradução de endereço.

o Software vê a memória e os ativos de armazenamento do computador como um único trecho contíguo da memória virtual. Mas fisicamente, esses ativos são divididos e compartilhados entre diferentes programas e processos. A tradução de endereços transforma um endereço de memória virtual em um endereço de memória física.

circuitos especializados no microprocessador ajudam com a tradução de endereços de memória virtual para física, mas pode ser lento, requerendo múltiplas pesquisas de memória. Para acelerar as coisas, os microprocessadores Intel permitem especulações durante o processo de tradução, permitindo que um programa leia especulativamente o conteúdo de uma parte do cache chamado L1, independentemente de quem possui esses dados. O atacante pode fazer isso, e então divulgar os dados usando a abordagem de canal lateral que já descrevemos.

de certa forma, Foreshadow é mais perigoso do que a fusão, de outras formas é menos. Ao contrário de fusão, Foreshadow pode ler o conteúdo apenas do cache L1, devido às especificidades da implementação da Intel de sua arquitetura de processador. No entanto, Foreshadow pode ler qualquer conteúdo em L1-não apenas dados endereçáveis pelo programa.

/image/MzIzODYyNw.jpeg

os ataques Spectre manipulam o sistema de previsão de ramos. Este sistema tem três partes: o predictor de direção do ramo, o predictor de alvo do ramo, e o buffer de pilha de retorno.

o predictor de direção do ramo prevê se um ramo condicional, como um usado para implementar uma declaração “se” em uma linguagem de programação, será tomado ou não. Para fazer isso, ele rastreia o comportamento anterior de ramos semelhantes. Por exemplo, pode significar que se um ramo é tomado duas vezes seguidas, previsões futuras dirão que ele deve ser tomado.

o predictor de ramificação-alvo prevê o endereço de memória-alvo do que são chamados ramificações indiretas. Em um ramo condicional, o endereço da instrução seguinte é escrito para fora, mas para um ramo indireto que o endereço tem que ser calculado primeiro. O sistema que prevê esses resultados é uma estrutura de cache chamada de buffer de branch-target. Essencialmente, ele mantém o controle do último alvo computado dos ramos indiretos e usa estes para prever onde o próximo ramo indireto deve levar.

o tampão da pilha de retorno é usado para prever o alvo de uma instrução de” retorno”. Quando uma subrotina é” chamada ” durante um programa, a instrução de retorno faz o programa retomar o trabalho no ponto a partir do qual a subrotina foi chamada. Tentar prever o ponto certo para o qual voltar baseado apenas em endereços de retorno anteriores não vai funcionar, porque a mesma função pode ser chamada de muitos locais diferentes no código. Em vez disso, o sistema usa o buffer return stack, um pedaço de memória no processador, que mantém os endereços return das funções como eles são chamados. Ele então usa esses endereços quando um retorno é encontrado no código da subrotina.Cada uma destas três estruturas pode ser explorada de duas formas diferentes. Primeiro, o predictor pode ser deliberadamente confundido. Neste caso, o atacante executa um código aparentemente inocente projetado para embirrar com o sistema. Mais tarde, o atacante deliberadamente executa um ramo que vai mal especular, fazendo o programa Saltar para um pedaço de código escolhido pelo atacante, chamado gadget. O dispositivo então começa a roubar dados.

uma segunda forma de ataque de espectro é chamada de injeção direta. Acontece que sob algumas condições os três predictores são compartilhados entre diferentes programas. O que isso significa é que o programa de ataque pode preencher as estruturas de predictor com dados maus cuidadosamente escolhidos à medida que executa. Quando uma vítima involuntária executa o seu programa ao mesmo tempo que o atacante ou depois, a vítima vai acabar usando o estado de preditor que foi preenchido pelo atacante e involuntariamente ativar um gadget. Este segundo ataque é particularmente preocupante porque permite que um programa de vítimas seja atacado a partir de um programa diferente. Essa ameaça é especialmente prejudicial para os provedores de serviços na nuvem, pois eles não podem garantir que seus dados de clientes sejam protegidos.

as vulnerabilidades de espectro e fusão apresentaram um enigma para a indústria da computação porque a vulnerabilidade se origina em hardware. Em alguns casos, o melhor que podemos fazer para os sistemas existentes—que compõem a maior parte dos servidores instalados e PCs—é tentar reescrever o software para tentar limitar os danos. Mas essas soluções são ad hoc, incompletas, e muitas vezes resultam em um grande sucesso para o desempenho do computador. Ao mesmo tempo, pesquisadores e designers da CPU começaram a pensar em como projetar CPUs futuras que mantêm a especulação sem comprometer a segurança.

uma defesa , chamada de kernel page-table isolation (KPTI) , é agora incorporada ao Linux e outros sistemas operacionais. Lembre-se que cada aplicação vê a memória e os ativos de armazenamento do computador como um único trecho contíguo de memória virtual tudo o que é seu. Mas fisicamente, esses ativos são divididos e compartilhados entre diferentes programas e processos. A tabela de páginas é essencialmente o mapa do sistema operacional, dizendo-lhe quais partes de um endereço de memória virtual correspondem a que endereços de memória física. A tabela de página do kernel é responsável por fazer isso para o núcleo do sistema operacional. KPTI e sistemas similares se defendem contra a fusão, fazendo dados secretos na memória, como o SO, inacessível quando o programa de um usuário (e potencialmente o programa de um atacante) está em execução. Ele faz isso removendo as partes proibidas da tabela de páginas. Dessa forma, mesmo código executado especulativamente não pode acessar os dados. No entanto, esta solução significa trabalho extra para o sistema operacional mapear essas páginas quando ele executa e desfazê-las depois.

outra classe de defesas dá aos programadores um conjunto de ferramentas para limitar a especulação perigosa. Por exemplo, o patch Retpoline do Google reescreve o tipo de ramos que são vulneráveis à variante 2 da Spectre, de modo que força a especulação para atingir um gadget benigno e vazio. Programadores também podem adicionar uma instrução em linguagem de montagem que limita Spectre v1, restringindo a memória especulativa lê que seguem ramos condicionais. Convenientemente, esta instrução já está presente na arquitetura do processador e é usada para impor a ordenação correta entre as operações de memória originadas em diferentes núcleos de processador.

como os designers do processador, Intel e AMD tiveram que ir mais fundo do que um patch de software regular. Suas correções atualizam o microcódigo do processador. Microcódigo é uma camada de instruções que se encaixa entre a linguagem de montagem de software regular e o circuito real do processador. O microcódigo adiciona flexibilidade ao conjunto de instruções que um processador pode executar. Ele também torna mais simples de projetar uma CPU porque ao usar microcódigo, instruções complexas são traduzidas para múltiplas instruções mais simples que são mais fáceis de executar em um pipeline.

basicamente, Intel e AMD ajustaram seu microcódigo para mudar o comportamento de algumas instruções em linguagem de montagem de maneiras que limitam a especulação. Por exemplo, os engenheiros da Intel adicionaram opções que interferem com alguns dos ataques, permitindo que o sistema operacional esvaziasse as estruturas do ramo-predictor em certas circunstâncias.

uma classe diferente de soluções tenta interferir com a capacidade do atacante de transmitir os dados usando canais laterais. Por exemplo, a tecnologia DAWG do MIT divide de forma segura o cache do processador de modo que programas diferentes não compartilham nenhum de seus recursos. Mais ambiciosa, há propostas para novas arquiteturas de processadores que introduziriam estruturas na CPU que são dedicadas à especulação e separado do cache do processador e de outro hardware. Desta forma, quaisquer operações que sejam executadas de forma especular, mas que não sejam eventualmente cometidas, nunca são visíveis. Se o resultado da especulação for confirmado, os dados especulativos são enviados para as principais estruturas do processador.

vulnerabilidades de especulação permanecem dormentes em processadores por mais de 20 anos, e eles permaneceram, tanto quanto se sabe, inexplorados. Sua descoberta abalou substancialmente a indústria e destacou como a segurança cibernética não é apenas um problema para sistemas de software, mas também para hardware. Desde a descoberta inicial, cerca de uma dúzia de variantes de espectro e fusão foram reveladas, e é provável que haja mais. Spectre e Meltdown são, afinal de contas, efeitos colaterais de princípios de design de núcleo que temos confiado para melhorar o desempenho do computador, tornando difícil eliminar tais vulnerabilidades em projetos de sistemas atuais. É provável que novos projetos de CPU evoluam para manter a especulação, evitando o tipo de vazamento de canal lateral que permite estes ataques. No entanto, futuros designers de sistemas de computador, incluindo aqueles que projetam chips de processador, devem estar cientes das implicações de segurança de suas decisões, e não mais otimizar apenas para o desempenho, tamanho e potência.Nael Abu-Ghazaleh é presidente do programa de engenharia de computadores da Universidade da Califórnia, Riverside. Dmitry Evtyushkin é um professor assistente de ciência da computação no College of William and Mary, em Williamsburg, Va. Dmitry Ponomarev é um professor de ciência da Computação da Universidade Estadual de Nova Iorque, em Binghamton.

para sondar mais além

Paul Kocher e os outros pesquisadores que coletivamente divulgaram Spectre primeiramente explicaram isso aqui . Moritz Lipp explicou o colapso nesta palestra na Usenix Security ‘ 18. Foreshadow foi detalhado na mesma conferência.

um grupo de investigadores, incluindo um dos autores, apresentaram uma avaliação sistemática dos ataques de espectro e fusão que revelam ataques potenciais adicionais . Engenheiros da IBM fizeram algo semelhante, e engenheiros do Google recentemente chegaram à conclusão de que o canal lateral e ataques especulativos de execução estão aqui para ficar .

Deixe uma resposta

O seu endereço de email não será publicado.

More: