Transbordamento de dados: diferenças entre revisões

12 219 bytes adicionados ,  6 de dezembro de 2011
Mais informação acresentada - traduzida parcialmente a versão do artigo em inglês (mais completo)
(Mais informação acresentada - traduzida parcialmente a versão do artigo em inglês (mais completo))
Em segurança computacional e programação um estouro de buffer (do inglês buffer overflow ou buffer overrun) é uma anomalia onde um programa, ao escrever dados em um buffer, ultrapassa os limites do buffer e sobrescreve a memória adjacente. Esse é um caso especial de violação de segurança de memória.
Na [[informática]], '''transbordamento de dados''' ({{lang-en|''buffer overflow''}}) acontece quando o tamanho de um ''[[Buffer (Ciência da computação)|buffer]]'' ultrapassa sua capacidade máxima de [[armazenamento de dados|armazenamento]].
Estouros de buffer podem ser disparados por entradas que são projetadas para executar código, ou alterar o modo como o programa funciona. Isso pode resultar em comportamento errado do programa, incluindo erros de acesso à memória, resultados incorretos, parada total do sistema, ou uma brecha num sistema de segurança. Portanto, eles são a base de muitas vulnerabilidade de software e pode ser explorados maliciosamente.
Linguagens de programação comumente associadas com transbordamentos de dados incluem [[C (linguagem de programação)|C]] e [[C++]], as quais não proveem proteção contra acesso ou sobrescrita de dados em qualquer parte da memória e não checam automaticamente se dados escritos em um array (cadeia de elementos – o tipo de buffer dessas linguagens) estão nos limites do array. Checagem de limites pode prevenir transbordamentos de dados. 
== Descrição técnica ==
Um estouro de buffer ocorre quando dados escritos em um buffer, devido a checagem de limites insuficiente, corrompe valores de dados no endereço de memória adjacente ao buffer alocado. Isso ocorre mais comumente quando se copia strings (cadeias de caracteres) de um buffer para outro.
=== Exemplo básico ===
No exemplo a seguir um programa define dois itens de dados que são adjacentes na memória: um buffer de strings A de tamanho de 8 bytes, e um inteiro B de tamanho de 2 bytes. Inicialmente A não contém nada além de bytes zero, e B contem o número 1979. O tamanho dos caracteres é 1 byte.
 
{| class="wikitable" style="width:30em; text-align:center;"
Se o programa não foi adequadamente [[programação|escrito]], esse excesso de dados pode acabar sendo armazenado em áreas de memória próximas, corrompendo dados ou travando o programa, ou mesmo ser executado, que é a possibilidade mais perigosa. Por exemplo, se um programa qualquer possuir uma vulnerabilidade no sistema de ''[[login]]'' por exemplo, pode-se criar um outro programa que fornece caracteres de texto até completar o ''buffer'' e que depois envie um executável, que acabaria rodando graças à vulnerabilidade.
! style="white-space:nowrap;" | variable name
! colspan="8" style="background:#ddf;" | A
! colspan="2" style="background:#fdd;" | B
|- style="background:#ddf; font-family:monospace;"
! style="font-family:sans;" | value
| colspan="8" style="font-size:smaller; font-family:sans;" | [<nowiki/>[[null string]]<nowiki/>]
| colspan="2" style="background:#fdd;" | 1979
|- style="background:#ddf; font-family:monospace;"
! style="font-family:sans;" | hex value
| 00 || 00 || 00 || 00 || 00 || 00 || 00 || 00
| style="background:#fdd;" | 07 || style="background:#fdd;" | BB
|}
 
Agora, o programa tenta guardar a string terminada em nulo “excessive” no buffer A. Ao falhar em checar o comprimento da string, ele sobrescreve o valor de B.
Um caso famoso foi descoberto em [[2000]] no [[Outlook Express]]. Graças a uma vulnerabilidade, era possível fazer com que um [[e-mail]] executasse arquivos apenas por ser aberto. Bastava anexar um arquivo com um certo número de caracteres no nome, que ele seria executado ao ser aberta a mensagem {{carece de fontes|data=Dezembro de 2008}}. Naturalmente, a [[Microsoft]] se apressou em lançar uma correção e alertar os usuários para o problema. Semanalmente são descobertas vulnerabilidades de ''buffer overflow'' em vários programas. Algumas são quase inofensivas, enquanto outras podem causar problemas sérios.
 
{| class="wikitable" style="width:30em; text-align:center;"
Nesse contexto o [[Windows Vista]] obtém um grande avanço {{carece de fontes|data=Março de 2011}}.
! style="white-space:nowrap;" | variable name
! colspan="8" style="background:#ddf;" | A
! colspan="2" style="background:#fdd;" | B
|- style="background:#ddf; font-family:monospace;"
! style="font-family:sans;" | value
| 'e' || 'x' || 'c' || 'e' || 's' || 's' || 'i' || 'v'
| colspan="2" style="background:#dbd;" | 25856
|- style="background:#ddf; font-family:monospace;"
! style="font-family:sans;" | hex
| 65 || 78 || 63 || 65 || 73 || 73 || 69 || 76
| style="background:#dbd;" | 65 || style="background:#dbd;" | 00
|}
 
