Bootcamp IIa: rozwiązanie (prawie)

Ponieważ zainteresowanie drugą częścią zadania (Bootcamp IIa: ciąg dalszy zadania) spadło, a ja mam kilka minut czasu, opiszę o co w nim chodziło. Jest to prawie rozwiązanie zadania. Prawie, bo rozwiązanie zadania dostępne jest w formie wizualnej: Bootcamp: lesson02a – hint/spoiler.

Zadanie dotyczyło kontroli dostępu, ale jego główną częścią była kryptografia. Bardzo źle użyta kryptografia. Najpierw szybkie wyjaśnienie – co kryptografia robi przy kontroli dostępu.

Wszystko, co przekracza granicę zaufania trzeba traktować jako niezaufane, ponieważ może zostać zmodyfikowane. W szczególności fakt, że użytkownikowi wypisaliśmy pewne rzeczy na stronie, na przykład konkretne identyfikatory obiektów, wcale nie znaczy, że tylko takie identyfikatory użytkownik do nas prześle. Jednym z pomysłów na rozwiązanie/ograniczenie tego problemu jest szyfrowanie. Pomysł ten oparty jest na założeniu, że użytkownik nie jest w stanie zmodyfikować zaszyfrowanych danych. Jest to założenie błędne, ponieważ szyfrowanie, co do zasady, nie chroni integralności danych (patrz też: Authenticated encryption).

Można wyróżnić dwa podstawowe typy szyfrów: szyfry strumieniowe i szyfry blokowe. Różnica jest taka, że szyfr blokowy operuje na blokach (np. na bloku o wielkości 16 bajtów), szyfr strumieniowy – na poszczególnych bajtach (w zasadzie to nawet na bitach). W rezultacie modyfikacja jednego bajtu w tekście tajnym (zaszyfrowanym) powoduje:

W obu przypadkach możliwe jest generowanie “w ciemno” pewnych danych, przepuszczanie ich przez czarną skrzynkę (proces deszyfrowania) i oczekiwanie, że za którymś razem skonstruuje się taką wiadomość, że po rozszyfrowaniu da ona oczekiwany tekst jawny. To jest na pewno wykonalne, jedyna kwestia – jak długo to zajmie. Pod tym względem szyfry blokowe i szyfry strumieniowe różnią się dość znacznie...

W przykładzie wykorzystany został szyfr strumieniowy. Konkretnie RC4, choć to akurat nie jest szczególnie kluczowa informacja. Szyfry strumieniowe mają jeszcze jedną ważną cechę – bardzo trudno jest je wykorzystać poprawnie. Wielokrotnie przekonał się o tym Microsoft i było to na tyle bolesne, że szyfry strumieniowe trafiły na czarną listę: Banned Crypto and the SDL.

Szyfrowanie w algorytmie RC4 opiera się na operacji XOR tekstu jawnego z kluczem (konkretnie ze strumieniem pseudolosowych bajtów generowanych przez algorytm na podstawie klucza). Dokładnie tak samo działa odszyfrowanie. XOR (właściwie One-Time Pad) jest szyfrem idealnym, pod warunkiem, że zostanie poprawnie wykorzystany. W szczególności jednym z problemów jest Known-plaintext attack. Czy w tym przypadku dysponujemy tekstem jawnym? Tak, zawartość parametru id jest wypisywana w kodzie strony w komentarzu. Efektywnie oznacza to tyle: koniec zabawy.

Dlaczego tak? Z bardzo prostego powodu:

W przypadku podpowiedzi przekazałem tekst tajny, który składał się z samych 0x00. Po wykonaniu operacji XOR uzyskałem... klucz (keystream), a konkretnie tak długi jego fragment, jak długi był tekst tajny. Pozostało zaszyfrować to, co akurat miałem ochotę, by serwer sobie odszyfrował...

Całość zadania można było również rozwiązać przy pomocy brute force. Bez większego problemu można było przygotować algorytm, który pozwoliłby przygotować tekst tajny o dowolnej długości po rozszyfrowaniu dający zadany tekst jawny. Algorytm ten wymagałby maksymalnie 256 żądań do serwera, niezależnie od długości tekstu. Ktoś chętny do opisania? :)

Oryginał tego wpisu dostępny jest pod adresem Bootcamp IIa: rozwiązanie (prawie)

Autor: Paweł Goleń