A ESCOLHA DO PATTERN IMPORTA ?


        ----------------------------------------------------------------
        offensive think :: artigo técnico em formato zine / nfo
        ----------------------------------------------------------------

        titulo : A escolha do pattern importa ?
        autor  : offensive think
        data   : Sat, Feb 6, 2021
        tags   : binary-exploitation, bof

        ----------------------------------------------------------------
        --> https://www.offensivethink.com/posts/choosing-correct-patterns.html <--

_Neste artigo trataremos de um Buffer Overflow do tipo Vanilla em um binário compilado sem nenhuma proteção, com o ASLR do sistema operacional desabilitado. O ambiente é um Kali Linux e usaremos o gdb com o plugin gef para debug._
No geral, em um processo de exploração de Buffer Overflow,  o primeiro passo é fazer _fuzzing_ na aplicação para encontrar entradas vulneráveis a técnica. Testa-se exaustivamente todas as possíveis entradas com combinações de caracteres dos mais variados tipos e tamanhos, até achar uma combinação que leve a falha.
Após a confirmação de que a aplicação está vulnerável elabora-se um POC (proof-of-concept), enviando a string, que levou ao erro no programa, seguida de uma sequência de caracteres que permita o atacante confirmar, através de um debugger, que consegue manipular o registrador EIP e consequentemente controlar o fluxo de execução do programa.

    $ python -c "print'A'*100+'BBBB'" 

Bem, este é o caso de uma aplicação onde podemos enxergar a importância na escolha do Pattern para atingir nossos objetivos.
A escolha da string, no processo inicial de exploração, já é importante uma vez que o erro pode ser causado não apenas pelo tamanho da string mas pelo tamanho e/ou formação da string. Já ouvi falar por ai que o _fuzzing_ por si só é uma ciência.  Observe, o que seria um exemplo de _fuzzing_ manual com duas strings de tamanhos iguais mas usando caracteres diferentes:

    $ python -c "print 'A'*100" | ./jdark-overflowme-if-you-can
    Enter your name: Filtered Name: AAAAAAAAAAAA
     
    [1]    17427 done                python -c "print 'A'*100" | 
           17428 segmentation fault  ./jdark-overflowme-if-you-can


    $ python -c "print 'a'*100" | ./jdark-overflowme-if-you-can
    Enter your name: Filtered Name: aaaaaaaaaaaa
     
    $

BoF - same size , different chars, different results.
https://asciinema.org/a/389638
Observe, do exemplo acima, que a mesma quantidade de caracteres podem ou não levar a um buffer overflow, dependendo da combinação de caracteres escolhidos.
O próximo passo então, diante de um segmentation fault, seria, utilizando um debuger, validar que conseguiremos, por exemplo, controlar o valor que irá ser atribuído ao registrador EIP. Vamos confirmar para o caso onde conseguimos gerar o segmentation fault:
Executando o POC dentro do debugger para validar a sobrescrita do EIP
Observe que conseguimos escrever em várias regiões de memória e alguns registradores, mas apenas parcialmente no EIP que ficou apontando para o endereço 0x414141F6,transformando em caracteres ASCII, "AAAö".
Para o caso deste binário, este comportamento se repetirá inserindo 100,200,300,400,1000 e até mais caracteres, sempre com o endereço terminando em 0xF6.
BoF - Always Same EIP
https://asciinema.org/a/389644
Podemos então observar que poderemos apenas controlar 3 bytes do endereço que será colocado no EIP, montando a nossa exploração para tentar abusar destes fato.
Vamos avançar e observar que a correta escolha do conjunto de caracteres pode nos levar não somente a controlar todo o endereço do EIP mas também influenciará na descoberta do offset que nos levará ao controle deste registrador.
_Os padrões aqui escolhidos não foram ao acaso. Existe um motivo que tentarei explicar em um segundo artigo mostrando o porque de tudo isso estar ocorrendo!_
Como primeiro exemplo, admitindo que conseguiremos explorar direcionando o fluxo do programa para uma região de memória que termina em 0xF6, vamos então achar o offset que nos levaria a sobrescrita do endereço parcial do EIP, usando o comando msf-pattern_create do Metasploit Framework e em seguida enviando o pattern na entrada do nosso binário.
Mesma quantidade (100) de caracteres enviados, resultado normal!
Observe que enviamos a mesma quantidade de caracteres de antes (100) só que não conseguimos gerar o erro! Já começamos a ver que a escolha do pattern terá influência em como conseguiremos explorar o binário.
Vamos tentar usando o pattern da pwntools!
Mesmo resultado do anterior!
Mais uma vez, não conseguimos gerar a falha!
Vamos então criar patterns personalizados, usando o pwntools,  e ver o que acontece.
_Obs.: É possível usar o msf-pattern_create também para criar patterns personalizados._
Usando um pattern personalizado para tentar controlar o EIP!
Com a opção -a , do comando pwn cyclic, conseguimos personalizar qual a sequência de caracteres que desejamos que exista em nosso pattern.
Observe que conseguimos forçar a falha e o EIP, desta vez, aponta para um suposto endereço que na verdade é um pedaço de nosso pattern, diferentemente do que aconteceu anteriormente, onde só conseguimos controlar uma parte do endereço. Ou seja, com esse padrão conseguimos não só forçar a falha mas também controlar completamente o EIP.
Vamos confirmar se o pattern funcionou, procurando o offset, e tentando forçar o EIP a receber o valor 0x42424242 ("BBBB").
Observe que dos comandos acima, conseguimos definir qual o offset. Para fins de facilitar, geramos o mesmo padrão anterior só que com o tamanho exato que precisamos (32 bytes neste caso). Em seguida enviamos nosso POC  composto agora do padrão do tamanho exato e a sequência de BBBB's.  Desta forma, conseguimos validar que conseguimos controlar o EIP.
Veja o que acontece com simplesmente adicionando um caracter na sequência que gerará o novo padrão, neste caso, mudando de 0123 para 01234.
Usando um novo pattern e obtendo um novo offset!
continuando com a exploração usando o novo offset.
Observe, das imagens acima, que a mudança do pattern também nos permitiu gerar a interrupção que desejamos, mas observe que o offset mudou de 32 para 29. Mesmo assim, conseguimos controlar o EIP uma vez que ele aponta para o nosso BBBB.
Agora é continuar a explorar.
Com isso, conseguimos observar que até mesmo a escolha do nosso pattern pode influenciar em vários detalhes na nossa exploração que pode nos levar ao êxito ou a falha!
Já era esperado que mudanças de caracteres enviados para a entrada do programa gerassem falhas ou não, uma vez que esse é o processo de fuzzing. Mas é interessante notar que mesmo depois de atingida a falha, a escolha do pattern pode dificultar achar o offset (caso 1 onde usamos o  msf-pattern_create e o programa não gerou um erro sequer!) e talvez o offset seja apenas um dos possíveis.
Qual o motivo da mudança do offset ?
Bem , de acordo como o programa é estruturado, podem existir várias funções aninhadas ou não e consequentemente vários retornos (instrução ret) que podem ser sobrescritos em pontos diferentes da execução.
Em uma próxima mostro o código do binário e a importância de aprofundar os conhecimentos em assembly, engenharia reversa, dentre outros assuntos correlatos. Estes conhecimentos podem se tornar cruciais para a exploração ou não de uma falha.
Até a próxima!

---[ EOF ]--------------------------------------------------------------

                    offensive think / 2026