[RoLUG] RSG: Buffer overflow paper update

Roccatello Eduard rolug@lists.linux.it
Sat, 15 Feb 2003 23:10:20 +0100


--------------Boundary-00=_8XEDTBSM91X4ORKHRG6P
Content-Type: text/plain;
  charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Questa =E8 la situazione del paper:
v Introduzione
v Definizione di Stack
v Gestione della memoria dei processi
v Analisi strutturale dello stack di un processo
IP Violazione dello stack (Buffer Overflow)=20
IP Shellcode e loro generazione
x Sfruttare la vulnerabilit=E0

Legenda:=20
v =3D testing (presente nell'allegato)
IP =3D in corso di scrittura (non presente dell'allegato)
x =3D da fare

Mi sto impegnando per farli + semplice possibile ma sono argomenti ostici=
 da=20
trattare quindi non so cosa ne ho ricavato. Provate a dare un occhio e a=20
commentare in lista in modo da poter modificare le parti difficili.
Grazie a tutti per l'attenzione :-)

PS: Ferdi che ne dici di accennare RSG sul sito web di rolug? (lo so, ti=20
faccio sempre lavorare :-)

- --=20
Roccatello Eduard
RoLUG member @ http://rovigo.linux.it
Webmaster @ http://www.pcimprover.it
Look to the headers for my GnuPG key
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (GNU/Linux)

iD8DBQE+TrrOjUY2i0dNbbARAr0tAJ4kpYRjUt0+4+EIBjXvoBI5xroEIQCfWGDt
xZwv/xb0212co+8HFKIk8Hw=3D
=3DTZZi
-----END PGP SIGNATURE-----

--------------Boundary-00=_8XEDTBSM91X4ORKHRG6P
Content-Type: text/plain;
  charset="us-ascii";
  name="bufferoverflow2"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="bufferoverflow2"

RoLUG Security Guide
Buffer Overflows

INDEX
- Introduzione
- Definizione di Stack
- Gestione della memoria dei processi
- Analisi strutturale dello stack di un processo
- Violazione dello stack (Buffer Overflow)
- Shellcode e loro generazione
- Sfruttare la vulnerabilit=E0

INTRODUZIONE
Una delle vulnerabilit=E0 pi=F9 diffuse nei programmi =E8 sicuramente il =
buffer
overflow, che detiene il primato insieme a format string bug.
Usata da tempo dagli hacker per verificare la sicurezza dei sistemi
informatici, =E8 stata illustrata pubblicamente da AlephOne nel numero 49
della famosissima rivista elettronica underground Phrack (raggiungibile
all'indirizzo http://www.phrack.org).
Consiste essenzialmente nell'esecuzione arbitraria di codice malizioso e
sfrutta il mancato controllo delle dimensioni delle zone di memoria (buff=
er)
sulle quali verranno scritte le variabili del programma.

DEFINIZIONE DI STACK
Lo stack =E8 una struttura dati astratta (ADT) molto utilizzata nelle
archittetture informatiche odierne.
Per struttura dati astratta si intende un modello di struttura in grado d=
i
immagazzinare i dati e di compiere operazioni sui dati inseriti.
Uno stack pu=F2 essere rappresentato come una pila di oggetti, vincolata =
dal
fatto che la rimozione e l'aggiunta degli elementi pu=F2 essere fatta sol=
o in
cima. Si pu=F2 parlare quindi di una struttura LIFO (Last In First Out), =
dove
l'ultimo elemento ad entrare nello stack =E8 anche il primo ad uscire.
I metodi principali definibili di uno stack sono 3: PUSH, POP e TOP (dove
per metodo si intende una funzione eseguibile dallo stack).
PUSH =E8 il metodo standard per l'aggiunta degli oggetti allo stack, POP =
serve
a leggere ed a togliere l'elemento in cima allo stack mentre TOP esegue
solamente la lettura dell'oggetto pi=F9 in alto nella pila senza levarlo =
dalla
stessa. Le prestazioni di uno stack sono ottimali; ogni operazione
effettuabile ha prestazioni asintotiche O(1), cio=E8 il numero di element=
i
contenuti non influenza minimamente le prestazioni ottenibili, che dipend=
ono
invece dal tipo di implementazione effettuato dagli sviluppatori.
La creazione di una struttura dati come lo stack deriva dalla necessit=E0=
 di
avere un'architettura pi=F9 consona possibile ai linguaggi di programmazi=
one
ad alto livello (molto pi=F9 simili al linguaggio parlato che al linguagg=
io
adottato dalla macchina). Non =E8 difficile trovare implementazioni di st=
ack
nelle moderne apparecchiature. Lo stack si presta infatti agevolmente al=20
passaggio degli argomenti di una funzione e all'archiviazione di dati
sequenziali LIFO come la sospensione dei metodi in un programma ed =E8 la
struttura utilizzata dalle archittetture i386 per la gestione delle varia=
bili
in memoria durante l'esecuzione di un programma.

GESTIONE DELLA MEMORIA DEI PROCESSI NEI SISTEMI LINUX
Per rendersi conto di come funzioni l'exploit della vulnerabilit=E0
generata dai buffer overflow bisogna aver chiaro come i processi vengano
allocati nella memoria.
Ogni processo attivo possiede tre regioni di memoria con caratteristiche=20
differenti, generalmente individuate con i nomi "Text", "Data" e "Stack".
La regione "Text" =E8 fissata dal programma e contiene istruzioni e dati
raggiungibili in modalit=E0 di sola lettura. Un eventuale accesso in scri=
ttura
a questa zona produce un errore irreversibile che termina il programma.
La regione "Data" contiene dati sia inizializzati che non e indirizza anc=
he
le variabili statiche, imagazzinate in questa regione di memoria.
La dimensione di questa regione di memoria pu=F2 essere modificata da una
chiamata di sistema particolare e nel caso venga esaurito lo spazio di
memoria allocata per il processo, questo viene reimpostato dallo schedule=
r
con una dimensione di memoria pi=F9 grande della precendente.
Questa zona di memoria viene comunemente chiamata come zona DATA-BSS di u=
n
file eseguibile.
La terza regione di memoria allocata per ogni processo =E8 lo stack dove=20
vengono memorizzate le variabili non statiche del programma ed =E8 l'ogge=
tto
della vulnerabilit=E0 trattata in questo capitolo.

=09=09[---------------] Zona di memoria con
=09=09[    T E X T    ] indirizzi bassi
=09=09[---------------]
=09=09[    D A T A    ]
=09=09[---------------]
=09=09[   S T A C K   ] Zona di memoria con
=09=09[---------------] indirizzi alti

ANALISI STRUTTURALE DELLO STACK DI UN PROCESSO
Abbiamo visto che ogni processo genera tre regioni di memoria, una delle
quali =E8 lo stack. Ma cos'=E8 contenuto esattamente nello stack?
Si tratta di una zona di memoria creata dinamicamente all'avvio del proce=
sso
e viene modificata durante dell'esecuzione del programma.
Per capire meglio com'=E8 strutturato lo stack di un processo, =E8 bene i=
ntrodurne
uno che, seppur molto semplice, permette una schematizzazione chiara dell=
a
zona di memoria "Stack":

// Start: code.c
void funzione(int i)
{=09char buffer[64];
}
void main(void)
{=09funzione(15);
}
// End: code.c

Salviamo questa piccola porzione di codice in un file (che io chiamer=F2 =
code.c)
e compiliamo con "gcc -o code code.c".
Eseguendo il programma in questione ci accorgeremo che il programma non
invia nulla allo standard output.
Proviamo ad analizzarlo in profondit=E0 con GDB per capire cosa fa questa
piccola porzione di codice:

bash-2.05a$ gdb code
GNU gdb 5.2
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you =
are
welcome to change it and/or distribute copies of it under certain conditi=
ons.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for detail=
s.
This GDB was configured as "i386-slackware-linux"...
(gdb) disassemble main
Dump of assembler code for function main:
0x80481c8 <main>:       push   %ebp
0x80481c9 <main+1>:     mov    %esp,%ebp
0x80481cb <main+3>:     sub    $0x8,%esp
0x80481ce <main+6>:     add    $0xfffffff4,%esp
0x80481d1 <main+9>:     push   $0xf
0x80481d3 <main+11>:    call   0x80481c0 <funzione>
0x80481d8 <main+16>:    add    $0x10,%esp
0x80481db <main+19>:    xor    %eax,%eax
0x80481dd <main+21>:    jmp    0x80481e0 <main+24>
0x80481df <main+23>:    nop
0x80481e0 <main+24>:    leave
0x80481e1 <main+25>:    ret
0x80481e2 <main+26>:    nop
=2E..altri NOP
0x80481ef <main+39>:    nop
End of assembler dump.

Il programma in questione, come prima cosa, salva il frame pointer nello =
stack
e imposta come nuovo frame pointer, lo stack pointer.
Nel frame pointer =E8 contenuto l'indirizzo di memoria della funzione app=
ena
eseguita dal microprocessore.
Vengono successivamente sottratti 8 byts dallo stack pointer per creare l=
o
stack frame privato della funzione. All'indirizzo 0x80481d1 (<main+9>)
inizia la chiamata alla funzione.
Le funzioni vengono chiamate dai programmi tramite l'inserimento (push) d=
egli
argomenti nello stack, in ordine inverso (dall'ultimo al primo), e tramit=
e
la funzione "call indirizzo".
In questo caso viene fatto il push di 0xf (15 in decimale) nello stack e =
poi
viene chiamata la funzione all'indirizzo 0x80481c0.
Al ritorno della funzione viene aggiunto il valore esadecimale 10 allo st=
ack
pointer facendo "avanzare" il puntatore, viene azzerato il registro
accumulatore EAX (si vedano le propriet=E0 dello XOR, detto anche OR Escl=
usivo,
per ulteriori chiarimenti sull'istruzione) e viene eseguito un salto forz=
ato
all'istruzione "leave".

Entriamo nel profondo della funzione "funzione" e vediamo come vengono
allocate le variabili di un programma, probabilmente la parte pi=F9 inter=
essante
di un programma :-)

(gdb) disassemble funzione
Dump of assembler code for function funzione:
0x80481c0 <funzione>:   push   %ebp
0x80481c1 <funzione+1>: mov    %esp,%ebp
0x80481c3 <funzione+3>: sub    $0x48,%esp
0x80481c6 <funzione+6>: leave
0x80481c7 <funzione+7>: ret
End of assembler dump.

Dopo aver eseguito l'istruzione "call", la routine "main" viene sospesa e=
 il
controllo passa alla routine "funzione".
Viene salvato il frame pointer nullo stack e viene copiato il valore dell=
o
stesso nello stack pointer.
Immediatamente dopo avviene l'allocazione delle variabili, in questo caso=
 un
array (successione) di 64 char.
I computer basati su architettura Intel considerano gli array di char com=
e
serie di caratteri terminata da un carattere nullo (0x0 in esadecimale).
Per allocare una variabile il sistema sposta lo stack pointer di tanti by=
tes
quanti occorrono al programma cio=E8 della dimensione delle variabili rif=
erita
alle dword occupate. La parola binaria pi=F9 piccola considerata nei sist=
emi
Intel =E8 di 4 bytes, per cui la dimensione delle variabili sar=E0 riferi=
ta a 4
bytes. Ad esempio se ho un array di 5 char il sistema allocher=E0 8 bytes=
,
corrispondenti a 2 dword.
Molto probabilmente avrete gi=E0 fatto i conti e come credo vi sembrer=E0=
 strano
che al posto di 0x40 troviate 0x48. Teoricamente il compilatore dovrebbe
allocare esattamente la dimensione di tutte le variabili dichiarate nella
funzione ma, generalmente, viene allocata pi=F9 memoria del necessario pe=
r
contenere eventuali overflow (fuoriuscite).

Ora che abbiamo capito come funzionano le chiamate alle funzioni possiamo
schematizzare lo stack del nostro programma in questo modo:

   [--buffer--][--stack pointer--][--instruction pointer--][--i--]
   Cima dello stack=09=09=09=09=09Fondo dello stack

Riassumendo, sul fondo dello stack troviamo gli argomenti passati alla
funzione, poi troviamo l'istruction pointer (detto anche RET) e lo stack
pointer. Sulla cima dello stack possiamo trovare la locazione fisica dell=
e
variabili elaborate dal nostro programma (in questo caso solo buffer).
--------------Boundary-00=_8XEDTBSM91X4ORKHRG6P--