55
Capítulo 3 Segurança de Software em Sistemas Embarcados: Ataques & Defesas Bruno Silva , Diógenes Cecilio da Silva Jr. , Evaldo M. Souza , Fernando Pereira , Fernando A. Teixeira , Hao Chi Wong , Henrique Nazaré , Izabela Maffra , Jean Freire , Willer F. Santos , Leonardo B. Oliveira Universidade Federal de Minas Gerais {brunors@dcc, diogenes@cpdee, evaldoms@dcc, fpereira@dcc, fateixeira@dcc, hnsantos@dcc, karennina@dcc, jean@dcc, [email protected], leob@dcc}.ufmg.br Intel Corporation hao-chi.wong@intel.com Abstract Software Security is key for the overall security of information systems. Day by day, more and more software exploitations happen and thus Software Security increasingly relevant. At the same time, Embedded Systems are becoming not only ubiquitous but also pervasive. As a result, it is paramount that those systems are secured. The problem, however, is that existing solutions – as is – are inadequate to Embedded Systems. This course therefore aims at giving an overview on the state-of-the-art of Software Security and, subsequently, show how these solutions can be adapted and evaluated in the context of Embedded Systems. Resumo Segurança de Software é um tema central na segurança de sistemas como um todo. Ata- ques que exploram vulnerabilidades em código são cada vez mais frequentes e Segurança de Software, então, é cada vez mais relevante. Paralelamente, Sistemas Embarcados fa- zem cada vez mais parte de nossas vidas e tendem a se tornar, na prática, onipresentes. Assim sendo, a segurança desses dispositivos é de suma importância. As propostas de Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013 101 c 2013 SBC — Soc. Bras. de Computação

Segurança de Software em Sistemas Embarcados: Ataques & Defesas

Embed Size (px)

Citation preview

Capítulo

3

Segurança de Software em Sistemas Embarcados:Ataques & Defesas

Bruno Silva†, Diógenes Cecilio da Silva Jr.†, Evaldo M. Souza†, FernandoPereira†, Fernando A. Teixeira†, Hao Chi Wong∗, Henrique Nazaré†, IzabelaMaffra†, Jean Freire†, Willer F. Santos†, Leonardo B. Oliveira†

† Universidade Federal de Minas Geraisbrunors@dcc, diogenes@cpdee, evaldoms@dcc, fpereira@dcc, fateixeira@dcc,hnsantos@dcc, karennina@dcc, jean@dcc, [email protected],[email protected]

∗ Intel [email protected]

Abstract

Software Security is key for the overall security of information systems. Day by day,

more and more software exploitations happen and thus Software Security increasingly

relevant. At the same time, Embedded Systems are becoming not only ubiquitous but also

pervasive. As a result, it is paramount that those systems are secured. The problem,

however, is that existing solutions – as is – are inadequate to Embedded Systems. This

course therefore aims at giving an overview on the state-of-the-art of Software Security

and, subsequently, show how these solutions can be adapted and evaluated in the context

of Embedded Systems.

Resumo

Segurança de Software é um tema central na segurança de sistemas como um todo. Ata-

ques que exploram vulnerabilidades em código são cada vez mais frequentes e Segurança

de Software, então, é cada vez mais relevante. Paralelamente, Sistemas Embarcados fa-

zem cada vez mais parte de nossas vidas e tendem a se tornar, na prática, onipresentes.

Assim sendo, a segurança desses dispositivos é de suma importância. As propostas de

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

101 c©2013 SBC — Soc. Bras. de Computação

Segurança de Software existentes, no entanto, não são plenamente apropriadas para Sis-

temas Embarcados. Tais dispositivos possuem um grande número de particularidades

como restrições de processamento, memória e energia, quando comparados à computa-

dores convencionais. Consequentemente, a eficiência energética de Segurança de Soft-

ware para esses Sistemas Embarcados deve ser também mensurada. O objetivo deste

capítulo é apresentar uma visão geral da área de Segurança de Software e mostrar como

adaptar e avaliar as soluções existentes no contexto de Sistemas Embarcados.

3.1. Introdução

Segurança de Software é um tema cada vez mais relevante [Jones 2007, McGraw 2006].Na medida em que ataques que exploram vulnerabilidades em software crescem verti-ginosamente [Alhazmi et al. 2007], a Segurança de Software torna-se um tema centralna segurança de sistemas computacionais como um todo. Por essa razão, essa tem sidouma área de pesquisa bastante ativa e inúmeras técnicas foram propostas recentemente( [Molnar et al. 2009, Wang et al. 2009, Dietz et al. 2012, Rodrigues et al. 2013], porexemplo). A maioria destas técnicas são baseadas na análise estática [Misra 1987], naanálise dinâmica [Bell 1999], ou na combinação de ambas, isto é, na análise híbrida [Ruset al. 2003].

Sistemas Embarcados, por outro lado, são sistemas especializados [Barr 1999,Carro and Wagner 2003, Marwedel 2011]. Diferentemente de um elemento computaci-onal convencional – voltado a aplicações genéricas –, um sistema embarcado dedica-sea executar bem uma ou poucas tarefas. Isso aliado ao fato de que eles são comumenteinclusos em outros sistemas faz com que Sistemas Embarcados tenham suas dimensõesreduzidas. Tal redimensionamento aliado à necessidade de redução de custos, por suavez, torna Sistemas Embarcados “pobres” de recursos computacionais [Hamacher et al.2012].

Sistemas Embarcados são cada vez mais comuns em nossas vidas [Zurita ]. Elessão os campeões de venda no mercado de elementos computacionais e estão presentes emgrande parte de outros dispositivos1. Com o advento da Internet das Coisas (Internet of

Things – IoT) e os smartphones tornando-se a plataforma de comunicação móvel padrão,a tendência é que Sistemas Embarcados tornem-se praticamente onipresentes.

Paralelamente à essa ubiquidade de Sistemas Embarcados – e os benefícios queela acarreta –, surge também certa inquietação. Parte dela advém da preocupação acercada Segurança de Software [Koopman 2004, DAVID and TIRI 2005] dispositivos, dadoque maioria das propostas existentes não levam em consideração as peculiaridades deSistemas Embarcados e, consequentemente, não lhes são apropriadas. Por exemplo, aocontrário de um computador tradicional, Sistemas Embarcados usualmente (i) possuemmenor capacidade de processamento e memória; (ii) possuem fonte restrita de energia; eiii) possuem um grau de rede maior [Akyildiz et al. 2002] (não apenas porque fazem partede redes cuja escala é maior, mas também porque encontram-se no núcleo das mesmas,não raro exercendo também o papel de roteadores [Akyildiz et al. 2002]). No entanto, aspropostas de Segurança de Software existentes foram concebidas para sistemas convenci-

1http://www.simoneconcepts.com/embeddedsystems/realembeddedsystems.

html

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

102 c©2013 SBC — Soc. Bras. de Computação

onais e, portanto, não consideram essas particularidades.

Objetivos. O objetivo deste capítulo é, derradeiramente, apresentar uma visão geral dosataques e defesas na área de Segurança de Software. Em relação aos ataques, pretende-se apontar os mais comuns, demonstrando seu funcionamento, potencial de impacto esuas variações. No tocante a defesas, objetivamos demonstrar como ataques podem serevitados ou identificados, seja em tempo de compilação, seja em tempo de execução.Por fim, pretendeu-se mostrar por meio de um estudo de caso como adaptar soluçõesexistentes para o contexto de Sistemas Embarcados.

Organização. O restante deste trabalho está organizado da seguinte forma.

A seção 3.2 e a seção 3.3 discorrem mais sobre Sistemas Embarcados e Segurançade Software, respectivamente.

A seção 3.4 versa sobre diversos tipos ataques. Por exemplo, sobre

1. o Estouro de Arranjo (seção 3.4.1);

2. o Estouro de Inteiro (seção 3.4.2);

3. e o Vazamento de Endereço (seção 3.4.3).

Já a seção 3.5 discorre acerca de mecanismos de defesa. Por exemplo, acerca de:

1. Aleatorização de Espaço de Endereço (Address Layout Space Randomization –ALSR) (seção 3.5.1);

2. Prevenção contra a Execução de Dados (Data Execution Prevention – DEP) (se-ção 3.5.2);

3. Canários (seção 3.5.3);

4. Verificação de Limites de Arranjo (Array bounds-checking) (seção 3.5.4);

5. Análise de Intervalo (seção 3.5.5);

6. Análise Distribuída (seção 3.5.7).

Ao final, apresentamos mais três seções, a saber:

1. Metodologia de Avaliação (seção 3.6);

2. Estudo de Caso (seção 3.7);

3. Conclusões (seção 3.8).

A maioria dos títulos das seções supracitadas já sugerem o seu conteúdo. No en-tanto, três delas merecem ser destacadas por apresentarem soluções exclusivamente vol-tadas à Sistemas Embarcados. São elas as seções 3.5.7, 3.6 e 3.7. A Análise Distribuída(seção 3.5.7), como ressaltamos anteriormente, explica como cruzar e extrair informa-ções de códigos que são executados de forma distribuída, com o objetivo de aumentar asegurança global do sistema. Isso é interessante pois a literatura apresenta mecanismosvoltados para programas centralizados, ou seja, que são executados dentro de um mesmo

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

103 c©2013 SBC — Soc. Bras. de Computação

nó da rede. Ademais, essa estratégia é fundamental no contexto de Sistemas Embarcados,pois estes são usualmente inseridos em um contexto de rede onde nós interagem frequen-temente. Na Metodologia de Avaliação (seção 3.6) a ideia é mostrar como um mecanismode segurança pode ser avaliado sob a ótica energética. Diferentemente de soluções volta-das para elementos computacionais convencionais (como desktops), em que a avaliaçãomais importante é a sobrecarga (overhead) em termos de tempo e armazenagem, aqui, amais relevante é a energia consumida em razão da sua escassez em Sistemas Embarcados.E na seção 3.7 apresentamos um estudo de caso em que ilustramos todo o processo de seproteger um sistema embarcado, da percepção do problema, passando pela concepção dasolução e, por fim, sua avaliação.

3.2. Sistemas Embarcados

Sistemas Embarcados, também conhecidos como Sistemas Embutidos, são sistemas com-putacionais dedicados que fazem parte de um sistema mais complexo. O termo embutidosignifica que este sistema está incrustado em um ambiente e que apresenta interconexõesbem definidas. Esta dedicação se deve ao fato que um Sistema Embarcado implementauma única função, ou no máximo algumas poucas funções. Como diversos processadorespodem ser empregados, o que os diferencia é exatamente este programa dedicado. So-mado a estas principais características funcionais, eles devem responder a seus estímulosapós um intervalo de tempo definido. Assim, os Sistemas Embarcados são também siste-mas de tempo real. Por serem embutidos em algo maior, os Sistemas Embarcados devemser confiáveis, uma vez que falhas podem comprometer esta única função e por talvez serdifícil sua substituição ou reconfiguração remota. Finalmente, os Sistemas Embarcadossão voltados para um mercado de alto volume, o que implica em alta competitividadeentre fornecedores e em baixo custo individual.

Podemos agora definir um Sistema Embarcado como:

Um Sistema Embarcado é um sistema computacional que implementa

um única tarefa, dirigido (ou definido) por software, que utiliza interfaces

dedicadas, devem responder a estímulos em tempo real, serem confiáveis e

serem comercialmente competitivos.

Sistemas Embarcados utilizam agressivamente plataformas de hardware, uma vezque são dirigidos por software e diversas implementações de processadores podem serutilizados. Isso implica em uma forte redução do custo do projeto do processador, quepode empregar circuitos integrados comerciais. O custo de hardware se concentra en-tão no projeto das interfaces de entrada e saída, que como são definidos pelo ambiente,geralmente utilizam padrões industriais.

Como visto na tabela 3.1, Sistemas Embarcados podem ser encontrados nos maisvariados produtos como aviões, sistemas de defesa, aparelhos biomédicos, automóveis,dispositivos de E/S, instrumentos eletrônicos, aparelhos domésticos, industriais e brin-quedos.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

104 c©2013 SBC — Soc. Bras. de Computação

Aviões e Sistemas de defesa piloto automático, sistemas de navegação, controle de motores,sistemas de aterrizagem

Sistemas Biomédicos Ultrassom e CAT, monitores para pacientes, marcapassosAutomóveis controle do motor, sistemas ABS e de tração, controle de air-bags,

diagnóstico embarcadoComunicações e Redes satélites, roteadores de rede, switches, modemsWireless Pontos de acesso, adaptadores, rede de sensores sem fioEletrônica de Consumo TV, tocadores de DVS e Blue-ray, fornos de micro-ondas,

câmeras, linha brancaDispositivos de E/S teclados, mouse, impressoras, escaneadores, modemsInstrumentos Eletrônicos Osciloscópios, Multímetros, analisadores lógicosEquipamentos Industriais controlador de elevadores, robôs, sistemas de segurança,

PLC, máquinas CNCEscritório FAX, copiadoras, telefones, calculadoras, máquinas registradorasDispositivos Pessoais celulares, relógio de pulso, tabletsDispositivos Bancários cartão bancário ou de crédito, ponto-de-venda, terminal de acessoBrinquedos Video games (XBox, Wii, etc.), Furby, Nintendo DS

Tabela 3.1. Exemplos de Sistemas Embarcados

3.2.1. Características

A principal característica que diferencia Sistemas Embarcados de computadores de usogeral é a sua alta especificidade, geralmente uma única funcionalidade. Isso implica emescolhas que maximizem funcionalidade e minimizem custos de fabricação e tempo dedesenvolvimento. Ao contrário, computadores de uso geral (ou PCs) são voltados a usuá-rios de perfis diversos o que leva a inclusão de múltiplas interfaces de conexão e interfaceshomem-máquina.

Processador (CPU). As plataformas de hardware para Sistemas Embarcados são im-plementadas usando microprocessadores ou microcontroladores comerciais, módulos dememória para programa e dados, suporte para interrupções, temporizadores e móduloscontroladores para interfaces de entrada e saída. Na medida em que novas funcionalida-des foram demandadas outros módulos foram adicionados como relógios de tempo real,gerentes de energia e tensão, e até mesmo gestores de perfis de consumo de energia.

A grande maioria dos Sistemas Embarcados utilizam microcontroladores que sãocircuitos integrados que agregam um processador, blocos de memória para programase dados, gerador de clock, controlador de interrupções e controladores de interfaces deentrada e saída. Fabricantes oferecem famílias de microcontroladores onde em torno deum mesmo processador diversas opções de memórias, entradas e saídas, e outros módulose que são oferecidas em diferentes circuitos integrados.

Uma tendência mais recente é o oferecimento de Sistemas-em-um-Chip (Systems-

on-Chip – SoCs), onde um processador é integrado a memórias, interfaces de entradae saída, temporizadores, e eventualmente a outro processador de uso geral ou um deProcessamento de Sinais (Digital Signal Processor – DSP).

Quanto ao número de bits, as CPUs podem ser de 8, 16 ou 32 bits. Apesar deCPUs para PCs usarem 32 bits, e mais recentemente 64 bits, a maior parte de de aplica-ções embarcadas utilizam 8 ou 16 bits. Fabricantes como a Atmel e Microchip com as

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

105 c©2013 SBC — Soc. Bras. de Computação

linhas AVR e PIC, respectivamente, dominam as soluções de 8 bits com processadoresque rodam com frequências entre 20 e 30 MHz. A Texas Instruments e a mesma Mi-crochip oferecem as linhas MSP430 e PIC16 (respectivamente) para CPUs de 16 bits. Alinha TI MSP430 é uma das líderes em soluções de ultra baixo consumo de energia.

Entretanto a demanda por CPUs de maior poder computacional tem crescido re-centemente e diversas soluções de 32 bits estão disponíveis, como as famílias de micro-controladores PIC32, ARM M0, M1 e M4. Uma característica marcante das linhas de 32bits é sua maior capacidade de memória, que pode chegar a 256 KBytes.

Ainda assim, existem classes de aplicações que demandam maior desempenho, oque implica em maiores frequências de operação e capacidade de memória. Estes dispo-sitivos são chamados de microprocessadores embutidos (ou embedded microprocessors)para distinguí-los dos microcontroladores. Eles permitem funções de controle de super-visão, utilizam MMU (unidade de gerência de memória) que controla caches e provêmemória virtual. Operam com frequência de clock de centenas de megaherz até mais de 1GHz, podem incluir coprocessadores aritméticos de ponto flutuante e aceleradores gráfi-cos. Com isso podem usar sistemas operacionais mais elaborados como Linux embutidos.

Nível de Integração. A demanda de baixos custos, alta densidade e menores fatores deescala tem levado a um nível de integração em que uma plataforma de hardware seja im-plementada com poucos CIs. Na medida que o nível de integração aumentou, mais e maislógica foi adicionada ao processador, periféricos padronizados e módulos de memóriaforam agregados em um único chip, criado famílias de processadores com alto grau deespecificidade. Tais processadores são chamados de SoC.

Alimentação e Potência. Sistemas Embarcados utilizam fontes de alimentação das maisvariadas formas e geralmente precisam adaptar o valor de tensão disponível para o(s)valor(es) necessário(s). Uma plataforma típica de hardware pode apresentar módulos dealto consumo de energia, como discos magnéticos, discos baseados em memórias flash(SSD), displays coloridos, e interfaces wireless.

A potência dissipada por sistemas computacionais se deve principalmente a ativi-dade de troca de valores binários dos sinais elétricos, o que provoca a geração de calor.Fabricantes de CIs provêm valores típicos que são uma média de consumo de potênciapara aplicações que utilizam porções representativas do circuito interno e suas entradase saídas. Nem sempre é necessária a utilização de uma ventoinha para remover o calorgerado, e o uso de um dissipador metálico acoplado ao CI pode ser suficiente.

Confiabilidade/Disponibilidade. Sistemas Embarcados estão incrustados em máquinasou sistemas mais complexos que devem rodar continuamente por anos sem erros, e emmuitos casos se recuperarem por si mesmos. Deste modo o software deve ser desenvol-vido e testado com muito mais cuidado do que software para PCs, e o hardware deve evitardispositivos mecânicos com peças móveis. Alguns problemas de confiabilidade são:

