DEFCON 20: "Timing-Based Attestation"

Essa foi uma das palestras mais técnicas da DEFCON 20 e uma das poucas centradas em defesa, e não em ataque. Nela, os autores apresentaram um sistema de checagem de integridade de código (para tentar garantir que o núcleo do sistema operacional esteja em um estado conhecido, livre de malware) cuja dificuldade de forjar se baseia não apenas no resultado do hash calculado, mas também no tempo que o cálculo leva. A idéia central é simples: o procedimento de cálculo foi cuidadosamente planejado, a nível de código assembler, para que, se algum malware tentar interceptá-lo ou bancar o impostor, as instruções extras necessárias para tal já impõem uma demora extra que pode ser medida por um servidor externo (mesmo levando em conta a latência da rede local).

Se por um lado a essência da idéia é simples e até fácil de sumarizar, na prática a implementação é recheada de detalhes técnicos complicados e desafiadores. Então, vamos por partes – e peço desculpas de antemão se o excesso de jargão técnico acabou por tornar o texto pouco acessível aos iniciantes, mas, o assunto é complicado mesmo.

Primeiro, vamos descrever a arquitetura: imagine que você tem um "servidor de atestamento" que tem uma cópia do software cuja integridade você quer testar – no caso dos autores, eles queriam atestar o kernel de algumas versões particulares do Windows. Eles tinham nesse servidor cópias dos arquivos principais, tais como eles ficam carregados em memória, muito embora os kernels a serem atestados não estão realmente em execução – eles são tratados pelo software de atestamento apenas como "dados".

Considera-se, ainda, como premissa, que o servidor de atestamento é intrinsecamente confiável e livre de malware; naturalmente, se ele fosse invadido, todo o esquema iria por água abaixo. Mas isso não me parece uma exigência exagerada; toda arquitetura de segurança precisa de um certo mínimo de "pontos de partida".

O servidor de atestamento tem um cadastro de todas as máquinas-cliente e as versões do software rodando nelas. Desse cadastro, ele escolhe (aleatoriamente, em sequência, ou segundo alguma política qualquer) uma máquina-cliente para desafiar. Ele sorteia aleatoriamente um nonce e calcula o hash do software, tal como contido no seu banco de dados. Em seguida, ele envia um pacote de "desafio" para a máquina-cliente, contendo apenas o nonce.

Se a máquina-cliente não responder em um certo número de milissegundos, o servidor dá um alarme informando que o software de atestamento ou não está instalado, ou está sendo bloqueado, ou impedido de executar, o que já se considera uma situação suspeita.

Se a máquina cliente responder, o servidor compara o hash contido na resposta com o que calculou previamente. Se o resultado não bater, aciona o alarme – o software rodando no cliente deve estar comprometido.

Agora vem o "pulo do gato": se a resposta bater, o servidor repete várias vezes a pergunta, cronometrando cuidadosamente o tempo que o cliente leva para responder. Se essa resposta estiver dentro de uma faixa de valores esperados, previamente determinados quando a máquina foi originalmente cadastrada, o servidor declara tudo ok. Mas, se o tempo médio da resposta exceder esse baseline, o servidor alerta de novo.

No lado do cliente, eles instalam o "cliente de atestamento", implementado na forma de um driver de rede (que roda em modo privilegiado, no anel zero). Esse cliente, ao receber o desafio do servidor, pega o nonce e o usa como parte de uma elaborada rotina de hashing para calcular a resposta que o servidor espera em termos dos bytes exatos, na memória, do segmento de texto do kernel e demais arquivos críticos sendo atestados.

A idéia básica é que se o algum malware tentar alterar o kernel ou instalar hooks, o valor do hash calculado pelo cliente não vai bater com o esperado pelo servidor. E, se o malware interceptar o código de cálculo do hash (tipicamente fazendo um hook com instruções jmp) na tentativa de forçar a conta a dar certo, as instruções extras, quando repetidas várias milhões de vezes devido à necessidade de operarem por sobre todo o código sendo atestado, vai gerar um atraso que o servidor será capaz de identificar.

