:

Sandbox w Google Chrome

chrome:easy:windows:security:re
Wczoraj miałem okazję się przyjrzeć 'piaskownicy' zawartej w Google Chrome. Ale po kolei...
Od kilku dni trwa dyskusja o niejako innowacyjnym pomyśle Google aby za każdy wyświetlany tab był odpowiedzialny osobny proces. Jak już pisałem wcześniej, mi osobiście ten pomysł się spodobał, więc postanowiłem skorzystać z open-source'owości Chrome i przyjrzeć mu się z bliska. Okazuje się że każdy renderer (czyli proces odpowiedzialny za pojedynczy tab) działa na maksymalnie ograniczonych prawach. Jak można przeczytać w wyżej zlinkowanym dokumencie, w użyciu są 4 Windowsowe mechanizmy (chwilowo nie miałem czasu się przyjrzeć Chrome na inne OSy):

Tokeny: Dostępne pozostają Logon SID (Mandatory) i NULL SID (S-1-0-0, Mandatory, Restricted), reszta SIDów jest Deny. Zostają również usunięte wszystkie prawa (Se*)
Job objects: Chyba wszystkie możliwe limity zostały wprowadzone - są limity na Desktop, Display Settings, Exit Windows, Global Atoms, USER Handles, Read/Write Clipboard, System Parameters i Administrator Access
Alternatywny pulpit: Dodatkowo proces podczepiony jest pod nowo-utworzony Desktop, dzięki czemu proces nie może wysyłać wiadomości do innych procesów które są na normalnym pulpicie
Poziom integralności: To akurat dostępne jest tylko na Viście - renderery działają na niskim integrity level

Dodatkowo panowie z Google zaimplementowali pewien mechanizm hooków na niektóre procedury z ntdll.dll, i kilka innych z kernel32.dll. Hooki są założone na następujące funkcje:

(NT) NtCreateFile
(NT) NtOpenFile
(NT) NtQueryAttributesFile
(NT) NtQueryFullAttributesFile
(NT) NtSetInformationFile
(NT) NtOpenThread
(NT) NtOpenProcess
(NT) NtOpenProcessToken
(NT) NtSetInformationThread
(NT) NtOpenThreadToken
(NT) NtOpenProcessTokenEx
(NT) NtOpenThreadTokenEx
(NT) NtCreateKey
(NT) NtOpenKey
(EAT) CreateNamedPipeW
(EAT) CreateProcessW
(EAT) CreateProcessA
(EAT) CreateEventW
(EAT) OpenEventW
(NT) NtMapViewOfSection
(NT) NtUnmapViefOfSection

Hooki które oznaczyłem jako NT są tak na prawdę inline-patchami umieszczonymi tuż przed wywołaniem SYSENTER. Przykładowy hook wygląda następująco:

; Założony hook
7C90DD7B > B8 7A000000      MOV EAX,7A      ; Numer syscalla
7C90DD80   BA A5011500      MOV EDX,1501A5  ; Adres wrappera
7C90DD85   FFE2             JMP EDX         ; Skok do 1501A5
7C90DD87   C2 1000          RETN 10         ; To się nigdy nie wykona

; Wrapper na handler hooka
001501A5   83EC 08          SUB ESP,8
001501A8   52               PUSH EDX
001501A9   8B5424 0C        MOV EDX,DWORD PTR SS:[ESP+C]
001501AD   895424 08        MOV DWORD PTR SS:[ESP+8],EDX
001501B1   C74424 0C 900115>MOV DWORD PTR SS:[ESP+C],150190 ; Adres oryginału
001501B9   C74424 04 600D44>MOV DWORD PTR SS:[ESP+4],440D60 ; Adres handlera funkcji
001501C1   5A               POP EDX
001501C2   C3               RETN

; Zarchiwizowany oryginalny kod
00150190   B8 7A000000      MOV EAX,7A
00150195   BA 0003FE7F      MOV EDX,7FFE0300
0015019A   FF12             CALL DWORD PTR DS:[EDX]
0015019C   C2 1000          RETN 10

Wrapper i oryginalny kod jest umiejscowiony w dynamicznie zaalokowanej pamięci o niestałym adresie. Sam handler funkcji napisany jest w C++, a w źródle zazwyczaj ma prefix Target (np. TargetNtCreateFile). W momencie "złapania EIP" w hook, następuje przesłanie komunikatu z renderera do browsera z pytaniem, czy dana operacja (np. otwarcie pliku) może zostać wykonana. W zależności od decyzji browsera zwracany jest albo Access Denied, albo dana operacja jest wykonywana.

