Buffer Overflow ou Stack Overrun ou Stack Smashing
Buffer Overflow ou Stack Overrun ou Stack Smashing
Overflow: Rotina Vulnerável • Exemplo de rotina vulnerável para executar um Shell. Code exemplo 3. c void read_username (void) { char username[128]; char readbuf[256]; fgets(readbuf, 256, stdin); strcpy(username, readbuf); }
Overflow: Correção 1 • Correção do número de caracteres lidos exemplo 3. c void read_username (void) { char username[128]; char readbuf[256]; fgets(readbuf, 128, stdin); strcpy(username, readbuf); }
Overflow: Correção 2 • Uso da rotina strncopy exemplo 3. c void read_username (void) { char username[128]; char readbuf[256]; fgets(readbuf, 128, stdin); strncpy(username, readbuf, 128); }
Overflow: Correção 3 • Inversão da ordem de declaração das variáveis exemplo 3. c void read_username (void) { char readbuf[256]; char username[128]; fgets(readbuf, 128, stdin); strncpy(username, readbuf, 128); }
Overflow: Correção 4 • Uso de um valor “canário” exemplo 3. c void read_username (void) { int canary = 17; char readbuf[256]; char username[128]; fgets(readbuf, 128, stdin); strncpy(username, readbuf, 128); if (canary !=17) {exit(1)} }
Overflow: Tamanho do String • Endereço de retorno na pilha deve receber um endereço (metade final do buffer) readbuf N N N N N O O RET O O RET O RET RET RET Shell. Code P P P P P strcpy() username 0 x 00 . . . username[128] 128 bytes RET . . .
Overflow: Tamanho do String • Envio de string muito curto - endereço de retorno não é alterado readbuf N N N N N O O RET O RET RET Shell. Code P P P P P strcpy() username 0 x 00 . . . username[128] 128 bytes RET . . .
Overflow: Tamanho do String • String muito longo - shellcode (ou nop's) substituem o endereço de retorno readbuf N N N N N N O O RET O O RET O O RET RET RET RET Shell. Code P P P P P P strcpy() username 0 x 00 . . . username[128] 128 bytes RET . . .
Red Hat / Fedora e a pilha • Normalmente atacante usa o valor do Stack Pointer da própria máquina como referência – Os valores iniciais do SP tendem a ser próximos entre máquinas semelhantes (arquitetura e sistema operacional) servidor atacante ESP offset antes da execução outras variáveis username[128] outras variáveis ret. . . 0 x 00 ESP
Randomização da pilha • Para cada processo criado, o Stack Pointer varia randomicamente /proc/sys/kernel/exec-shield-randomize Área vazia de tamanho variável e aleatório servidor atacante ESP Offset variável antes da execução ESP outras variáveis username[128] outras variáveis ESP ret. . . 0 x 00
Evitando Detectores de Intrusão • Camuflando a sequência de NOPs (0 x 90) N N N N N O O RET O O RET O RET RET RET Shell. Code P P P P P – Utilizar outras instruções de 1 byte • • inc eax inc ebx inc ecx inc edx dec eax dec ebx dec ecx dec edx 0 x 40 0 x 43 0 x 41 0 x 42 0 x 48 0 x 4 B 0 x 49 0 x 4 A @ C A B H K I J – Registradores são alterados; mas shellcode recarrega. .
Evitando o Ataque • Arquitetura: Segmento de Pilha não executável (NX) • Compilador: inserção de valor randômico na pilha (canário) e verificação antes do retorno (stack guard) • Sistema Operacional: alocação de espaço randômico na pilha (Red Hat: Exec Shield) • Programador: uso de bibliotecas mais seguras (ex. : strncpy) e “boas práticas”
Overflow: Rotina Vulnerável • gcc: flag fstack-protector Rotina teste do exemplo 1. c fno-stack-protector <teste+0>: push %ebp <teste+1>: mov %esp, %ebp <teste+3>: sub $0 x 10, %esp <teste+6>: lea <teste+9>: mov <teste+12>: leave <teste+13>: ret 0 xfffffff 2(%ebp), %eax, 0 xfffffffc(%ebp) fstack-protector <teste+0>: push %ebp <teste+1>: mov %esp, %ebp <teste+3>: sub $0 x 28, %esp <teste+6>: mov %gs: 0 x 14, %eax <teste+12>: mov %eax, 0 xfffffffc(%ebp) <teste+15>: xor %eax, %eax <teste+17>: lea 0 xfffffff 2(%ebp), %eax <teste+20>: mov %eax, 0 xffffffec(%ebp) <teste+23>: mov 0 xfffffffc(%ebp), %eax <teste+26>: xor %gs: 0 x 14, %eax <teste+33>: je <teste+40> <teste+35>: call <__stack_chk_fail@plt> <teste+40>: leave <teste+41>: ret
Outros Overflows
Heap Overflow • Explora implementações específicas para malloc() e free() • Permite a escrita de dados quaisquer para endereços quaisquer • Documentado na Phrack 57 • Cada compilador tem algoritmos distintos para gerencia do heap • Sistemas operacionais fornecem algoritmos de gerência para os aplicativos utilizarem
Heap Overflow: Win 2 k • Depois da alocação de dois blocos de 32 bytes a memória fica com a seguinte organização +0 +32 +64 Block A control data Memory Block A Block B control data Memory Block B Uninteresting memory
Heap Overflow: Win 2 k • Fazendo um overflow no primeiro bloco, pode-se sobre-escrever o segundo bloco de controle +0 +32 +64 Block A control data Memory Block A Block B control data Memory Block B Uninteresting memory
Heap Overflow: Win 2 k • Quando o segundo bloco for liberado, o atacante forneceu o seu bloco de controle +0 +32 Block A control data Memory Block A Block B control data Memory Block B • Alterando convenientemente os campos do bloco de controle B, dados para atualização da memória serão lidos do bloco de memória A
Overflow de pilha por-um • Off-by-one: escreve um byte além do tamanho do buffer • Motivação: strncopy limita o tamanho da cópia, mas não garante a terminação do string • Programador "desleixado" pensa que buffer vai de 1 a n (quando na realidade vai de 0 a n-1) • Colocar um zero em buffer[size_buffer] coloca um zero além do buffer!
Overflow por-um Buffer totalmente preenchido ebp byte inferior de saved_EBP é zerado (arquitetura little endian) saved_EBP saved_EIP Retorno da rotina mov pop ret esp, ebp
Overflow por-um esp saved_EBP byte inferior de EBP é zerado ebp saved_EBP saved_EIP Retorno da rotina mov pop ret esp, ebp
Overflow por-um O valor em EBP (o ponteiro base do stack frame é agora o valor modificado ! esp saved_EIP ip saved_EIP (retorno normal) Retorno da rotina mov pop ret esp, ebp
Overflow por-um Retorno da próxima função: mov esp, ebp ESP desliza na memória (seu byte inferior foi zerado) para a área de dados. Pode-se então fornecer um novo endereço de retorno (saved_EIP) Dados (fornecidos pelo usuário) . . mas aponta para cá. . . saved_EBP ESP deveria apontar para cá. . . saved_EIP
Bugs em strings de formatação • Tipo de bug surgido em 2000 • Afeta a função printf() • Facílimo de detectar (análise do código fonte) • Permite leitura e escrita de posições quaisquer de memória • Não sobre-escreve registradores da CPU • Praticamente "extinto"
Bugs em strings de formatação • Função printf("Mary has %d cats", cats); • Printf mescla dois "fluxos" – String de formatação – Lista de variáveis (parâmetros) - que estão na pilha! – Nada garante que os dois fluxos estão sincronizados • Exemplos: printf("Mary has %d cats and %d dogs"); printf("%08 x"); Imprimem dados da pilha (onde os parâmetros deveriam estar…)
Bugs em strings de formatação • E para escrever valores? • Uso de %n : fornece o número de caracteres impressos até agora na variável especificada como parâmetro • Exemplo: ver artigo de Tim Newsham em http: //www. securityfocus. com/archive/1/81565 e artigo de David Lichfield em http: //www. nextgenss. com/papers/win 32 format. doc
Bugs em strings de formatação • Falha básica: int main (int argc, char *argv[]) { printf(argv[1]); exit(0); } • Usuário controla a impressão! • Melhor seria prinft("%s", argv[1]);
Bugs em strings de formatação Estrutura da pilha durante uma chamada normal para printf() : printf(“%lx---%s----%d”, v 1, puf, var 2); Dados locais Endereço de retorno Ponteiro para string de formato
Bugs em strings de formatação Estrutura da pilha durante uma chamada maliciosa de printf() : printf(stuff); // Stuff é ajustado para conter // “%. 200 lx%n%. 40 lx%n“ Dados maliciosos fornecidos pelo atacante Endereço de retorno Ponteiro para string de formato
- Slides: 30