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?
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
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
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
i 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"
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
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()
i
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
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
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 W
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