Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

$EFS, cz. 1

System operacyjny dostarcza wiele usług, których szczegóły implementacyjne nie interesują nas tak długo, jak długo wszystko jest w porządku i możemy żyć sobie w błogiej nieświadomości. Jednak pad dysku, poważne uszkodzenie systemu, lub inna przyczyna, która uniemożliwia nam normalne uruchomienie systemu (oraz wszelkie inne powody, które balansują na krawędzi prawa) zachęcają nas do poznania niektórych tajemnic systemu, skrywanych pod przyjaznymi oknami dialogowymi eksploratora, lub lakonicznymi komunikatami narzędzi konsolowych. Sięgamy wówczas po narzędzia, o których istnieniu nie zdawaliśmy sobie sprawy i w ciągu paru chwil wpadamy między śrubki i koła zębate, czy raczej pojedyncze bity powtykane tu i ówdzie w systemie.
Jednym z takich arcyciekawych, niezwykle bogatych w walory edukacyjne obszarów jest EFS - podsystem umożliwiający szyfrowanie plików oraz katalogów na partycjach NTFS. I jeśli chcesz wziąć udział w podróży, w czasie której dowiesz się jak działa EFS, w którym miejscu rozdziału 12 'Windows Internals' Mark Russinovich wprowadza Cię w błąd, a także - jak zrobiłem to wcześniej ja, a później Grzesiek Tworek, który słuchał moich opowieści o zabawach z EFS - być może stwierdzisz, że 'ciężko uwierzyć, że tak to zrobili', to usiądź wygodnie i rozpocznij lekturę tego i kolejnych tekstów.

Wprowadzenie

Jakkolwiek w poniższym tekście nie zamierzam opisywać narzędzi, z pomocą których można zarządzać EFS, to jednak minimalny wstęp należy uczynić. Loguję się zatem jako Admin i w katalogu c:\Roboczy\EFS tworzę plik encrypted_file.txt o zawartości

1234567890abcde

zapisany z kodowaniem ANSI - plik ma zatem rozmiar 15. bajtów. Otwieram właściwości pliku, przechodzę do zakładki General, klikam Advanced i zaznaczam 'Encrypt contents to secure data' (Ogólne -> Zaawansowane, 'Szyfruj zawartość, aby zabezpieczyć dane'). Zatwierdzam kolejne komunikaty, klikam Apply i wchodząc po raz kolejny do okna 'Advanced Attributes' mogę podejrzeć szczegóły ('Details'), a wszystko to razem ilustruje Rys 1.


Rys 1. Szczegóły zaszyfrowanego pliku.

Jak widać, mamy jednego użytkownika, który ma dostęp do tego pliku i żadnego wpisu na liście certyfikatów pozwalających na odzyskanie pliku. Tu drobna uwaga: gdybyśmy podobną czynność powtórzyli w środowisku domenowym, to najprawdopodobniej na liście Recovery certificates pojawiłby się wpis powiązany z administratorem domeny i jego certyfikatem.
Plik zmienił kolor swojej nazwy w eksploratorze windows na zielony i jest to niechybny dowód na to, że od tej pory tylko Admin może odczytać jego zawartość, a próba otwarcia tego pliku przez innych użytkowników skończy się lakonicznym 'Access is denied', pomimo istnienia ACLi zezwalających na dowolny dostęp do pliku.
Oczywiście, gdybyśmy powtórzyli te czynności jako zwykły użytkownik, to będąc administratorem systemu, ani nawet samym SYSTEMem nie uzyskamy dostępu do zawartości pliku, a precyzyjniej odszyfrowanej zawartości.
I to tyle słowem wstępu. Czas przejść do mięska!
Zanim jednak to zrobimy, sugeruję zajrzeć do wspomnianej wcześniej książki 'Windows Internals' Marka Russinovicha, a dokładniej podrozdziału 'Encrypting File System Security' w rozdziale 12 -  'File Systems' (wydanie 6, tom II), a jeszcze lepiej - całego rozdziału 12. Nie jest to konieczne dla zrozumienia reszty, ale na pewno bardzo pomocne.