• o sistema não pode ser desligado com segurança para reparos;

• o sistema deve rodar sempre e modos de desempenho reduzido não são admissíveis;

• o ambiente ou sistema sofrerá perdas econômicas se for desligado.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

106 c©2013 SBC — Soc. Bras. de Computação

Uma variedade de técnicas são empregadas, e muitas vezes combinadas, para serecuperar de erros de software e hardware, como por exemplo vazamentos de memória ouintegridade de sinal comprometida por crosstalk. As técnicas mais comuns são:

• temporizador watchdog, para reinício do software;

• redundância de hardware total ou parcial;

• modo reduzido em software;

• hipervisor embutido, baseado em virtualização de software;

• memória com correção de erros (ECC).

Fator de Forma e Expansibilidade. Sistemas Embarcados utilizam diversos fatores deforma e que geralmente é determinado pelo ambiente onde o sistema será embutido. Amaioria é composta por uma única placa de circuito impresso, denominadas Single Bo-

ard Computer (SBC). Nesta placa existem um conector de alimentação, para uma únicatensão de entrada, e diversos conectores para as interfaces dos periféricos padrão, comoUSB, SATA, etc., e quando necessário a placa pode apresentar um conector para sinaisdiscretos de E/S para interfaces especiais, como por exemplo o acionamento de relés ousensoriamento de chaves e interruptores. Os padrões mais conhecidos são o ConsórcioPC/104, que define um conjunto de placas de dimensões fixas e conectores padronizadospara um barramento usando o protocolo PCIe e que permitem o empilhamento de placas;o padrão COM Express e o padrão Qseven, onde cada placa contêm toda a lógica e CIspara um sistema computacional completo.

Como Sistemas Embarcados são projetados para uma aplicação específica, e ocusto é um requisito importante, expansibilidade é geralmente sacrificada. Mais ainda,ao utilizar microcontroladores de 8 e 16 bits a memória já vem com tamanho fixo e nãopode ser expandida, pois o barramento do processador não está disponível para conexão.Microprocessadores e SoCs de 32 bits geralmente usam memórias externas, pois progra-mas e dados podem ocupar centenas de kilobytes ou até mesmo megabytes. As soluçõesempregadas empregam CIs externos de memórias flash, para programas, e DRAM paradados, ou eventualmente apenas DRAM.

Conectividade. Conectividade é a característica de sistemas embutidos que mais temcrescido atualmente. Diversas previsões apontam para um número de 15 bilhões dispo-sitivos conectados à Internet em 2015, e a maioria deles são Sistemas Embarcados. Istoimplica que eles devem suportar pilhas IPv4 e brevemente IPv6. Outros padrões comoEthernet, WiFi, Bluetooth e Zigbee, devem também ser suportados, às vezes vários deles,dependendo da aplicação. Novos protocolos como o Near Field Communication (NFC)começam a serem usados para interligar dispositivos móveis, como celulares, com siste-mas de automação doméstica e bancária, e até mesmo com aparelhos eletrodomésticoscomo televisores inteligentes. Finalmente, as redes telefônicas móveis e sua comunica-ção GPRS, 3G e 4G, são formas atraentes de conexão remota para sistemas embutidos dedifícil acesso físico.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

107 c©2013 SBC — Soc. Bras. de Computação

Segurança. A segurança em Sistemas Embarcados nem sempre foi levada em conta umavez que, inicialmente, a maioria deles operavam embutidos em sistemas sem conectivi-dade exterior, como a internet. Em um automóvel uma rede local, baseada em protocolosCAN e LIN, interligam diversos sistemas embarcados dedicados, como por exemplo con-trole do motor, ABS e painel. Entretanto as novas aplicações que mais utilizam o conceitode Sistemas Embarcados são dispositivos móveis que precisam se interconectarem à Web

via protocolos Internet e diversas conexões sem fio como WiFi, 3G/GPRS e mesmo aEthernet com fio.

Aliado a isso está o fato de que aplicações para Sistemas Embarcados são comu-mente desenvolvidas em C. A opção pela linguagem é em razão da sua eficiência, ouseja, aplicações escritas em C são usualmente mais rápidas e com isso mais adequadasa sistemas com pouco recursos como Sistemas Embarcados. Contudo, tal eficiência tempreço. Quando comparada a outras linguagens de programação, C não implementa algunsmecanismos de segurança, o que deixa suas aplicações mais vulneráveis que as demais,em geral. Ao longo deste capítulo será detalhado a segurança da linguagem C e como issopode afetar a segurança Sistemas Embarcados como um todo.

3.3. Segurança de Software: Visão Geral

Segurança de Software é um tema cada vez mais relevante [Jones 2007, McGraw 2006].Na medida em que ataques que exploram vulnerabilidades em software crescem vertigi-nosamente [Alhazmi et al. 2007], a Segurança de Softwaretorna-se um tema central nasegurança de sistemas computacionais como um todo.

Ataques são comumente dividas em duas categorias: aqueles que concernem aosigilo da informação e aqueles relativos a integridade

O Vazamento de Endereço (Address Leak ou Program Data Leak) e o Vazamentode Dados (Data Leak) são exemplos de ataques relativos ao sigilo. A ideia é que o ad-versário force o vazamento de um dado que possa ser usado para comprometer o fun-cionamento do sistema. Por exemplo, o advento de mecanismos de segurança como oPrevenção contra a Execução de Dados evita que dados injetados pelo adversário sejamusados pelo sistema. Assim, uma alternativa para o adversário é descobrir o endereço deuma função sensível (\bin\sh, por exemplo) já contida no sistema para, subsequente-mente, alterar o fluxo de execução para a mesma. Tal descoberta não é sempre trivial euma das formas de determinar o endereço de uma função é antes realizar um ataque deVazamento de Endereço.

Ainda acerca do sigilo, é possível que o resultado de um vazamento, por si, jásatisfaça os anseios do adversário. Isso fica evidente quando se examina o trabalho deAranha et. al. sobre a urna eletrônica brasileira [Aranha et al. 2012]. Nele, observa-seque existe um Vazamento de Dados na urna, um vazamento da semente da função pseudo-aleatória responsável pelo baralhamento da ordem dos votos. Isso, derradeiramente, podelevar à quebra da propriedade de sigilo do voto em um pleito.

Agora vamos versar um pouco sobre os ataques que ferem a integridade de umsistema. Aqui resta o popular ataque de Estouro de Arranjo (Buffer Overflow). Nele, oadversário explora o fato de que linguagens como C não são fortemente tipadas e, por

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

108 c©2013 SBC — Soc. Bras. de Computação

Ataques

Sigilo

Vazamento

de Dados do

Programa

Vazamento

de Dados

do Usuário

Integridade

Estouro

de Inteiro

Estouro de

Arranjo

Figura 3.1. Vulnerabilidades

conseguinte, não verificam limites de arranjos. Ou seja, é possível preencher um arranjopara além dos seus limites, violando regiões de memória e sobrescrevendo de forma ilegalseus valores. Isso, por sua vez, permite desviar o fluxo de execução de programas para,por exemplo, execução de ações maliciosas.

Outro ataque conhecido é o ataque de Estouro de Inteiro (Integer Overflow). Aqui,o limite violado não é o de limites de arranjo, mas sim o de limites de inteiros. Este ataquepode ser empregado da seguinte maneira. Suponha um inteiro que determinará o tamanhode uma região de memória alocada dinamicamente. O adversário então força o estourodeste inteiro que agora passa a ter um valor pequeno, quem sabe negativo. A região dememória alocada será menor que esperada o que, por sua vez, pode viabilizar um ataquede Estouro de Arranjo.

Apontar vulnerabilidades que levam ao Estouro de Inteiro é algo particularmentedesafiador, dado que alguns deles são realizados de forma deliberada pelo programadorpara fins de eficiência2. Em outras palavras, a dificuldade resta não em apontar um Es-touro de Inteiro, mas em determinar-se se o mesmo é benigno ou maligno.

Felizmente, paralelamente à difusão de ataques de software, surgem também inú-meras propostas de defesa ( [Molnar et al. 2009,Wang et al. 2009,Dietz et al. 2012,Rodri-gues et al. 2013], por exemplo). A maioria das propostas de defesa existentes são basea-das na Análise Estática [Misra 1987,Wagner and Dean 2001], na Análise Dinâmica [Bell1999, Mock 2003], ou na combinação de ambas, isto é, na Análise Híbrida [Rus et al.2003, Ernst 2003]. A concepção dessas propostas é uma tarefa extremamente desafiadorajá que qualquer propriedade não trivial de linguagens recursivamente enumeráveis é umproblema indecidível [Hopcroft 2008] – em outras palavras, não existe programa genéricocapaz de decidir se um outro programa qualquer é ou não vulnerável.

A Análise Estática [Misra 1987] inspeciona o código antes do programa ser ins-

2Por exemplo, um programador pode mimetizar uma operação de módulo eficientemente por meio deuma operação que estoure o limite de um tipo inteiro.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

109 c©2013 SBC — Soc. Bras. de Computação

talado (deployed) e, por essa razão, é também conhecida como análise de código. Essaanálise varia de um simples script para o casamento de padrões até um sofisticado analisa-dor sintático (parser). A vantagem do método é que não há sobrecarga (overhead) diretaem tempo de execução, já que a análise é realizada a priori e o código fonte permaneceinalterado. O lado negativo é que no momento da análise não há as informações que oprograma dispõe em tempo de execução. Tal desinformação impossibilita determinar-sese há ou não vulnerabilidades em certos trechos de código. Na dúvida, a estratégia con-servadora é adotada e presume-se que a vulnerabilidade, sim, existe. Isso, por sua vez,traduz-se em falso-positivos (posteriormente, veremos que falso-positivos podem indire-tamente resultar em sobrecarga).

Por meio da Análise Estática é possível abordar algumas questões que já relata-mos. Por exemplo, para mitigar ou mesmo impedir ataques que ferem o sigilo de umsistema, pode-se empregar a Análise Estática para responder à seguinte questão: existeum caminho que vai de um dado sensível (ou secreto) até um canal público? Tal questão,na prática, pode ser mapeada para: o valor de uma variável sensível pode ser propagadoao longo da execução de um programa até ser passado como parâmetro para uma funçãodo tipo printf, fput etc.

Caso sim, então a análise retorna que há uma vulnerabilidade no programa.

Analogamente, a Análise Estática pode responder se a integridade de um programapode ser violada. Agora, a questão posta à análise é a seguinte: existe um caminho quevai de uma entrada pública, não confiável, até uma operação sensível? Novamente, naprática, essa questão pode ser refraseada, por exemplo, assim: há uma operação de leiturado tipo fscan cuja a entrada pode ser eventualmente propagada para dentro de um arranjocujos limites não são checados?

A Análise Dinâmica [Bell 1999], por sua vez, envolve a execução propriamentedita do programa objeto da análise, razão pela qual é também chamada de verificaçãoem tempo de execução (run-time checking). A ideia é rodar o programa para um con-junto provável de entradas e analisar seu comportamento (com respeito a vazamento dedados confidenciais, por exemplo). A vantagem da técnica é que ela pode tirar proveitode informações só disponíveis em tempo de execução. Isso, por sua vez, mitiga signifi-cantemente o problema dos falso-positivos, comum na análise estática. Desvantagem dométodo é que os resultados são pertinentes apenas às entradas testadas e, assim, não sepode derivar conclusões acerca do comportamento geral do programa. Por esse motivo, aAnálise Dinâmica é inerentemente inconsistente (unsound), isto é, não se pode afirmar seum programa é ou não vulnerável a partir desta técnica.

Exemplos de Análise Dinâmica para evitar ataques de Estouro de Arranjo sãoVerificação de Limites de Arranjo e Canários. O primeiro atua proativamente, verificandose o acesso à memória está dentro dos limites, caso contrário, o mecanismo aborta aexecução do programa. Para tal é necessário instrumentar o programa de forma a guardaros tamanhos de arranjos e checar seus limites sempre que ocorre um acesso à memoria.O Canário, por sua vez, atua de forma reativa abortando o programa logo após um ataquede Estouro de Arranjo ter sido efetuado. Com esse fim, o mecanismo insere uma valoraleatório entre o arranjos e endereços de retornos de funções. Ao final da função, verifica-se se o valor do canário foi alterado. Se foi, é sinal que um Estouro de Arranjo ocorreu e

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

110 c©2013 SBC — Soc. Bras. de Computação

então o programa é abortado.

Uma técnica comum é a Análise Híbrida [Rus et al. 2003], ou seja, a combi-nação das técnicas das Análises Estática e Dinâmica. Usualmente, a Analise Estática éprimeiramente empregada para encontrar-se o maior número de vulnerabilidades possívele, posteriormente, a Análise Dinâmica entra para monitorar o código nestes supostamentevulneráveis. Aqui resta o motivo pelo qual falso-positivos resultantes da Análise Dinâ-mica podem indiretamente acarretar sobrecarga. Todo trecho de código em que AnáliseDinâmica apontar vulnerabilidades será instrumentado com um monitor, o qual é utili-zado em tempo de execução, e incorre em sobrecarga. Portanto, é fundamental para aeficiência de um sistema que o número de falso-positivos seja baixo.

Defesas

Análise

Estática

Análise de

Intervalo

Grafo de

Dependências

Análise

Dinâmica

Verificação

de LimitesGuardas

Figura 3.2. Defesas

Por fim, uma análise pioneira, concebida por nós, é a Análise Distribuída. Veja,um sistema distribuído é formado por vários processos em execução que colaboram en-tre si para atingir uma meta comum. Tais sistemas estão presentes no nosso dia-a-diaem aplicações bancárias, comércio eletrônico, sistemas de telecomunicações entre outros.Ferramentas de Análises Estáticas convencionais não foram concebidas com foco nestainterlocução entre processo. Se uma ferramenta for capaz de cruzar informações oriundasdos vários processos que constituem o sistema distribuído, então mais constatações acercada segurança de um sistema poderão ser feitas. Entretanto, analisá-los concomitante nãoé uma tarefa trivial, pois o problema de análise de fluxo de informação entre os interlocu-tores pode ser computacionalmente ineficiente. A ideia da Análise Distribuída é realizaresse cruzamento de informações de maneira eficiente.

3.4. Ataques

3.4.1. Estouro de Arranjo

3.4.1.1. Visão Geral

Um buffer ou arranjo pode ser definido simplesmente como um bloco contíguo de me-mória, com a finalidade de armazenar um conjunto de um certo tipo de dados. O Estouro

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

111 c©2013 SBC — Soc. Bras. de Computação

de Arranjo é uma anomalia na qual tenta-se escrever sobre um arranjo mais dados do queele tem capacidade para armazenar. Dessa forma, as posições de memória adjacentes aoarranjo acabam sendo sobrescritas, configurando-se uma violação de memória. Os efeitosde tal anomalia podem variar desde um comportamento inesperado do programa a gravesvulnerabilidades de segurança.

As linguagens de programação que são afetadas por tal problema são as chamadasfracamente tipadas, como por exemplo C, amplamente utilizada em Sistemas Embarca-dos por sua eficiência. Nestas linguagens, quando ocorre um acesso para escrita ou leiturade um arranjo, não se verifica automaticamente se a escrita ou leitura ocorre dentro doslimites alocados. Em contrapartida, numa linguagem fortemente tipada, tal como Java,para cada porção de memória alocada são mantidos metadados que permitem a realizaçãode verificações de limites de arranjos para todo e qualquer acesso, em tempo de execu-ção. A realização ou não de tais verificações implica, na prática, num compromisso entredesempenho e robustez.

Dessa forma, se a linguagem de programação não provê mecanismos automáticospara evitar o Estouro de Arranjo, essa tarefa fica dependente da disciplina do programa-dor. A consequência de tal fato é que o Estouro de Arranjo configura-se como uma dasvulnerabilidades mais frequentes e mais exploradas por atacantes. Casos famosos de ex-plorações relacionadas a vulnerabilidades de Estouro de Arranjo ocorrem desde a décadade 80, como por exemplo o Morris worm que foi um dos primeiros worms disseminadospela Internet; todavia, os ataques permanecem atuais, como atestam os recentes ataques àplataforma de jogos Xbox, que permitiram o uso de software não licenciado.

Embora tais vulnerabilidades possam, em geral, ser facilmente corrigidas, se con-siderados casos individuais, o caso geral difícil de caracterizar, pelas diversas formascomo essa anomalia pode se apresentar. O Estouro de Arranjo permanece, portanto,como um desafio para a comunidade científica, pois as ferramentas de detecção e cor-reção desenvolvidas até o presente momento apresentam ainda grandes possibilidades demelhoria.

3.4.1.2. Funcionamento

Para compreender como funciona um ataque de estouro de arranjos, é necessário algumconhecimento sobre a memória de um processo computacional. Em geral, ela encontra-sedividida nas seguintes regiões:

• Texto: Uma região de tamanho fixo, que contém as instruções do programa, sendohabilitada apenas para leitura;

• Dados: Contém variáveis globais e estáticas do programa;

• Pilha: A pilha é um bloco de memória contíguo que contém uma sequência de qua-dros, que são inseridos quando uma função é chamada e retirados quando ela re-torna. Um quadro contém diversos tipos de dados necessários para a função, comoseus parâmetros, suas variáveis locais e endereço de retorno, bem como informaçãonecessária para recuperar o estado da pilha tal qual antes de sua chamada;

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

112 c©2013 SBC — Soc. Bras. de Computação

• Heap: Uma região de memória que contém as variáveis alocadas dinamicamente.Nessa região, o controle da memória deve ser realizado explicitamente pelo progra-mador.

Ataques de Estouro de Arranjo podem acontecer na pilha ou no heap. A seguir,serão discutidos os tipos de ataque mais comuns.

Ataque baseado em pilha. Em um ataque baseado em pilha, variáveis de controle podemser sobrescritas, de forma a alterar o fluxo de execução do programa. Em sua formamais elementar, esse ataque sobrescreve o endereço de retorno de uma função, conformedescrito no histórico artigo apresentado por Aleph One [Aleph One 1996]. Outras formasde ataque são possíveis também; em [Richarte et al. 2002], por exemplo, é descrito umataque no qual o stack frame pointer é sobrescrito de maneira a comprometer e controlaro fluxo de execução de um programa. Um atacante pode, então, desviar o controle parauma sequência de código executável previamente injetada na pilha.

