Opera – Null Pointer Dereference

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

[+]Gdzie znajduje sie bug?
Rzućmy okiem na wartosci rejestrow,elementow na stos podczas wystapienia tego bledu.
Opera 10.10b PL
ok_10.10_first_instruction
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.
compare_pl_eng
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”
null_pointer_dereference
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 ;).

Ten wpis został opublikowany w kategorii Analiza, RE, Security i oznaczony tagami , , , . Dodaj zakładkę do bezpośredniego odnośnika.

Możliwość komentowania jest wyłączona.