$EFS

NTFS obsługuje alternatywne strumienie plików (ADS), pośród których te najbardziej nas interesujące w kontekście EFS to atrybut $DATA, który zawiera dane pliku oraz $EFS typu $LOGGED_UTILITY_STREAM, o którym za chwilę więcej. Niestety, żadne z wbudowanych narzędzi nie daje bezpośredniego wglądu we wspomniane atrybuty (ani dir /q, ani nawet streams z pakietu narzędzi Systinernals) i aby się do nich dobrać, potrzebujemy nieco innego podejścia. Ja sięgnąłem po bibliotekę The Sleuth Kit (TSK), napisanej w C oraz biblioteki sleuthkit-sharp, która po pewnych przeróbkach okazała się bardzo interesującym rozwiązaniem. Szczególnie ciekawy jest sposób, w jaki z poziomu C# dostajemy się do funkcji napisanych w C i dostępnych w ramach statycznej biblioteki (.lib) TSK (bo TSK to właśnie .lib do zlinkowania w projektach c/c++). Przy samodzielnych przeróbkach sugeruję zapamiętać, że CallingConvention to Cdecl i w przypadku konieczności stosowania delegatów jako natywnych callbacków polecam stosować atrybut [MarshalAs(UnmanagedType.FunctionPtr)] w parametrach importowanych funkcji, np.:

[DllImport(NativeLibrary, CallingConvention = CallingConvention.Cdecl)]

internal static extern byte tsk_fs_file_walk_type(FileHandle a_fs_file, int a_type, UInt16 a_id, UInt32 a_flags, [MarshalAs(UnmanagedType.FunctionPtr)] TSK_FS_FILE_WALK_CB a_action, IntPtr a_ptr);


Z drugiej strony, korzystając z funkcji NtQueryInformationFile z ntdll.dll oraz wartości FILE_INFORMATION_CLASS.FileInternalInformation (6) można dosyć szybko dobrać się do interesującego nas węzła pliku (inode) poprzez jego reference number.
Pamiętając dodatkowo, że atrybut $DATA to 128, a $LOGGED_UTILITY_STREAM to 256 możemy pokusić się o zrzucenie zawartości obu atrybutów do osobnych plików.
Tak oto uzyskujemy plik encrypted_file.txt.data oraz encrypted_file.txt.efs z odpowiednimi zawartościami.

Dla tych z was, którzy nie chcą wchodzić w szczegóły TSK i brudzić sobie rąk siedzeniem w kodzie polecam użycie kolejno narzędzi z zestawu TSK (uwaga! bezpośredni dostęp do dysku wymaga uprawnień administratora):

>mmls \\.\PhysicalDrive0

W wyniku otrzymujemy tablicę partycji, bierzemy offset startowy partycji NTFS zawierającej nasz plik, niech to będzie np. 731136. Przeczesujemy rekursywnie (-r) system plików w poszukiwaniu naszego pliku (-p <- print full path):

>fls -o731136 -r -p \\.\PhysicalDrive0

i tak uzyskany inode wrzucamy do:

>istat -o731136 \\.\PhysicalDrive0 1898365

dzięki czemu uzyskujemy informacje o powiązanych strumieniach, w tym nasz $EFS - powiedzmy 256-8. Zrzucamy jego zawartość do pliku

>icat -o731136 \\.\PhysicalDrive0 1898365-256-8 > encrypted_file.txt.efs

i powtarzamy to dla $DATA. Zawartość tego ostatniego ilustruje Rys 2.



Rys 2. Zawartość $DATA zaszyfrowanego pliku

Dla testu sugeruję sprawdzić zawartość $DATA dla innych, niezaszyfrowanych plików i upewnić się, że wszystko się zgadza i niczego nie pokręciliśmy.
No cóż - tak, jak możemy się spodziewać, zawartość jest zaszyfrowana. Wybieramy długą ścieżkę i spróbujemy odzyskać oryginalne dane.

