Format Strings
Este
post aborda a vulnerabilidade conhecida por “Format Strings”, que pode
ser utilizada na exploração de binários em sistemas Unix.
O objetivo não é ensinar ao
leitor como explorar a vulnerabilidade, mas sim alertar que implementações
desenvolvidas de maneira incorreta podem se tornar um crítico vetor de
exploração, comprometendo drasticamente a segurança da aplicação.
O conteúdo aqui apresentado
não é “entry level” e, embora a postagem tenha sido elaborada buscando a
utilização de uma linguagem facilitada, o assunto depende da assimilação prévia
de conceitos implícitos ao tema.
O que é String?
É a identificação de uma
sequência de caracteres, geralmente usada para representações de texto. Em linguagem C, não existe um tipo de dado
que declare nativamente uma string, na verdade, trata-se de um vetor do
tipo “char”, obrigatoriamente terminado em um caractere nulo “\0”, que
identifica o final da string.
char nome[80];
A declaração da string
acima cria um vetor de caracteres com 81 posições, considerando a necessidade
da indicação do final da string ser referenciada por um caractere nulo.
Dessa maneira, o armazenamento do nome deve ocupar as 80 posições e ser
finalizado por “\0” na posição 81.
Entendendo a Vulnerabilidade
Como
em grande parte das vulnerabilidades, a exploração de Format Strings é
diretamente relacionada a um erro de implementação, onde o programador não
trata corretamente o “tipo” de saída da string. Por meio desta técnica é
possível ganhar controle do fluxo de execução de um programa vulnerável.
Essa vulnerabilidade pode ser
encontrada em funções da família printf (printf, vsprintf, fprintf, vsnpr,
sprintf, vfprintf, snprintf, vprintf) e
explorada por meio do abuso dos formatadores (%d, %s, %u, %n):
Para exemplificar de maneira
prática vamos ao seguinte trecho de código:
//dados de
entrada
AAAA.%x.%x.%x.%x
// recebendo
valor para variável texto
strcpy(texto, argv[1]);
// método de
exibição utilizado de maneira correta
printf("Saída: ");
printf("%s", texto);
Saída: AAAA.%x.%x.%x.%x
// método de
exibição utilizado de maneira incorreta
printf("Saída: ");
printf(texto);
Saída: AAAA.bff6e518.0.4e91f3.41414141
Perceba que, quando a
implementação é feita de maneira incorreta (vulnerável), são retornados
endereços de memória em formato hexadecimal. Por meio da técnica de exploração de Format Strings, essa saída,
devidamente manipulada, pode ser utilizada por um atacante para ganhar controle
do fluxo de execução do programa.
Os argumentos passados para a
função são inseridos na pilha em ordem reversa, no modo e operação LIFO (last
in first out), logo, o trecho de código abaixo justifica essa afirmação.
printf("A eh %d e esta em %08x. B eh
%x.\n", A, &A, B);
A eh 5 e esta em 71dba9d8. B eh 7.
Endereço da string formatada
|
Valor de A
|
Endereço de A
|
Valor de B
|
Fundo da Pilha
|
* Representação da pilha de memória
Ainda analisando a execução,
se o último parâmetro for removido, a saída apresentada deve ser algo do tipo:
printf("A
eh %d e esta em %08x. B eh %x.\n", A, &A);
A eh 5 e esta em
bfe541d8. B e 4771d0.
Nota-se que o valor 0x4771d0, foi despejado pois não havia
um valor armazenado na pilha, logo, a resposta apresentou os dados de onde o
último argumento seria armazenado.
Leitura e Escrita com Format Strings
Por
meio da manipulação dos parâmetros de formatação, essa vulnerabilidade permite
que seja possível realizar procedimentos de leitura e gravação de endereços de
memória arbitrários.
Utilizando o formatador %s é
possível efetuar a leitura de endereços, bem como, utilizando a mesma técnica
por meio do formatador %n é possível a gravação.
Por exemplo:
Leitura: Para ler a
variável de ambiente “LOGNAME” (lembrando que é necessário adicionar 4 bytes
devido a diferença de 2 bytes entre os nomes “poc_fms1” e “getenvaddr”):
root@kali:~ #
env|grep LOGNAME
LOGNAME=root
root@kali:~#
./getenvaddr LOGNAME ./poc_fms1
LOGNAME will be
at 0xbffffeac
root@kali:~# ./poc_fms1
$(printf "\xAC\xfE\xff\xbf")%08x.%08x.%08x.%s
O metodo
correto:
%08x.%08x.%08x.%s
O metodo
incorreto:
bfffeea0.b7fd0110.080491dd.root
[*] valor_teste
-> 0x0804c02c = -72 0xffffffb8
Escrita: para exemplificar
o processo de escrita, aos mesmos moldes das operações realizadas para a
leitura, porém formatando a saída com %n, foi possível sobrescrever a variável valor_teste.
root@kali:~ #
./poc_fms1 $(printf "\x2c\xc0\x04\x08")%08x.%08x.%08x.%n
O metodo
correto:
,%08x.%08x.%08x.%n
O metodo
incorreto:
,bfffeea0.b7fd0110.080491dd.
[*] valor_teste
-> 0x0804c02c = 31 0x0000001f
Obs.: é possível realizar a manipulação também
alterando o tamanho do campo a ser apresentado
root@kali:~ #
./poc_fms1 $(printf "\x2c\xc0\x04\x08")%x.%x.%120x.%n
O metodo
correto:
,%x.%x.%120x.%n
O metodo
incorreto:
,bfffeeb0.b7fd0110.
80491dd.
[*] valor_teste
-> 0x0804c02c = 143 0x0000008f
*perceba a criação de linhas em branco
O formatador %n grava os dados
e não apresenta nenhuma saída. Ao encontrar o formatador %n, a função grava o
número de bytes que foram escritos até então no endereço do argumento da
função correspondente e o operador do endereço unitário é usado para gravar os
dados das variáveis.
Operações de Acesso Direto
Para “reduzir” esforços
durante o desenvolvimento dos exploits que utilizam “Format String”,
é possível que os parâmetros sejam acessados diretamente utilizando o sinal de
“$” %n$d, conforme exemplificado abaixo:
printf("Quinto
elemento: %5$d, Quarto elemento: %4$05d\n", 1, 2, 3, 4, 5, 6, 7, 8);
Quinto
elemento: 5, Quarto elemento: 00004
Escritas Curtas
Um dado do tipo “short”,
é uma palavra (WORD) de dois bytes e, recorrendo ao formatador %hn, esta
técnica pode ser utilizada para também facilitar a exploração. A execução
consiste na utilização dos formatadores para escrever um valor de quatro bytes
com o uso de apenas dois parâmetros (%hn).
Conclusão
Para
que seja possível efetivar a exploração por meio da técnica de Format
Strings, permitindo a possibilidade e interação do atacante com o alvo por
meio de um interpretador de comandos, o uso de alguns outros recursos se faz
necessário, como por exemplo a utilização de construtores e destrutores
(.ctors, .dtors), ou mesmo sobrescrevendo a “Global Offset Table”. Detalhes foram suprimidos intencionalmente,
considerando que este post não tem a finalidade de instruir o processo de
exploração.
Referências
Book: Hacking, The Art Of
Exploitation
Book: The Shellcoders Handbook
Conteúdo top. Parabéns!
ResponderExcluirEu estou gostando da informação,
ResponderExcluir