background image

30

 

HAKIN9

ATAK

7-8/2008

G

adu–gadu jest komunikatorem 
internetowym, który zrzesza już prawie 
6 milionów użytkowników. Swoją 

popularność zawdzięcza prostocie działania, 
intuicyjnej obsłudze i przede wszystkim 
pochodzeniu – jest to produkt made in Poland
Żółte słoneczko jest znakiem rozpoznawalnym 
chyba przez każdego polskiego internautę. 
Nie jest to jednak aplikacja wolna od błędów 
– są one normalnym zjawiskiem, ściśle 
powiązanym z tworzeniem oprogramowania 
komputerowego. Błąd odkryty w Gadu–Gadu 
7.7 pozwala na na wykonanie dowolnego kodu 
na komputerze osoby, która korzysta z tego 
programu. Tkwi on w obsłudze emotikonów 
– plik 

emots.txt

, zawierający emotikony, 

podczas wczytywania łańcuchów znakowych 
nie sprawdza ich długości. Możliwe jest zatem 
wykonanie przepełnienia bufora (aczkolwiek nie 
w klasyczny sposób). Opisywana podatność 
dotyczy Gadu–Gadu w wersji 7.7 build 3669. Jak 
to zrobić, jak wykorzystać tę podatność – o tym 
traktuje niniejszy artykuł.

Początki Gadu – Gadu

Najpopularniejszy polski komunikator 
internetowy, Gadu–Gadu, liczy sobie już ładne 
parę lat – a dokładniej osiem. Za narodziny 
Gadu–Gadu uważa się datę 15 sierpnia 2000 
roku. Ceny połączeń internetowych w Polsce 
spadają, Internet staje się medium dostępnym 
niemal dla każdego. Ludzie zaczynają korzystać 

KONRAD ZUWAŁA

Z ARTYKUŁU 

DOWIESZ SIĘ

jak działa protokół Gadu–Gadu,

na czym polega obsługa 

wyjątków w C++,

jak napisać exploit dla aplikacji 

Gadu–Gadu.

CO POWINIENEŚ 

WIEDZIEĆ

czym jest Gadu–Gadu,

znać podstawy programowania 

w C/C++ i asemblerze,

na czym polega przepełnienie 

bufora.

z możliwości komunikacyjnych, jakie zapewnia 
im Sieć. Szybko jednak dostrzegają lukę – niszę, 
którą wykorzystał właśnie komunikator GG. Otóż 
brakuje im prostego, przejrzystego programu, 
który umożliwiałby rozmowy ze znajomymi. 
Program w dodatku powinien być bezpłatny. 
Gadu–Gadu idealnie wpasowuje się w lukę, 
komunikator lawinowo zyskuje popularność. Już 
pierwszego tygodnia, gdy ukazał się on w Sieci, 
pobrało go ponad dziesięć tysięcy osób – co 
jest świetnym wynikiem, zważywszy, że był on 
wtedy nikomu nie znanym, nowym programem. 
Gadu–Gadu musiał walczyć o popularność, 
a konkurencję miał silną. Nie brak w końcu 
produktów zagranicznych, o ugruntowanej 
już pozycji, w dodatku również dostępnych 
bezpłatnie. Co jednak zadecydowało?

Gadu Gadu oferuje nam prosty, przejrzysty 

interfejs. Pisanie i wysyłanie wiadomości nie 
wymaga od użytkownika wiele wysiłku. Często 
nie jest nawet konieczne używanie myszki 
– wystarczy przysłowiowy Enter, aby wiadomość 
trafiła do adresata. Okienko rozmowy jest 
przejrzyste, a sam interfejs programu nie budzi 
większych zastrzeżeń. No i przede wszystkim 
program jest po polsku, co wtedy było nie lada 
rarytasem – nie zapominajmy, że w 2000 roku 
Internet w Polsce dopiero raczkował, niewiele 
aplikacji było dostępnych w rodzimym języku.

Rosnąca popularność komunikatora niesie 

ze sobą pewne problemy. Serwery często 
bywają przeciążone bądź wręcz niedostępne. 

Stopień trudności

Hakowanie 

Gadu – Gadu

Gadu–Gadu to z pewnością najpopularniejszy w Polsce 

komunikator internetowy. Niemal każdy kiedyś się z nim zetknął, 

najczęściej aby porozmawiać ze znajomymi. Jednak nawet tak 

popularna aplikacja nie ustrzeże się błędów – czy możemy czuć 

się bezpieczni z Gadu–Gadu?

background image

31

 

HAKIN9 

HAKOWANIE GADU – GADU

7-8/2008

Wiąże się to z trudnościami w połączeniu 
i opóźnieniami w dostarczaniu 
wiadomości. Problemy nasilają się wraz 
ze wzrostem ilości użytkowników, który na 
stronie producenta określony jest mianem 
lawinowego. Trudności te nie stanowią 
jednak dyskomfortu dla kolejnych rzesz 
użytkowników. Ćwierć miliona, pół miliona, 
milion – liczby zdają się nie mieć końca. 
Aż dochodzimy do dnia dzisiejszego i 
mamy niespełna (a może już ponad?) 
sześć milionów wiernych użytkowników.

Możliwości komunikatora

Dzisiejsze komunikatory internetowe 
oferują swoim użytkownikom znacznie 
więcej aniżeli tylko możliwość rozmowy 
tekstowej. Choć ta w Gadu–Gadu jest 
niezwykle prosta – okienko jest schludne 
i przejrzyste. Widać, do czego służy każdy 
przycisk, nie ma większych problemów 
z nawigacją wewnątrz programu. Można 
pisać szybko i wygodnie – słowem 
dokładnie tak, jak być powinno.