API EFS

Zacznijmy od skorzystania z narzędzia Sysinternals, efsdump:

>efsdump.exe encrypted_file.txt

EFS Information Dumper v1.02
Copyright (C) 1999 Mark Russinovich
Systems Internals - http://www.sysinternals.com

C:\Roboczy\EFS\encrypted_file.txt:
DDF Entry:
    VM7\Admin:
        Admin(Admin@VM7)
DRF Entry:

Staruszek efsdump użył funkcji QueryUsersOnEncryptedFile z winefs.h (advapi32.dll) aby dobrać się do listy użytkowników z powiązanych certyfikatów i to w zasadzie wszystko. Wiemy, ze w sekcji DDF (za chwilę więcej) jest wpis odnoszący się do usera VM7\Admin i brak jakiegokolwiek wpisu w sekcji DRF. Moglibyśmy spędzić jeszcze chwilkę przeglądając API EFS (np. funkcję AddUsersToEncryptedFile), ale na tym poprzestaniemy i przejdziemy do samej zawartości atrybutu $EFS.


Analiza zawartości $EFS

Przyjrzyjmy się bliżej zawartości strumienia $EFS. Russinovich w swojej książce opisuje z grubsza całą strukturę i w zasadzie opierając się na tym opisie oraz pewnych domysłach (stringi zapisane są w kodowaniu UTF-16LE i poprzedzone intem zawierającym długość łańcucha; poszczególne wpisy w sekcjach DDF/DRF muszą być poprzedzone nagłówkiem zawierającym liczbę + offsety do odpowiednich pól; cały plik zaczyna się od nagłówka zawierającego odniesienia do poszczególnych sekcji (DDF/DRF) i tym podobnych) można pokusić się o odtworzenie całej struktury. Można również zajrzeć do pliku layout.h ze sterownika linuxowego ntfs-3g i skorzystać z opisanych tam struktur.

DDF i DRF

Tuż po nagłówku pliku, EFS umieszcza Data Decryption Fields, a następnie Data Recovery Fields, jedne i drugie zawierające szczegóły związane z kluczami szyfrującymi oraz użytymi certyfikatami.
Poczynając od zgrubnego podziału (Header, DDFs, DRFs) uzyskujemy Rys 3.


Rys 3. Plik .efs z zaznaczonymi nagłówkiem oraz sekcją DDF

W przypadku, gdyby były jeszcze jakieś wpisy w sekcji DRFs, to byłyby one umieszczone bezpośrednio po sekcji DDFs, w której w naszym przypadku mamy tylko jeden wpis. Bliższe szczegóły odnajdziemy na Rys 4.


Rys 4. Najciekawsze elementy sekcji DDF ze strumienia $EFS

Przy czym szczegółowa legenda znajduje się na Rys 5.


Rys 5. Strumień $EFS po rozpisaniu na poszczególne struktury

Z naszego punktu widzenia najciekawsze są elementy zaznaczone kolorami:
  1. [ ] - SID użytkownika, z którym powiązany jest certyfikat (S-1-5-21-580747136-2243477503-2994681153-1000);
  2. [ ] - thumbprint (odcisk palca) certyfikatu: EFF5EDAB8123D06B6EFEA7D87716B03F9C8F48CD;
  3. [ ] - 256 bajtów zawierających zaszyfrowany FEK.

Jako za(na)stępcę narzędzia efsdump Sysinternalsów używam własnego toolka, który zbiera powyższe dane 'do kupy' i prezentuje jak poniżej:

>EFSAnalysis.exe encrypted_file.txt.efs

EFS Stream Header:
Len: 664, state: 0, ver: 2, crypto api ver: 0
Num of DDFs: 1, DRFs: 0

DDFs:
SID: S-1-5-21-580747136-2243477503-2994681153-1000:
Certificate:
   Thumbprint: EFF5EDAB8123D06B6EFEA7D87716B03F9C8F48CD
   Container name: 0d29406b-f9e6-4659-90e4-c407201049d2
   Provider name: Microsoft Enhanced Cryptographic Provider v1.0
   User name: Admin(Admin@VM7)
