Local Target

Descrição do Desafio:

Autor: OverTheWire.org / LT 'syreal' Jones Plataforma: PicoCTF Categoria: Binary Exploitation Dificuldade: Média Descrição:

Smash the Stack Can you overflow the buffer and modify the other local variable?

Passo a passo da solução

1. Análise do arquivo fonte fornecido

Este desafio já fornece o arquivo fonte, .c, tornando as coisas relativamente mais fáceis.

vuln.c
#include <stdio.h>
#include <stdlib.h>



int main(){
  FILE *fptr;
  char c;

  char input[16];
  int num = 64;
  
  printf("Enter a string: ");
  fflush(stdout);
  gets(input);
  printf("\n");
  
  printf("num is %d\n", num);
  fflush(stdout);
  
  if( num == 65 ){
    printf("You win!\n");
    fflush(stdout);
    // Open file
    fptr = fopen("flag.txt", "r");
    if (fptr == NULL)
    {
        printf("Cannot open file.\n");
        fflush(stdout);
        exit(0);
    }

    // Read contents from file
    c = fgetc(fptr);
    while (c != EOF)
    {
        printf ("%c", c);
        c = fgetc(fptr);
    }
    fflush(stdout);

    printf("\n");
    fflush(stdout);
    fclose(fptr);
    exit(0);
  }
  
  printf("Bye!\n");
  fflush(stdout);
}

Observando o código, podemos ver que ele abre a flag e imprime ela caso a variável num seja igual a 65. Podemos também notar que a variável tem seu valor atribuído logo na declaração, e não tem o valor alterado em mais nenhum local, porém outra coisa chama a atenção, logo após a declaração da variável num inicia uma sequência de código responsável por pedir ao usuário para escrever alguma coisa na variável input, que tem tamanho máximo de 15 caracteres.

E é aqui que está a vulnerabilidade, a entrada do usuário está sendo capturada pela função gets, que não contém nenhum limitador, logo o usuário pode escrever quantos caracteres ele quiser.

2. Exploit

Descobrimos que podemos facilmente estourar o buffer, agora temos que descobrir onde está a variável num para reescrevermos o valor dela. Nessa parte podemos fazer de duas formas, a primeira é imprimir a pilha e procurar nela o valor 64 (há alguns, mas com poucas tentativas você encontraria o correto), e a segunda é olhando para o assembly, isso nos mostra o local exato da variável.

A sequência acima é o início da função main, mas o que nós queremos são as instruções próximas da printf que imprime o valor do num (no caso é o último printf). Como o printf está imprimindo o valor de num por meio das strings de formato, ele deve receber o valor dela como parâmetro, e analisando as instruções antes da chamada da função, vemos duas que se destacam: mov eax,DWORD PTR [rbp-0x8] e lea rdi,[rip+0xd85] # 0x402015.

A primeira é o num e a segunda é a string inteira. Note que a instrução pega o valor do num subtraindo 8 do rbp, então nós podemos fazer a mesma coisa e descobrir a posição.

E pronto, agora é só pegar o endereço de início do input e subtrair pelo valor encontrado. Como resultado descobriremos que devemos escrever 24 caracteres para chegar no num sendo o 25º o que vai reescrever.

3. Solução

A verificação no código pede para o valor de num ser 65 em decimal, esse número representa o caractere "A" na tabela ascii, então podemos resolver diretamente pelo terminal, ou usando um script simples em python.

Flag

picoCTF{l0c4l5_1n_5c0p3_7bd3fee1}

Autor da WriteUp

Membro de Exploitation - HenriUz

Atualizado