Rozmowa tekstowa to jednak nie 

wszystko, na co pozwala nam ten 
program. Jeśli ktoś nie lubi pisać, można 
porozmawiać bez użycia klawiatury. 
Rozmowy głosowe, bo o nich mowa, 
są stosunkowo świeżym dodatkiem. 
Dodatkowo możemy za pomocą połączeń 
bezpośrednich przesyłać pliki zupełnie 
tak, jak za pomocą programu P2P (ang. 
peer to peer). Pliki graficzne możemy 
wygodnie umieścić w okienku programu – 
pod warunkiem, iż mają one odpowiedni 
rozmiar, gdyż zbyt duża grafika nie może 
zostać przesłana z użyciem serwera 
(rozmowy na GG prowadzone są poprzez 
serwer, który pośredniczy w konwersacji).

Ostatnią z wyraźnie rzucających się 

w oczy funkcji jest możliwość słuchania 
radia za pomocą komunikatora 
– mamy do wyboru kilka kanałów, 
każdy o innym profilu muzycznym, 
co z pewnością zadowala wielu 
wybrednych melomanów. Do dyspozycji 
mamy jeszcze portal użytkowników i 
pomoc techniczną. Jednym słowem 
– pięknie. Czy komunikator ten nie 
ma wad? Ma – chodzi oczywiście o 
bezpieczeństwo. Nie jest to oczywiście 
domena tylko tej aplikacji, albowiem 
większość programów komputerowych, 
które są dostępne na rynku, zawiera 
jakieś błędy bezpieczeństwa. Są one 

ustawicznie likwidowane przez twórców 
oprogramowania, co powoduje, że rzadko 
kiedy istnieje możliwość exploitacji danej 
podatności. Jednak użytkownicy Gadu–
Gadu często zapominają o regularnej 
aktualizacji posiadanego komunikatora 
– przyczynia się do tego również brak 
funkcji automatycznej aktualizacji, 
która bez jakiejkolwiek ingerencji ze 
strony użytkownika dokonywałaby 
systematycznego uaktualnienia 
najważniejszych komponentów programu. 
A tak – musimy to zrobić sami, 
pobierając najnowsza wersję ze strony 
producenta.

Jest to tylko jedna z nielicznych wad 

komunikatora, bo nie jest on od nich 
niestety wolny. Przede wszystkim serwery 
są często przeciążone, co powoduje 
niekiedy niemożność podłączenia się 
do sieci. Dodatkowo, w najnowszych 
wersjach usunięto możliwość rozmowy 
tekstowej z użyciem połączeń 
bezpośrednich – sprawiając, iż cały ruch 
sieciowy transferowany jest poprzez 
dedykowane do tego serwery. Może to 
budzić obawy niektórych użytkowników co 
do poufności ich konwersacji. 

Kryptografia również nie należy do 

mocnych stron aplikacji. Ustawienie opcji 
pozwalającej na zapamiętanie hasła w 
programie, tak, aby logować się do sieci 
bez jego podawania, jest dość ryzykowne. 
Hasło jest bowiem przechowywane w 
praktycznie niezaszyfrowanej postaci w 
pliku na komputerze użytkownika. Gdy 
jakaś niepowołana osoba uzyska dostęp 
do zawartości naszego dysku twardego, 
ma możliwość odszyfrowania takiego 
hasła (w Sieci dostępnych jest wiele 
programów, które służą do tego celu). 

Sama rozmowa również nie jest 

szyfrowana. Brak szyfrowania SSL, które 
jest w dzisiejszych czasach standardem 
poufności przesyłanej informacji, jest 
poważną wadą. Umożliwia to bowiem 
przechwycenie naszej rozmowy każdej 
osobie, która umie obsługiwać program 
sniffujący. A przecież narzędzi takich 
nie brakuje – warto wspomnieć choćby 
tcpdumpa, który jest chyba sztandarowym 
narzędziem badającym ruch w sieci. 
Pojawiły się nawet specjalistyczne sniffery, 
których użycie nie wymaga większej 
wiedzy. Nasłuchują one tylko na portach 
obsługiwanych przez komunikator. 

Pozwala to niemal każdemu oglądać treść 
naszych rozmów. 

Błąd, który odkryto w Gadu–Gadu, 

pozwala na wykonanie dowolnego kodu 
na komputerze ofiary. Zanim jednak 
przejdziemy do jego omawiania, warto 
zapoznać się z samym protokołem 
używanym przez Gadu–Gadu – należy 
wiedzieć, z czym mamy do czynienia. 

Protokół Gadu – Gadu

Jak każda aplikacja działająca w 
Internecie, Gadu–Gadu potrzebuje 
jakiegoś protokołu, z pomocą którego 
może przesyłać dane przez sieć. Warto 
więc wiedzieć, jak wygląda ten protokół, 
aby mieć ogólne pojęcie o działaniu 
programu. Oczywiście nie będzie to 
bardzo szczegółowy opis, jeśli jednak 
Czytelnik jest takowym zainteresowany, 
adres odpowiedniej witryny internetowej 
znajduje się w Ramce W Sieci.

Gadu–Gadu korzysta z protokołu TCP/

IP. Jest to bowiem protokół gwarantujący 
poprawność przesyłanych danych. A 
przecież niezawodność i pewność, że 

Rysunek 1. 

Interfejs programu Gadu–

Gadu

background image

ATAK

32

 

HAKIN9 7-8/2008

HAKOWANIE GADU – GADU

33

 

HAKIN9 

7-8/2008

wiadomość dotrze do odbiorcy jest 
chyba najważniejszą cechą każdego 
komunikatora.