Encrypted FEK:
FE 1B 0D 07 2F 56 96 09 6C D8 DD 54 92 FF 23 2E   ..../V?.lOYT?.#.
15 93 D2 4C 5F 4D 76 F6 35 DB 75 BF 5C 24 4C D5   .?OL_Mvö5Uu¿\$LO
46 12 B7 DD C2 09 F5 DB 0E 01 CC E0 51 B0 A6 87   F.·YA.oU..IàQ°▌?
15 AB A9 FC 56 BE 3C BD 0A BA C6 D8 DF 6A DE 4B   .«cüV_<½.ºÆOßj_K
78 BB E0 CC 2F 88 86 BC 20 23 BB 86 EB 72 AC 4B   x»àI/??¼.#»?ër¬K
4F AE 05 9F 1A 99 BC BC 80 52 FB D1 8B 7B 90 B1   Or.?.?¼¼?RûÑ?{?±
8B E9 B7 A4 85 D0 FF 18 3C C9 C5 8C 9A 7A 1C 04   ?é·☼?D..<ÉÅ??z..
D6 C7 A6 0B A8 76 4E 9C 87 3E B8 DC 62 C3 ED 57   ÖÇ▌."vN??>,ÜbAíW
CB FE 01 B8 20 91 87 EB 96 18 14 0B DF 2E E4 F8   E..,.??ë?...ß.äo
7A 5D C8 1D 77 0F E6 9A 7F 89 B9 C0 E5 8E DC 4D   z]E.w.æ?.?1Aå?ÜM
5A C5 9C 3E 05 24 89 21 C9 79 73 9F 42 FE 64 AC   ZÅ?>.$?!Éys?B.d¬
4E 28 B6 25 1A 58 0E A0 D0 68 4B 38 6B 78 6D F6   N(¶%.X. DhK8kxmö
41 6A 8A 70 24 CB 45 48 11 81 C9 06 9B 75 18 A1   Aj?p$EEH.?É.?u.¡
10 33 46 9B 63 D9 4E CF 44 C5 4E 4A 58 F6 F8 15   .3F?cUNIDÅNJXöo.
BF FD 43 98 74 2B E5 55 9A 9E FF 77 55 5B CD F9   ¿yC?t+åU??.wU[Iù
CC 48 31 BC 51 17 67 F5 61 BB A2 C4 88 2A 42 83   IH1¼Q.goa»¢Ä?*B?


FEK


Czym jest FEK? FEK (File Encryption Key) to całkowicie przypadkowy ciąg bajtów, którego system użył do zaszyfrowania zawartości strumienia $DATA w momencie, kiedy potwierdziliśmy w eksploratorze chęć zaszyfrowania pliku. Następnie system użył klucza publicznego certyfikatu użytkownika (Admin, certyfikat z przeznaczeniem do użycia w EFS) o thumbprincie opisanym w pkt. 2 i zaszyfrował nim FEK, po czym tak uzyskany ciąg 256 bajtów zapisał w omawianej sekcji DDF. Szczegóły dotyczące użytych algorytmów oraz ich parametrów omówię w kolejnych częściach. Już teraz jednak napiszę, że:
  1. FEK został zaszyfrowany przy użyciu RSA z kluczem 2048 bitowym;
  2. Dane pliku zostały zaszyfrowane z użyciem algorytmu AES256 w trybie CBC, a nie - jak pisze Russinovich w podrozdziale ‘Encrypting File Data’ - DESX, lub 3DES. Tak było, owszem, ale w poprzednich wersjach Windows, w Windows 7 domyślnie stosowany jest AES. Odnoszę wrażenie, że Mark padł ofiarą ctrl+c, ctrl+v z poprzednich edycji książki, tym bardziej, że na samym początku rozdziału; w 3 akapicie pisze:
    "By default, EFS will use the Advanced Encryption Standard (AES) for symmetric encryption (256-bit key) and the Rivest-Shamir-Adleman (RSA) public key algorithm for assymetric encryption (2,048-bit keys)."
    No cóż, happens :)
Wrócmy jednak do meritum.
Co się dzieje, gdy użytkownik (Admin) chce dodać kogoś do listy pozwalającej na dostęp do pliku? System wykorzystuje klucz prywatny powiązany z certyfikatem użytkownika (Admin) i odszyfrowuje FEK, po czym dodaje kolejny wpis DDF, w którym umieszcza thumbprint certyfikatu użytkownika, któremu chcemy zezwolić na dostęp (powiedzmy, User) oraz wykorzystując klucz publiczny tegoż certyfikatu szyfruje FEK, i tak zaszyfrowany ciąg zapisuje w odpowiednim miejscu sekcji klucza DDF.
Jak widać, klucz użyty do szyfrowania pliku jest jeden i aby się do niego dobrać, musimy mieć dostęp do klucza prywatnego któregokolwiek z uprawnionych użytkowników (z sekcji DDF, lub DRF).

Na koniec tego podrozdziału i wyprzedzając nieco fakty rzućmy jeszcze okiem na przykładowy FEK. Po odszyfrowaniu 256 bajtów znajdujących się w sekcji DDF (lub DRF, bez znaczenia) otrzymujemy 48-bajtową tablicę (Rys 6)


Rys 6. FEK wraz dodatkowymi informacjami

zawierającą szczegóły dotyczące długości klucza (32*8 = 256 bitów), użytego algorytmu oraz wyróżniony na czerwono sam klucz deszyfrujący (Rys 7)


Rys 7. FEK - szczegóły

Dla porządku zestaw dostępnych algorytmów (na podstawie ntfs-3g):
CALG_DES = 0x6601,

CALG_3DES = 0x6603,

CALG_DESX = 0x6604,

CALG_AES = 0x6610.


Thumbprint

Znając odcisk palca certyfikatu użytkownika oraz jego SID, możemy pokusić się o poszukanie odpowiadającego im certyfikatu. Z jednej strony możemy sięgnąć do rejestru użytkownika (a dokładniej klucza Software\Microsoft\Windows NT\CurrentVersion\EFS\CurrentKeys w pliku ntuser.dat) oraz gałęzi SOFTWARE (Microsoft\SystemCertificates), lub - co ma większy sens i tą drogą pójdziemy, udać się do katalogu profilu użytkownika, a dokładniej podkatalogu
%APPDATA%\Microsoft\SystemCertificates\My\Certificates, co w moim przypadku przekłada się na C:\Users\Admin\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates. Bez trudu znajdujemy tam plik o wiele mówiącej nazwie (Rys 8):


Rys 8. Zawartość katalogu z certyfikatami użytkownika
która jest po prostu thumbprintem certyfikatu.

W tym miejscu zatrzymujemy się na odpoczynek, a w następnej części spróbuję odpowiedzieć na - być może - rodzące się pytania:
  • skąd wziąłem klucz deszyfrujący dla FEKa?
  • w jaki sposób korzystam z FEKa do odszyfrowania zawartości pliku?
I niby wszystko wydaje się być oczywiste - w końcu mam działający system i mogłem sobie wyeksportować klucze do pliku, gdzież tu trudność?! Ja jednak tego nie zrobiłem i całą operację przeprowadziłem offline, a po szczegóły zapraszam niebawem.
Opublikowane 16 marca 2015 23:16 przez mgrzeg
Filed under:

Powiadamianie o komentarzach

Jeżeli chciałbyś otrzymywać email gdy ta wypowiedź zostanie zaktualizowana, to zarejestruj się tutaj

Subskrybuj komentarze za pomocą RSS

Komentarze:

Brak komentarzy

Co o tym myślisz?

(wymagane) 
(opcjonalne)
(wymagane) 

  
Wprowadź kod: (wymagane)
Wyślij

Subskrypcje

W oparciu o Community Server (Personal Edition), Telligent Systems