Ataque baseado em heap. Ataques que ocorram no heap são mais difíceis de explorar ecompreender, devido à sua natureza dinâmica. Geralmente, ataques dessa natureza ocor-rem através da corrupção de estruturas internas de controle. No exemplo canônico, umaestrutura que controla os blocos livres de memória é sobrescrito e ao ser liberado acabapor sobrescrever o endereço de retorno de uma função na pilha. Mais detalhes sobre essetipo de ataque podem ser encontrado em [Robertson et al. 2003].

Ataque de retorno à libc. Um dos mecanismos de defesa mais amplamente adotados,conforme será descrito na seção 3.5.2, denomina-se Prevenção contra a Execução de

Dados. Esta proteção, como o próprio nome sugere, impediria um atacante de executarum código injetado no programa a partir da entrada, tornando a tomada de controle doprograma bem mais improvável. Entretanto, esse tipo de defesa dificulta, mas não impedeum ataque, pois um atacante pode, ainda, desviar o fluxo de execução para um códigobinário já carregado, como por exemplo uma biblioteca compartilhada. Dessa forma,código legítimo é reutilizado para fins maliciosos. O exemplo canônico de bibliotecautilizada nesse tipo de ataque é a libc, a biblioteca padrão da linguagem C. Emborapossa-se imaginar que tal ataque ofereça um controle bastante limitado ao atacante, talfato não se confirma na prática: conforme demonstrado em [Tran et al. 2011], um ataquede retorno à libc Turing-completo é possível, ou seja, um atacante pode realizar todotipo de computação.

3.4.2. Estouro de Inteiro

Algumas das linguagens de programação mais populares, como C, C++ e Java, limi-tam o tamanho de tipos numéricos inteiros. Por exemplo, o tipo int, em Java, contémos números inteiros entre −231 e 231 − 1. Existem, portanto, números que não podemser representados por esses tipos. Operações aritméticas inteiras, nessas linguagens deprogramação, possuem uma semântica modular [Warren 2002]. Se um número n é arma-zenado em uma variável v de tipo primitivo T , e o valor de n é maior que o limite superiorde T , aqui chamado Tmax, então parte dos bits que compõem n são descartados. O valorarmazenado em v termina por ser n módulo Tmax. A Figura 3.3 ilustra essa semânticamodular. Neste exemplo, estamos mostrando um programa escrito em C, que opera sobreo tipo char. Esse tipo número possui oito bits em complemento de dois. Assim, sete

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

113 c©2013 SBC — Soc. Bras. de Computação

bits representam valor, e um bit representa sinal. O inteiro 128 não possui representaçãonesse tipo. A tentativa de armazenar esse número em uma variável char produz o valor128 módulo 128 =−1 em complemento de dois.

1 1 1 10 0 1 1

1 0 0 0 0 0 0 0

1 0 0 0 0 1 0 1

123 =

128 =

133 =

= 123char

= −128char

= −123char

int main()

char i = 118;

while (i < 125)

i += 5;

printf("%8d", i);

printf("\n");

123 -128 -123 -118 -113 -108 -103 -98

-93 -88 -83 -78 -73 -68 -63 -58

-53 -48 -43 -38 -33 -28 -23 -18

-13 -8 -3 2 7 12 17 22

27 32 37 42 47 52 57 62

67 72 77 82 87 92 97 102

107 112 117 122 127

(a)

(c)

(b)

Figura 3.3. (a) Programa que ilustra a semântica modular de operações aritméti-cas inteiras em C. (b) Saída produzida pelo programa exemplo. (c) Representa-ção de três diferentes números usando o tipo char, com sete bits de valor, e umbit de sinal.

A tentativa de armazenar um número n em um tipo T , sendo n maior que a capaci-dade de T , produz um Estouro de Inteiro. Existem situações em que estouros de inteirossão aceitáveis [Dietz et al. 2012]. Por exemplo, programadores podem usar esse com-portamento para implementar funções hash e geradores de números aleatórios. Por outrolado, o mal uso desse semântica pode ter consequências catastróficas. Possivelmente, ocaso mais famoso de falha de software devido a Estouro de Inteiros aconteceu em 1996.Naquele ano, o foguete Ariane 5 teve de ser destruído devido a uma perda de precisãoem aritmética de inteiros. Essa falha custou ao programa espacial europeu cerca de 370milhões de dólares.

3.4.2.1. Vulnerabilidade de Software devido a Estouro de Inteiro

Estouros de inteiro podem levar à implementação não somente de programas semantica-mente incorretos, mas também à implementação de programas vulneráveis. Como apon-tado por Dietz et al. [Dietz et al. 2012], a semântica modular de C é a causa de diversasvulnerabilidades em aplicações bem conhecidas, como OpenSSH e Firefox. A Figura 3.4ilustra uma vulnerabilidade desse tipo. A função read_matrix copia uma matriz, emformato linearizado, isto é, representada como um vetor, a partir do arranjo de origemdata para o arranjo de destino buf. Uma faixa de memória de tamanho BUF_SIZE

é alocada para buf na linha 5 de nosso exemplo. Uma vez alocada essa região, que iráreceber dados, a função read_matrix realiza a cópia, caracter a caracter, nos laçosvistos nas linhas 6 e 7. O correto funcionamento da função assume que o produto w *h é menor que BUF_SIZE. Tal garantia é dada pelo teste condicional na linha 3. Sendo

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

114 c©2013 SBC — Soc. Bras. de Computação

esse teste verdadeiro, o programador entende que nunca serão copiados para buf maisdados que o arranjo buf comporta.

void read_matrix(int* data, char w, char h)

char buf_size = w * h;

if (buf_size < BUF_SIZE)

int c0, c1;

int buf[BUF_SIZE];

for (c0 = 0; c0 < h; c0++)

for (c1 = 0; c1 < w; c1++)

int index = c0 * w + c1;

buf[index] = data[index];

process(buf);

strlen(data) = 132char

BUF_SIZE = 120char

= 0 0 0 0 0 1 1 0 = 6char

= 0 0 0 1 0 1 1 0 = 22char

= 1 0 0 0 0 1 0 0 = -124char

w

h

h * w

1

2

3

4

5

6

7

8

9

10

11

12

13

14

buf_size = -124char

Figura 3.4. Exemplo de estouro de inteiro que pode ser usado para habilitar umataque de estouro de arranjo.

Existe, contudo, a possibilidade que um Estouro de Arranjo atribua à variávelbuf_size um valor muito menor que a expressão w * h produziria, se o tipo char

possuísse capacidade infinita. Por exemplo, se w for 6, e h for 22, então o produto w *h é 132. Esse número, quando atribuído a uma variável do tipo char, gera o valor -124.Isso faz com que o teste na linha 3 seja inicialmente verdade, ainda que BUF_SIZE sejaum valor menor que 132. Em nosso exemplo, temos que BUF_SIZE É 120. O arranjobuf será totalmente preenchido com dados, e os 12 bytes restantes irão sobre-escrevermemória da pilha da função read_matrix. Essa situação configura um caso de ataquede Estouro de Arranjo. Se a aritmética inteira de C possuísse precisão infinita, então afunção read_matrix estaria totalmente guardada contra esse tipo de ataque.

Estouro de Arranjo podem também tornar possíveis ataques de não terminação deprogramas. Um adversário realiza um ataque desse tipo fornecendo ao programa alvoentradas cuidadosamente produzidas para forçar iterações eternas sobre um laço vulne-rável. Tal cenário é ilustrado pela Figura 3.5, que contém um programa que computao fatorial de um número inteiro. O tamanho do tipo int, em C, não é parte da espe-cificação da linguagem. Essa informação depende, antes, da implementação do com-pilador. Entretanto, é usual que inteiros sejam representados como números de 32 bits

na maior parte das arquiteturas modernas. Neste caso, o maior inteiro representável éMAX_INT = 231 −1 = 2,147,483,647. Se o parâmetro n for igual a MAX_INT , então acondição da linha 4 sempre será verdadeira, e o laço nunca termina. A não-terminaçãoocorre porque quando i finalmente chega a MAX_INT , a soma i+1 produz o menor in-teiro possível, isto é, −231. A função fact vista na Figura 3.5 (b) não apresenta esse tipo

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

115 c©2013 SBC — Soc. Bras. de Computação

de vulnerabilidade, uma vez que o teste na linha 3 exclui parâmetros muito grandes.

int fact(int n)

int r = 1;

int i = 2;

while (i <= n)

r *= i;

i++;

return r;

1

2

3

4

5

6

7

8

9

(a)int fact(int n)

int r = 1;

if (n < 13)

int i = 2;

while (i <= n)

r *= i;

i++;

return r;

1

2

3

4

5

6

7

8

9

10

11

(b)se MAX_INT = 232 - 1

e se i = MAX_INT,

então i + 1 = -232

Figura 3.5. (a) Uma função em C, que calcula o fatorial de um número inteiroe está sujeita a ataques de não-terminação devido a estouros de arranjos. (b)Função similar, protegida contra a não-terminação.

3.4.2.2. Proteção contra Estouro de Inteiros

É possível sanear programas contra a ocorrência de Estouro de Inteiros automaticamente.Tal saneamento dá-se via a inserção de testes que verificam a ocorrência de estouros, edirecionam o fluxo de execução do programa para rotinas de tratamento de erro [Brumleyet al. 2007, Dietz et al. 2012, Rodrigues et al. 2013]. O código que constitui cada umdesses testes é formado por uma guarda, mas um tratador de eventos. Essas guardas usamtestes como aqueles ilustrados na Figura 3.6 para verificar a ocorrência de estouros deprecisão. A Figura 3.6 mostra testes que detectam estouros nas seguintes operações arit-méticas: adição, subtração, multiplicação e arredamentos para a esquerda. As operaçõesde adição, subtração e multiplicação podem ser com ou sem sinal aritmético.

Os testes são implementados como sequências de operações binárias, executadoslogo após a instrução guardada, e podem ser inseridos pelo compilador durante a geraçãode código. Para ilustrar esse ponto, a Figura 3.7 mostra o código que instrumenta umasoma com sinal de duas variáveis. A Figura usa código no formato intermediário de trêsendereços. Essa representação é padrão entre vários compiladores, como gcc e LLVM.Omitimos, nesse exemplo, o código do tratador de evento de estouro, pois ele simples-mente chama uma rotina implementada em uma biblioteca dinamicamente compartilhada.Conforme podemos observar pela Figura, uma guarda aumenta o código instrumentadosubstancialmente. Nesse exemplo em particular, a verificação requer a inserção de 14novas instruções no programa guardado. Embora tal crescimento a princípio possa pa-recer proibitivamente grande, diversos grupos de pesquisa já realizaram experimentosindicando que somente uma parcela muito pequena das instruções do programa alvo pre-cisam ser guardadas [Brumley et al. 2007, Dietz et al. 2012, Rodrigues et al. 2013].Consequentemente, o custo, em termos de crescimento de código e perda de desempe-nho, é negligível, ficando em torno de 1% a 5%.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

116 c©2013 SBC — Soc. Bras. de Computação

Instrução Verificação

x = o1 +s o2 (o1 > 0∧o2 > 0∧ x < 0) ∨

(o1 < 0∧o2 < 0∧ x > 0)

x = o1 +u o2 x < o1 ∨ x < o2

x = o1 −s o2 (o1 < 0∨o2 > 0∨ x > 0) ∨

(o1 > 0∨o2 < 0∨ x < 0)

x = o1 −u o2 o1 < o2

x = o1 ×u/s o2 x 6= 0 ⇒ x÷o1 6= o2

x = o1 shift n (o1 > 0∧ x < o1)∨ (o1 < 0∧n 6= 0)

x = ↓n o1 cast(x, type(o1)) 6= o1

Figura 3.6. Testes para detecção de Estouro de Inteiros. Usamos ↓n para descre-ver a operação que trunca em n bits. O subscrito s indica uma operação aritmé-tica com sinal, e o subscrito u indica uma operação sem sinal.

entry:

%add = add nsw i32 %x, %y

%0 = icmp sge i32 %x, 0 %1 = icmp sge i32 %y, 0 %2 = and i1 %0, %1 %3 = icmp slt i32 %add, 0 %4 = and i1 %2, %3 %5 = icmp slt i32 %x, 0 %6 = icmp slt i32 %y, 0 %7 = and i1 %5, %6 %8 = icmp sge i32 %add, 0 %9 = and i1 %7, %8 %10 = or i1 %4, %9 br i1 %10, label %11, label %12

%11: call void %handle_overflow(...) br label %12

%12: ret i32 %add

entry:

%add = add nsw i32 %x, %y

ret i32 %add

(b)

(e)

int foo(int x, int y)

return x + y;

(a)

(c)

(d)

x = o1 +

s o

2

(o1 > 0 ∧ o

2 > 0 ∧ x < 0) ∨

(o1 < 0 ∧ o

2 < 0 ∧ x > 0)

Figura 3.7. Instrumentação usada para prevenir Estouro de Arranjos. (a) Pro-grama a ser instrumentado. (b) Representação intermediária do programa a serprotegido. (c) Soma com sinal: operação que será guardada contra Estouro deInteiros. (d) Teste usado para verificar a ocorrência de estouros na soma comsinal. (e) Representação intermediária do programa protegido.

3.4.3. Vazamento de Endereço

Um vazamento de endereços ocorre quando um adversário descobre em que partes da me-mória estão carregados os dados ou códigos de um programa. Este tipo de conhecimento,que pode parecer inofensivo, acaba por anular dois mecanismos de segurança impostospelo sistema operacional: Aleatorização de Espaço de Endereço– (ASLR) [Shacham et al.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

117 c©2013 SBC — Soc. Bras. de Computação

2004a], [Bhatkar et al. 2003] e a Prevenção contra a Execução de Dados– (DEP).

Sistemas Operacionais modernos usam um mecanismo de proteção chamado Ale-atorização de Espaço de Endereço–(ALSR) que consiste em carregar os binários do pro-grama em partes diferentes da memória a cada execução. Esta técnica protege o software

contra ataques bem conhecidos, tais como return-to-libc [Shacham et al. 2004a]e return-oriented-programming (ROP) [Shacham 2007]. Entretanto, mesmo um sistemaprotegido por Aleatorização de Espaço de Endereço pode ser atacado caso o programapossua o bug conhecido como vazamento de endereço.

Como forma de melhor visualizarmos o problema de vazamento de endereço, se-guiremos com um exemplo deste tipo de vulnerabilidade e como um adversário poderiaprejudicar o sistema. Os trechos que seguem podem ser encontrados em sua versão origi-nal em [Quadros and Pereira 2012b].

3.4.3.1. Exemplo de vazamento de endereço

A Figura 3.8 mostra um simples echo server que contém um vazamento de endereço e quepossibilita à um adversário realizar um ataque de Estouro de Arranjo. Ele basicamenteaguarda por uma conexão na porta 4000 e quando isso acontece, ele ecoa a string recebida.A DEP evita um ataque por Estouro de Arranjo clássico, pois impede a execução de dados.Porém, um Estouro de Arranjo pode ser usado para redirecionar o fluxo de controle parauma das funções da libc, que nos permite realizar tarefas sensíveis como gerar novosprocessos, enviar e-mails e abrir conexões via socket.

Neste exemplo, o adversário conseguirá abrir um terminal telnet com a máquinaque está executando o echo server. Este tipo de ataque é chamado de return-to-libce tem como precondição o conhecimento prévio da função alvo, neste caso a funçãosystem da libc. Esta informação não é facilmente obtida pelo adversário em um sistemaprotegido por ASLR, a menos que exista uma vulnerabilidade de vazamento de endereço.

O vazamento de informação ocorre na função process_input. Sempre que o ser-vidor encontra a string debug ele retorna para o cliente dois endereços internos: a basede localbuf, que é um endereço de pilha e o endereço da função send de libc. Paraconstruir o exploit, usaremos o endereço de send para encontrar o endereço de system eexit. Em seguida, utilizaremos o endereço base de localbuf para encontrar o endereço dosargumentos de system. A Figura 3.9 mostra um script em Python que implementa esseexploit.

O script realiza duas conexões ao echo server: na primeira, ele envia a string

debug para obter os dois endereços e na segunda, ele envia os dados contaminados paracriar a conexão telnet. Os dados maliciosos são composto por 52 A’s que preenchema pilha até imediatamente antes do ponteiro de retorno; os endereços de system e exit

são calculado a partir do endereço de send obtido na primeira conexão. Finalmente, oendereço da string com o comando para criar a conexão telnet é calculado a partir doendereço base de localbuf.

O ataque se concretiza quando sobrescrevemos o endereço de retorno de pro-

cess_input com o endereço da função system. Neste momento ganhamos o controle da

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

118 c©2013 SBC — Soc. Bras. de Computação

Figura 3.8. Echo Server

máquina que está executando o echo server. Executando a função exit no fim do scriptde ataque, nós garantimos a terminação do cliente depois de obtermos um terminal abertocom o servidor.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

119 c©2013 SBC — Soc. Bras. de Computação

Figura 3.9. Client

3.4.3.2. Anatomia de um vazamento de endereços

Um programa em execução possui dois fluxos de informação nos quais um endereço podepercorrer até atingir um canal público e assim acarretar um vazamento de endereços:

1. Fluxos explícitos estão relacionados às dependências de dados. Se um programacontém uma instrução que define a variável v, e usa a variável u, tal como v = u+1,então existe um fluxo explícito de informação de u para v.

2. Fluxos implícitos estão relacionados ao fluxo de controle do programa. Se o pro-grama contém um desvio tal como i f p = 0 then v = u+ 1 else v = u− 1 , entãoexiste um fluxo implícito de informação de p para u, pois o valor atribuído ao últimodepende do primeiro.

A fim de tornar mais claro a importância do fluxo implícito na detecção do va-zamento de endereços, a Figura 3.10 mostra dois programas escritos em linguagem Cque contêm tal vulnerabilidade. Na Figura 3.10 a) observamos um fluxo explícito entre

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

120 c©2013 SBC — Soc. Bras. de Computação

a variável i e a variável k impressa por print f (). Considerando que o adversário tem oacesso ao código fonte, é possível conhecer o endereço de memória retornado pela funçãomalloc().