Każdy pakiet wysyłany przez 

komunikator zawiera dwa pola, które 
zawsze znajdują się w tym samym 
miejscu – na początku pakietu. Można 
je zatem nazywać nagłówkiem, słowo to 
doskonale bowiem oddaje charakter tych 
danych. Pola te prezentuje Listing 1.

Nazwy zmiennych są chyba intuicyjne, 

toteż nie jest potrzebne ich tłumaczenie 
– typ to po prostu rodzaj pakietu, jaki 
wysyłamy, natomiast dlugość to rozmiar 
w bajtach pozostałej części pakietu (czyli 
rozmiar pakietu bez rozmiaru nagłówka). 

Ale zanim połączymy się z siecią 

Gadu–Gadu i zaczniemy rozmawiać z 
którymś z naszych znajomych, musimy 
najpierw połączyć się z serwerem. 
Serwerów jest jednak kilka, toteż musimy 
w jakiś sposób otrzymać informacje, z 
którym z nich mamy się połączyć. W tym 
celu wysyłamy żądanie HTTP do strony 
odpowiedzialnej za przydzielenie nam 
serwera. Odpowiedni kod zaprezentowany 
jest na Listingu 2. 

Takiego połączenia możemy dokonać 

za pomocą zwykłego programu, takiego 
jak 

telnet

 czy 

netcat

. Wystarczy, że 

połączymy się na 80 porcie ze stroną 

appmsg.gadu–gadu.pl

 i prześlemy 

odpowiednie żądanie – takie, jak na 
Listingu 2. Wartości zapisane wielkimi 
literami oznaczają zmienne przesyłane 
do strony – są to kolejno: nasz numer 
GG, wersja komunikatora, 

FORMAT

 

określa sposób przesyłania wiadomości 
(czy ma być to czysty tekst, czy też 

użyty zostanie HTML), 

MESSAGE

 zaś to 

numer wiadomości systemowej, jaką 
otrzymaliśmy poprzednio od Gadu–Gadu, 
gdy nawiązywaliśmy próbę połączenia. 
Wreszcie 

BROWSER

 to wersja przeglądarki, 

np. 

Mozilla 4.0 (compatible; MSIE 

5.0, Windows NT)

Strona zwróci nam adres IP serwera, 

z którym mamy się połączyć, dodając 
do tego numer wiadomości systemowej 
(tej, którą podawaliśmy w żądaniu HTTP) 
oraz port, na którym mamy dokonać 
połączenia z serwerem (zwykle jest to 
port 8074). 

Teraz pora na zalogowanie się do 

sieci GG. W tym celu serwer najpierw 
wysyła do nas pakiet, liczbę całkowitą 

int

, która jest zwana ziarnem. Ziarno 

odpowiada za wygenerowanie skrótu 
CRC hasła. Przed ziarnem otrzymujemy 
jednak – na samym początku połączenia 

– pakiet powitalny. Ilustruje to Listing 3.

Aby zalogować się do sieci, potrzebny 

jest szereg informacji. Przede wszystkim 
są to nasz numer GG oraz hasło. 
Niezbędne będą również m.in. dane 
dotyczące wersji używanego klienta. 
I znów z pomocą przychodzi nam 
odpowiednia struktura, która skupia 
w sobie wszelkie dane wymagane do 
zalogowania naszego klienta w sieci. 
Prezentuje ją Listing 5.

Struktura Logowanie jest pakietem, 

który należy wysłać do odpowiedniego 
serwera – po to, by ten dopuścił 
naszego klienta do działania w sieci 
GG. Pierwsze pole, uin, to nic innego jak 
używany przez nas numer Gadu–Gadu. 
Dwa następne pola powiązane są z 
hasłem – pierwsze, 

hash _ typ

, to wybór 

algorytmu szyfrowania hasła. Dostępne 
są dwa algorytmy: CRC i SHA1. Algorytm 

Listing 1. 

Nagłówek Gadu–Gadu

struct

 

NaglowekGadu

 

{

 

int

 

typ

;

 

int

 

dlugosc

;

}

;

Listing 2. 

Żądanie HTTP wysyłane do serwera przydzielającego adresy IP serwerów GG

GET

 /

appsvc

/

appmsg4

.

asp

?

fmnumber

=

NUMBER

&

version

=

VERSION

&

fmt

=

FORMAT

&

lastmsg

=

MESSAGE

Accept

:

 

image

/

gif

image

/

jpeg

image

/

pjpeg

, ...

Accept

Language

:

 

pl

User

Agent

:

 

BROWSER

Pragma

:

 

no

cache

Host

:

 

appmsg

.

gadu

gadu

.

pl

Listing 3. 

Inicjacja połączenia z serwerem GG

#define PAKIET_POWITALNY 0x0001

struct

 

Powitalna

 

{

 

int

 

ziarno

;

}

;

Listing 4. 

Logowanie komunikatora do sieci

#define LOGUJ 0x0019

struct

 

Logowanie

{

        

int

 

uin

;

        

char

 

hash_typ

;

        

char

 

hash

[

64

];

        

int

 

status

;

        

int

 

wersja

;

        

char

 

nieznany1

;

        

int

 

lokalny_ip

;

        

short

 

lokalny_port

;

        

int

 

zewnetrzny_ip

;

        

short

 

port_zewnetrzny

;

        

char

 

rozmiar_obrazka

;

        

char

 

nieznany2

;

        

char

 

opis

[];

        

int

 

czas

;

}

;

Rysunek 2. 

Okno rozmowy programu 

Gadu–Gadu

background image

ATAK

32

 

HAKIN9 7-8/2008

HAKOWANIE GADU – GADU

33

 

HAKIN9 