Powyższy mechanizm ma na celu oczywiście wprowadzić pewien layer bezpieczeństwa, tak aby utrudnić życie potencjalnemu atakującemu (a raczej jego shellcode'owi). Natomiast co tu dużo mówić, samo jego położenie (user mode) powoduje że mechanizm nie stanowi praktycznie żadnej przeszkody dla potencjalnego atakującego. Atakujący może albo posłużyć się bezpośrednio syscallami (o czym zresztą napisali panowie z Google w dokumentacji Chrome), albo po prostu zdjąć hook. Poniżej znajduje się kod PoC który zdejmuje hook z podanej procedury (dodam że jest dostosowany pod XP SP2, to tylko PoC):

void RemoveNtIntercept(LPCSTR FunctionName, BYTE Retn)
{
 HINSTANCE NtDll = LoadLibraryA("ntdll.dll");
 DWORD OldPriv;
 DWORD FuncAddr = (DWORD)GetProcAddress(NtDll, FunctionName);
 if(FuncAddr == 0) return;

 FuncAddr += 6; // Skip 6 bytes (MOV EAX, SYSCALL_NUMBER + first byte of MOV EDX, SYSCALL_HANDLER)
 VirtualProtect((LPVOID)FuncAddr, 10, PAGE_EXECUTE_READWRITE, &OldPriv);

 *((DWORD*)(FuncAddr))   = 0x7FFE0300; // Restore original handler
 *((DWORD*)(FuncAddr+4)) = 0xC212FF | (((DWORD)Retn << 24) & 0xFF000000) ; // call + retn
 *((BYTE* )(FuncAddr+8)) = 0x00;       // retn cd.

 FreeLibrary(NtDll);
}

Przykładowe użycie wygląda następująco:

 RemoveNtIntercept(output, "NtCreateFile", 0x2C);

Co tu dużo mówić. Kod jest dość krótki, niezbyt piękny, ale z hookami radzi sobie całkiem nieźle. Szczerze mówiąc z jednej strony sam pomysł z hookami mi się podoba (warto btw rzucić okiem na kod, jest ładnie napisany, i do tego na licencji BSD ;>), ale z drugiej strony, jako zabezpieczenie jest Vulnerable By Design.
Zachęcam do zabawy z Sandboxem. Ichni sandbox_poc jest dość łatwy do skompilowania i umożliwia potestowanie sandboxa bez konieczności babrania się ze stosunkowo dużym Chrome.

Na zakończenie postu kilka ciekawostek które znalazłem przeglądając Chrome. Na początek kilka zabawnych nazw funkcji z pliku testowego chromium\src\sandbox\src\interception_unittest.cc:

 interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx",
   INTERCEPTION_SMART_SIDESTEP, function);
 interceptions.AddToPatchedFunctions(L"b.dll",
   "TheIncredibleCallToSaveTheWorld", INTERCEPTION_EAT, function);
 interceptions.AddToPatchedFunctions(L"a.dll", "BIsLame",
   INTERCEPTION_EAT, function);
 interceptions.AddToPatchedFunctions(L"a.dll", "ARules",
   INTERCEPTION_EAT, function);

Kolejną sprawą jest fakt że do obsługi protokołu FTP Chrome korzysta z WinAPI, przez co logując się na FTP wysyła następujące dane:

USER anonymous
PASS IEUser@

Cóż. Prawie jak IE ;>
Swoją drogą widziałem że dzisiaj był patchowany jakiś crash związany z FTP i datą modyfikacji plików. Czyżby kolejny remote DoS?

OK. Tyle na dziś.

UPDATE: Artykuł okazał się trochę nieprawidłowy. Polecam zapoznać się z tym postem.

Comments:

2008-09-06 11:47:12 = ged_
{
nie wiem po co google sie tak natrudzil z tym sandboxem lol. ciekawe czy se sprawe zdaja, ze calosc jest bez sensu..
}
2008-09-06 11:56:03 = Gynvael Coldwind
{
Akurat nie cały sandbox jest bez sensu, tylko część - np. te hooki. Co do hooków, cóż, to akurat wiedzą, sami w dokumentacji zaznaczyli że można syscalle bezpośrednio wywołać, i to obejdzie ten mechanizm.
}

Add a comment:

Nick:
URL (optional):
Math captcha: 8 ∗ 4 + 6 =