Exploity w praktyce
Plan prelekcji Powtórka assembly x86 32. Pamięć uruchamianych programów. Prosty stack overflow exploit. Tworzenie shellcode i jego uruchomienie. Wstrzykiwanie shellcode wykorzystując stack overflow. Inne błędy umożliwaiające exploitowanie. Jak sytuacja wygląda dzisiaj. Analiza oprogramowania zamkniętego oraz otwartego. Techniki i narzędzia.
Cel wykładu Przedstawienie działania exploitów u podstaw na przykładzie stack overflow. Nie będziemy wykorzystywać gotowych rozwiązań aby wyexploitować istniejące aplikacje (tu odsyłam do http://www.metasploit.com/ oraz http://www.exploit db.com/)
Po co? Zabezpieczyć się przed zagrożeniem możemy tylko poprzez wcześniejsze poznanie zagrożenia. Ponadto jest to świetny sposób na zrozumienie niskopoziomowego działania programów.
Założenia oraz informacje wstępne Architektura Intel x86 32 bit Kali Linux 32 Bit (następca BackTrack) Assembly oraz C Kompilator tcc (brak stack canaries w kompilowanych programach w przeciwieństwie do gcc) execstack s
Powtórka Assembly x86 32
Assembly Language Język asemblera jest bezpośrednio zależny od architektury procesora. Polecenia są bezpośrednio tłumaczone do języka maszynowego. Umożliwia bezpośredni dostęp do rejestrów procesora Przykład:
Rejestry procesora EAX Accumulator (akumulator jego pamięć wykorzystuje arytmometr; używa się go do przechowywania wyników wielu operacji) EBX Base Register (rejestr bazowy służy do adresowania) ECX Counter Register (rejestr licznikowy służy jako licznik w pętli) EDX Data Register (rejestr danych umożliwia przekaz/odbiór danych z portów wejścia/wyjścia) ESP Stack Pointer (przechowuje wskaźnik wierzchołka stosu) EBP Base Pointer (rejestr bazowy służy do adresowania) ESI Source Index (rejestr źródłowy trzyma źródło łańcucha danych) EDI Destination Index (rejestr przeznaczenia przetrzymuje informacje o miejscu docelowym łańcucha danych) Source: http://pl.wikipedia.org/wiki/asembler_x86
Podstawowe instrukcje mov eax 4 int 0x80 xor ebx, ebx push 0x67ff3f2a cmd db '/bin/sh/',0x0
Wywołania systemowe Wywołania systemowe? open, read, write, exit, fork, execve,. Lista numerów wywołań systemowych: /usr/include/i386 linux gnu/asm/unistd_32.h eax numer wywołania systemowego ebx, ecx, edx parametry wywołania systemowego int 0x80 wywołanie przerwania informującego jądro o chęci wykonania wywowłania systemowego
Pamięć uruchamianych programów
Kernel/user mode Kernel mode pełny dostęp do pamięci oraz sprzętu User mode dostęp tylko do pamięci własnej programu (brak dostępu do pamięci innych programów, dostęp do sprzętu pośrednio poprzez wywołania systemowe udostępnione przez jądro)
Pamięć uruchamianego programu Code Segment (.text) kod programu Data Segment (.data) Data zmienne globalne i statyczne zainicjalizowane BSS zmienne globalne i statyczne niezainicjalizowane Heap malloc() Stack zmienne lokalne oraz kontrola wykonywania programu
Pamięć uruchamianego programu
Co się dzieje gdy wywołujemy funkcję?
Prosty stack overflow exploit
A jeśli zrobimy to samo używając gcc z flagą fstack protector all?
Tworzenie shellcode i jego uruchomienie
Prosty program w Assembly
Shellcode ma kilka wymagań Należy pozbyć się wszystkich bajtów 0x00 oraz 0x0a (new line). NULL byte oraz New Line byte kończą kopiowanie danych w funkcjach operujących na stringach. Nie możemy korzystać z sekcji.data mamy dostęp tylko do stosu przechowującego zmienne lokalne. Instrukcje x86 są zmiennej długości, zgadywanie, w którym miejscu w pamięci powinna zacząć się kolejna instrukcja nie jest dobrym rozwiązaniem. Lepsze rozwiązanie? Instrukcja NOP (0x90)
Spełnione wymagania shellcode:
Kilka przydatnych poleceń nasm f elf shellcode.asm objdump d disassembler options=intel shellcode.o ld o shellcode shellcode.o for i in $(objdump d shellcode.o M intel grep "^ " cut f2); do echo n '\x'$i; done; echo perl e 'print ''\x90''x10' >payload xxd g 1 payload xxd i payload
Jak uruchomić shellcode bezpośrednio z kodu w C?
Wstrzykiwanie shellcode wykorzystując stack overflow
Przygotowanie payload'u NOP NOP NOP SHELLCODE XXXX RETURN_ADDR
Inne błędy umożliwiające exploitowanie. Jak sytuacja wygląda dzisiaj?
Jeszcze raz: stack overflow? Dzisiaj już prawie niespotykany. łatwy do wykrycia pozytywne nawyki programistyczne np. strncpy() zamiast strcpy() zabezpieczenia wprowadzone na poziomie bibliotek: np. strcpy_s() jako bezpieczniejsza wersja strcpy() zabezpieczenia wprowadzone na poziomie kompilatora Nonexecutable stack
Heap overflow Zupełnie inne podejście (heap overflow jest zależny od implementacji alokatora pamięci (malloc), podczas gdy stack overflow zależy od implemetacji stosu powiązanego ściśle z architekturą systemu). Exploitacja polega na nadpisywaniu struktur wewnętrznych alokatora pamięci. Znacznie trudniejszy do wyexploitowania.
Format strings
Format strings Formatki stringów mogą być wykorzystane do nadpisania dowolnych 4 bajtów (np. adres powrotu?) Niestety (?) te podatności wyginęły poprzez wprowadzenie zabezpieczeń w bibliotekach.
Interger overflow Jaki jest tu problem? Co się stanie jeśli pierwszym argumentem będzie 4 294 967 295 (największa wartość uint32)?
Signedness Jaki jest tu problem? Co się stanie jeśli pierwszym argumentem będzie 1?
Podsumowanie integer overflow oraz signedness Integer overflow oraz konwersje signed/unsinged są jednymi z częstszych błędów jednak są trudne do wykrycia i wyexploitowania.
Analiza oprogramowania z zamkniętym oraz otwartym kodem źródłowym techniki i narzędzia
Jeśli jest możliwość wywołania crash'a, jest potencjalne miejsce do wyexploitowania. Zaprezentowane wcześniej podatności zostały specjalnie przygotowane. W realnym świecie takie błędy są bardzo subtelne i trudne do dostrzeżenia.
Zamknięty kod źródłowy Stack fuzzing (wprowadzanie niepoprawnych / nieoczekiwanych / losowych danych w celu wywołania crash'a) np. sharefuzz Analizowanie crash'ów Reverse engineering Binary diffing
Otwarty kod źródłowy Oprócz technik stosowanych przy zamkniętym kodzie źródłowym, wykonywana jest analiza kodu. np. splint analizuje kod C, jednakże automatyczne skanery zazwyczaj znajdują tylko oczywiste błędy ręczna analiza kodu żmudne i monotonne Paradoksalnie łatwiej jest znaleźć błędy w zamkniętym oprogramowaniu ponieważ kod open source jest zazwyczaj przeglądany przez wielu ludzi, tym samym błędy są szybko poprawiane.
Common Vulnerabilities and Exposures (CVE) Listy znanych publicznie podatności w oprogramowaniu: http://nvd.nist.gov/ http://www.osvdb.org/ http://cve.mitre.org/
Co dalej? Prezentacja będzie dostępna pod adresem: http://topu.pl Zapraszam na warsztaty po świętach. (Każdy napisze exploita!) Polecam: http://opensecuritytraining.info/