7-8/2008

CRC jest uważany za słabszy, albowiem 
przy danym ziarnie (pakiet z serwera 
będący liczbą całkowitą) możliwe jest 
wygenerowanie tego samego hasha 
dla dwóch różnych haseł. Prowadzi 
to do możliwości zalogowania się z 
czyjegoś numeru GG bez znajomości 
jego hasła. Jest to jednak raczej kwestia 
szczęścia i wielu godzin żmudnych analiz 
generowanych hashy. Listing 6. można 
potraktować jako ciekawostkę – jest na 
nim przedstawiony algorytm generowania 
hasha hasła CRC32. Następne pola, czyli 
status oraz wersja oznaczają kolejno: 

status ustawiany po połączeniu się z 
serwerem (np. zaraz wracam) i wersję 
używanego klienta GG. Pola nieznany1 
nieznany2 nie są jeszcze poznane. 
Znaczenia reszty pól chyba nie trzeba 
tłumaczyć, albowiem ich nazwy mówią 
same za siebie. 

Ostatnią rzeczą, którą przydałoby 

się wiedzieć o protokole, jest sposób, 
w jaki wysyłana jest wiadomość. W 
końcu właśnie to jest najważniejsze 
w komunikatorze internetowym, stąd 
koniecznym jest poznanie tego procesu 
od środka. W zrozumieniu organizacji 

procesu wysyłania wiadomości pomoże z 
pewnością struktura zaprezentowana na 
Listingu 7.

Interesującym jest tylko pole 

klasaWiadomosci – znaczenie 
pozostałych jest raczej oczywiste. Klasa 
wiadomości określa, czy wysyłana przez 
nas wiadomość ma ukazać się w nowym, 
czy też w istniejącym już okienku. Jest 
to sposób, w jaki komunikator rozróżnia 
sesje prowadzone w tym samym czasie 
– dzięki czemu wie, kiedy inicjowana 
jest nowa rozmowa, a kiedy informacja 
dotyczy już istniejącej konwersacji.

Z naszego punktu widzenia 

ważnym jest przesyłanie emotikonów. 
Jak zatem odbywa się to w protokole 
Gadu–Gadu? Emotikony są zwykłymi 
plikami graficznymi (z rozszerzeniem gif). 
Odpowiednie struktury odpowiedzialne za 
przesyłanie obrazka zaprezentowane są 
na Listingu 8.

Struktury te odpowiadają za 

wyznaczenie parametrów przesyłanego 
obrazka. Kolejne zmienne określają sumę 
kontrolną i rozmiar obrazka. Obrazek 
wysyłany w oknie komunikatora jest 
traktowany jak specjalny tekst, albowiem 
jest on de facto jednym z atrybutów 
tekstu.

Co się dzieje, gdy przesyłane są 

emotikony? Cóż, wszystko odbywa się 
w ten sam sposób. Różnica polega na 
tym, że nie przesyłamy obrazka, tylko jest 
on wczytywany z dysku użytkownika. W 

Listing 5. 

Funkcja generująca hash hasła

int

 

hashHasla

(

unsigned

 

char

 

*

haslo

unsigned

 

int

 

ziarno

)

 

{

        

unsigned

 

int

 

x

y

z

;

       

y

 

=

 

ziarno

;

        

for

 

(

x

 

=

 

0

;

 

*

haslo

;

 

haslo

++)

 

{

                

x

 

=

 

(

x

 

&

 

0xffffff00

)

 

|

 

*

haslo

;

                

y

 ^

=

 

x

;

                

y

 

+=

 

x

;

                

x

 

<<=

 

8

;

                

y

 ^

=

 

x

;

                

x

 

<<=

 

8

;

                

y

 –

=

 

x

;

                

x

 

<<=

 

8

;

                

y

 ^

=

 

x

;

                

z

 

=

 

y

 

&

 

0x1f

;

                

y

 

=

 

(

y

 

<<

 

z

)

 

|

 

(

y

 

>>

 

(

32

 – 

z

));

        

}

        

return

 

y

;

}

Listing 6. 

Struktura odpowiadająca za wysyłanie wiadomości

#define GG_SEND_MSG 0x000b

struct

 

wiadomosc

 

{

        

int

 

numerOdbiorcy

;

        

int

 

sekwencja

;

        

int

 

klasaWiadomosci

;

        

char

 

tresc

[];

}

;

Listing 7. 

Struktury odpowiedzialne za przesyłanie obrazków

struct

 

atrybutyTekstu

 

{

        

short

 

pozycja

;

        

char

 

czcionka

;

        

char

 

kolorCzcionki

[

3

];

        

struct

 

obrazekGG

 

image

;

}

;

struct

 

obrazekGG

 

{

        

short

 

nieznany1

;

 

        

int

 

rozmiar

;

        

int

 

sumaKontrolna

;

}

;

struct

 

odpowiedzObrazka

 

{

        

char

 

flaga

;

        

int

 

rozmiar

;

        

int

 

sumaKontrolna

;

        

char

 

nazwaPliku

[];

        

char

 

zawartoscObrazka

[];

}

;

Listing 8. 

Fragment pliku emots.txt

":)"

,

"usmiech.gif"

,

"_usmiech.gif"

":>"

,

"ostr.gif"

,

"_ostr.gif"

":]"

,

"kwadr.gif"

,

"_kwadr.gif"

":("

,

"smutny.gif"

,

"_smutny.gif"

":|"

,

"yyyy.gif"

,

"_yyyy.gif"

"<uoee>"

,

"uoeee.gif"

,

"_uoeee.gif"

"<lol>"

,

"haha.gif"

,

"_haha.gif"

"<rotfl>"

