Professional Documents
Culture Documents
Sekurak - Pl-Zabawy Z Padding Oracle
Sekurak - Pl-Zabawy Z Padding Oracle
sekurak.pl/zabawy-z-padding-oracle
16 lipca 2014
Tekst zdobył drugie miejsce w sekurakowym konkursie na najlepszy tekst o bezpieczeństwie (kolejna
edycja konkursu jest w trakcie – czekamy na teksty jeszcze prawie 2 miesiące).
Jakiś czas temu, zainspirowany przez sekuraka, postanowiłem zabawić moich kolegów z zespołu w
pracy i zorganizować CTF. Jednym z etapów było rozszyfrowanie tokena zaszyfrowanego przy pomocy
AES w trybie CBC. Token można było wprowadzić na stronie podatnej na atak padding oracle.
Chciałbym zaznaczyć, że atak typu padding oracle nie ma nic wspólnego z bazą danych Oracle. W
kryptografii wyrocznia (ang. oracle) to urządzenie (funkcja), która wykona proces deszyfrowania i zwróci
pewne informacje.
W tym artykule postaram się pokazać, jak przeprowadzić taki atak, ale najpierw…
Trochę teorii
1. PKCS#7 Padding
Szyfry blokowe, takie jak AES czy 3DES, wymagają na wejściu tekstu jawnego o długości, która jest
wielokrotnością wielkości bloku. Najczęściej długość tekstu jawnego nie spełnia tego warunku, więc
należy dopisać „coś” na końcu, aby warunek ten spełnić. To „coś”, to właśnie padding. Wszystkie znane
mi szyfry blokowe do konstruowania paddingu używają PKCS#7. W PKCS7 wartość każdego bajtu
dodanego na końcu tekstu jawnego ma wartość równą liczbie dodanych bajtów.
Przykład:
Jeśli długość bloku to 16 bajtów, a tekst jawny to „ TekstJawny ”, to po dodaniu paddingu PKCS#7
otrzymamy „ TekstJawny\x06\x06\x06\x06\x06\x06 ”.
2. Alternatywa wykluczająca
Cytując Wikipedię: alternatywa wykluczająca to logiczny funktor zdaniotwórczy. W naszym rozumieniu
alternatywa wykluczająca, czyli operacja xor, to operacja na bitach zdefiniowana następująco:
A B A ^ B
1 1 0
1 0 1
0 1 1
1/5
0 0 0
Warto również przypomnieć parę własności operacji xor wynikających bezpośrednio z definicji, które
wykorzystamy w dalszej części:
A ^ B = B ^ A
A ^ 0 = A
A ^ A = 0
Powyżej opisałem, jak działa xor na bitach. Działanie xor na bajtach, to wykonanie xor na
poszczególnych bitach tych bajtów, a działanie xor na łańcuchach znaków, to po prostu wykonanie xor na
poszczególnych bajtach tych łańcuchów (oczywiście długości tych łańcuchów muszą być równe).
Pierwszy blok szyfrogramu jest deszyfrowany, a następnie xorowany z wektorem inicjalizującym. Każdy
kolejny blok szyfrogramu jest deszyfrowany i xorowany z zaszyfrowanym blokiem z poprzedniej rundy
algorytmu.
2/5
Praktyka: atak padding oracle
Sama idea ataku nie jest szczególnie skomplikowana, niestety wymaga trochę matematyki.
Przyjmijmy następujące oznaczenia:
C1 = E(P1 ^ IV)
Cn = E(Pn ^ Cn-1) , dla n > 1
Analogicznie P definiujemy jako ciąg bloków P1…Px , gdzie:
P1 = D(C1) ^ IV
Pn = D(Cn) ^ Cn-1 , dla n > 1
Atak polega na odszyfrowaniu C bez znajomości klucza, przy pomocy którego był zaszyfrowany.
Ważne, aby zrozumieć, że atak ten jest skuteczny przeciwko CBC, a nie algorytmowi, który został w nim
użyty. Swoją drogą, atak jest bardzo „hollywoodzki”, ponieważ atakujący łamie szyfrogram bajt po bajcie;
idealnie nadaje się do zekranizowania w kolejnej części matrixa. :-)
A zatem, jako atakujący, będziemy przesyłać do wyroczni spreparowany szyfrogram składający się z
dwóch bloków: B oraz Cn , gdzie Cn , to kolejne bloki szyfrogramu C . Niestety musimy wprowadzić
kilka kolejnych definicji.
Przyjmijmy dodatkowo, że:
3/5
bloków: B oraz Cn
P’ – tekst jawny powstały po odszyfrowaniu przez wyrocznię C’ .
Oczywiście P’ jest znany tylko wyroczni i składa się również z dwóch bloków: P’1 i P’2 .
P’1 = D(B) ^ IV
P’2 = D(Cn) ^ B
Przypomnijmy, że:
Cn = E(Pn ^ Cn-1)
czyli:
Zauważcie, że w tym równaniu nie jest używana funkcja szyfrująca. Dlatego właśnie atak jest skuteczny
niezależnie od tego, jak silny algorytm został użyty do szyfrowania.
Wróćmy jednak do matematyki.
Jak wiadomo, nie da się jednoznacznie rozwiązać równania z dwiema niewiadomymi, zatem musimy
pozbyć się jednej niewiadomej. Jak już wspomniałem, xorowanie łańcuchów, to xorowanie
poszczególnych bajtów na tych łańcuchach.
Jeśli przyjmiemy, że i to ostatni bajt w bloku, to wysyłając do wyroczni różne C’ dla wszystkich
możliwych wartości B[i] w pewnym momencie wyrocznia zwróci nam komunikat o poprawnym
paddingu (bardziej prawdopodobne, o braku błędu w paddingu). Wiemy więc, na jaką wartość należy
ustawić ostatni bajt w B , aby poprawnie odszyfrować C’ .
Ale co to oznacza?
Ni mniej ni więcej, że ostatni bajt P’2 jest równy \x01 ! (lub \x02 , jeśli przedostatni bajt w P’2 też
miał wartość \x02 ).
4/5
Pn[i] = \x01 ^ Cn-1[i] ^ B[i] , gdzie i , to ostatni bajt bloku
Powyższe równanie, to równanie z jedną niewiadomą: właśnie „złamaliśmy” ostatni bajt n-tego bloku
oryginalnego tekstu jawnego P .
Kolejnym etapem będzie „złamanie” przedostatniego bajtu tekstu jawnego. Jak to zrobić?
Ustawmy B[i] na taką wartość, aby P’2[i] = \x02 , gdzie i to ostatni bajt bloku. Dlaczego
\x02 ?
Bo chcemy, żeby odszyfrowany tekst jawny P’ kończył się ciągiem \x02\x02 . Jak obliczyć nową
wartość dla B[i] ?
Należy iterować po wszystkich bajtach w bloku B w sposób opisany powyżej, aż „złamiemy” cały blok
Pn . Iterując po wszystkich blokach P , złamiemy cały szyfrogram C . No, prawie cały, bo bez
znajomości IV , nie jesteśmy w stanie odszyfrować pierwszego bloku C 1 .
Na (nie)szczęście programiści często popełniają błąd i ustawiają IV = 0 lub dołączają go jako pierwszy
blok szyfrogramu C.
Podsumowanie
Mam nadzieję, że nie zanudziłem was tą matematyką. Starałem się wytłumaczyć logikę stojącą za
atakiem, ponieważ wydaje mi się, że przeciętny pentester jest w stanie to ogarnąć.
Na koniec chciałbym Wam zaproponować nietypowy CTF. Pod tym linkiem znajdziecie podatną aplikację,
a zadanie polega na napisaniu własnego narzędzia do wykonania wyżej opisanego ataku.
Powodzenia!
— Piotr Chmyłkowski
5/5