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.
#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.
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 paraprintf(). A solução é dividir o endereço dasystem()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
%hnpara escrita de 2 bytes.
Com isso em mente, criamos o script:
Flag
picoCTF{G07_G07?_92325514}
Autor da WriteUp
Atualizado