Na Figura b) apesar de não existir nenhum fluxo explícito entre a variável i e ovalor impresso pela função print f (), podemos inferir que o endereço retornado é maiorque zero se for impresso 1 e menor ou igual à zero caso print f imprima 2, pois o valorimpresso depende do predicado (int)k > 0.

Figura 3.10. Exemplo de vazamento de endereço por a) fluxo explícito e b) fluxo implícito.

Uma solução para o vazamento de endereços por fluxo explícito pode ser encon-trada em [Quadros and Pereira 2011] e [Quadros and Pereira 2012a]. Além disso, uma fer-ramenta pública capaz de detectar vazamento de endereços em tempo de compilação estádisponível em [Quadros and Pereira 2012b]. Entretanto, as soluções acima não são capa-zes de lidar com vazamentos por fluxos implícitos. Neste caso, recomendamos a soluçãoproposta em [Bruno R. Silva 2013], bem como a ferramenta disponibilizada em [BrunoR. Silva 2013] que é capaz informar ao desenvolvedor quais os caminhos no fluxo decontrole pode estar vulneráveis com relação ao problema de vazamento de endereços.

3.5. Defesas

3.5.1. Aleatorização de Espaço de Endereço

Trabalhos existentes mostram que Prevenção contra a Execução de Dados não é efetivocontra ataques do tipo return-to-lib ou Programação Orientada a Retorno (Return

Oriented Programming – ROP), em que o atacante utiliza códigos existentes em bibliote-cas de sistema como lib [Shacham et al. 2004a]. Aleatorização de Espaço de Endereço(Address Space Layout Randomization – ASLR) é uma técnica usada para neutralizareste tipo de ataques. Em Aleatorização de Espaço de Endereço, posições de memóriade certos componentes (por exemplo, a pilha, o heap, e o código executável) do sistema,incluindo lib, são aleatorizadas. Isto faz com que ataques do tipo return-to-lib

[Solar Designer 1997] e ROP sejam difíceis, já que o atacante não tem como saber ondelibc (ou outras funções desejadas) está localizada na memória. O atacante poderia ten-tar adivinhar valores arbitrários (para a posição de funções desejadas), mas a execuçãotipicamente falha (termina anormalmente) se um valor incorreto é usado.

Além de dificultar cada instância de ataque, Aleatorização de Espaço de Ende-

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

121 c©2013 SBC — Soc. Bras. de Computação

reço também serve para prevenir atacantes de usarem o mesmo código de ataque contramúltiplas instâncias de um programa contendo a mesma vulnerabilidade no código. Paraobter sucesso em sistemas que usam Aleatorização de Espaço de Endereço, o atacanteteria que gerar códigos diferentes para cada instância de um programa aleatorizado, ourealizar ataques de força-bruta para adivinhar o layout do espaço de endereçamento. Aefetividade de Aleatorização de Espaço de Endereço depende da probabilidade do ata-cante conseguir adivinhar a posição de áreas alocadas aleatoriamente. Quanto maior oespaço de busca, maior é o nível de segurança. Assim, a efetividade de Aleatorização deEspaço de Endereço aumenta com o nível de entropia dos offsets aleatórios. Aumento deentropia pode se dar de duas formas: 1) aumento do tamanho da memória virtual sobre aqual a aleatorização é aplicada; e 2) aumento da frequência de realeatorização.

O espaço de aleatorização depende do tipo de sistema. Um dos primeiros traba-lhos [Shacham et al. 2004a] que estuda o relacionamento entre o nível de segurança e otamanho do espaço de aleatorização mostra que, para sistemas de 32-bits e 16-bits de ale-atorização, a Aleatorização de Espaço de Endereço pode ser derrotada por força-bruta emquestão de minutos (naturalmente, este tempo baseia-se na velocidade de computadoresda época). Note que o tempo mencionado supõe que o atacante pode executar o ataquerepetidamente, sem interrupção. Na prática, tais tentativas podem ser desaceleradas. Porexemplo, o sistema pode impedir um executável que tenha falhado (terminado anormal-mente) um certo número de vezes em um curto intervalo de tempo de executar por umperíodo de tempo , antes de ele poder voltar a executar.

Quanto a realeatorização, ela pode ser feita em tempo de compilação ou tempo deexecução. Em realeatorização em tempo de compilação, as aleatorizações são executadasquando o sistema é construído (“built”, compiled and linked). A desvantagem destasolução é que o layout permanece o mesmo entre tentativas sucessivas de um ataque deforça-bruta (exceto em casos onde o sistema é reconstruído). Em realeatorização emtempo de execução, um layout diferente é gerado depois de cada tentativa mal sucedida.

Aleatorização de Espaço de Endereço é disponível em um grande número de siste-mas operacionais atuais (incluindo Linux, Windows Vista e 7, e Mac OS X), mas funcionaum pouco diferente em cada caso [Schwartz et al. 2011]. Linux [PaX Team 2001] alea-toriza a pilha, o heap, e as bibliotecas (compartilhadas), mas não a imagem do programa.Windows Vista e 7 [Howard and Thomlinson 2007] podem aleatorizar as posições da ima-gem do programa, da pilha, do heap, e das bibliotecas, mas apenas quando o programae suas bibliotecas optarem por Aleatorização de Espaço de Endereço. Caso contrário,parte do código não será aleatorizada. Por exemplo, no caso de Windows, se alguma desuas aplicações populares (e.g., Adobe Reader) não suportar ou não for compatível comAleatorização de Espaço de Endereço, o sistema teria códigos binários não-aleatorizados[Pop and Specialist 2010].

Finalmente, enquanto que Prevenção contra a Execução de Dados e Aleatoriza-ção de Espaço de Endereço são amplamente reconhecidos teoricamente como mecanis-mos efetivos de proteção, suas implementações em geral realizam tradeoffs em termos decompatibilidade e desempenho, o que possibilita que eles sejam neutralizados na prática.Por exemplo, em [Shacham et al. 2004a], foi mostrado um ataque de desaleatorização,em que um ataque padrão de Estouro de Arranjo é convertido em um ataque que funciona

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

122 c©2013 SBC — Soc. Bras. de Computação

contra sistemas protegidos pela Aleatorização de Espaço de Endereço. [Schwartz et al.2011] apresenta um outro exemplo.

3.5.2. Prevenção contra a Execução de Dados

Ataques de Estouro de Arranjo continuam sendo um dos tipos mais comuns de ataquesvistos hoje. Neste tipo de ataque, é comum o atacante forçar um programa a armazenarcódigos maliciosos em áreas de memória destinadas a dados, e executar o código dentrodesta área [Aleph One 1996]. Uma forma de impedir que este tipo de ataque seja bemsucedido é obrigar o sistema a usar a memória de forma disciplinada e impedir execuçãode código nas áreas de armazenamento de dados do sistema. A Prevenção contra a Exe-cução de Dados (Data Execution Prevention – DEP) é uma funcionalidade incluída emsistemas operacionais atuais [PaX Team 2000, Microsoft Support a] que implementa talproteção. Num sistema com Prevenção contra a Execução de Dados habilitada, regiõesde memória não destinadas a códigos executáveis são marcadas como “não-executáveis”.Durante o tempo de execução, se um programa tenta executar código nessas regiões, umaexceção é lançada e o programa é abortado. Isto acontece independentemente de o códigoser malicioso ou não. Note que a Prevenção contra a Execução de Dados não foi propostapara impedir que programas maliciosos sejam instalados em sistemas. Ele foi propostopara monitorar execuções de programas já instalados e ajudar a assegurar que estes usema memória do sistema de forma segura. Prevenção contra a Execução de Dados tambémé conhecido como W xor X (Write or Execute) [Schwartz et al. 2011]. De acordo com W

xor X, páginas no heap, na pilha, e em outros segmentos de memória são marcadas comowritable (W) ou executable (X), mas não ambos.

3.5.2.1. Implementação em Hardware

Prevenção contra a Execução de Dados pode se fazer cumprir em hardware em sistemasonde o processador implementa a função. A arquitetura do processador determina comoa função é implementada em hardware. Entretanto, como a Prevenção contra a Execuçãode Dados funciona no nível de páginas de memória virtual, tipicamente um bit é reser-vado nas entradas de tabela de páginas para indicar se execução de código é permitida naspáginas correspondentes. Dependendo do conteúdo previsto para a página (código exe-cutável ou dado), o processador pode ou não setar o bit. Se houver tentativa de execuçãode código a partir de uma página cujo bit é setado (a página contendo dados, suposta-mente), o processador lançará uma exceção e a execução do programa será abortada. Estafunção é disponível em várias arquiteturas de processadores e recebe nomes diferentes demarketing, como bit XD (eXecute Disable) [Intel Corporation ] e bit XN (eXecute Never)[ARM Holdings 2008]. Note que implementação de hardware, por si só, não traria pro-teção. Tanto o BIOS de sistema quanto o sistema operacional precisam oferecer suporteà função, a qual necessita estar habilitada. A Prevenção contra a Execução de Dados édisponível nos principais sistemas operacionais, incluindo Linux [PaX Team 2000], MacOS X, iOS, Microsoft Windows [Microsoft Support a] e Android.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

123 c©2013 SBC — Soc. Bras. de Computação

3.5.2.2. Efetividade

É importante mencionar que a Prevenção contra a Execução de Dados é efetiva apenascontra uma subclasse de ataques de estouro de arranjos, nas quais o atacante tenta colocaro código malicioso na pilha, no heap, ou em outras áreas destinadas a dados. Ela não éefetiva contra ataques do tipo return-to-libc [Solar Designer 1997,Schwartz et al.2011, Shacham et al. 2004b] (veja discussão abaixo), nos quais o atacante tenta utilizarfunções de bibliotecas existentes.

Em sistemas protegidos por Prevenção contra a Execução de Dados, atacantes nãopodem injetar e executar seus próprios códigos. Entretanto, eles podem usar códigos.executáveis existentes – seja o código do próprio programa, ou o código das bibliotecascarregadas pelo programa. Por exemplo, atacantes podem escrever nas posições da pilhaacima do endereço de retorno do quadro de execução corrente e alterar o endereço deretorno para apontar para a função que eles gostariam de chamar. Quando a função noquadro de execução corrente retornar, o fluxo de controle do programa será dirigido paraa função escolhida, e os dados fornecidos nas posições acima do endereço de retorno napilha serão usados como argumentos da função.

Tradicionalmente, as funções da biblioteca padrão da linguagem C são as maispopulares para este propósito. Isto porque ela é carregada junto a qualquer programaem Unix e está por trás das APIs das chamadas de sistema usadas pelos programas paraacessarem serviços de kernel como process fork e network sockets. Por esta razão, estaclasse de ataques passou a ser chamada de return-to-libc. Note que em ataquesdo tipo return-to-libc o atacante precisa conhecer os endereços das funções libc.Introduzindo-se aleatoriedade ao endereço base da libc, pode-se aumentar a dificuldadede os atacantes se aproveitarem dessas funções Aleatorização de Espaço de Endereço(Address Space Layout Randomization – ASLR) é uma técnica de aleatorização propostapara criar este tipo de barreira, e será vista na próxima seção.

3.5.2.3. Prevenção contra a Execução de Dados Baseada em Software

Além do mecanismo de hardware descrito acima, existe um mecanismo implementadoem software, comumente conhecido como Prevenção contra a Execução de Dados Base-ada em software. Em vez de oferecer proteção contra execução de código em páginas dedados, o software Prevenção contra a Execução de Dados ajuda a impedir códigos ma-liciosos de explorarem os mecanismos de tratamento de exceção em Windows. Com osoftware Prevenção contra a Execução de Dados, quando uma exceção é lançada, o sis-tema simplesmente checa se a rotina de tratamento da exceção é registrada na tabela defunções para a aplicação e incluída no código executável. Note que, embora software

DEP aparentemente impeça execução de código a partir de páginas de dados, ela é umaforma diferente de proteção.

O Software Prevenção contra a Execução de Dados também é conhecido comoSafe Structured Exception Handling – SafeSEH). [Microsoft Support b].

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

124 c©2013 SBC — Soc. Bras. de Computação

3.5.3. Canários

3.5.3.1. Visão geral

Canários são valores de guarda constantes que são inseridos na pilha, entre um buffer

e dados sensíveis da pilha, de maneira a monitorar estouros de arranjos em tempo deexecução, conforme pode-se visualizar na figura3.11. A ideia básica é que o Canárioatue como um indicador de que houve corrupção de dados da pilha e que, portanto, asegurança está comprometida. Dessa forma, antes do retorno de uma função, checa-sea integridade do Canário; caso ela não tenha se mantido, assume-se que outros dados dapilha tenham sido corrompidos e um código de tratamento de erros é disparado, o que, emgeral, implica no encerramento do programa. Os Canários são inseridos pelo compiladore não exigem qualquer tipo de intervenção do usuário.

Conforme será discutido adiante, apesar de algumas limitações de que sofrem osCanários, essa medida de proteção conta com a grande vantagem de simplicidade e baixocusto computacional. Logo, demonstram-se uma boa opção para os sistemas embarcados,que contam com recursos limitados.

Diferentemente de recursos sofisticados que imbuem num grande custo temporale espacial, os Canários resumem-se a algumas instruções de máquina adicionais e umaconstante por função protegida. Na linguagem intermediária do LLVM, por exemplo, opreâmbulo e o epílogo da função ficam como se segue:

entry:

StackGuardSlot = alloca i8*StackGuard = load __stack_chk_guard

call void @llvm.stackprotect.create(StackGuard, StackGuardSlot)

...

return:

...

%1 = load __stack_chk_guard

%2 = load StackGuardSlot

%3 = cmp i1 %1, %2

br i1 %3, label %SP_return, label %CallStackCheckFailBlk

SP_return:

ret

...

CallStackCheckFailBlk:

call void @__stack_chk_fail()

unreachable

3.5.3.2. Evolução

Os Canários foram inicialmente propostos no projeto StackGuard. Desde então já sofre-ram diversas adaptações. Além de variações no tipo de Canário, ao longo de sua história,os Canários evoluíram principalmente com relação a três aspectos: o tipo de constante a

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

125 c©2013 SBC — Soc. Bras. de Computação

arranjo

variáveis locais

endereço deretorno

código de ataque

crescimentodo arranjo

crescimentoda pilha

arranjo

variáveis locais

endereçode retorno

código de ataque

crescimentodo arranjo

crescimentoda pilhacanário

Figura 3.11. À esquerda, a disposição tradicional dos dados na pilha, que permitea sobrescrita do endereço de retorno para que ele aponte para código malicioso;à direita, a pilha após a inserção do Canário: uma sobrescrita além dos limites doarranjo sobrescreverá o Canário antes de sobrescrever o endereço de retorno.

ser armazenada, quando empregar um Canário e onde dispô-lo na pilha. Esses aspectossão discutidos a seguir.

Tipos de constantes. A primeira solução proposta previa o uso de uma constante ter-

minadora, baseando-se no fato de quer os ataques geralmente ocorrem através de ope-rações sobre strings. Dessa forma, caso um atacante tente sobrescrever o Canáriosem corrompê-lo, o comportamento esperado é que a constante terminadora seja o últimodado a ser escrito e, assim, os dados sensíveis, como o endereço de retorno, permaneçamintactos.

Uma outra proposta é o uso de uma constante aleatória que é calculada no mo-mento de inicialização do programa e armazenada numa variável global, armazenada detal forma que seja impossível para um atacante lê-la. Tal mecanismo dificultaria que umacorrupção de dados passasse ilesa num teste de verificação de integridade do Canário.

Finalmente, uma terceira proposta sugere a utilização Canários Aleatórios XOR.Nesse tipo de constante, é verificado se o resultado da operação XOR entre o Canárioe o dado sensível, como o endereço de retorno, permanece inalterado. Dessa forma, aleitura apenas do valor do Canário não seria suficiente para que um atacante contornassea defesa.

Heurísticas para emprego de Canários. A solução mais trivial para decidir-se quandoempregar o Canário é empregá-lo em toda e qualquer função. Essa solução, entretanto,implica em um overhead que, embora pequeno, poderia ser menor, pois funções quenunca estarão sujeitas a um Estouro de Arranjotambém passam pela verificação do Ca-nário. Dessa forma, a comunidade de compiladores emprega algumas heurísticas parasolucionar o problema.

A heurística mais simples para emprego de Canários consiste em incluir um Caná-rio em toda função que possui um arranjo declarado localmente, ou um registro contendoum arranjo. Na prática, muitas implementações reais adicionam a restrição adicional deo arranjo local ter um tamanho maior do que uma constante pré-definida. As razões paratal decisão não são claras.

Mais recentemente, novas heurísticas foram propostas para compiladores como

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

126 c©2013 SBC — Soc. Bras. de Computação

gcc3 e LLVM 4. As condições para emprego dos Canários passam então a ser mais abran-gentes e representam um compromisso entre desempenho e segurança. Uma função passaa ser protegida com o Canário caso:

• Ela possua um arranjo, independentemente de seu tipo e tamanho;

• Ela possua um registro que contém um arranjo, igualmente de maneira indepen-dente de tipo e tamanho;

• Alguma de suas variáveis locais tem seu endereço tomado como parte do RHS (ladodireito) de uma atribuição;

• Alguma de suas variáveis locais tem seu endereço tomado para ser passado comoargumento para uma função.

A ideia básica por trás dessa nova heurística é que qualquer ataque de Estouro deArranjo na pilha necessita de um endereço de quadro5.

Disposição dos dados na pilha. As primeiras implementações de Canários focaram-seem resolver um problema mais clássico: a sobrescrita do endereço de retorno. Dessaforma, o Canário era posicionado entre um arranjo e o endereço de retorno. Esse tipo dedisposição apresentava muitas falhas, pois variáveis locais, como por exemplo o ponteirode quadro de pilha, permaneciam vulneráveis à sobrescrita. Essa vulnerabilidade especí-fica foi explorada em [Richarte et al. 2002], de forma a desviar o fluxo de execução de umprograma. Em compiladores mais recentes, esse problema é contornado através de umadisposição diferente dos dados, de forma que o Canário se localize entre os arranjos e asdemais variáveis locais.

3.5.3.3. Limitações

