Ahh… sporo czasu upłynęło od ostatniego wpisu, lecz bez wdawania się w szczegóły chciałem uspokoić, że prac nad tworzeniem tego blogu nie porzuciłem i w miarę możliwości będą się tu pojawiały nowe wpisy ;).
A teraz do rzeczy…Tak jak w tytule post będzie traktował o bug’u typu Null Pointer Dereference znajdującym się dokładnie w opera.dll do wersji Opera 10.53 włącznie, jedynie od wersji 10.10 blad nie bedzie wystepowal w sposob “automatyczny”, a będzie wymagal od nas jeszcze wiekszej ingerencji niz w wersjach poprzednich. Jednak tak jak wspomniałem kod odpowiedzialny za ten bug nie zostal poprawiony do dzisiaj ;/.
Wspominam o tej „niedociągłości” bardziej w ramach ciekawosteki niż prezentacji konkretnego bug’u do wyexploitowania, chociazby z tego powodu ze wywolanie go wymaga NIE MALEJ ingerencji od user’a.
[+]Dlaczego warto o nim wspomnieć?
1. Dla zdobycia odpowiedzie na pytania, które tygrysy lubią najbardziej : czyli po co, na co ,dlaczego ,dlaczego tak a nie inaczej 😉
2. Dla zaprezentowania wektora ataku np. na przekladarke.
[+]Jak wywołać bug?
Wystarczy stworzyc pusty plik i nadac mu rozszerzenie .eml (trudno okreslic dla jakich jeszcze rozszerzenie opera tak zareaguje, ze wzgledu na pokazny kod do rewersowania, w celu ustalenia tych danych oraz mala uzytecznosc tego bug’a jak dla mnie 😉 ),wrzucic ten plik do katalogu np. apacha’a ( to istotne zeby odwolywac sie do pliku zdalnie) odwolac sie do niego i nastepnie wywolac opcje ‘Zapisz jako’.
[+]Gdzie znajduje sie bug?
Rzućmy okiem na wartosci rejestrow,elementow na stos podczas wystapienia tego bledu.
Opera 10.10b PL
Pointer na unikodowy string leżący na stosie może sugerować nam, że błąd może być związany z wypelnianiem kontrolki trzymajacej opisy typow plikow do jakich opera moze zapisac bierzacy kontent. Zdziwiłem się delikatnie, kiedy na tej samej wersji przegladarki, jedynie z zaladowanym angielskim tlumaczeniem wywolanie bug’a nie udalo sie powtorzyc. Co tu moze byc problemem?!….
[+]Wektor ataku
Wektorem ataku okazują sie tutaj pliki z tlumaczeniami,
($INSTALDIRlocale[jezyk][jezyk])
ktorych nie do końca poprawne zdefiniowanie może spowodować DoS’a ze względu na nie sprawdzanie w kodzie poprawności zawartych tam danych. Taka sytuacja bez naszej ingerencji ma miejsce do Opery 10.10b ze wzgledu na nie poprawnie zdefiniowany plik z tlumaczeniem dla jezyka polskiego.
Odnajdzmy wczesniej prezentowany interesujacy string na screen’e w pliku z tlumaczeniem PL oraz sprobujmy znaleść rożnice w formacie dla pliku z tlumaczeniem ANG.
Baah!!!Jak widać string zawierający tekst, który jest umieszczany pozniej w kontrolce, zawierajacej liste typow plikow mozliwych do zapisu przez opera oraz rozszerzenie pod jakim plik zostanie zapisany w wersji PL nie zawiera znaku ‘|’ co jak okazuje sie jest zrodlem problemu.
[+]Malo bezpiecznie zdefiniowana funkcja
Powróćmy teraz do Olka i przyjrzymy sie miejscu gdzie nastepuje dereferencja pointer’a zerowego:
Analiza kodu dla poprawnie zdefiniowanego parametru:
-1371929526=”Archiwum strony WWW (jeden plik)|*.mht|”
registers:
EAX = 0x1732F8C
ECX = 0x1732FA0
entire code:
67ADDA16 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4] //eax == save_as_struct
67ADDA1A 8D48 14 LEA ECX,DWORD PTR DS:[EAX+14]
67ADDA1D 8B40 2C MOV EAX,DWORD PTR DS:[EAX+2C]
67ADDA20 57 PUSH EDI
67ADDA21 E8 F842D8FF CALL Opera_1.67861D1E
67ADDA26 8BC8 MOV ECX,EAX
67ADDA28 83C1 08 ADD ECX,8
67ADDA2B 33C0 XOR EAX,EAX
67ADDA2D E8 EC42D8FF CALL Opera_1.67861D1E
>>> 67ADDA32 8B00 MOV EAX,DWORD PTR DS:[EAX] //eax = pointer on string contains file extension (e.g *.mht) or NULL
67ADDA34 8B7C24 0C MOV EDI,DWORD PTR SS:[ESP+C] //esp+c == buffer_for_copyOf_file_extension
67ADDA38 6A FF PUSH -1
67ADDA3A 50 PUSH EAX
67ADDA3B E8 60B3CBFF CALL Opera_1.67798DA0
67ADDA40 5F POP EDI
67ADDA41 C3 RETN
struct save_as_struct
{
0x1732F8C /* +0 */ 0x00000000
0x1732F90 /* +4 */ 0x00000000
0x1732F94 /* +8 */ 0x00000000
0x1732F98 /* +C */ 0x60D76F01
0x1732F9C /* +10 */ 0x38000000
0x1732FA0 /* +14 */ 0x4805E967 //ecx pointer
0x1732FA4 /* +18 */ 0x0A000000
0x1732FA8 /* +1C */ 0xC0E59301 //ecx + 0x8 //pointer on array->pointer->string('Archiwum strony WWW (jeden plik)')
0x1732FAC /* +20 */ 0x01000000 //ecx + 0xc // count of elements
0x1732FB0 /* +24 */ 0x0A000000
0x1732FB4 /* +28 */ 0x00000000
0x1732FB8 /* +2C */ 0x00000000
};
first call of 67861D1E:
ECX = 0x1732FA0 /* +0x14 */
EAX = 0
67861D1E 3B41 0C CMP EAX,DWORD PTR DS:[ECX+C] /* eax = 0; ecx + C == count of elements*/
67861D21 73 07 JNB SHORT Opera_1.67861D2A
67861D23 8B49 08 MOV ECX,DWORD PTR DS:[ECX+8] /* ecx = [ecx + 8]; [ecx+8]==pointer on array->pointer->string */
67861D26 8B0481 MOV EAX,DWORD PTR DS:[ECX+EAX*4] /* eax = pointer->string */
67861D29 C3 RETN
67861D2A 33C0 XOR EAX,EAX
67861D2C C3 RETN
second call of 67861D1E:
ECX = 0x0166C270
EAX = 0
stack view:
0x0166C270 /* +0 */ 0xB832DC67
0x0166C274 /* +4 */ 0x0A000000
0x0166C278 /* +8 */ 0xC0DF9301 //pointer on array->pointer->string('*.mht')
0x0166C27C /* +C */ 0x01000000 // count of elements
Dereferencja null pointer’a dla blednie zdefiniowanego parametru:
-1371929526=”Archiwum strony WWW (jeden plik)|*.mht”
Tak jak pisalem wczesniej nie analizowałem dogłebnie calego procesu parsowania pliku z tlumaczeniem …
Bez doglebnej analizy wczesniej wykonanego kodu przed kodem prezentowanym powyzej, mozemy stwierdzic ze brak znaku ‘|’ na koncu ‘parametru’ -1371929526 powoduje bledny split lancucha znakow i tym samym nie wypelnienie pola struktury gdzie powinien sie znalesc pointer na rozszerzenie pliku pod jakim miala by byc zapisana zawartosc strony.
Całe zło zaczyna sie dziać przy drugi wywołaniu funkcji 67861D1E:
second call of 67861D1E: // for bad parameter
ECX = 0x0166C270
EAX = 0
Stack view:
0x0166C270 /* +0 */0xB832DC67
0x0166C274 /* +4 */0x00000000
0x0166C278 /* +8 */0x00000000
0x0166C27C /* +C */0x00000000
67861D1E 3B41 0C CMP EAX,DWORD PTR DS:[ECX+C] /* eax = 0; ecx + C == count of elements*/
67861D21 73 07 JNB SHORT Opera_1.67861D2A
Warunek skoku zostaje spelniony i mamy skok do instrukcji:
67861D2A 33C0 XOR EAX,EAX
67861D2C C3 RETN
zerujacej nam eax i wychodzacej z funkcji. Nastepnie bez sprawdzania czy wartosc w eax jest poprawnym wskaznikiem mamy dereferencje:
>>> 67ADDA32 8B00 MOV EAX,DWORD PTR DS:[EAX] //eax = pointer on string contains file extension (e.g *.mht) or NULL
[+]Konkluzja
Tak jak pisałem na początku wspominam o tym bug’u raczej z formie ciekawostki niz jakiegos żalu do twórców opery ze wzgledu na sposob jaki „pozbyli” sie problemu(od wersji 10.10 plik z tlumaczeniem we wspomnianej linijce zawiera na koncu ‘|’ :D). Co niektorzy mogą się przyczepic ze mozna przeciez wykonac tutaj sprawdzanie formatu i ewentualnie wyswietlic komunikat ze plik z tlumaczeniem zostal zmodyfikowany lub jest uszkodzony i nalezy go zastapic poprawna wersja,itd….Takie rozwazania i ocene podejscia dev’ow z opery pozostawia wam Drodzy Czytelnicy ;).