,

"ROTFL.gif"

,

"_ROTFL.gif"

";)"

,

"oczko.gif"

,

"_oczko.gif"

":P"

,

"jezyk.gif"

,

"_jezyk.gif"

";P"

,

"jezyk_oko.gif"

,

"_jezyk_oko.gif"

"<radocha>"

,

"zacieszacz.gif"

,

"_

zacieszacz.gif"

"<luzik>"

,

"luzik.gif"

,

"_luzik.gif"

"<figielek>"

,

"figielek.gif"

,

"_figielek.gif"

"<milczek>"

,

"milczek.gif"

,

"_milczek.gif"

"<stres>"

,

"stres.gif"

,

"_stres.gif"

"<nerwus>"

,

"nerwus.gif"

,

"_nerwus.gif"

":["

,

"zly.gif"

,

"_zly.gif"

"<zalamka>"

,

"zalamka.gif"

,

"_zalamka.gif"

"<wnerw>"

,

"wnerw.gif"

,

"_wnerw.gif"

"<glupek>"

,

"glupek.gif"

,

"_glupek.gif"

background image

ATAK

34

 

HAKIN9 7-8/2008

tym celu skanowany jest odpowiedni plik, 

emots.txt

, następnie w miejsce danego 

ciągu znaków podstawiany jest obrazek. 
Jednak kod odpowiedzialny za wykonanie 
tych czynności jest błędny – nie 
sprawdza on długości wczytywanych 
łańcuchów. Wykorzystamy ten fakt w 
naszym exploicie. 

Skoro znamy podstawy protokołu, 

wiemy jak wygląda logowanie do sieci i 
wysyłanie wiadomości, czas zabrać się za 
wykorzystanie tej wiedzy do zbudowania 
exploita. Przyjrzyjmy się najpierw 
błędom, jakie dotychczas pojawiały się w 
komunikatorze.

Przegląd błędów 

Gadu–Gadu, jak każdy program, nie jest 
wolne od błędów. W tej części tekstu 
przyjrzymy się niektórym z nich, aby lepiej 
przygotować się do zbudowania exploita, 
wykorzystującego najnowszy błąd odkryty 
w popularnym komunikatorze.

Pierwszym z ataków, jakie omówimy, 

będzie atak DoS na komunikator. Otóż, 
w poprzednich wersjach komunikatora 
możliwe było unieruchomienie 
programu poprzez wysłanie specjalnie 
spreparowanego obrazka. Gdy obrazek 
zaczynał się od specjalnych słów, np. 
AUX: , wywoływany był błąd jego obsługi, 
co zawieszało cały wątek odpowiedzialny 
za tę czynność. Mało tego, gdy nazwa 
pliku graficznego zaczynała się od LPT: 
, możliwe było przesłanie go do drukarki 
użytkownika komunikatora – mało 
śmieszny kawał.

Pozostaniemy ciągle przy 

obrazkach. Gdy nazwa pliku obrazka 
miała dostatecznie dużą długość, 
możliwe było wykonanie ataku DoS 
poprzez przepełnienie bufora zmiennej 
obsługującej nazwy plików graficznych. 
Jeśli długość nazwy pliku zawierała 
się w przedziale od 192 do 200 bajtów 

(jeden znak to jeden bajt), wywoływało 
to przepełnienie bufora na stosie, 
powodując błąd programu i jego 
terminację. 

Połączenia bezpośrednie również 

były przyczyną powstania podatności 
umożliwiającej atak DoS. Przesyłanie 
specjalnie spreparowanych pakietów 
mogło doprowadzić do konsumpcji 
ogromnej ilości pamięci przez program 
kliencki, co w konsekwencji mogło 
zmusić użytkownika nawet do restartu 
systemu. Brak pamięci powodował 
bowiem znaczne zwolnienie działania 
komputera, praca na nim stawała 
się więc niezwykle mało komfortowa. 
Mamy więc do czynienia z atakiem typu 
Denial of Service, którego celem jest 
unieruchomienie maszyny ofiary, bądź 
przynajmniej części oprogramowania na 
niej działającego.

Ostatnim błędem, o którym 

wspomnimy, była luka wynikająca z 
rejestracji rozszerzenia gg: w systemie. 
Gdy użytkownik wchodził na stronę 
internetową, na której znajdowały 
się odnośniki w rodzaju gg:1234567
wszystko było w porządku. Sprawa 
zaczynała się nieco komplikować, gdy po 
literach gg: znajdowało się coś zupełnie 
innego, aniżeli poprawny identyfikator. 
Mogło to doprowadzić nawet do utraty 
plików konfiguracyjnych komunikatora 

– co zapewne nie należało do 
najprzyjemniejszych doświadczeń. 

Jak widać, najczęściej błędy 

komunikatora umożliwiały atak typu 
DoS. Zabicie procesu komunikatora czy 
unieruchomienie systemu były tym, co 
mogło spotkać użytkownika Gadu–Gadu. 
Błąd, który opiszemy w dalszej części 
artykułu, pozwala na coś znacznie 
więcej: możemy wykonać dowolny kod na 
komputerze ofiary, czyli przejąć nad nim 
pełną kontrolę. Jest to więc podatność 
o wiele groźniejsza aniżeli te, które 
dotychczas odnajdowano w programie 
klienckim.

Emitokony – exploit 

dla Gadu – Gadu

Skończmy wreszcie z tą aurą tajemnicy. 
Mamy już odpowiednie przygotowanie 
teoretyczne, zajmijmy się zatem 
budowaniem właściwego exploita dla 
Gadu–Gadu. 

Błąd, który umożliwia wykonanie 