Canários não são a solução derradeira para os ataques de Estouro de Arranjo. Dentre suaslimitações, podem-se destacar:

• Limitação de escopo: Canários protegem apenas contra ataques de Estouro de Ar-ranjo que ocorram na pilha. Dessa forma, faz-se necessária a utilização de algummecanismo adicional de proteção contra ataques de Estouro de Arranjo baseadosem heap.

• Momento da verificação: A verificação de integridade do Canário só ocorre logoantes de a função retornar. Isso significa que um atacante possui uma janela detempo considerável para agir antes de ser detectado. Além disso, caso haja umdesvio ou o disparo de uma exceção, a verificação pode até mesmo nunca ocorrer.

3http://gcc.gnu.org/ml/gcc-patches/2012-06/msg00974.html.4http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-October/053931.html.5https://docs.google.com/document/d/1xXBH6rRZue4f296vGt9YQcuLVQHeE516stHwt8M9xyU/.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

127 c©2013 SBC — Soc. Bras. de Computação

• Algumas variáveis locais permanecem vulneráveis à sobrescrita. Isso pode ocorrer,por exemplo, se existem mais de um buffer declarados localmente em uma função;nesse caso, um pode sobrescrever o outro. Além disso, dados dispostos em regis-tros (structs, no caso da linguagem C), não podem ser rearranjados, de formaque se um registro contém um ou mais buffers, é possível a existência de uma vul-nerabilidade sobre a qual o compilador não pode agir. Técnicas de compiladorespodem, todavia, ser aplicadas com o objetivo de apontar quais os trechos de códigosão vulneráveis. Uma análise de fluxo contaminado pode determinar quais arran-jos do programa são alcançáveis a partir de mecanismos de entrada, representando,portanto, uma porta de entrada para usuários maliciosos.

3.5.4. Verificação de Limites de Arranjo

O padrão ISO/IEC 9899:2011 define uma escrita fora dos limites de um arranjo ou acessoout-of-bounds como 6:

uma tentativa de acesso que, em tempo de execução, para um dado estado

computacional, iria modificar [...] um ou mais bytes que se encontram fora

dos limites permitidos por este padrão.

Um acesso às posições antes do início do arranjo ou depois do seu término caracterizam,portanto, um acesso fora de seus limites – um buffer overflow ou buffer overrun. Tais aces-sos, possuem, como indica o mesmo padrão, semântica indefinida. Com isso, podem, porexemplo, abortar o programa ou continuar a execução em um estado desconhecido. Essaúltima opção é passível de causar falhas de segurança em software, como aconteceu comcom o Morris worm em 1988, descrito na seção 3.4.1. A primeira opção, porém, garanteque um estado inválido ou desconhecido não seja atingível por meio de acessos inváli-dos à memória. Essa é a abordagem necessária para linguagens fortemente tipadas comoJava e C#, uma vez que o sistema de tipos poderia ser contornado caso tipos inválidospudessem se referenciar. Existem ferramentas que proveem tais garantias para programasdesenvolvidos em C e C++; como estado da arte podem ser citadas SAFECode [Dhurjatiet al. 2006], AddressSanitizer [Serebryany et al. 2012] e SoftBound+CETS [Nagarakatteet al. 2009] [Nagarakatte et al. 2010]. Essas ferramentas combinam técnicas de aná-lise estática e de instrumentação para detectar acessos inválidos em tempo de execução.Cada uma usa uma metodologia diferente; elas serão explicadas a seguir, além disso serãoapresentadas outras abordagens para o problema dos acessos out-of-bounds.

Memória espelho. O AddressSanitizer usa uma memória espelho ou shadow-memory.Essa memória espelha alocações na pilha e no heap. Quando uma posição de memória éalocada nas duas últimas, uma posição correspondente na memória espelho é modificadapara indicar esta alocação. De forma similar, quando uma posição é desalocada, a me-mória correspondente é modificada para refletir a desalocação. Com isso, para verificarse uma posição de memória acessada está alocada, basta verificar a memória espelho. Oacesso à memória espelho é feito em tempo constante. No AddressSanitizer, para umposição p da memória, a posição correspondente é dada por (p << 3)+ c, onde c é umoffset constante.

6traduzido da seção L.2.1.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

128 c©2013 SBC — Soc. Bras. de Computação

Essa abordagem porém, ainda permite que uma indexação inválida a uma arranjoacesse uma posição de memória alocada. No caso de dois arranjos contiguamente aloca-dos, o acesso a uma posição além do término do primeiro acessaria o primeiro elementodo segundo e seria erroneamente considerado válido. Para amenizar esse problema, masnão corrigi-lo completamente, cada alocação é acompanhada de zonas vermelhas (red zo-

nes), que são posições de memória envenenadas (poisoned). Essas são posições às quaistodo acesso é considerado inválido e são posicionadas em volta das zonas de alocação.Esta solução, ainda assim, não é completa, uma vez que acessos poderiam ultrapassaruma zona vermelha e acessar uma posição alocada de memória.

O plugin Memcheck do Valgrind também utiliza memória espelho. Nesse caso,todas as escritas e leituras da memória são checadas por sua validade. Chamadas para asfunções de alocação e desalocação de C e C++ são interceptadas e a memória espelho éatualizada de acordo com a chamada. O Valgrind será discutido mais adiante.

Análise de regiões 7. A solução do SAFECode para o problema dos acessos out-of-

bounds é a de particionar o heap em regiões (pools), utilizando um algoritmo chamadoAutomatic Pool Allocation [Lattner and Adve 2005]. Cada região contém objetos de umtipo homogêneo e conhecido. Uma combinação de análises estáticas e dinâmicas sãoutilizadas para garantir o isolamento entre as regiões. Essa abordagem é incompleta, umavez que quaisquer acessos a posições dentro da mesma região são válidos, ainda que essaposição não seja parte do arranjo sendo indexado. De fato, a garantia que o SAFECodedá é que objetos correspondentes em uma points-to-graph estão em uma mesma regiãodo heap e que o aliasing de ponteiros não é invalidado.

Essa técnica é utilizada em algumas linguagens de programação. Cyclone [Jimet al. 2002] é uma linguagem que foi inicialmente construída para evitar problemas co-muns em programas C, mantendo, porém, sua semântica e sintaxe. Para isso, a linguagemimpõe várias restrições quanto à aritmética de ponteiros, coerções e inicialização de va-riáveis. Para evitar, especificamente, problemas de dangling pointers, são usadas técnicasde análise de regiões e imposição de restrições no uso da função free. A linguagemtambém usa fat pointers para fazer a verificação de limites de arranjo, como descrito naseção que segue.

Similarmente, a linguagem Control-C [Sumant Kowshik and Adve 2002], um sub-conjunto de C, impõe várias restrições a esse subconjunto para que seja possível verificara segurança do código em tempo de compilação. Uma das restrições notáveis é no quetange às alocações - regiões são explicitamente alocadas ao invés de objetos.

Fat pointers. Uma outra forma de detecção de acessos out-of-bounds é através de umarepresentação expandida de ponteiros chamada fat pointers. Nessa representação os meta-dados são atrelados aos ponteiros associados, recordando informação de alocação e detamanho dos mesmos. Tal abordagem, porém, modifica o layout de memória do programade forma a dificultar chamadas a funções externas, as quais poderiam levar à problemasde corrupção dos metadados. A ferramenta SoftBound+CETS utiliza uma representaçãobaseada em fat pointers, mas mantém os metadados disjuntos, através de mecanismos dememória espelho descritos acima. Com isso, o layout de memória permanece o mesmo e

7Tradução livre to termo region analysis.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

129 c©2013 SBC — Soc. Bras. de Computação

os problemas com chamadas a funções externas são reduzidos.

Abordagens puramente estáticas. Frama-C [Cuoq et al. 2012] e Astreé [Cousot et al.2005] são ferramentas de análise estática e verificação formal de programas. Astreé uti-liza técnicas de interpretação abstrata para verificar formalmente programas para sistemasembarcados. Interpretação abstrata é uma forma de aproximar a semântica de programasutilizando funções monotônicas sobre um conjunto ordenado, com reticulados sendo a es-colha notável. Astreé é fruto do trabalho de Patrick Cousot, que formalizou as técnicas deinterpretação abstrata - hoje muito usada em análises e transformações em compiladores.A ferramenta se propõe a provar a ausência de erros em tempo de execução para pro-gramas escritos em C. Ela analisa programas complexos, mas sem alocação dinâmica ourecursão. Isso engloba grande parte dos programas utilizadas em sistemas de transportee de energia nuclear, por exemplo. Astreé verificou a corretude de sistemas aeronáuticosutilizados em aviões da Airbus e, mais recentemente, do veículo de transferência auto-matizado Jules Verne, não tripulado e lançado para a Estação Espacial Internacional em2008.

Por outro lado, o plugin Jessie8 para o Frama-C utiliza técnicas de prova de teore-mas para propriedades arbitrárias sobre programas, especificadas por uma linguagem deanotação chamada ANSI/ISO C Specification Language (ACSL). Os programas são ano-tados com as propriedades que se deseja verificar e o programa é, então, analisado parachecar se satisfaz ou não as dadas propriedades. A abordagem, porém, requer que o có-digo fonte do programa sob análise seja modificado, tornando-se infactível para sistemaslegados.

Abordagens dinâmicas. Valgrind [Nethercote and Seward 2007] é uma ferramenta deanálise dinâmica. Ela recebe como entrada um executável e o converte para uma lin-guagem intermediária. A nova representação é instrumentada e convertida de volta paracódigo de máquina. Exatamente pela conversão, retradução e instrumentação, surge arelativa ineficiência da ferramenta. Uma restrição notável da ferramenta é a sua incapa-cidade de detectar erros no acesso a dados alocados na pilha, não detectando, portanto,problemas de stack smashing.

A ferramenta Eletric Fence 9 substitui certas funções da libc - a biblioteca padrãode C - com funções próprias, de forma que imediatamente após ou antes de cada objetoalocado, haja uma página de memória inacessível. Com isso, sempre que ocorrer umbuffer overrun e uma posição de memória da página inacessível for acessada, o overrun

será detectado. A ferramenta, porém, só é capaz de detectar acessos inválidos após ouantes do objeto, mas não nunca ambos. Além disso, a alocação extra de páginas causa umoverhead indesejável quanto ao uso de memória.

3.5.5. Análise de Intervalo

Uma das principais ferramentas para encontrar vulnerabilidades de estouro de arranjos eestouro de inteiros é a análise de intervalos. Essa análise procura estimar, estaticamente,quais são os menores e maiores valores que cada variável inteira pode assumir durante aexecução de um programa. Além de prestar-se a encontrar vulnerabilidades de forma au-

8http://frama-c.com/jessie.html.9http://sunsite.unc.edu/pub/linux/devel/lang/c/electricfence-2.0.5.tar.gz.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

130 c©2013 SBC — Soc. Bras. de Computação

tomática, a análise de intervalos possui várias outras aplicações, em termos de otimizaçãode código. Por exemplo, esse tipo de análise é usado para melhorar a qualidade do códigogerado por alocadores de registradores [Barik et al. 2006, Pereira and Palsberg 2008, Tal-lam and Gupta 2003], para apurar a predição de desvios condicionais [Patterson 1995],na síntese de hardware [Cong et al. 2005, Lhairech-Lebreton et al. 2010, Mahlke et al.2001, Stephenson et al. 2000] e em diversas eliminações de código redundante [Bodiket al. 2000, Logozzo and Fahndrich 2008, Souza et al. 2011, Venet and Brat 2004]. Dataessa importância, existem diversos algoritmos que implementam a análise de intervalos.Neste capítulo, abordaremos um desses algoritmos.

A figura 3.12 ilustra dois usos da análise de intervalos no contexto da segurançade software. Em primeiro lugar, as informações produzidas por essa análise permitemao compilador provar que acessos a arranjos são seguros. Por acesso seguro, entende-seque as leituras e escritas de dados endereçam somente memória legitimamente alocadapelo programa. A análise de intervalos poderia, também, provar a existência de bugs. Talseria o caso se o arranjo a fosse declarado com uma posição a menos, por exemplo. Emsegundo lugar, informações de intervalos dão ao compilador o conhecimento necessáriopara eliminar testes de estouro de arranjos, ou provar que estouros irão acontecer. Na fi-gura 3.12, por exemplo, todas as operações aritméticas são seguras. Nesse novo contexto,entende-se que uma operação aritmética é segura quando ela não pode levar à ocorrênciade estouros de inteiros.

int foo()

char* a = malloc(16);

int k = 0;

while (k < 16)

int i = 0;

int j = k;

while (i < j)

if (a[i] > a[j])

SWAP(a, i, j);

i++;

j--;

k++;

Essas leituras no arranjo a são

sempre seguras, pois a possui 16

posições, 0 ≤ i ≤ 15 e 0 ≤ j ≤ 15

Esses incrementos nunca podem gerar estouros de arranjo, pois, i ≤ j, j ≤ k e k ≤ 15

Esses decremento nunca pode gerar estouros de arranjo, pois, j é um inteiro com sinal, e j ≥ 0

Figura 3.12. Exemplo que ilustra o uso de informações produzidas pela análisede intervalos.

3.5.6. Calculando Intervalos de Variáveis Inteiras

A literatura é rica em algoritmos que resolvem a análise de intervalos. Neste livro adota-remos o algoritmo proposto por Rodrigues et al. [Rodrigues et al. 2013]. Esse algoritmosegue o fluxograma visto na figura 3.13. Conforme pode ser visto na figura, a análisede intervalos possui cinco grandes fases. O restante desta seção descreve cada uma des-sas fases. A fim de facilitar o entendimento das explicações que se seguem, usaremos o

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

131 c©2013 SBC — Soc. Bras. de Computação

programa mostrado na figura 3.12 para ilustrar cada um dos passos do algoritmo.

Extração deequações

Construção do grafo de equações

Componentes Fortemente Contexos

Ordenação topológica

AlargamentoResolução de

futurosEstreitamento

Para cada Componente Forte, em ordem topológica

Figura 3.13. Algoritmo para resolução de análise de intervalos.

Extração de Equações. A análise de intervalos é, em sua essência, um sistema deEquações Diofantinas. Uma equação diofantina é uma igualdade entre expressões cons-tituídas por incógnitas e coeficientes inteiros. Tais equações são extraídas da represen-

tação intermediária do programa. A representação intermediária de um programa é aforma como o compilador enxerga aquele código. Normalmente, compiladores adotamuma representação de três endereços em formato de atribuição estática única (SSA) 10.Neste caso, o programa é visto como uma sequência de instruções de montagem. A maiorparte dessas instruções tem a forma a = b⊕ c, sendo ⊕ um operador qualquer. Exis-tem, contudo, instruções unárias, como a = b, que copia o conteúdo de b para a memóriaapontada por a. Existem também instruções de aridade variável, como as funções phi:a = φ(a1, . . . ,an), cuja semântica descreveremos a seguir. A figura 3.14 (a) mostra umaversão simplificada de nosso programa, e sua representação intermediária pode ser vistana parte (b) da figura. Uma função phi, como i1 = φ(i0, i2), copia o valor de i0 ou i2 parai1, dependendo de qual o caminho que o programa percorre até executá-la.

A figura 3.14 (c) mostra as equações que descrevem o problema de determinarintervalos para as variáveis inteiras do programa visto na figura 3.14 (b). Cada uma des-sas equações segue diretamente de uma instrução de montagem presente na figura 3.14(b). Algumas igualdades, como kt = k1 ∩ [−∞,99], representam informações aprendidasa partir de testes condicionais. Essa equação, por exemplo, advém da parte verdadeira doteste k < 100. Nesse caso, temos que a variável k tem seu maior valor limitado por 99.

O Grafo de Restrições. As equações extraídas de um programa podem ser resolvi-das de diversas formas. Poder-se-ia, por exemplo, resolvê-las via mecanismos gerais de

10Neste livro, adotamos o nome inglês SSA, sigla para Single Static Assignment, uma vez que ele é bemestabelecido na comunidade de compiladores.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

132 c©2013 SBC — Soc. Bras. de Computação

k = 0

while k < 100:

i = 0

j = k

while i < j:

i = i + 1

j = j - 1

k = k + 1

k0 = 0

k1 = ϕ(k

0, k

2)

kt = k

1 ∩ [−∞, 99]

i0 = 0

j0 = k

t

i1 = ϕ(i

0, i

2)

j1 = ϕ(j

0, j

2)

i2 = i

t + 1

j2 = j

t - 1

k2 = k

t + 1

jt = j

1 ∩ [ft(i

1), +∞]

it = i

1 ∩ [−∞, ft(j

1)]

k0 = 0

k1 = ϕ(k

0, k

2)

(k1 < 100)?

kt = k

1∩[-∞,99]

i0 = 0

j0 = k

t

i1 = ϕ(i

0, i

2)

j1 = ϕ(j

0, j

2)

(i1 < j

1)?

k2 = k

t + 1

it = i

1∩[-∞,ft(j

1)-1]

jt = j

1∩[ft(i

1),+∞]

i2 = i

t + 1

j2 = j

t - 1

(a) (b) (c)

Figura 3.14. (a) Versão simplificada do programa visto na figura 3.13. (b) Repre-sentação intermediária do programa. (c) Equações diofantinas que representama análise de intervalos.

programação linear inteira. Elas, contudo, podem ser resolvidas por algoritmos mais es-pecíficos, e por conseguintes, mas eficientes que a programação linear inteira. Seguindoa abordagem proposta por Rodrigues et al. [Rodrigues et al. 2013], explicaremos um me-canismo de resolução de equações baseado no chamado grafo de restrições. Esse grafodirecionado possui um vértice para cada incógnita do sistema de equações, e um vérticepara cada equação. Ele possui uma aresta u → c, se a variável u é usada do lado direitoda equação c, e uma aresta c → v se a variável v é usada do lado esquerdo de c.