Todavia, os apresentadores foram cuidadosos em dizer que esse atraso só é confiavelmente identificável em redes locais (testaram com uma ethernet com até dez níveis de switches cascateados) e que não tinham esperança de que isso funcionasse sob WANs porque a latência típica na Internet facilmente excede o desvio padrão de algumas dezenas de milissegundos que é a diferença entre o código legítimo e o código troianizado.

Para que o cálculo do hash seja o mais rápido possível, ele teve de ser escrito diretamente em linguagem assembly e estruturado de uma maneira bem específica, sobre a qual os palestrantes passaram um bom tempo discorrendo, para que não houvessem atalhos ou subterfúgios que permitissem alterá-lo sem torná-lo mais lento ou dar o resultado errado. O emprego do nonce aqui é o clássico: evitar ataques de replay, que, do lado do cliente, o resultado pudesse ser hardcoded.

Os autores delimitaram o escopo desse trabalho exclusivamente para o atestamento do kernel; informam que um outro trabalho e outro artigo tratarão da questão de, uma vez o com o kernel atestado e declarado íntegro, medirem também os processos userspace.

Mesmo assim, há trocentos detalhes técnicos capciosos e várias lacunas e incertezas. Por exemplo, software rodando no modo SMM (system management mode) tem ainda mais privilégio que o anel zero – se houver algum troiano ou malware que tenha conseguido se infiltrar no modo SMM, talvez ele possa burlar o cliente de atestamento (mas talvez não rápido o bastante para evitar detecção). Mesmo sem precisar invocar os super-poderes do SMM, há vários lugares que o software de atestamento não inclui no cálculo hash, por serem variáveis em tempo de execução, pois o servidor não teria como saber quais são. A aleatorização do mapa de memória virtual, feita pelo ASLR e afins, também atrapalha.

Não obstante, eles conseguiram implementar esse sistema todo para Windows XP, Vista e 7 de 32 bits e estão portando as coisas para 64-bit. Melhor ainda, estão ofercendo a implementação como código aberto:

Mas a complexidade da infra-estrutura pode ser vista pela quantidade de vídeo-tutoriais necessários à instalação.

Segue abaixo a íntegra do artigo acadêmico deles:

O artigo não é fácil de ler; não apenas porque é denso e o assunto é complexo, mas também porque ele assume (e diz isso claramente na introdução) que o leitor já leu os artigos anteriores da série no qual ele se baseia.

Pelo que entendi do artigo e da apresentação, eles testaram isso em umas trinta máquinas lá no laboratório deles, o que ainda é pouco para se estabelecer a viabilidade prática em grandes ambientes corporativos – mas certamente já é promissor. Senti falta de uma aprofundamento na questão dos falsos positivos na prática; talvez porque o foco deles no momento seja validar as técnicas básicas.

Não obstante, é um trabalho monumental e uma das possibilidades mais críveis que já vi até agora para um sistema robusto de atestamento de software para hardware convencional que não se baseie em TPM ou afins. Definitivamente vale aprofundar-se nesse assunto e acompanhar essa linha de pesquisa. Por outro lado, será que esse tipo de tecnologia ainda vai ser relevante em um cenário onde a Microsoft está empurrando o UEFI Secure Boot goela abaixo do mundo inteiro? Só o tempo dirá.

Partes anteriores

Comentários
Aceita-se formatação à la TWiki. HTML e scripts são filtrados. Máximo 15KiB.

 
Enviando... por favor aguarde...
Comentário enviado com suceso -- obrigado.
Ele aparecerá quando os moderadores o aprovarem.
Houve uma falha no envio do formulário!
Deixei uma nota para os admins verificarem o problema.
Perdoe-nos o transtorno. Por favor tente novamente mais tarde.
Tímido Visitante Anônimo | 2012-08-13 12:16:57 | permalink | topo

Parece muito promissor também quando aplicado no userspace, para conferir a saúde das aplicações.