zdalnego kodu, związany jest z 
emotikonami. Przyjrzymy się zatem 
strukturze plików komunikatora (Rysunek 4).

Dla nas szczególnie interesujące 

będą katalogi zawierające emotikony. 
Czym są emotikony? Otóż, gdy 
rozmawiamy z użyciem Gadu–Gadu, 
możliwe jest używanie animowanych 
minek w okienku rozmowy. Są to właśnie 

Rysunek 4. 

Struktura pików komunikatora – plik emots.txt

Rysunek 3. 

Ryzykowne zapamiętanie 

hasła w programie

background image

HAKOWANIE GADU – GADU

35

 

HAKIN9 

7-8/2008

emotikony. Odpowiednia kombinacja 
znaków, na przykład buźki :) czy :
P
, powoduje wczytanie określonego 
pliku graficznego i wstawienie go do 
okna rozmowy. Plik ten jest animacją 
GIF, zawierającą treść adekwatną 
do wpisanego ciągu znaków, np. 
uśmiechającą się twarz. 

Dla nas istotną kwestią jest to, 

skąd komunikator umie powiązać plik z 
konkretnym ciągiem znaków. Słowem, 
skąd wie, że :) to uśmieszek, a ;( to płacz. 
W tym celu w katalogach zawierających 
animacje GIF z emotikonami 
umieszczono plik tekstowy, nazwany 
emots.txt. Zawartość pliku przedstawiona 
jest na Listingu 9.

Jak widać na Listingu 9, każdemu 

specjalnemu ciągowi znaków 
przypisywana jest konkretna nazwa 
pliku, co powoduje wstawienie go w 
miejsce tegoż ciągu w oknie rozmowy 
komunikatora. 

Na czym polega błąd? Otóż, wstawiając 

odpowiedni ciąg znaków do pliku emots.txt
możemy dokonać przepełnienia bufora. 
Funkcja, która kopiuje dane z tego pliku, nie 
sprawdza długości pobieranych łańcuchów. 
Powoduje to możliwość przepełnienia 
bufora na stosie. Nie będzie to jednak 
klasyczne przepełnienie bufora – nie 
nadpiszemy bowiem adresu powrotu 
z funkcji. Nasze wysiłki skupią się na 
nadpisaniu struktury SEH (ang. Structured 
Exception Handling
, Strukturalna Obsługa 

Wyjątków). Otóż, gdy w Gadu–Gadu 
wystąpi błąd, zgłaszany jest wyjątek. Wyjątki 
charakteryzują się tym, że trzeba je w jakiś 
sposób obsłużyć – napisać odpowiedni 
kod, odpowiedzialny za wykonanie 
działania mającego na celu wyjście z 
awaryjnej sytuacji, jaką jest niepoprawne 
zachowanie się programu. Musimy więc 
przyjrzeć się strukturze obsługi wyjątków, 
ona bowiem będzie naszym celem. Należy 
nadpisać kod funkcji obsługującej wyjątki. 
Konsekwencją tego będzie możliwość 
wykonania ataku DoS lub, co jest bardziej 
wyrafinowaną metodą, wykonanie 
dowolnego kodu na maszynie ofiary.

SEH – obsługa wyjątków

Język C cechuje dość poważna wada. Ma 
on problemy z obsługą wyjątków. Tworząc 
procedury obsługujące wyjątki sprawiamy, 
iż nasz kod jest mało przejrzysty. Jak 
bowiem obsłużyć niepoprawne zachowanie 
funkcji w języku C? Sposobów na to nie 
mamy zbyt wiele. W zasadzie są trzy 
główne techniki, które można nazwać jakąś 
obsługą sytuacji awaryjnych.

Pierwszym, i chyba 

najpopularniejszym, sposobem jest 
zwracanie odpowiedniej wartości przez 
funkcję. Wyobraźmy sobie funkcję, której 
zadaniem jest dzielenie liczb. Niech 
funkcja wygląda następująco:

double dzielenie(double liczba1, 

double liczba2)

Funkcja dzieli 

liczbę1

 przez 

liczbę2

zwracając wynik takiej operacji. Ale co 
zrobimy w przypadku, gdy użytkownik 
wprowadzi niepoprawne dane? Na 
przykład liczba2 będzie zerem. Jak wiemy 
z matematyki, dzielenie przez zero jest 
niemożliwe (wynik takiej operacji będzie 
dążył do nieskończoności, jednak dla liczb 
doskonale bliskich zeru, nie będących 
zerem). Co wtedy? Gdy nie przewidzimy 
takiej sytuacji, program może ulec 
zawieszeniu, może wygenerować błąd 
systemowy i zostać zakończonym. Musimy 
więc przygotować jakąś obsługę tego 
kodu. W języku C moglibyśmy zrobić to na 
trzy sposoby:

•   sprawdzamy, czy dzielnik nie jest 

zerem, jeśli tak – to generujemy 
odpowiedni komunikat, 
implementujemy odpowiednie 
zachowanie funkcji, bądź po prostu 
zwracamy odpowiednią wartość;

•   jeśli dzielnik jest zerem, możemy ustawić 

globalną zmienną odpowiedzialną za 
kod błędu, następnie w dalszej części 
programu dokonamy jej sprawdzenia;

•   najbardziej wyrafinowanym 

sposobem jest użycie funkcji 

setjmp() 

longjmp()

. Funkcje te 

powodują kolejno: zapisanie stanu 
komputera w specjalnym buforze 
(zapisywane są rejestry, zawartość 
stosu, wskaźnik stosu – słowem 
stan programu z danego momentu 

R

E

K

L

A

M

A

background image

ATAK

36

 

HAKIN9 7-8/2008

HAKOWANIE GADU – GADU

37

 

HAKIN9 

7-8/2008

wykonywania) oraz przeniesie 
programu w poprzedni (zapisany) 
stan. Zatem jeśli dzielnik jest zerem, 
możemy przenieść program w 
stan sprzed podania niepoprawnej 
wartości zmiennej, dając tym 
samym użytkownikowi możliwość 
jej ponownego, poprawnego już 
podania;

•   w celu obsługi błędu możemy 

użyć jeszcze biblioteki sygnałów. 
Gdy program napotka określoną 
sytuację (np. dzielnik jest zerem), 
generuje sygnał, który jest następnie 
obsługiwany przez odpowiednią 
funkcję. Jednak w dużych projektach 
jest to rozwiązanie mało komfortowe, 
ponieważ numery sygnałów (każde 
zdarzenie generuje sygnał o 
określonym numerze) mogą się ze 
sobą pokrywać, wprowadzając tym 
samym chaos do projektu.

Wyraźnie widać, że w tej kwestii język C 
jest wysoce niedoskonały. Język C++ 
to swoisty krok do przodu w stosunku 
do C, toteż likwiduje ten problem, 
wprowadzając obsługę wyjątków. Jak 
mówi sama nazwa – SEH – tworzona 
jest odpowiednia struktura/klasa, która 
jest odpowiedzialna za obsługę wyjątków. 

Zawiera ona niezbędne informacje 
dotyczące konkretnego błędu. Gdy 
występuje błąd, opisany w strukturze 
wyjątek jest wyrzucany (ang. throw) i 
następnie przechwytywany (ang. catch
przez odpowiednią procedurę obsługi 
wyjątków.

Mechanizm ten jest użyty w 

komunikatorze Gadu–Gadu. Gdy coś 
pójdzie nie tak, generowany jest wyjątek, 
który następnie przejmowany jest przez 
odpowiednią funkcję. Ta, po określeniu 
typu błędu, ma za zadanie wykonać 
odpowiednie działania. Co się jednak 
stanie, gdy nadpiszemy tę funkcję i 
wywołamy wyjątek? No właśnie, tu 
otwierają się dla nas różne możliwości...

Budujemy exploit

Nasz exploit powinien wykonywać kod na 
komputerze ofiary. W tym celu musimy 
podmienić plik zawierający emotikony i 
czekać, aż ofiara użyje odpowiedniego 
emotikona. Jak to zrobimy – zależy 
tylko od nas. Możemy namówić 
użytkownika do ściągnięcia pliku, 
zaproponować nowy zestaw emotikon. 
Z pewnością, aby namówić ofiarę do 
wykorzystania dostarczonego przez 
nas pliku, przydatne będą umiejętności 
socjotechniczne.

Zajmijmy się teraz stroną techniczną 

exploita. Na Listingu 10. widzimy kod, w 
którym znaleziono błąd. Wyraźnie widać, 
że występują tam pętle kopiujące dane 
do bufora. Nie jest jednak sprawdzana 
długość kopiowanych łańcuchów. 
To otwiera nam pierwszą możliwość 

– możemy wykonać atak typu DoS. 
Zamieniając ciąg _usmiech.gif na 
1080 liter A, możliwe jest zmuszenie 
programu do wygenerowania 
komunikatu o błędzie i zakończenia 
działania. Dzieje się tak, ponieważ 
zawartość pliku jest przepisywana do 
odpowiedniego miejsca w pamięci, bez 
uprzedniej weryfikacji rozmiaru danych. 
Nadpisujemy tym samym procedury 
obsługi wyjątków, powodując przerwanie 
działania aplikacji i wygenerowanie 
komunikatu o błędzie.

Zwykle w takiej sytuacji nasuwa się 

pomysł na przepełnienie bufora metodą 
nadpisania adresu powrotu z funkcji. 
W tym przypadku nie jest to jednak 
możliwe. Dlaczego? Otóż Gadu–Gadu 
używa zaawansowanej obsługi wyjątków. 
Gdy chcemy nadpisać adres powrotu 
z funkcji, nadpisujemy przy tym wiele 
różnych zmiennych, co powoduje w tym 
przypadku wyrzucenie wyjątku. Jest to 
zaś niczym innym, jak przekazaniem 
sterowania nad programem do innej jego 
części. Nasz kod jest więc omijany i nie 
zostaje wykonany. Proste, acz skuteczne 
zabezpieczenie. 

Musimy więc stworzyć takiego 

exploita, aby nie nadpisywał adresu 
powrotnego z funkcji. Musi być bardziej 
wyrafinowany – jego celem powinna stać 
się SEH – struktura odpowiedzialna za 
obsługę wyjątków. Tylko w ten sposób 
możemy zmusić program do wykonania 
naszego kodu.

Dysponujemy odpowiednią ilością 

danych do zbudowania exploita. 

Listing 9. 

Wadliwy kod – 

przepełniamy bufor

.

text

:

00443E37

 

loc_443E37

:

 

.

text

:

00443E37

 

cmp

 

al

, '

"' 

.text:00443E39 jz short loc_443E48
 .text:00443E3B mov [ecx], al.
 .text:00443E3D inc ecx
 .text:00443E3E inc edi 
.text:00443E3F mov [ebp–18h], edi
 .text:00443E42
 .text:00443E42 loc_443E42: 
.text:00443E42 mov al, [edi] 
.text:00443E44 cmp al, ' ' 
.text:00443E46 jnb short loc_443E37
 (…) 
.text:00443E87 loc_443E87: 
.text:00443E87 cmp cl, '"

.

text

:

00443

E8A

 

jz

 

short

 

loc_443E9F

 

.

text

:

00443

E8C

 

mov

 

[

eax

]

cl

 .

text

:

00443

E8E

 

inc

 

eax

 .

text

:

00443E8F

 

inc

 

edi

 

.

text

:

00443E90

 

.

text

:

00443E90

 

loc_443E90

:

 .

text

:

00443E90

 

mov

 

cl

[

edi

]

 .

text

:

00443E92

 

cmp

 

cl

, ' ' 

.

text

:

00443E95

 

mov

 

[

ebp

–18

h

]

edi

 

.

text

:

00443E98

 

jnb

 

short

 

loc_443E87

Rysunek 5. 

Emotikony

background image

ATAK

36

 

HAKIN9 7-8/2008

HAKOWANIE GADU – GADU

37

 

HAKIN9 

7-8/2008

Możemy wywołać wyjątek w programie, 
a następnie nadpisać procedurę jego 
obsługi. Zlokalizowanie procedury obsługi 
wyjątków nie jest trudne – debugger 
wyraźnie pokazuje jej lokalizację pod 
adresem w pamięci 

fs:[0]

, czyli 

0x0032FCA8

. Bufor zaczyna się pod 

adresem 

0x0032F868

. Różnica tych 

dwóch wartości daje nam dokładnie 
1088 bajtów. Nasz plik winien więc 
wyglądać następująco:

•   buźka, np. „:)”,
•   „1095 liter A”,
•   „szelkod”.

Dlaczego 1095 liter A, skoro różnica 
wyniosła tylko 1088 bajtów? Cóż, po 
prostu musimy zacząć nadpisywanie 
struktury od pewnego jej miejsca, nie od 
samego początku. Stąd wynika, iż 1088 
liter to za mało – testy autorów exploita 
(www.vexillium.org) wykazały, iż właśnie ta 
wartość jest odpowiednia. 

Prowadząc dalszą analizę możemy 

się przekonać, że to jeszcze nie wszystko. 
Program nie wywołał wyjątku – nie 
pokazało się okienko z błędem. Autorzy 
zauważyli, iż wynika to z działania 
systemu operacyjnego. Nie każdy uchwyt 
procesu, który ustawimy, jest przez niego 
wywoływany. Do sprawdzenia, czy dany 
uchwyt może być wywołany, służy funkcja 

RtlIsValidHandler()

, znajdująca się 

w bibliotece 

ntdll.dll

. Sprawdza ona 

poprawność adresów, zapewniając 
niejakie zabezpieczenie przed wykonaniem 
niewłaściwego kodu. Jeśli wartość rejestru 
AL po wywołaniu tej funkcji wynosi 1, 
wtedy kod może zostać uruchomiony 
– adres jest poprawny i instrukcje pod nim 
zamieszczone są dozwolone do wykonania. 
Musimy więc, po każdym wywołaniu tej 
funkcji, zmienić wartość rejestru AL na 1 
– tak, aby możliwe było wykonanie naszego 
szelkodu. 

Podsumujmy więc posiadane 

informacje. Musimy stworzyć ciąg 

ponad tysiąca liter A, wstawić tam adres 
w pamięci, pod który przekierujemy 
wykonanie programu oraz umieścić 
szelkod. Adres ten powinien byś jakimś 
adresem, z którego korzysta Gadu–
Gadu. Może to być na przykład funkcja 
znajdująca się w jednej z bibliotek, z 
których korzysta aplikacja. Pozwoli to na 
poprawne przejście przez sito funkcji 

RtlIsValidHandler()

, ustawiając 

wartość rejestru AL procesora na 1 
– wszystko wyglądać będzie na poprawny 
kod. Autorzy exploita proponują użyć 
w tym celu biblioteki 

LIBEAY32.dll

Tworzymy więc szelkod i konstruujemy 
odpowiednio exploit. Gotowy plik 

emots.txt

 powinien zatem wyglądać 

następująco:

•   :)
•   967 liter A,
•   szelkod,
•   37 bajtów danych,