A figura 3.15 mostra o grafo criado para representar as equações vistas na fi-gura 3.14 (c). Note que esse grafo apresenta duas arestas pontilhadas. Essas setas sãochamadas arestas de controle, e elas conectam variáveis que possuem futuros. Um futuroé uma promessa de valor ainda por conhecer. Esse tipo de construção existe devido àcomparações condicionais entre duas variáveis, como por exemplo, i1 < j1 no programada figura 3.14 (b). Uma vez que o limite superior de i1 depende do limite inferior de j1,existe uma dependência futura entre essas variáveis. Quando o intervalo de j1 for co-nhecido, poderemos estimar o intervalo de i1. É interessante notar que essa dependênciafutura também existe no sentido inverso: quando o intervalo de i1 for conhecido, pode-remos determinar o limite inferior do intervalo de j1. Ciclos de dependências como essesão resolvidos em uma etapa posterior, conhecida como resolução de futuros, sobre a qualfalaremos mais adiante.

Uma vez construído o grafo de restrições, o algoritmo de resolução de equaçõespassa para a fase de processamento: componentes fortemente conexos são encontradosno grafo, e, a partir desse ponto, passam a ser tratados como nós individuais. O grafoque resulta da contração dos componentes fortemente conexos é acíclico; portanto, eleadmite ordenação topológica. A seguir cada componente fortemente conexo é processadode acordo com a sua ordem topológica. Nesta etapa, as arestas de controle são removidasdo grafo de restrições. Dá-se o nome de micro-algoritmo à fase de processamento de cadacomponente conexo.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

133 c©2013 SBC — Soc. Bras. de Computação

0 k0

k1

kt

k2

j0

j1

jt

j2

0 i0

i1

it

i2

[-∞,99]

[-∞, ft(j1)-1] [ft(i1), +∞]

+1

=

−1+1

ϕ

ϕ ϕ

[100, +∞]kf

Figura 3.15. Grafo de restrições criado para o programa visto na figura 3.14.

O Micro-Algoritmo. Cada componente conexo sofre três análises, que, ao final, pro-duzem uma estimativa para o intervalo de suas variáveis. A primeira dessas análises échamada alargamento. Durante a etapa de alargamento, define-se como cada variávelcresce. Se uma variável pertence a um ciclo do qual fazem parte somente operações deincremento, dizemos que a variável cresce em direção à +∞. Por outro lado, se ela fazparte de um ciclo que contém somente operações de decremento, então ela cresce emdireção à −∞. Doutro modo, a variável cresce em ambas as direções. A figura 3.16 (a)mostra o maior componente conexo visto na figura 3.15. Note que arestas de controleforam removidas. A figura 3.16 (b) mostra o resultado da análise de alargamento aplicadaàquele componente.

j1

[⊥, ⊥]

jt

[⊥, ⊥]

j2

[⊥, ⊥]i1

[⊥, ⊥]

it

[⊥, ⊥]

i2

[⊥, ⊥]

[-∞, ft(J1)-1] [ft(I

1), +∞]

−1+1

ϕ ϕ[0, 0] [0, 99]

j1

[−∞, 99]

jt

[−∞, 99]

j2

[−∞, 98]i1

[0, +∞]

it

[0, +∞]

i2

[1, +∞]

[-∞, ft(J1)-1] [ft(I

1), +∞]

−1+1

ϕ ϕ[0, 0] [0, 99]

j1

[-1, 99]

jt

[0, 99]

j2

[−1, 98]i1

[0, 99]

it

[0, 98]

i2

[1, 99]

[-∞, 98] [0, +∞]

−1+1

ϕ ϕ[0, 0] [0, 99]

(a) (b)

(c) (d)

(i0) (j

0)

j1

[-∞, 99]

jt

[-∞, 99]

j2

[− ∞, 98]i1

[0, +∞]

it

[0, +∞]

i2

[1, +∞]

[-∞, 98] [0, +∞]

−1+1

ϕ ϕ[0, 0] [0, 99]

Figura 3.16. Processamento do último componente fortemente conexo do grafode restrições visto na figura 3.15.

Realizada a análise de alargamento, sabe-se como cada variável cresce. Essa in-formação permite-nos realizar a resolução de futuros. Nessa etapa, intervalos simbólicos,isso é, os futuros, são substituídos por limites concretos. Se um futuro ft(v) aparece do

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

134 c©2013 SBC — Soc. Bras. de Computação

lado esquerdo de um intervalo, e.x.: u = t ∩ [ft(v), . . .], então ft(v) é substituído pelo li-mite superior de v. Por outro lado, se ft(v) aparece do lado direito de um intervalo, comou = t ∩ [. . . , ft(v)], então ft(v) é substituído pelo limite inferior de v. A figura 3.16 (c)mostra o resultado da resolução de futuros em nosso exemplo.

Finalmente, passa-se a última etapa do micro-algoritmo: o estreitamento de inter-valos. Nessa fase, o resultado de testes condicionais é utilizado para refinar os resultadosencontrados para cada variável. Por exemplo, o valor de it , na figura 3.16 (c) é dado porit = i1 ∩ [−∞,98]. O limite superior de it pode ser, portanto, no máximo 98. Porém, apóso alargamento, tínhamos que esse limite era +∞. Assim, substituímos o valor daquele li-mite superior por 98, e propagamos essa informação para outras variáveis que dependemde it . Essa propagação acontece até que todos os vértices do grafo tenham sido visitados.A figura 3.17 mostra a solução da análise de intervalos. A mesma variável pode estarassociada a diferentes intervalos, dependendo do ponto em que ela exista no programa. Afigura mostra, por exemplo, os diferentes intervalos associados à variável k.

k = 0

while k < 100:

i = 0

j = k

while i < j:

i = i + 1

j = j - 1

k = k + 1

I[k0] = [0, 0]

I[k1] = [0, 100]

I[k2] = [1, 100]

I[kt] = [0, 99]

I[kt] = [100, 100]

I[i0] = [0, 0]

I[i1] = [0, 99]

I[i2] = [1, 99]

I[it] = [0, 98]

I[j0] = [0, 99]

I[j1] = [-1, 99]

I[j2] = [-1, 98]

I[jt] = [0, 99]

Figura 3.17. Solução da análise de intervalos para o exemplo visto na figura 3.14(a). A função I mapeia um nome de variável, na representação intermediária doprograma, para um intervalo. A figura explicita os diversos intervalos associadoscom a variável k.

3.5.7. Análise Distribuída

Com o advento da Internet das Coisas (Internet of Things – IoT) [Atzori et al. 2010]e a popularização dos smartphones a tendência é que Sistemas Embarcados tornem-sepraticamente onipresentes e conectados à Internet. A IoT pretende conectar uma enormediversidade de dispositivos computacionais e prover serviços com garantias de alta dispo-nibilidade, eficiência e segurança. Dessa forma, os Sistemas Embarcados terão acesso epoderão ser acessados via Internet além de comunicarem entre si de forma distribuída. Pa-ralelamente a essa ubiquidade de Sistemas Embarcados – e os benefícios que ela acarreta– surge também certa inquietação: como garantir a Segurança de Software embarcadosnesses dispositivos?

A maioria das propostas de Segurança de Software se baseia na análise do códigofonte e/ou binário da aplicação a fim de detectar operações que possam levar o programa

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

135 c©2013 SBC — Soc. Bras. de Computação

a sofrer um ataque. Técnicas usadas variam desde análise estática e instrumentação di-nâmica de código [Dietz et al. 2012, Rodrigues et al. 2013] até soluções que procuramgerar testes dinamicamente usando execução simbólica [Molnar et al. 2009]. Tais pro-postas, contudo, não levam em consideração as peculiaridades de sistemas distribuídos e,portanto, suas respectivas análises são conservadoras quando se deparam com chamadasde procedimento que fazem interface com a rede.

Há exceções como o Kleenet [Sasnauskas et al. 2010] e o T-Check [Li and Regehr2010] que tem por objetivo encontrar defeitos em aplicações de redes de sensores sem fioque utilizem o ContikiOS (Kleenet) ou o TinyOS (T-check). Tais ferramentas utilizamexecução simbólica para gerar testes dinâmicos a fim de avaliar os cenários nos quaiso sistema podem apresentar algum tipo de defeito. Mas, além do foco ser em detecçãode bugs e não em segurança, a quantidade de caminhos a serem avaliados pode tornar aanálise inviável por restrições de tempo e recursos computacionais.

Na área de sistemas distribuídos, há trabalhos de análise de vulnerabilidades deprotocolos, incluindo model checking [Lin et al. 2009] e engenharia reversa de pro-tocolos [Comparetti et al. 2009]. Esses trabalhos, no entanto, focam no protocolo decomunicação e tratam as partes comunicantes como caixas-pretas, sem examinar seu có-digo fonte. Já na área de aplicações Web, trabalhos como [Tripp et al. 2009, Balzarottiet al. 2008] avaliam o fluxo entre dados de entrada e seu uso em uma aplicação Web paradetectar pontos vulneráveis no programa. Tais trabalhos, contudo, geram muitos falsospositivos já que qualquer interação com a rede é considerada insegura.

Portanto é necessário que novas soluções de Segurança de Software sejam desen-volvidas voltadas especialmente para Sistemas Embarcados Distribuídos. Mais precisa-mente, deve-se levar em consideração uma intensa comunicação via rede com dispositivoscomunicando entre si e com a Internet. A solução deve ser avaliada não apenas em ter-mos de tempo e memória, mas também em termos energéticos. Por fim, a solução deveser “leve” (lightweight) o suficiente para ser executada sobre plataformas restritas.

Por exemplo, na figura 3.18 temos dois programas: Reader e Writer. O programaWriter envia uma mensagem para o Reader inciando o protocolo (code=1, size=7 e buf-

fer=begin). O programa Reader recebe a mensagem e imprime o código. Se apenas oReader é analisado, a maioria das ferramentas de segurança de código concluirá que orecv é um ponto de entrada vulnerável, pois um adversário poderia, via programa Wri-

ter, enviar um dado maior que o previsto pelo e sobrescrever o campo code na struct

MSG. Mas ao analisar o programa como um todo fica claro que o Writer envia apenasdados internos e, portanto, não representa ameaça para o sistema. Dessa forma, é possívelevitar um falso-positivo e reduzir o número de instrumentações necessárias para tornar oprograma seguro.

3.5.7.1. Grafo de Controle de Fluxo de Sistemas Distribuídos

A análise distribuída pode ser realizada utilizando estruturas de dados semelhantes àspropostas para sistemas convencionais. Por exemplo, muitas análises utilizam o grafo decontrole de fluxo (CFG) como representação do programa a fim de verificar os possíveiscaminhos no programa. O CFG é um grafo dirigido onde os nós são blocos básicos do

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

136 c©2013 SBC — Soc. Bras. de Computação

/ / Reader# i n c l u d e "mysocket.h"

s t r u c t msg char buf [MAX] ;char code ;

;i n t main ( )

i n t mySocket , n ;char buf [MAX] ;s t r u c t msg MSG;mySocket = getMySocket ( ) ;n= r e c v ( mySocket , buf ,MAX, 0 ) ;MSG. code = buf [ 5 ] ;p r i n t f ("code: %c" ,MSG. code ) ;c l o s e ( mySocket ) ;

Programa Reader

/ / W r i t e r# i n c l u d e "mysocket.h"

i n t main ( )

i n t mySocket ;mySocket= g e t S e r v e r S o c k e t ( ) ;

char b u f f e r [ ] ="begin17" ;send ( mySocket , b u f f e r , 7 , 0 ) ;

c l o s e ( mySocket ) ;

Programa Writer

Figura 3.18. A análise individual considera o arranjo (buf) vulnerável no Reader.Ao analisar o todo, verifica-se um falso-positivo.

a =10;send ( a ) ; / / s1a =20;send ( a ) ; / / s2

Programa P1

b= r e c v ( a ) ; / / r 1b= r e c v ( a ) ; / / r 2

Programa P2

Figura 3.19. Programa P1 envia valor de a para P2. Programa P2 recebe valor dea atribui a b.

programa [Allen 1970]. Um bloco básico é uma sequência de instruções que não contémdesvios, só é possível entrar no bloco pela primeira instrução e só é possível sair depoisda última instrução. Há uma aresta entre um bloco básico B1 e um bloco básico B2 seo programa pode fluir de B1 para B2. Cada programa de um sistema distribuído possuiseu próprio CFG. Dessa forma, para se analisar um sistema distribuído como um todo énecessário criar um CFG global do sistema.

Por exemplo, a figura 3.19 mostra o código de um programa P1 que envia valorde a para o programa P2 que recebe esse valor e o atribui a b. Não há muito o que inferirde possíveis valores de b se analisarmos apenas P2. Por outro lado, ao analisar os doisprogramas em conjunto verifica-se que o maior valor que b pode assumir é 20.

Na figura 3.20a temos os CFGs simplificados do programa P1 e do programa P2.Para analisar a aplicação como um todo é necessário unir os dois grafos o que resulta emum novo CFG que representa o sistema distribuído como pode ser visto na figura 3.20b.Agora é possível saber que o valor b em P2 depende do valor de a que foi recebido de P1e que esse valor variou entre 10 e 20. Dessa maneira, a análise se torna mais precisa emenos suscetível a falsos positivos.

O problema passa a ser, então, como unir esses grafos de forma que as análises jáexistentes possam ser aplicadas. A solução trivial desse problema é unir todos os sends

com todos os receives. Mas nesse caso, várias arestas inválidas serão incluídas comoacontece com as arestas pontilhadas na figura 3.20c. Portanto é necessário utilizar umalgoritmo que leve em consideração o CFG de cada programa e sua interação via rede a

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

137 c©2013 SBC — Soc. Bras. de Computação

fim de gerar um CFG do sistema como um todo sem arestas redundantes ou inválidas.

Figura 3.20. (a) CFG simplificado dos programas P1 e P2. (b) União dos CFGssimplificados de P1 e P2. (c) CFG simplificado resultante da ligação de todos ossends de P1 com todos os receives de P2, as aresta pontilhadas indicam ligaçõesinválidas.

As arestas redundantes podem ser eliminadas ou, pelo menos, reduzidas a partirda aplicação do conceito de Dominância em Grafos. Um nó x em um CFG G domina umnó y (x e y podem ser os mesmos) se e somente se todo caminho em G para y a partir doinício – s – contém x. Já um nó x é dominador imediato de y se x 6= y e qualquer outronó z que domine y também domine x. Para otimizar as ligações entre sends e receives

podemos detectar qual é o dominador imediato xL de cada send ou receive encontrado,onde L é o nível desse dominador imediato. Esses sends/receives passam a fazer partedo conjunto de pontos de comunicação de rede dominados por xL. O mesmo é feito parao outro programa. Cria-se, então, arestas apenas entre sends e receives de programasdiferentes que estejam no mesmo nível. O nível de cada nó (L) pode ser determinado daseguinte maneira: (i) inicia-se o nível de cada send ou recv com nível 0; (ii) Se um send

ou recv n é alcançável por um nó x com nível L, o nível de n será o maior valor entre L+1e o nível de n; (iii) repete-se o passo anterior até que o sistema se estabilize.

Por exemplo, na figura 3.21 mostramos um código onde um programa Send enviamensagens para o programa Recv simulando um trecho de um protocolo de um sistemadistribuído. O CFG do programa Send pode ser visto na figura 3.22 e do programa Recv

na figura 3.24. Através da árvore de dominância do programa Send é possível verificarquais os dominadores imediatos de cada send, veja figura 3.23. Cada send dos blocosbásicos i f .then e i f .else tem como dominador imediato o bloco entry. Já o send do blocobásico do.end tem como dominador imediato o bloco do.cond. A árvore de dominância

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

138 c©2013 SBC — Soc. Bras. de Computação

# i n c l u d e "myserversocket.h"

main ( ) i n t s , code ;s t r u c t msgServer m;s = ge t M y Se r ve r S ock e t ( ) ;s c a n f ("%d" ,& code ) ;i f ( code ==7)

s t r c p y (&m. buf ,"begin17" ) ;send ( s , &m. buf , 7 , 0 ) ;

e l s e s c a n f ("%s" ,&m. buf ) ;send ( s ,&m. buf , s t r l e n (m. buf ) , 0 ) ;

do

/ / do some th ing and b r e a k whi le ( 1 ) ;s c a n f ("%s" ,&m. buf ) ;send ( s , &m. buf , s t r l e n (m. buf ) , 0 ) ;c l o s e ( s ) ;

Programa Send

# i n c l u d e "mysocket.h"

i n t main ( ) i n t s ;i n t n ;s t r u c t msg m;s = getMySocket ( ) ;

n= r e c v ( s ,&(m. buf ) ,MAX, 0 ) ;

i f (MSG. code ==7) p r i n t f ("Begin" ) ;n= r e c v ( s ,&(m. buf ) ,MAX, 0 ) ;

e l s e p r i n t f ("code: %c" ,m. code ) ;n= r e c v ( s ,&(m. buf ) ,MAX, 0 ) ;

c l o s e ( s ) ;

Programa Recv

Figura 3.21. Trecho de um sistema distribuído onde o programa Send envia men-sagens para o programa Recv de acordo com entradas passadas pelo usuário.

do programa Recv é apresentada na figura 3.25. O primeiro recv tem como dominadorimediato o início do programa, já os demais tem como dominador imediato o primeirobloco básico (entry).

Portanto para gerar o CFG desse exemplo, devemos criar arestas entre cada send

e cada recv obedecendo suas árvores de dominância. O resultado final pode ser visto nafigura 3.26. É criada uma aresta entre cada send dominado pelo bloco básico entry e orecv inicial. Já o send dominado pelo bloco básico do.end foi ligado cada recv dominadospelo bloco básico entry.

3.5.7.2. Grafo de Dependência de Sistemas Distribuídos

Há muitos algoritmos que usam o grafo de dependências [Ottenstein et al. 1990] paraanalisar programas. O Grafo de Dependência pode ser usado, por exemplo, para mapearas dependências entre as entradas (fontes externas) e os arranjos. A figura 3.27 mostraum grafo de dependência criado para um pequeno programa C. O grafo de dependênciapossui um nó para cada variável e cada operação do programa. Há uma aresta entre cadavariável u e uma instrução i se i representa uma instrução que usa u. De forma similar, ografo contem uma aresta entre i e v se i define uma variável.

