38
HAKIN9
ATAK
7-8/2008
W
pierwszej części artykułu,
opublikowanej w Hakin9 5/2008,
opisałem podstawową strukturę pliku
graficznego GIF oraz omówiłem kompresję
LZW. Niniejsza – druga – część artykułu
poświęcona będzie rozszerzeniom formatu
GIF (takim, jak animacja, komentarze czy
wreszcie rozszerzenia aplikacji), miejscom,
w których można ukryć/szukać dodatkowych
danych oraz możliwym do popełnienia podczas
implementacji błędom.
Przed przystąpieniem do lektury warto
przypomnieć sobie ogólną budowę formatu
GIF, podział na części oraz podział danych
na fragmenty – data Sub-blocks (w tym celu
można posłużyć się na przykład poprzednią
częścią artykułu lub sięgnąć do opisu
standardu GIF [1]).
Animowane GIFy
Plik GIF nawet bez korzystania z rozszerzeń
oferuje możliwość zapisania więcej niż jednej
klatki obrazu – w takim przypadku wszystkie
klatki są używane do stworzenia jednej wynikowej
grafiki. Standard 89a wprowadził dodatkową
strukturę – blok kontroli grafiki (ang. Graphic
Control Extension, GCE), który zawiera dodatkowe
informacje umożliwiające stworzenie z serii
klatek faktycznej animacji. Blok zawiera między
innymi takie informacje, jak wielkość opóźnienia
przed wyświetleniem następnej klatki czy sposób
przejścia do następnej klatki.
MICHAŁ „GYNVAEL
COLDWIND”
SKŁADNIKIEWICZ
Z ARTYKUŁU
DOWIESZ SIĘ
jakie rozszerzenia zostały
wprowadzone do formatu GIF w
wersji 89a,
na co uważać podczas
implementowania rozszerzeń
formatu GIF,
gdzie szukać błędów w
aplikacjach korzystających z GIF,
gdzie ukryć lub szukać ukrytych
danych w plikach GIF.
CO POWINIENEŚ
WIEDZIEĆ
mieć ogólne pojęcie na temat
plików binarnych,
mieć ogólne pojęcie na temat
bitmap,
mieć pojęcie o plikach GIF.
Według dokumentacji jeden taki blok może
(ale nie musi) poprzedzać dane obrazu (a
konkretniej nagłówek Image Descriptor) lub
rozszerzenie zwykłego tekstu (ang. Plain Text
Extension). Dany blok ma zasięg lokalny, czyli
wpływa jedynie na następującą po nim klatkę
animacji.
Znaczna część pól w strukturze GCE
(patrz Tabela 1) ma stałą, ustaloną z góry,
wartość. Wyjątkami są pola DisposalMethod,
UserInputFlag, TransparentColorFlag,
DelayTime oraz TransparentColorIndex. Pole
DisposalMethod (metoda usunięcia) określa, w
jaki sposób dana klatka ma zostać skasowana
przed narysowaniem następnej. Dostępne są 4
opcje:
• [0] – Nieokreślona metoda usunięcia
(zazwyczaj równoważna z opcją 1),
• [1] – Nie usuwaj (następna klatka zostanie
wyrysowana na obecnej),
• [2] – Wypełnij obraz kolorem tła,
• [3] – Powróć do poprzedniego obrazu.
Dokumentacja zaleca, aby opcji trzeciej używać
jedynie w przypadku niewielkich fragmentów
obrazu (z uwagi na subiektywnie wysokie zużycie
pamięci – chociaż dla obecnych komputerów nie
stanowi to problemu), a w razie, gdyby dekoder
nie potrafił poradzić sobie z przywróceniem
poprzedniego obrazu, dopuszczalne jest
wypełnienie obrazu kolorem tła.
Stopień trudności
Format GIF
okiem hakera
Format GIF, oprócz podstawowej funkcjonalności oferowanej
przez wszystkie standardowe formaty graficzne, udostępnia
również kilka rozszerzeń umożliwiających stworzenie animacji czy
też dodanie komentarza do grafiki.
39
HAKIN9
FORMAT GIF OKIEM HAKERA
7-8/2008
Pozostałe opcje (4-7) są
zarezerwowane do przyszłego użytku
– ale w praktyce różne dekodery
(przy testach zostały wykorzystane: FF
– Mozilla Firefox 2.0.0.14, O – Opera 9.24,
S – Safari 3.1 (525.13), IE – Microsoft
Internet Explorer 7.0.6000.16643, IV
– IrfanView 4.10) już je implementują, i to
w odmienny sposób:
• [4] – IE, IV i FF traktują jako powrót do
poprzedniego obrazu, O i S jako nie
usuwaj,
• [5] – IE i IV traktują jako powrót do
poprzedniego obrazu, a FF, O, S jako
nie usuwaj,
• [6] – IE i IV traktują jako powrót do
poprzedniego obrazu, FF jako nie
usuwaj, a O i S jako wypełnij obraz
kolorem tła,
• [7] – IE, IV, S i O traktują jako powrót
do poprzedniego obrazu, a FF jako nie
usuwaj.
Jak widać, nie ma w dekoderach zgody
co do tego, jak traktować metody
niezdefiniowane przez dokumentację.
IE oraz IV są zgodne, iż wszystkie
niestandardowe wartości powinny być
traktowane jako powrót do poprzedniego
obrazu, natomiast pozostałe dekodery
nie mają jednego określonego zdania.
Różnice te teoretycznie pozwalają na
stworzenie GIFa, który wyglądać będzie
inaczej w każdej z wspomnianych
przeglądarek graficznych czy
internetowych.
Należy jeszcze dodać, iż Safari ma
poważne problemy wydajnościowe
w przypadku opcji nie usuwaj – dla
porównania, testowy GIF (50 klatek
256x256) na Firefoksie renderował
się około 5 sekund, natomiast Safari
poświęciło na niego prawie 3 minuty,
dodatkowo każda kolejna klatka
renderowała się dłużej od poprzedniej,
a przeglądarka odpowiadała na
polecenia użytkownika tylko pomiędzy
klatkami. Końcowe klatki renderowały
się około 20 sekund, więc możliwe
jest przeprowadzenie skutecznego
ataku DoS przy pomocy odpowiednio
spreparowanej strony WWW (test został
wykonany na komputerze Intel Core 2
Quad 2.4GHz z 4GB RAM).
Pole TransparentColorFlag
(flaga przezroczystości) oraz
TransparentColorIndex (numer
przezroczystego koloru) odpowiedzialne
są oczywiście za istnienie i wybór koloru,
który będzie uznany za przezroczysty
– czyli oznaczał piksele nie narysowane,
pod którymi prześwitywać będzie
poprzednia zawartość bufora. Warto
zaznaczyć, iż jeżeli flaga przezroczystości
jest wygaszona (tj. równa 0), to pole
TransparentColorIndex może zostać
przeznaczone na przechowanie
dowolnego bajtu danych.
Pole DelayTime (opóźnienie) jest
informacją o wielkości opóźnienia (w
setnych sekundy) przed wyświetleniem
kolejnej klatki. Ostatnie pole (a w zasadzie
flaga) – UserInputFlag – określa, czy
dekoder powinien zaczekać na interakcję
z użytkownikiem (pojęcie to nie jest
definiowane przez dokumentację, tak
więc interakcją z użytkownikiem może
być dowolne zdarzenie uznane za
takowe przez dekoder, np. naciśnięcie
przycisku myszy lub dowolnego
klawisza). W przypadku, gdy flaga jest
aktywna oraz gdy ustawiony jest czas
opóźnienia (tj. jest niezerowy), dekoder
powinien zaprezentować kolejną klatkę
po odczekaniu wskazanego czasu lub
po zaistnieniu określonego zdarzenia. W
praktyce wszystkie z testowanych przeze
mnie dekoderów ignorowały tę flagę i nie
czekały na interakcję.
Dodatkowe dane mogą zostać
przechowane w polu Reserved, które
zazwyczaj nie jest kontrolowane przez
dekodery.
Warto zaznaczyć, iż w praktyce
możliwe jest wielokrotne umieszczenie
struktury GCE przed danymi.
Wszystkie testowane dekodery traktują
wtedy ostatnią strukturę GCE jako
obowiązującą, a resztę ignorują – jest to
więc całkiem niezłe miejsce na ukrycie
dodatkowych informacji.
Dodatkowo, programista może
popełnić ewentualny błąd zakładając, iż
BlockSize będzie na pewno równe 4 i
jednocześnie korzystając z niego w celu
załadowania danych do statycznego
bufora – taka sytuacja prowadzi w
prostej linii do przepełnienia bufora. W
praktyce większość dekoderów wymaga,
aby BlockSize był rzeczywiście równy 4
(jest to sprawdzane, w przypadku innej
wartości dekoder przerywa pracę).
Rozszerzenie Netscape
W GCE zabrakło informacji o tym,
czy animacja jest jednorazowa,
czy też powinna być zapętlona w
nieskończoność. W tym celu powstało
rozszerzenie aplikacji NETSCAPE2.0
(jego obsługa została wprowadzona
w Netscape Navigator 2.0 Beta 4),
którego pole NumberOfIterations (patrz
Tabela 2) określa, czy aplikacja powinna
animować GIF w nieskończoność
(wartość 0), czy zaprzestać po określonej
Tabela 1.
Struktura Graphic Control Extension
Typ i nazwa pola
Opis
BYTE ExtensionIntroducer
Znacznik rozszerzenia, zawsze 0x21
BYTE GraphicControlLabel
Rodzaj rozszerzenia, zawsze 0xF9
BYTE BlockSize
Wielkość następującego bloku danych, zawsze 4
BYTE Reserved:3
Zarezerwowane
BYTE DisposalMethod:3
Metoda usunięcia klatki
BYTE UserInputFlag:1
Flaga oczekiwania na interakcje
BYTE TransparentColorFlag:1
Flaga przezroczystości
WORD DelayTime
Czas opóźnienia
BYTE TransparentColorIndex
Numer koloru przezroczystego
BYTE BlockTerminator
Terminator bloku, zawsze 0
Rysunek 1.
Artystyczna wizja własnej
interpretacji GIF przez różne przeglądarki
ATAK
40
HAKIN9 7-8/2008
FORMAT GIF OKIEM HAKERA
41
HAKIN9
7-8/2008
liczbie przebiegów. Co ciekawe,
NN 2.0 Beta 4 uznawała wszystkie
animacje posiadające niniejszą
strukturę jako mające się wykonywać w
nieskończoność, bez względu na wartość
pola NumberOfIterations ; zostało to
poprawione w wersji Beta 5. Animacja
nie posiadająca tej struktury (która
powinna się znajdować zaraz za GCT)
jest traktowana przez dekodery wedle
uznania – zazwyczaj jako animacja,
którą powinno odegrać się raz (IE, FF,
O, S). Istnieją jednak dekodery (IV),
które uznają, iż animacja powinna być
odgrywana w nieskończoność.
Niektóre dekodery (np. IE i FF)
wyświetlają klatki animacji również
podczas ładowania GIFa. To wyświetlenie
nie jest przez nie zaliczane jako
faktyczne wyświetlenie animacji, przez
co efektywnie animacja wyświetlana jest
o jeden raz więcej. IrfanView natomiast
ignoruje nagłówek całkowicie.
Pole One powinno mieć zawsze
wartość 1, natomiast nie wszystkie
dekodery je testują. Ze sprawdzonych
dekoderów jedynie Firefox zwrócił uwagę
na jego zmianę – jest to więc miejsce, w
którym można ukryć bajt danych.
Kolejną możliwą modyfikacją jest
powiększenie bloku zawierającego
wartość Magic. Dekodery zawarte
w IE oraz FF nie zwróciły uwagi na
zmianę (powstało trochę miejsca
do umieszczenia danych), natomiast
dekoder zawarty w Operze uznał, iż
animację należy odtworzyć jedynie raz,
a dekoder z Safari – że animację należy
odtwarzać w nieskończoność.
W przypadku zwiększenia drugiego
sub-bloku danych jedynie Opera
zareagowała identycznie, jak poprzednim
razem, reszta dekoderów zignorowała
zmianę i zachowała się tak, jak gdyby
otrzymała prawidłowo wypełniony
nagłówek.
Analogicznie, jak w poprzednim
przypadku, programista powinien zwrócić
uwagę na możliwość wystąpienia
przepełnienia bufora.
Komentarze
Struktura komentarzy (ang. Comment
Extension, CE) jest bardzo prosta (co
pokazuje Tabela 3) – składa się ze
stałego nagłówka komentarzy, po nim
następuje seria sub-bloków z danymi
(dla przypomnienia: każdy sub-blok
składa się z bajtu określającego
wielkość danych w bajtach oraz
odpowiednią ilość bajtów danych)
i wreszcie sub-blok terminujący (z
wielkością danych równą 0).
Komentarze z plików GIF wyświetlają
jedynie nieliczne aplikacje, większość
dekoderów je ignoruje.
Głównym błędem, jaki można
popełnić podczas obsługi komentarzy
GIF, jest brak sprawdzania, co komentarz
faktycznie zawiera – czy nie znajdują
się w nim znaki specjalne (np. końca
linii, terminatora tekstu) lub specjalnych
sekwencji. Tego typu błąd został
znaleziony w roku 2001 w przeglądarce
Netscape Navigator przez Floriana
Wescha i umożliwiał umieszczenie kodu
JavaScript wewnątrz komentarza. Skrypt
ten zostałby wykonany w momencie
wyświetlenia pliku GIF (NN wyświetlał
również komentarze) przez przeglądarkę,
np. w wyniku wejścia przez użytkownika
na daną stronę WWW.
Programista może łatwo przeoczyć
także inny błąd, zakładając podczas
implementacji obsługi komentarzy, iż
komentarz nie będzie większy od jednego
sub-bloku (czyli nie będzie przekraczał
255 znaków) – takie założenie może
doprowadzić do przepełnienia bufora.
Plik GIF może zawierać dowolną ilość
struktur CE.
Rozszerzenie Plain Text
Ostatnim rozszerzeniem GIF jest Plain
Text Extension – rozszerzenie teoretycznie
umożliwiające zapisanie tekstu na
bitmapie jako faktycznego tekstu, a nie
serii pikseli. W takim wypadku dekoder
byłby odpowiedzialny za prawidłowe
wyrysowanie tekstu na wynikowej bitmapie.
Niestety, w praktyce okazuje się, iż
żaden z przetestowanych dekoderów nie
miał zaimplementowanej obsługi tego
rozszerzenia.
Co więcej, w kilku artykułach, na które
natknąłem się podczas badań, autorzy
sugerują, iż jest to martwe rozszerzenie
i powinno zostać zignorowane przez
programistę.
Niemniej jednak z uwagi na
możliwość przyszłego pojawienia się
tego typu dekoderów, lub pojawienia się
potrzeby zaimplementowania takiego
Tabela 2.
Struktura NETSCAPE2.0
Typ i nazwa pola
Opis
BYTE
ExtensionIntroducer
Znacznik rozszerzenia, zawsze 0x21
BYTE ApplicationExtensi
onLabel
Rodzaj rozszerzenia, zawsze 0xFF
BYTE BlockSize1
Długość sub-bloku danych, zawsze 0x0B
BYTE Magic[11]
Zawsze „NETSCAPE2.0”
BYTE BlockSize2
Długość sub-bloku danych, zawsze 0x03
BYTE One
Zawsze 0x01
WORD
NumberOfIterations
Ilość powtórzeń animacji
BYTE BlockTerminator
Terminator bloku, zawsze 0
Tabela 3.
Struktura Comment Extension
Typ i nazwa pola
Opis
BYTE ExtensionIntroducer
Znacznik rozszerzenia, zawsze 0x21
BYTE CommentLabel
Rodzaj rozszerzenia, zawsze 0xFE
SubBlock CommentData[]
Jeden lub więcej sub-bloków danych
BYTE BlockTerminator
Terminator bloku, zawsze 0
W Sieci
• http://www.w3.org/Graphics/GIF/spec-
gif89a.txt – standard GIF89a.
ATAK
40
HAKIN9 7-8/2008
FORMAT GIF OKIEM HAKERA
41
HAKIN9
7-8/2008
dekodera, postanowiłem omówić również
i to rozszerzenie.
Rozszerzenie PTE opiera się o
koncept stary jak świat znany z konsoli
poleceń – stałą wielkość pojedynczego
znaku oraz podzielenie ekranu na siatkę
o stałej całkowitej rozdzielczości. W
strukturze PTE podaje się takie informacje
jak początek siatki (położenie lewego
górnego rogu) – pola TextGridLeftPosition
oraz TextGridTopPosition (siatka
w cale nie musi się zaczynać na
0,0!), szerokość i wysokość siatki
– pola TextGridWidth i TextGridHeight,
szerokość i wysokość pojedynczego
znaku – pola CharacterCellWidth oraz
CharacterCellHeight, i kolor czcionki
oraz tła (kolor korzysta z Global Color
Table). Sam tekst który ma zostać
wyrenderowany jest podzielony na
sub-bloki i jest dołączony po sub-
bloku zawierającym konfigurację PTE.
Całość zakończona jest podobnie jak
w przypadku poprzednich rozszerzeń
– terminującym bajtem o wartości 0.
Standard zaleca aby korzystać
jedynie ze znaków o wielkości 8x8 lub
8x16, oraz aby szerokość i wysokość
siatki była tak dobrana aby ilość znaków
w wierszu czy kolumnie była całkowita
– w przypadku gdy nie jest, część
ułamkowa powinna zostać zignorowana.
Należy zauważyć iż wybór kroju
czcionki należy całkowicie do dekodera,
i jest niezależne od intencji osoby
tworzącej GIF'a.
Pierwszym wektorem ataku, który
nasuwa się od razu po przeczytaniu
zaleceń standardu, jest użycie innej
wielkości znaku niż 8x8 czy 8x16.
Zauważmy iż dla programisty ważną
informacją będzie ilość znaków w
linii, którą wylicza się za pomocą
prostego dzielenia – TextGridWidth/
CharacterCellWidth (można się obyć
bez tego równania, jednak z dużym
prawdopodobieństwem pojawi się ono
w programie; analogicznie liczy się
maksymalną ilość linii w siatce). Celnym
atakiem na każde dzielenie jest użycie z
zera w mianowniku – co, niesprawdzone,
powoduje wygenerowanie wyjątku Divide
By Zero, a z kolei nieobsłużony wyjątek
prowadzi do skutecznego ataku typu DoS.
Innym podejściem do problemu
wielkości znaku może być użycie
większego znaku od siatki, oraz, w
drugim podejściu, większego znaku
od całego obrazu. Można w tym celu
użyć odpowiednio dużego znaku (np.
255x255), lub odpowiednio małego
obrazu (np. 4x4) dla standardowej
wielkości znaku. Jeżeli programista
zaniedba sprawdzanie takich
przypadków, taki atak doprowadzi do
przepełnienia bufora obrazu – jednak
prawdopodobieństwo uruchomienia
zewnętrznego kodu w takim wypadku
wydaje się być niezwykle niskie.
Niejako rozwinięciem tego pomysłu
jest użycie siatki większej niż obraz,
ale z wieloma małymi znakami
– nieuważny programista mógł nie
sprawdzić wielkości, co skończyło by
się przepełnieniem bufora – z nadal
niewielkim (ale jednak większym niż
poprzednio) prawdopodobieństwem
wykonania kodu.
Innym wektorem ataku mogły by
być indeksy kolorów – tła i znaku. Jeżeli
programista nie sprawdzi czy index nie
wykracza po za paletę kolorów, może
to doprowadzić do wycieku informacji
(analogicznego jak w przypadku palety,
plików BMP i przeglądarek Mozilla
Firefox i Opera – błąd został opisany
w hakin9 3/2008), lub odmowy usługi
(DoS). Pojawia się również pytanie – a
co jeśli plik nie będzie w ogóle posiadał
globalnej palety kolorów? Kolejny
możliwy wyciek informacji lub DoS.
Podobnie jak w przypadku
poprzednich rozszerzeń, można
przetestować zachowanie dekodera
w przypadku zmiany wielkości sub-
bloku konfiguracyjnego. Warto również
sprawdzić czy dekoder jest w jakiś
sposób wrażliwy na nieobecność
sub-bloku konfiguracyjnego, oraz
na ekstremalną ilość sub-bloków
zawierających tekst.
Z uwagi na fakt iż obecnie mało który
dekoder obsługuje rozszerzenie PTE, to
rozszerzenie staje się bardzo dobrym
miejscem na ukrycie dodatkowych
informacji.
Podsumowanie
We wszystkich wymienionych
rozszerzeniach warto zwrócić również
uwagę na możliwość dołożenia
kolejnego sub-bloku danych do
wymaganych sub-bloków – powinno
to pomóc stworzyć miejsce do ukrycia
kolejnych danych.
Tym artykułem kończę tematykę
plików GIF. Po lekturze obu artykułów
uważny Czytelnik powinien być w stanie
zaimplementować bezpiecznie obsługę
formatu GIF, wraz z rozszerzeniami, a
także skutecznie odnaleźć błędy ukryte w
testowanych aplikacjach.
Michał Składnikiewicz
Michał Składnikiewicz, inżynier informatyki, ma
wieloletnie doświadczenie jako programista oraz
reverse engineer. Obecnie pracuje w Hispasec
– międzynarodowej firmie specjalizującej się w
bezpieczeństwie komputerowym.
Kontakt z autorem: gynvael@coldwind.pl
Tabela 4.
Struktura Plain Text Extension
Typ i nazwa pola
Opis
BYTE ExtensionIntroducer
Znacznik rozszerzenia, zawsze 0x21
BYTE PlainTextLabel
Rodzaj rozszerzenia, zawsze 0x01
BYTE BlockSize
Wielkość sub-bloku – zawsze 0x0C
WORD TextGridLeftPosition
Początek siatki – współrzędna X
WORD TextGridTopPosition
Początek siatki – współrzędna Y
WORD TextGridWidth
Szerokość siatki
WORD TextGridHeight
Wysokość siatki
BYTE CharacterCellWidth
Szerokość pojedynczego znaku
BYTE CharacterCellHeight
Wysokość pojedynczego znaku
BYTE TextForegroundColorIndex
Kolor znaku
BYTE TextBackgroundColorIndex
Kolor tła znaku
SubBlock PlainTextData[]
Sub-bloki zawierające tekst
BYTE BlockTerminator
Terminator bloku, zawsze 0