Embora o programador não tenha intensão alguma de mudar B, o valor de B foi substituído pelo número formado por parte de uma cadeira de caracteres. Neste exemplo, em um sistema big-endian que usa ASCII, “e” seguido por um byte zero se tornam o número 25856. Se Be fosse a única outra variável definida pelo programa, escrever uma string ainda maior que ultrapassasse B poderia causar um erro como uma falha de segmentação, terminando o processo.
== Exploração ==
As técnicas para se explorar uma vulnerabilidade de estouro de buffer podem variar de acordo com a arquitetura, sistema operacional e região da memória. Por exemplo, explorar um heap (usado para alocar memória dinamicamente) é muito diferente de explorar uma pilha de chamadas.
=== Exploração baseada em pilha ===
Um usuário malicioso e com habilidades técnicas pode explorar estouro de buffer em pilhas para manipular o programa de uma das seguintes maneiras:
* Sobrescrever uma variável local que está próxima do buffer na memória da pilha para mudar o comportamento do programa de modo a beneficiar o atacante.
* Sobrescrever o endereço de retorno da pilha. Uma vez que a função retorna, a execução irá continuar no endereço de retorno especificado pelo atacante, geralmente um buffer preenchido por entrada do usuário.
* Sobrescrever um ponteiro de função, ou tratador de exceção, que é posteriormente executado.
Com um método chamado “trampolining”, se um endereço de um dado fornecido pelo usuário é desconhecido, mas a sua localização é guardada em um registrador, então o endereço de retorno pode ser sobrescrito com um opcode que fará a execução saltar para os dados fornecidos pelo usuário. Se a localização é guardada num registrador R, então um salto de execução para a localização contendo o opcode para um salto para R, chamada para R ou uma instrução similar, causará a execução de dados fornecidos pelo usuário. A localização de opcodes adequados, or bytes na memória, podem ser encontradas em DLLs ou no próprio executável. Entretnato, o endereçamento de opcodes tipicamente não pode conter nenhum caractere nulo e as localizações desses opcodes podem mudar entre aplicações e versões do sistema operacional. O Projeto Metasploit é um banco de dados de opcodes, apesar de apenas os opcodes encontrados nos sistemas Windows estarem listados.
Não se deve confundir estouro de buffer baseado em pilha com estouro de pilha.
Essas vulnerabilidade são descobertas geralmente com uso de um fuzzer.
=== Exploração baseada em heap ===
Um estouro de buffer que ocorre na área de dados da heap é chamado de estouro de heap e é explorado de forma diferente do estouro de buffer baseado em pilha. A memória na heap é alocada dinamicamente pela aplicação em tempo de execução e contem tipicamente dados do programa. A exploração se dá corrompendo-se esses dados de modos específicos para fazer a aplicação sobrescrever estruturas internas como listas encadeadas e ponteiros. A técnica de estouro de heap canônica sobrescreve a linkagem de alocação de memória dinâmica (como o malloc) e usa a troca de ponteiro resultante para sobrescrever um ponteiro de função do programa.
A vulnerabilidade do GDI+ da Microsoft em tratar JPEGs é um exemplo do perigo que um estouro de heap pode apresentar.
=== Barreiras à exploração ===
Manipulação do buffer, que ocorre antes que ele seja lido ou executado, pode levar à falha de uma tentativa de exploração. Essas manipulações podem mitigar a ameaça de eploração, mas não pode fazê-la se tornar impossível. Manipulação pode incluir conversão de maiúscula em minúscula, remoção de caracteres especiais e filtragem de strings não-alfanuméricas. Entretanto, existem técnicas para burlar esses filtros e manipulações: códigos alfanuméricos, códigos polimórficos, códigos auto-deslocáveis, e ataques return-to-libc. Os mesmo métodos podem ser usado para evitar detecção em sistemas de detecção de invasão. Em alguns casos, incluindo onde o código é convertido para Unicode, a ameaça da vulnerabilidade tem sido erroneamente confundida pelos divulgadores como apenas negação de serviço, quando de fato a execução remota de código arbitrário é possível.
=== Aspectos práticos da exploração ===
Em explorações do mundo real existe uma variedade de desafios que precisam ser superados para as explorações operarem de fato. Esses fatores incluem bytes nulos em endereços, variação na localização do shellcode, diferenças entre ambientes e várias contra-medidas em operação.
== Medidas de proteção ==
Várias técnicas foram utilizadas para detector ou prevenir estouros de buffer, com vantagens e desvantagens. O modo mais confiável de evitar ou prevenir estouros de buffer é usar proteção automática a nível de linguagem. Esse tipo de proteção, entretanto, não pode ser aplicado a códigos legados. As seguintes seções descrevem as opções e implementações disponíveis.
=== Escolha de linguagem de programação ===
A escolha da linguagem de programação pode ter um efeito profundo na ocorrência de estouros de buffer. Como em 2008, entre as linguagens mais populares estão C e sua derivativa C++, com uma ampla gama de softwares sendo escritos nessas linguagens. C e C++ não proveem proteção embutida contra acesso indevido ou sobrescrita de qualquer parte da memória. Mais especificadamente, elas não checam se um dado escrito em um buffer está nos limites do buffer. Entretanto, as bibliotecas de C++ padrão proveem muitas maneiras de copiar dados para um buffer de forma segura, e técnicas para evitar estouros de buffer também existem em C.
Muitas outras linguagens de programação proveem checagem em tempo de execução e em alguns casos até checagens em tempo de compilação, que enviam um alerta um lançam uma exceção quando C ou C++ iriam sobrescrever os dados e continuar a executar instruções até que resultados errados fossem obtidos, os quais poderiam ou não fazer o programa parar. Exemplos de tais linguagens incluem Ada, Eiffel, Lisp, Modula-2, Smalltalck, Objective Caml e derivadas de C como Cyclone e D. Os ambientes de bytecodes de Java e do .Net Framework também requerem checagem de limites em todos os arrays. Quase todas as linguagens interpretadas irão proteger contra estouros de buffer, sinalizando uma condição de erro bem definida. Na maioria das vezes, quando uma linguagem prove informação de tipos suficiente para se fazer checagem de limites de arrays, é dada a opção de habilitar ou desabilitar a checagem. Análise de código estática pode remover muitas checagens de limites e tipos dinâmicas, mas implementações pobres e casos estranhos podem diminuir significativamente o desempenho. Engenheiros de software devem considerar cuidadosamente os ganhos e perdas de segurança versus custos de desempenho ao decidirem qual linguagem e configuração de compilador utilizar.
=== Uso de bibliotecas seguras ===
O problema de estou de buffer é comum em C e C++ porque elas expõem detalhes de baixo nível de implementação dos buffers como conteúdos e tipos de dados. Estouros de buffer devem ser evitados para se manter um alto nível de corretude no código que executa o gerenciamento de buffer. Também recomendado há muito tempo que se evite as funções de bibliotecas padrão que não fazem checagem de limites, como gets, scanf e strcpy. O worm de Morris explorou uma chamada de gets no fingerd.
Bibliotecas de tipos de dados abstratos bem escritas e testadas que centralizam e checam automaticamente o gerenciamento de buffer, incluindo checar os limites, podem reduzir a ocorrência e impacto de estouros de buffer. Os dois principais tipos de dados básicos nessas linguagens nos quais estouros de buffer geralmente ocorrem são strings e arrays. Então, bibliotecas que previnem estouro de buffer nesses tipos podem prover a grande maioria da segurança necessária. Ainda assim, falhas em se usar bibliotecas seguras podem resultar em estouros de buffer e outras vulnerabilidades, e naturalmente, qualquer erro na própria biblioteca é uma vulnerabilidade em potencial. Implementações “seguras” de bibliotecas incluem “The Better String Library”, Vstr e Erwin. A biblioteca C do sistema operacional OpenBSD prove as funções strlcpy e strlcat, mas elas são mais limitadas que a implementação completa da biblioteca segura.
== História ==
Estouros de buffer foram entendido e parcialmente documentados publicamente em 1972, quando o Computer Security Technology Planning Study divulgou a técnica: “O código que executa essa função não checa os endereços de origem e destino devidamente, permitido que porções do monitor sejam sobrepostas pelo usuário. Isto pode ser usado para injetar código no monitor que permitirá ao usuário tomar controle da máquina.” (Página 61) Hoje, o monitor seria chamado de kernel.
A primeira exploração hostil documentada de um estouro de buffer foi em 1988. Era uma de várias explorações usadas pelo worm criado por Morris para se propagar pela internet. O programa explorado era um serviço do Unix chamado finger. Depois, em 1995, Thomas Lopatic redescobriu independentemente o estouro de buffer e publicou suas descobertas na lista de mensagens de segurança Bugtraq. Um ano depois, em 1996, Elias Levy (também conhecido como Aleph One) publicou na revista Phrack o artigo “Destruindo a Pilha por Diversão e Lucro”, uma introdução passo-a-passo para explorar vulnerabilidades de estouro de buffer baseado em pilha.
Desde então, no mínimo dois grandes worms de internet exploraram estouro de buffer para comprometer um grande número de sistemas. Em 2001 o worm Code Red explorou um estouro de buffer no Internet Information Services (ISS) 5.0 da Microsoft e em 2003 o worm SQL Slammer comprometeu máquinas ruando o Microsoft SQL Server 2000.
Em 2003 estouros de buffer presents em jogos licenciados do Xbox foram explorados para permitir que softwares não-licenciados, incluindo jogos caseiros, rodassem no console sem a necessidade de modificações de hardware, também conhecidas como modchips. O PS2 Indepentence Exploit também usou um estouro de buffer para fazer o mesmo com o Playstation 2. O hack Twilight consegui o mesmo no Wii usando um estouro de buffer no jogo The Lengend of Zelda: Twilight Princess.
 
<ref>http://en.wikipedia.org/wiki/Buffer_overflow</ref>
=={{Ver também}}==
* ''[[Buffer underflow]]''
Utilizador anónimo