É possível utilizarmos esses algoritmos no mundo distribuído, desde que existauma definição de grafo de dependências apropriado. Tal grafo pode ser construído apartir da união dos grafos de dependências individuais, via uma estratégia semelhanteàquela que usamos para unir CFGs. Cada programa do sistema possui seu próprio grafode dependência. Para cada instrução de acesso a rede, é criado um vértice no grafo pararepresentar essa comunicação. O vértice é marcado como um nó de rede de leitura ou deescrita e inserido em uma lista correspondente. Insere-se, então, uma aresta de dependên-cia entre o vértice de rede encontrado e os vértices correspondentes no outro programade acordo com o CFG global do sistema construído anteriormente. Dessa forma, o grafode dependências final é a união dos grafos de cada programa e representa o grafo de

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

139 c©2013 SBC — Soc. Bras. de Computação

Figura 3.22. CFG do Programa Send

Figura 3.23. Send: Árvore deDominância

Figura 3.24. CFG do Programa Recv

Figura 3.25. Recv: Árvore deDominância

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

140 c©2013 SBC — Soc. Bras. de Computação

Figura 3.26. CFG do sistema distribuído. Os nós preenchidos correspondem ablocos básicos do programa Send. As arestas tracejadas representam as liga-ções entre send e recv de cada programa.

dependência do sistema distribuído como se fosse um único programa.

O grafo de dependência resultante pode ser usado, por exemplo, para verificar de-pendências entre arranjos e entradas de dados. Para cada operação com arranjo, verifica-seno grafo se há dependência com as fontes de dados. As dependências podem ser locaisou remotas. As locais são aquelas que representam caminhos entre arranjos e fontes dedados no mesmo programa. As dependências remotas são aquelas derivadas de caminhosentre arranjos de um programa e fontes de dados de outro programa, através da rede.

3.6. Metodologia de Avaliação

A inserção de código extra para verificação de segurança causa um acréscimo energéticoque pode ser determinante para aplicações nas quais o consumo de energia é parâmetrocrítico, tais como aplicações embarcadas, sistemas movidos a bateria e outros. Nestaseção, será descrito um método para medição de energia extra consumida pelo acréscimode código.

3.6.1. Modelagem do Sistema de Medição

O método consiste em medir a energia total que a plataforma consome durante o tempode execução do código instrumentado e, em seguida, subtrair a energia correspondenteao consumo em estado de repouso (sem execução do código), de forma que o resultadocorresponda à energia consumida somente pelo código sob análise. Um diagrama deblocos do sistema de medição está representado na figura 3.28. Para medir a energia, é

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

141 c©2013 SBC — Soc. Bras. de Computação

int main(int argc, char** argv) int a = 0; a++; int* p = (int*)malloc(sizeof(int)); int* p2 = (int*)malloc(sizeof(int)); *p = a; if (argc % 2) free(p2); p2 = p; else *p2 = *p; return 0;

Figura 3.27. Exemplo de um grafo de dependências

preciso medir a corrente que entra na plataforma. Ao inserir uma resistência em série como circuito de alimentação (resistência shunt) é possível medir o valor da tensão sobre essaresistência e a corrente que entra na plataforma (passa pela resistência) é proporcional aovalor da tensão medida.

Para adquirir os valores de tensão Vs sobre a resistência shunt Rs, deve ser utili-zado um sistema de aquisição de dados (Data Acquisition System – DAQ)11. Esse sistemaamostra os valores analógicos de tensão e os converte para valores digitais, usáveis emcomputador. Cada valor de tensão Vs amostrado é processado no software de medição.Como gatilho externo de aquisição dos dados, foi usada a porta serial RS-232 da plata-forma (controlável em software). O pino da porta serial correspondente ao sinal RTS (Re-

ady to Send) permanece em nível lógico alto no estado de repouso. Quando o programaa ser avaliado inicia, o valor de tensão do pino RTS é alterado para nível lógico baixoe quando o programa termina, o nível lógico alto é novamente atribuído. Dessa forma,início e fim do programa estão claramente definidos por sinais externos, que servirão deentrada para o DAQ e serão processados no software de medição.

11Modelo utilizado: USB-6009 - National Instruments.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

142 c©2013 SBC — Soc. Bras. de Computação

DAQ

AC

Plataforma

Software

de

Medição

Computador

+

-

RTS

Figura 3.28. Diagrama de blocos para o esquema de medição

3.6.2. Códigos de teste

Para garantir a qualidade do teste, o sistema operacional (SO)12 precisa ser configuradopara não inicializar os módulos gráfico e de rede. Além disso, deve ser dada prioridademáxima13 ao programa em execução para evitar que outras tarefas realizadas concomi-tantemente pelo SO causem desvios nos resultados.

Os instantes exatos de início e fim da execução dos programas sob análise devemser determinados, portanto, é necessário modificar os códigos fonte inserindo comandosque controlem a porta serial. Esses comandos engatilham a medição através de um sinalexterno em hardware, dispensando qualquer tipo de emulação. As funções setrts_upe setrts_down realizam esse trabalho definindo os níveis de tensão do pino de saída daporta RS-232. No início do programa, a saída da porta serial é definida como nível baixo(setrts_down) e, ao final, o nível lógico alto (setrts_up) é atribuído. Abaixo, émostrado um exemplo da inserção do sinal RTS no código fonte de um programa sobavaliação.

//saída_porta_serial <= (nível baixo)

setrts_down();

//exemplo de corpo do código para testes

for(k=0; k < iterations; k++)

/* bound check*/

assert(sizes[1] > 15 && "Index out of bounds");

array[15]=2;

//fim do corpo

//saída_porta_serial <= (nível alto)

setrts_up();

12Para o teste e validação do método, os experimentos foram realizados em SO Fedora 11.13Usando o comando nice.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

143 c©2013 SBC — Soc. Bras. de Computação

3.6.3. Cálculo da Energia

A tensão Vs é proporcional à resistência Rs. O valor de Rs deve ser pequeno para que oacréscimo de resistência no circuito seja mínimo. Para que a tensão Vs corresponda aovalor real de corrente, é preciso multiplicar a tensão Vs pelo fator de ganho G. Pela lei deOhm, Is =Vs/Rs [Nilsson and Riedel 2009]. Daí,

Is = (1/Rs).Vs = G.Vs. (1)

A energia total pode ser calculada integrando a potência instantânea ao longo dotempo [Nilsson and Riedel 2009]

E =∫ t f

tip(t)dt =

∫ t f

tiv(t).i(t)dt. (2)

Como a corrente total que passa pelo sistema é Is = G.Vs e a tensão de alimentaçãoVpower é constante, tem-se:

E =∫ t f

tiVpower.G.vs(t)dt, (3)

onde ti e t f são os instantes de tempo de início e fim do programa em execução, de-terminados, respectivamente, pelas mudanças de níveis lógicos do sinal RTS. O diagramade blocos do cálculo da energia pode ser visto na Figura 3.29.

Figura 3.29. Esquema do cálculo da energia

O valor de Vs é multiplicado pelo ganho G para obter o valor real de correnteIs. Em seguida, esse valor é multiplicado por Vpower, chegando à potência instantâneaconsumida. A integral da potência é a energia total acumulada até o instante t. O programacomeça a processar os dados quando o sinal RTS vai para nível baixo e termina quandoo sinal volta para nível alto. Então, a energia acumulada é correspondente à execução doprograma testado. O resultado (Energia) é salvo em forma de texto no arquivo de saída.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

144 c©2013 SBC — Soc. Bras. de Computação

3.6.4. Software para cálculo da Energia

O DAQ utilizado lê periodicamente amostras de tensão em sua entrada e as converte paravalores digitais tratáveis em computador. O fabricante provê uma biblioteca de abstraçãodo hardware que permite que o programador desenvolva programas que interajam direta-mente com as entradas do DAQ através de comandos de alto nível. Assim, utilizando alinguagem C++, foi possível desenvolver um software para tratamento do sinal e cálculoda energia. Esse software é organizado em duas classes principais: a classe Channel ea classe Measurement. A classe Channel possibilita a criação (abstrações em soft-ware) e manipulação dos canais do DAQ. A classe Measurement é responsável pelocontrole e processamento das medições e cálculo da energia.

Ao ser executado, o DAQ é configurado e os canais (“Trigger” - A1 e “Principal”- A0) são criados. O programa, então, espera até que um sinal de gatilho (RTS) sejarecebido no canal A1. Quando o sinal de gatilho é recebido, marcando o início do código,a função de aquisição de dados é chamada e os valores de tensão do canal A0 são lidos.

A energia é a integral (soma) dos valores de potência instantâneos consumidos.A potência é calculada em software como (V SUPPLY )×(SHUNT GAIN)×Vs, onde Vs éo valor de tensão sobre a resistência Rs lido naquele instante. Uma variável, inicializadaem zero, é incrementada com o valor da potência, de forma que, ao final da execução,seu valor seja a igual a soma das potências instantâneas ao longo do tempo. A cadaamostra obtida, o canal A1 é lido e testado a fim de se determinar se houve término ounão da execução. Em caso positivo, o programa adiciona no fim do arquivo de saídaos valores do tempo total de execução (em segundos) e a energia total consumida (emJoules), voltando, então, à fase de espera por um novo sinal de gatilho. Esse fluxo deexecução contínuo possibilita a automação do processo o que permite a utilização descripts para execução de n programas de testes. O resultado final é a média (calculada emsoftware) dos n valores de energia medidos, garantido, dessa forma, maior confiabilidadena medição.

3.7. Estudo de Casos

3.7.1. Introdução

Como estudo de caso, iremos apresentar um exemplo didático de código em C e comoutilizar a ferramenta AddressSanitizer, apresentada na seção 3.5.4 para instrumentá-locontra erros de acessos inválidos. Opções plausíveis para a instrumentação incluem asferramentas SoftBound+CETS [Nagarakatte et al. 2009] [Nagarakatte et al. 2010] e SA-FECode [Dhurjati et al. 2006], mas atermo-emos apenas ao AddressSanitizer [Serebryanyet al. 2012] neste estudo. Mostraremos, também, o funcionamento de uma transformaçãode eliminação de Verificação de Limites de Arranjo e o impacto desta análise no gastoenergético do programa.

3.7.2. Exemplo

Tomemos como exemplo o programa na figura 3.30. Compilando-se este programa com oAddressSanitizer, utilizando a flag -fsanitize=address no clang, e executando-o, observamos que o programa aborta com a mensagem de erro:

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

145 c©2013 SBC — Soc. Bras. de Computação

ERROR : AddressSanitizer : stack−bu f f er−over f lowonaddress[...].

Essa mensagem indica que houve um Estouro de Arranjo ao acessar um objetona pilha. Não listamos a mensagem completa por questão de espaço. De fato, a indexa-ção na linha quatro acessa um elemento além do término do arranjo, acesso este que éidentificado pela ferramenta como sendo inválido.

i n t main ( i n t argc , char ** a rg v ) i n t a [ 1 5 ] ;f o r ( unsigned i = 0 ; i < 1 6 ; ++ i )

a [ i ] = i ;re turn a [ 0 ] + a [ 1 4 ] ;

Figura 3.30. Exemplo 1

Tomemos agora, o exemplo na figura 3.31. O programa está correto, e não háacessos inválidos, como há no programa anterior. Os acessos ao arranjo, ainda assim, sãoinstrumentados sem necessidade. Utilizando uma técnica de inferência de tamanhos dearranjo, descrita em por Santos et al. [Henrique Nazaré Santos 2013], e de uma Análisede Intervalos, descrita na seção 3.5.5, podemos provar que o acesso ao arranjo na linha 4é seguro, uma vez que o índice é sempre não-negativo e menor que o tamanho do arranjo.Caso seja, toda instrumentação na indexação é passível de ser removida.

i n t main ( i n t argc , char ** a rg v ) i n t a [ 1 5 ] ;f o r ( unsigned i = 0 ; i < 1 5 ; ++ i )

a [ i ] = i ;re turn a [ 0 ] + a [ 1 4 ] ;

Figura 3.31. Exemplo 2

Para arranjos com tamanhos dados por expressões simbólicos, porém, a Análisede Intervalos pode ser tornar inefetiva, como acontece para o exemplo na figura 3.32. Issoocorre pois, para um arranjo com n posições sendo acessado com índice i, podemos tera relação simbólica i < n, sem esta ser dada pelos intervalos numéricos de ambas. Paratratar estes casos, é necessária uma análise de tamanhos relativo, descrito também porSantos et al. Com esta análise podemos definir limites superiores simbólicos para cadavariável. Com isso, utilizamos a Análise de Intervalos para verificar se o índice sendoacessado é não-negativo e se o tamanho do arranjo é um limite superior simbólico para oíndice.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

146 c©2013 SBC — Soc. Bras. de Computação

i n t main ( i n t argc , char ** a rg v ) i n t * a = m a l l o c ( n * s i z e o f ( i n t ) ) ;f o r ( unsigned i = 0 ; i < n ; ++ i )

a [ i ] = i ;re turn a [ 0 ] + a [ n − 1 ] ;

Figura 3.32. Exemplo 3

Implementamos essas técnicas de eliminação de Verificação de Limites de Arranjoem nossa ferramenta GreenArrays e a utilizamos para remover instrumentações redundan-tes inseridas pelo AddressSanitizer. Os ganhos energéticos são mostrados abaixo.

3.7.3. Avaliação Energética das Ferramentas

Após instrumentado o código, é preciso garantir que o acréscimo energético causado pelainstrumentação seja viável, principalmente se tratando de Sistemas Embarcados. A seguir,será aplicado o método proposto na seção 3.6 para medir a energia consumida pelo códigoinstrumentado.

Como o código já foi compilado com a inserção dos sinais de controle RTS, inícioe fim da medição estão bem marcados e o arquivo executável já está pronto para sertestado. Para realizar os testes em ambiente mais próximo de um sistema embarcado (emque o consumo de energia é parâmetro crítico), foi feito uso da placa mãe Inforce14.

O arquivo é, então, executado no ambiente de testes. No momento em que oprograma inicia, o sinal de setrts_down coloca o nível de tensão da porta serial paranível lógico baixo. Nesse momento, o programa de medição (que já foi iniciado emoutro computador) identifica a mudança de tensão e inicia a amostragem de dados. Acada amostra, a variável contendo o valor de energia (inicialmente definida em zero) éincrementada com o valor calculado pela equação 3.6.3. Além disso, o valor de tensãodo pino RTS da porta serial é medido. Caso seja percebido o sinal de fim de programa detestes (setrts_up), o programa de medição termina e o valor acumulado para energiaé a energia correspondente à execução do código.

Para o sistema de medição o valor resistência usado foi Rs = 0,0989Ω15. Então,

o valor de G pode ser calculado como G = (1/Rs) = 1/0,0989 = 10,1112. Pelo manualfornecido pelo fabricante da plataforma usada pra executar o código de testes, tem-seque a tensão de alimentação Vpower = 12V . Para esta plataforma16, os valores de tensãodo pino RTS são Vlow = 0V e Vhigh = 5V , correspondendo aos níveis lógicos baixo ealto, respectivamente. A metodologia descrita foi aplicada para os códigos do Benchmark

Stanford17. Para garantir a confiabilidade da medição, o experimento foi executado dezvezes para cada código testado. O valor apresentado nos resultados é a média dos dez

14http://inforcecomputing.com/ - CPU 1.0GHz, Memória RAM = 512KB e ambiente Fedora11.

15A resistência foi medida com multímetro de 61/2 dígitos com medição a 4 terminais.16Os valores de tensão para os pinos da porta serial variam de acordo com o fabricante.17https://llvm.org/viewvc/llvm-project/test-suite/trunk/SingleSource/

Benchmarks/Stanford/.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

147 c©2013 SBC — Soc. Bras. de Computação

valores obtidos.

0

0,5

1

1,5

2

2,5

Bubblesort TreeSort IntMM Oscar Perm Puzzle Queens QuickSort RealMM Towers

Tem

po

de

Exe

cuçã

o (

log

(x))

(s)

Inseguro

SAFECode

AdressSanityzer

GreenArrays

Figura 3.33. Gráfico comparativo relativo ao tempo de execução dos programasdo Benchmark Stanford

Foram realizadas quatro formas de medição, para que as características do pro-grama compilado com as ferramentas de segurança de código sejam comparadas. A pri-meira delas é a medição do código sem instrumentação de segurança. A segunda coma ferramenta SAFECode. A terceira com a ferramenta AdressSanitizer. E, por último, aferramenta proposta GreenArrays. As figuras 3.33 e 3.34 mostram gráficos contendo asanálises do tempo de execução e energia total gasta, respectivamente, obtidos para cadaprograma do bechmark em estudo.

3.7.4. Discussão

Nota-se (figuras 3.33 e 3.34) que o acréscimo de código para torná-lo seguro causa umconsiderável acréscimo no tempo de execução do programa, levando, por consequência, aum grande acréscimo na energia consumida. Em muitos casos, os programas compiladoscom a ferramentas de segurança gastam mais do dobro de energia que o programa inse-guro. Mesmo assim, em determinadas aplicações, é necessário abrir mão de um menorconsumo energético e tempo de execução para se garantir segurança. Já em outras aplica-ções, como por exemplo, Sistemas Embarcados, o parâmetro energia é crítico, tratando-sede sistemas movidos a bateria (que é a maioria dos casos). Então, é preciso ponderar o usode segurança em código e fazê-lo somente quando o parâmetro segurança é tão importanteou mais que o parâmetro energia.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

148 c©2013 SBC — Soc. Bras. de Computação

0

0,5

1

1,5

2

2,5

3

3,5

4

Bubblesort TreeSort IntMM Oscar Perm Puzzle Queens QuickSort RealMM Towers

En

erg

ia C

on

sum

ida

()l

og

(x)

(J)

Inseguro

SAFECode

AdressSanityzer

GreenArrays

Figura 3.34. Gráfico comparativo relativo a energia total consumida pelos pro-gramas do Benchmark Stanford

3.8. Conclusões

Segurança de Software está no cerne da segurança de sistemas computacionais de maneirageral. Na medida em que ataques que exploram vulnerabilidades em software são maise mais comuns, Segurança de Software torna-se cada vez mais relevante. Paralelamentea isso, seja no trabalho ou no ambiente doméstico, Sistemas Embarcados estão cada vezmais presentes na vida de indivíduos. Em outras palavras, aparentemente a computaçãoubíqua enfim tornou-se uma realidade.