instrukcja przeskakująca do adresu w 
pamięci, pod którym będzie znajdować 
się szelkod (czyli adres początku 
bufora+967),

•   adres kodu w bibliotece LIBEAY32.DLL 

– aby wszystko się wykonało,

•   jakieś dane.

Stronę autora exploita oraz dokładny opis 
procesu znajdowania błędu i budowania 
exploita możemy znaleźć w Ramce 
Sieci.

Podsumowanie

Gadu–Gadu, najpopularniejszy polski 
komunikator internetowy, nie jest 
pozbawiony wad. Wystarczy przekonać 
naiwnego użytkownika do stworzenia 
nowego folderu z emotikonami i 
umieszczenia w nim pliku 

emots.txt

 

z exploitem. Należy jednak pamiętać, 
że stałe, systematyczne aktualizowanie 
programu ustrzeże Czytelnika przed tego 
typu atakami.

Rysunek 6. 

Gadu–Gadu pod debuggerem

W Sieci

•   http://vexillium.org – strona autora exploita, doskonale tłumacząca zasadę jego działania,
•   http://gadu–gadu.pl – strona domowa komunikatora Gadu–Gadu,
•   http://ekg.chmurka.net/docs/protocol.html – szczegółowy opis protokołu Gadu–Gadu,
•   http://msdn2.microsoft.com/en–us/library/ms680657(VS.85).aspx – opis SEH.

Konrad Zuwała

Autor zajmuje się bezpieczeństwem aplikacji 

internetowych oraz szeroko rozumianą ochroną 

systemów komputerowych. W wolnych chwilach 

programuje (głównie C/C++, PHP) oraz zarządza 

portalem internetowym. 

Kontakt z autorem: kzuwala@poczta.onet.pl