Format String 3

Descrição do Desafio:

Autor: SkrubLawd Plataforma: PicoCTF Categoria: Binary Exploitation Dificuldade: Médio Data: 2024 Descrição:

This program doesn't contain a win function. How can you win?

Passo a Passo da Solução

1. Análise do arquivo fornecido

Este desafio, assim como os anteriores, fornece o código-fonte junto com os arquivos executáveis.

vuln.c
#include <stdio.h>

#define MAX_STRINGS 32

char *normal_string = "/bin/sh";

void setup() {
	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);
	setvbuf(stderr, NULL, _IONBF, 0);
}

void hello() {
	puts("Howdy gamers!");
	printf("Okay I'll be nice. Here's the address of setvbuf in libc: %p\n", &setvbuf);
}

int main() {
	char *all_strings[MAX_STRINGS] = {NULL};
	char buf[1024] = {'\0'};

	setup();
	hello();	

	fgets(buf, 1024, stdin);	
	printf(buf);

	puts(normal_string);

	return 0;
}

Por ser um código simples, não há muito o que analisar.

Temos uma variável global contendo o valor "/bin/sh", que nos daria acesso ao terminal caso fosse passado como parâmetro para a função system().

Além disso, há uma função (hello()) que vaza um endereço da libc, e a main() contém uma vulnerabilidade de format string na chamada printf(buf), seguida por uma chamada à puts() com normal_string como argumento.

2. Exploit

Com essas informações, podemos suspeitar que o exploit envolverá reescrever a entrada da puts() na tabela .got, alterando seu valor para o endereço da função system(). Dessa forma, quando a main() chamar puts(normal_string), na realidade estará chamando system(normal_string), abrindo um shell.

Nota: Resumidamente, a tabela .got contém os endereços resolvidos das funções da libc. Como a libc é carregada separadamente, suas proteções podem diferir das do binário principal.

Antes de construir o exploit, é necessário verificar as proteções dos binários:

O executável não contém PIE, então seus endereços são sempre os mesmos, eliminando a necessidade de um vazamento para descobri-los. Além disso, ele possui Partial RELRO, o que significa que a tabela .got ainda pode ser reescrita através da vulnerabilidade de format string.

Já a libc está com PIE ativado, mas como temos um vazamento de endereço, podemos calcular a base da libc e encontrar a função system().

Agora, precisamos descobrir o offset do nosso input dentro da pilha.

Descobrimos que nosso input começa na posição 38.

3. Solução

O exploit pode ser construído, mas há algumas dificuldades técnicas:

  • Endereços da libc (devido ao PIE) costumam ter 6 bytes, resultando em valores muito grandes para printf(). A solução é dividir o endereço da system() em três partes de 2 bytes e escrevê-las em ordem crescente para evitar problemas com %n.

  • O tamanho do payload deve ser múltiplo de 8 para não corromper a pilha.

  • Devemos garantir que os ponteiros corretos sejam usados com %hn para escrita de 2 bytes.

Nota: %n escreve no ponteiro a quantidade de bytes impressos até o momento. Se um valor muito grande for escrito primeiro, valores menores se tornam impossíveis de escrever.

Com isso em mente, criamos o script:

Flag

picoCTF{G07_G07?_92325514}

Autor da WriteUp

Membro de Exploitation - HenriUz

Atualizado