Protections against attacks based on return-oriented programming - Mateus Felipe Tymburibá Ferreira (2020)
Programação Orientada a Retornos (ou ROP, de Return-Oriented Programming) é o nome de uma técnica usada para o desenvolvimento de códigos maliciosos que vem sendo amplamente utilizada para forçar a execução de códigos arbitrários em aplicações vulneráveis. Em função de seu vasto emprego em investidas contra sistemas computacionais modernos, proteções contra ROP têm sido extensamente estudadas. Apesar disso, ainda não existe uma solução definitiva contra esses ataques. Esta tese introduz abordagens que dificultam a consolidação de ataques dessa natureza. Criamos uma análise estática de código-fonte que estima a densidade máxima de instruções de desvio indireto executável por um programa. Sua importância advém do fato de que diversas proteções monitoram a densidade de desvios indiretos, a fim de detectar ataques ROP. Contudo, esses mecanismos utilizam limiares únicos: a frequência de desvios indiretos que caracteriza um ataque é a mesma para qualquer aplicação. Neste trabalho, mostramos que limiares padrão podem ser superados facilmente. Como alternativa, criamos um algoritmo que estabelece o melhor valor de limiar para cada aplicação. Nossa análise foi implementada no compilador LLVM e utilizada para encontrar limiares específicos para cada benchmark que compõe o SPEC CPU2006. Comprovamos a acurácia da nossa abordagem comparando os limiares estimados com valores reais obtidos durante a execução desses benchmarks no framework Pin. Apesar de percorrer estaticamente todos os possíveis caminhos de execução de um programa ser um problema teoricamente indecidível em situações gerais, para o contexto expecífico de proteção contra ataques baseados em ROP nós conseguimos desenvolver um algoritmo que encontra a solução para programas com até 700 mil instruções em 25 minutos. Em nossa segunda abordagem, apresentamos um sistema multicamadas para proteger programas contra ataques ROP. As camadas superiores validam a maior parte do fluxo de controle de um programa a um baixo custo computacional; portanto, não comprometendo o tempo de execução. As camadas inferiores fornecem fortes garantias para lidar com fluxos mais suspeitos; aumentando assim a segurança. Nossa solução multicamadas combina técnicas já descritas na literatura com uma verificação que introduzimos: checar se a instrução de chamada de função que precede um endereço de retorno aponta para um segmento executável do programa. Cuidamos para que nosso projeto utilize unidades microarquiteturais já disponíveis nas versões modernas dos processadores x86. Nosso modelo impõe uma sobrecarga no tempo de execução de apenas 0,57% em um conjunto de benchmarks compostos por todos os 29 programas do SPEC CPU2006 e 209 benchmarks do LLVM Test Suite. A nova verificação que introduzimos para validar as instruções CALL que precedem os endereços de retorno também é efetiva. Ela reduz em quase 35x o número de gadgets disponíveis para criar um ataque, quando comparada a uma técnica semelhante tradicionalmente empregada na literatura. Também analisamos o número de falsos positivos que atravessam nossas camadas de proteção. Nesse experimento, enriquecemos nosso conjunto de benchmarks com os três navegadores de desktop mais populares e quatro aplicativos vulneráveis para os quais existem explorações ROP conhecidas. Apenas 3,14% de todos os endereços de retorno são considerados inválidos nesses benchmarks. Demonstramos que o custo para tratar esses falsos positivos não afeta o tempo de execução dos programas.