Tal fato, por si só, já causa preocupação quanto à segurança de Sistemas Em-barcados. Mas, como se não bastasse, após uma análise conclui-se que as propostas deSegurança de Software existentes não são apropriadas para esses dispositivos. O objetivodeste capítulo foi, então, apresentar uma visão geral da área de Segurança de Software emostrar como adaptar e avaliar as soluções existentes no contexto de Sistemas Embarca-dos.

Em particular, discorremos, dentre outras coisas, sobre Estouro de Arranjo, Es-touro de Inteiro, Vazamento de Endereço, Aleatorização de Espaço de Endereço, Preven-ção contra a Execução de Dados, Canários, Verificação de Limites de Arranjo, Análisede Intervalos e Análise Distribuída. Ademais, descrevemos uma metodologia para avali-ação energética de mecanismos de Segurança de Software. E, ao final, apresentamos umasolução de Segurança de Software especialmente concebida para o contexto de SistemasEmbarcados.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

149 c©2013 SBC — Soc. Bras. de Computação

Referências

[Akyildiz et al. 2002] Akyildiz, I. F., Su, W., Sankarasubramaniam, Y., and Cayirci, E.(2002). Wireless sensor networks: a survey. Computer networks, 38(4):393–422.

[Aleph One 1996] Aleph One (1996). Smashing the stack for fun and profit. Phrack

magazine, 7(49):365.

[Alhazmi et al. 2007] Alhazmi, O. H., Malaiya, Y. K., and Ray, I. (2007). Measuring,analyzing and predicting security vulnerabilities in software systems. Computers &

Security, 26(3):219–228.

[Allen 1970] Allen, F. E. (1970). Control flow analysis. In ACM Sigplan Notices, vo-lume 5, pages 1–19. ACM.

[Aranha et al. 2012] Aranha, D. F., Karam, M. M., Miranda, A., and Scarel, F. (2012).Software vulnerabilities in the Brazilian voting machine. Tech Report.

[ARM Holdings 2008] ARM Holdings (2008). ARM11 MPCore Processor TechnicalReference Manual.

[Atzori et al. 2010] Atzori, L., Iera, A., and Morabito, G. (2010). The internet of things:A survey. Computer Networks, 54(15):2787–2805.

[Balzarotti et al. 2008] Balzarotti, D., Cova, M., Felmetsger, V., Jovanovic, N., Kirda, E.,Kruegel, C., and Vigna, G. (2008). Saner: Composing static and dynamic analysisto validate sanitization in web applications. In Security and Privacy, 2008. SP 2008.

IEEE Symposium on, pages 387–401. IEEE.

[Barik et al. 2006] Barik, R., Grothoff, C., Gupta, R., Pandit, V., and Udupa, R. (2006).Optimal bitwise register allocation using integer linear programming. In LCPC, vo-lume 4382 of Lecture Notes in Computer Science, pages 267–282. Springer.

[Barr 1999] Barr, M. (1999). Programming embedded systems in C and C++. O’Reilly.

[Bell 1999] Bell, T. (1999). The concept of dynamic analysis. SIGSOFT Softw. Eng.

Notes, 24(6):216–234.

[Bhatkar et al. 2003] Bhatkar, E., Duvarney, D. C., and Sekar, R. (2003). Address ob-fuscation: an efficient approach to combat a broad range of memory error exploits. InUSENIX Security, pages 105–120.

[Bodik et al. 2000] Bodik, R., Gupta, R., and Sarkar, V. (2000). ABCD: eliminating arraybounds checks on demand. In PLDI, pages 321–333. ACM.

[Brumley et al. 2007] Brumley, D., Song, D. X., cker Chiueh, T., Johnson, R., and Lin,H. (2007). RICH: Automatically protecting against integer-based vulnerabilities. InNDSS. USENIX.

[Bruno R. Silva 2013] Bruno R. Silva, Fernando M. Q. Pereira, L. B. O. A. A. F. L.(2013). Flow tracking: Uma ferramenta para detecção de vazamentos de informaçõessigilosas. In CBSoft. SBC.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

150 c©2013 SBC — Soc. Bras. de Computação

[Carro and Wagner 2003] Carro, L. and Wagner, F. R. (2003). Sistemas computacionaisembarcados. Jornadas de atualização em informática. Campinas: UNICAMP.

[Comparetti et al. 2009] Comparetti, P. M., Wondracek, G., Kruegel, C., and Kirda, E.(2009). Prospex: Protocol specification extraction. In Security and Privacy, 2009 30th

IEEE Symposium on, pages 110–125. IEEE.

[Cong et al. 2005] Cong, J., Fan, Y., Han, G., Lin, Y., Xu, J., Zhang, Z., and Cheng,X. (18-21 Jan. 2005). Bitwidth-aware scheduling and binding in high-level synthesis.Design Automation Conference, 2005. Proceedings of the ASP-DAC 2005. Asia and

South Pacific, 2:856–861.

[Cousot et al. 2005] Cousot, P., Cousot, R., Feret, J., Mauborgne, L., Miné, A., Monni-aux, D., and Rival, X. (2005). The astrÉe analyzer. In ESOP’05.

[Cuoq et al. 2012] Cuoq, P., Kirchner, F., Kosmatov, N., Prevosto, V., Signoles, J., andYakobowski, B. (2012). Frama-c: a software analysis perspective. In Proceedings

of the 10th international conference on Software Engineering and Formal Methods,SEFM’12, pages 233–247, Berlin, Heidelberg. Springer-Verlag.

[DAVID and TIRI 2005] DAVID, D. and TIRI, K. (2005). Securing embedded systems.

[Dhurjati et al. 2006] Dhurjati, D., Kowshik, S., and Adve, V. (2006). Safecode: enfor-cing alias analysis for weakly typed languages. In PLDI ’06: Proceedings of the 2006

ACM SIGPLAN conference on Programming language design and implementation, pa-ges 144–157, New York, NY, USA. ACM.

[Dietz et al. 2012] Dietz, W., Li, P., Regehr, J., and Adve, V. (2012). Understandinginteger overflow in c/c++. In ICSE, pages 760–770. IEEE.

[Ernst 2003] Ernst, M. D. (2003). Static and dynamic analysis: Synergy and duality. InWODA 2003: ICSE Workshop on Dynamic Analysis, pages 24–27. Citeseer.

[Hamacher et al. 2012] Hamacher, V. C., Vranesic, Z., Zaky, S., and Manjikian, N.(2012). Computer organization and embedded systems. McGraw-Hill.

[Henrique Nazaré Santos 2013] Henrique Nazaré Santos, Fernando Magno Quintão Pe-reira, L. B. O. (2013). Verificação estática de acessos a arranjos em c. In Anais do

XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais,

SBSEG 2013, Manaus, Brazil. Sociedade Brasileira de Computação (SBC).

[Hopcroft 2008] Hopcroft, J. E. (2008). Introduction to Automata Theory, Languages,

and Computation, 3/E. Pearson Education India.

[Howard and Thomlinson 2007] Howard, M. and Thomlinson, M. (2007). WindowsVista ISV Security. Microsoft Corporation, April, 6.

[Intel Corporation ] Intel Corporation. Intel 64 and ia-32 Architectures Software Deve-lopers Manual – System Programming Guide, part 1.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

151 c©2013 SBC — Soc. Bras. de Computação

[Jim et al. 2002] Jim, T., Morrisett, J. G., Grossman, D., Hicks, M. W., Cheney, J., andWang, Y. (2002). Cyclone: A safe dialect of c. In Proceedings of the General Track

of the annual conference on USENIX Annual Technical Conference, ATEC ’02, pages275–288, Berkeley, CA, USA. USENIX Association.

[Jones 2007] Jones, J. R. (2007). Estimating software vulnerabilities. Security & Privacy,

IEEE, 5(4):28–32.

[Koopman 2004] Koopman, P. (2004). Embedded system security. Computer, 37(7):95–97.

[Lattner and Adve 2005] Lattner, C. and Adve, V. (2005). Automatic Pool Allocation:Improving Performance by Controlling Data Structure Layout in the Heap. In Procee-

dings of the 2005 ACM SIGPLAN Conference on Programming Language Design and

Implementation (PLDI’05), Chigago, Illinois.

[Lhairech-Lebreton et al. 2010] Lhairech-Lebreton, G., Coussy, P., Heller, D., and Mar-tin, E. (2010). Bitwidth-aware high-level synthesis for designing low-power dsp appli-cations. In ICECS, pages 531–534. IEEE.

[Li and Regehr 2010] Li, P. and Regehr, J. (2010). T-check: bug finding for sensornetworks. In Proceedings of the 9th ACM/IEEE International Conference on Infor-

mation Processing in Sensor Networks, pages 174–185. ACM.

[Lin et al. 2009] Lin, H., Yang, M., Long, F., Zhang, L., and Zhou, L. (2009). Modist:transparent model checking of unmodified distributed systems. In NSDI.

[Logozzo and Fahndrich 2008] Logozzo, F. and Fahndrich, M. (2008). Pentagons: a we-akly relational abstract domain for the efficient validation of array accesses. In SAC,pages 184–188. ACM.

[Mahlke et al. 2001] Mahlke, S., Ravindran, R., Schlansker, M., Schreiber, R., andSherwood, T. (2001). Bitwidth cognizant architecture synthesis of custom hardwareaccelerators. Computer-Aided Design of Integrated Circuits and Systems, IEEE Tran-

sactions on, 20(11):1355–1371.

[Marwedel 2011] Marwedel, P. (2011). Embedded system design. Springer.

[McGraw 2006] McGraw, G. (2006). Software security: building security in, volume 1.Addison-Wesley Professional.

[Microsoft Support a] Microsoft Support. A detailed description of the data executionprevention (dep) feature in windows xp service pack 2, windows xp tablet pc edition2005, and windows server 2003. @ONLINE. 4http://support.microsoft.com/kb/875352/EN-US/.

[Microsoft Support b] Microsoft Support. Microsoft. /SAFESEH Compiler Switch.

[Misra 1987] Misra, D. K. (1987). A quasi-static analysis of open-ended coaxial lines(short paper). Microwave Theory and Techniques, IEEE Transactions on, 35(10):925–928.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

152 c©2013 SBC — Soc. Bras. de Computação

[Mock 2003] Mock, M. (2003). Dynamic analysis from the bottom up. In WODA 2003

ICSE Workshop on Dynamic Analysis, page 13. Citeseer.

[Molnar et al. 2009] Molnar, D., Li, X. C., and Wagner, D. A. (2009). Dynamic testgeneration to find integer bugs in x86 binary linux programs. In Proc. USENIX security

symposium.

[Nagarakatte et al. 2009] Nagarakatte, S., Zhao, J., Martin, M. M., and Zdancewic, S.(2009). Softbound: Highly compatible and complete spatial safety for c. In Procee-

dings of the 2009 ACM SIGPLAN Conference on Programming Language Design and

Implementation.

[Nagarakatte et al. 2010] Nagarakatte, S., Zhao, J., Martin, M. M., and Zdancewic, S.(2010). Cets: compiler enforced temporal safety for c. SIGPLAN Not., 45(8):31–40.

[Nethercote and Seward 2007] Nethercote, N. and Seward, J. (2007). Valgrind: a fra-mework for heavyweight dynamic binary instrumentation. SIGPLAN Not., 42(6):89–100.

[Nilsson and Riedel 2009] Nilsson, J. W. and Riedel, S. A. (2009). Circuitos Eletricos,volume 8. Pearson Prentice Hall.

[Ottenstein et al. 1990] Ottenstein, K. J., Ballance, R. A., and MacCabe, A. B. (1990).The program dependence web: a representation supporting control-, data-, anddemand-driven interpretation of imperative languages. In PLDI. ACM.

[Patterson 1995] Patterson, J. R. C. (1995). Accurate static branch prediction by valuerange propagation. In PLDI, pages 67–78. ACM.

[PaX Team 2000] PaX Team (2000). Pax Non-eXecutable Stack (nx) @ONLINE.http://pax.grsecurity.net/docs/noexec.txt.

[PaX Team 2001] PaX Team (2001). Pax address space layout randomization (aslr)@ONLINE. http://pax.grsecurity.net/docs/aslr.txt.

[Pereira and Palsberg 2008] Pereira, F. M. Q. and Palsberg, J. (2008). Register allocationby puzzle solving. In PLDI, pages 216–226. ACM.

[Pop and Specialist 2010] Pop, A. R. and Specialist, S. S. (2010). Dep/aslr implementa-tion progress in popular third-party windows applications.

[Quadros and Pereira 2011] Quadros, G. S. and Pereira, F. M. Q. (2011). Static detectionof address leaks. In SBSeg, pages 23–37.

[Quadros and Pereira 2012a] Quadros, G. S. and Pereira, F. M. Q. (2012a). Dynamicdetection of address leaks. In Anais do XII Simpósio Brasileiro em Segurança da

Informação e de Sistemas de Computacionais, SBESEG 2012.

[Quadros and Pereira 2012b] Quadros, G. S. and Pereira, F. M. Q. (2012b). A staticanalysis tool to detect address leaks. In CBSoft – Tools.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

153 c©2013 SBC — Soc. Bras. de Computação

[Richarte et al. 2002] Richarte, G. et al. (2002). Four different tricks to bypass stackshi-eld and stackguard protection. World Wide Web, http://www1. corest. com/files/fi-

les/11/StackGuardPaper. pdf.

[Robertson et al. 2003] Robertson, W. K., Kruegel, C., Mutz, D., and Valeur, F. (2003).Run-time detection of heap-based overflows. In LISA, volume 3, pages 51–60.

[Rodrigues et al. 2013] Rodrigues, R. E., Campos, V. H. S., and Pereira, F. M. Q. (2013).A fast and low overhead technique to secure programs against integer overflows. InCGO, pages 1–11. ACM.

[Rus et al. 2003] Rus, S., Rauchwerger, L., and Hoeflinger, J. (2003). Hybrid analysis:Static & dynamic memory reference analysis. International Journal of Parallel Pro-

gramming, 31(4):251–283.

[Sasnauskas et al. 2010] Sasnauskas, R., Landsiedel, O., Alizai, M. H., Weise, C., Kowa-lewski, S., and Wehrle, K. (2010). Kleenet: discovering insidious interaction bugs inwireless sensor networks before deployment. In Proceedings of the 9th ACM/IEEE

International Conference on Information Processing in Sensor Networks, pages 186–196. ACM.

[Schwartz et al. 2011] Schwartz, E. J., Avgerinos, T., and Brumley, D. (2011). Q: Exploithardening made easy. In USENIX Security Symposium.

[Serebryany et al. 2012] Serebryany, K., Bruening, D., Potapenko, A., and Vyukov, D.(2012). Addresssanitizer: a fast address sanity checker. In USENIX, pages 28–28.USENIX Association.

[Shacham 2007] Shacham, H. (2007). The geometry of innocent flesh on the bone:return-into-libc without function calls (on the x86). In CCS, pages 552–561. ACM.

[Shacham et al. 2004a] Shacham, H., Page, M., Pfaff, B., Goh, E.-J., Modadugu, N., andBoneh, D. (2004a). On the effectiveness of address-space randomization. In Proce-

edings of the 11th ACM conference on Computer and communications security, CCS’04, pages 298–307, New York, NY, USA. ACM.

[Shacham et al. 2004b] Shacham, H., Page, M., Pfaff, B., Goh, E.-J., Modadugu, N.,and Boneh, D. (2004b). On the Effectiveness of Address-Space Randomization. InProceedings of the 11th ACM conference on Computer and communications security,pages 298–307. ACM.

[Solar Designer 1997] Solar Designer (1997). Return-to-libc Attack.

[Souza et al. 2011] Souza, M. R. S., Guillon, C., Pereira, F. M. Q., and da Silva Bigonha,M. A. (2011). Dynamic elimination of overflow tests in a trace compiler. In CC, pages2–21.

[Stephenson et al. 2000] Stephenson, M., Babb, J., and Amarasinghe, S. (2000).Bitwidth analysis with application to silicon compilation. In PLDI, pages 108–120.ACM.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

154 c©2013 SBC — Soc. Bras. de Computação

[Sumant Kowshik and Adve 2002] Sumant Kowshik, D. D. and Adve, V. (2002). Ensu-ring Code Safety Without Runtime Checks for Real-Time Control Systems. In Proc.

Int’l Conf. on Compilers Architecture and Synthesis for Embedded Systems, 2002, Gre-noble, France.

[Tallam and Gupta 2003] Tallam, S. and Gupta, R. (2003). Bitwidth aware global registerallocation. In POPL, pages 85–96, New York, NY, USA. ACM.

[Tran et al. 2011] Tran, M., Etheridge, M., Bletsch, T., Jiang, X., Freeh, V., and Ning,P. (2011). On the expressiveness of return-into-libc attacks. In Recent Advances in

Intrusion Detection, pages 121–141. Springer.

[Tripp et al. 2009] Tripp, O., Pistoia, M., Fink, S. J., Sridharan, M., and Weisman, O.(2009). Taj: effective taint analysis of web applications. In ACM Sigplan Notices,volume 44, pages 87–97. ACM.

[Venet and Brat 2004] Venet, A. and Brat, G. (2004). Precise and efficient static arraybound checking for large embedded c programs. SIGPLAN Not., 39:231–242.

[Wagner and Dean 2001] Wagner, D. and Dean, R. (2001). Intrusion detection via staticanalysis. In Security and Privacy, 2001. S&P 2001. Proceedings. 2001 IEEE Sympo-

sium on, pages 156–168. IEEE.

[Wang et al. 2009] Wang, T., Wei, T., Lin, Z., and Zou, W. (2009). Intscope: Automati-cally detecting integer overflow vulnerability in x86 binary using symbolic execution.In Proc. of Network and Distributed System Security Symposium (NDSS). Citeseer.

[Warren 2002] Warren, H. S. (2002). Hacker’s Delight. Addison-Wesley Longman Pu-blishing Co., Inc.

[Zurita ] Zurita, M. E. Projeto de sistemas embarcados. Universidade Federal do Piauí,

Curso de Engenharia Elétrica, Campus Universitário Ministro Petrônio Portela.

Minicursos do XIII Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais — SBSeg 2013

155 c©2013 SBC — Soc. Bras. de Computação