Quantcast
Channel: mgrzeg.net - Admin on Rails :) : dpapi

DPAPI Internals, a SecureString w PowerShellu (cz.1)

$
0
0

Jedną z bolączek wielu administratorów jest konieczność przechowywania ‘wrażliwych’ danych w bezpieczny sposób. Chodzi o wszelkiej maści pliki konfiguracyjne zawierające parametry połączenia, tudzież pliki zawierające login i hasło lokalnego administratora, które stanowią dane wejściowe dla programów typu runas, tworzących nowe procesy w kontekście użytkownika uprzywilejowanego. Jakiś czas temu opisywałem program cpau, który umożliwia przechowywanie danych w postaci zaszyfrowanej i odszyfrowywanie ich dopiero w momencie uruchomienia, co jednak dosyć łatwo udało nam się przechwycić korzystając wyłącznie z debuggera i dzięki czemu bez większych problemów offline’owo mogliśmy odczytać wszystkie dane: login, hasło, etc.
Microsoft rozumie potrzeby administratorów i między innymi dlatego udostępnił w ramach powershella zestaw kilku cmdletów umożliwiający bezpieczne zapisywanie i odczytywanie danych ‘wrażliwych’, którymi nie dzielimy się nawet z żoną :). Dwa z nich to ConvertTo-SecureString oraz ConvertFrom-SecureString. Użycie jest bardzo proste:

C:\PS>$secure = Read-Host -AsSecureString
C:\PS> $secure
System.Security.SecureString
C:\PS> $encrypted = ConvertFrom-SecureString -SecureString $secure
C:\PS> $encrypted
01000000d08c9ddf0115d1118c7a00c04fc297eb010000008727d6117f2b0c429d84f2b9f7e9ea35
0000000002000000000003660000a8000000100000009403327f1497e1a4266ca7d45f84266a0000
000004800000a000000010000000ed0b8a5247be59846e80ca735a1a3f9720000000a5025c6e7ee4
cad037fcd99a5cbbe4aef20d54e685352fa67c1deec0999779c7140000002f96e55035c6760aba6f
4bb9aeffc52616cb300c

W pierwszym poleceniu wczytujemy z klawiatury ciąg znaków, który trafia do zmiennej $secure, która - co widać po drugim poleceniu - jest typu SecureString. W kolejnym zawartość naszej zmiennej $secure zostaje przekonwertowana i zaszyfrowana, po czym trafia do zmiennej $encrypted. Na końcu wypisujemy zawartość zmiennej w postaci łańcucha znaków. Tak przygotowany łańcuch możemy zapisać w pliku, po czym w dowolnym momencie skorzystać z niego, np. korzystając z set-content oraz get-content:

C:\PS> $encrypted | set-content encrypted.txt
C:\PS> $secure2 = get-content encrypted.txt | ConvertTo-SecureString

Możemy jeszcze pokusić się o konwersję jawnego tekstu do securestringa, a następnie tego ostatniego przekonwertować do postaci zaszyfrowanej:

C:\PS> $secure_string_pwd = ConvertTo-SecureString "P@ssW0rD!" -asplaintext –force
C:\PS> $encrypted_pwd = ConvertFrom-SecureString -SecureString $secure_string_pwd
C:\PS> $encrypted_pwd
01000000d08c9ddf0115d1118c7a00c04fc297eb010000008727d6117f2b0c429d84f2b9f7e9ea35
0000000002000000000003660000a80000001000000037f547b0feab4a12ab792d809a63fd0a0000
000004800000a000000010000000426ad5282726129ddce3675e82c68ca018000000b00bf7525cca
296dc3753d4aaaf867deec089bcbc94c73c21400000091011d6139ce2150b304f0cabaa7dc8b21fb
65d3

Po czym ponownie zapisać to w pliku

C:\PS> $encrypted_pwd | Set-Content encrypted_pwd.txt

Tyle słowem wprowadzenia, nadszedł czas na zajrzenie nieco głębiej.
Rozpoczynamy od ustalenia biblioteki, w której znajduje się definicja ConvertFrom-SecureString oraz ConvertTo-SecureString. Mamy dostępnych kilka metod: począwszy od podejrzenia listy załadowanych modułów powershella w process explorerze, a skończywszy na uruchomieniu kilku poleceń w powershellu:

C:\PS> get-command ConvertTo-SecureString | fl -property pssnapin
PSSnapIn : Microsoft.PowerShell.Security
C:\PS> Get-PSSnapin -Name Microsoft.PowerShell.Security | fl -Property ModuleName
ModuleName : C:\WINDOWS\system32\WindowsPowerShell\v1.0\Microsoft.PowerShell.Security.dll

I już wszystko jasne :). Tak, wiem, powinienem używać najnowszej wersji :)
Skoro mamy już bibliotekę, możemy spróbować dowiedzieć się jaka funkcja odpowiada za szyfrowanie danych. Zaczynamy od załadowania biblioteki snapina do reflectora, dalej przechodzimy do przestrzeni Microsoft.PowerShell.Commands i znajdujemy klasę ConvertFromSecureStringCommand. Słusznie domyślamy się, że za obsługę polecenia odpowiada jedyna metoda klasy, czyli ProcessRecord i dosyć szybko trafiamy na następujący kawałek kodu:

//zrzut z reflectora
        if (base.SecureKey !=null)
        {
            result = SecureStringHelper.Encrypt(this.SecureString, base.SecureKey);
        }
        elseif (base.Key !=null)
        {
            result = SecureStringHelper.Encrypt(this.SecureString, base.Key);
        }
        else
        {
            sendToPipeline = SecureStringHelper.Protect(this.SecureString);
        }

Przyznaję, że nieco z lenistwa niż jakiejś przebiegłości postanawiam, że ustalenie metody, która ma być wywołana przerzucę do WinDbg. A poza tym to jest dobra okazja, żeby zademonstrować zakładanie pułapek na kodzie zarządzanym, więc chyba nikt się nie obrazi ;)
Ładujemy zatem WinDbg, podpinamy się do aktywnego procesu powershella, po czym ładujemy standardowy zestaw sos.dll oraz sosex.dll i ostatecznie ustawiamy pułapkę:

0:003> .loadby sos mscorwks
0:003> .load sosex.dll
0:003> !mbm *!Microsoft.PowerShell.Commands.
ConvertFromSecureStringCommand.ProcessRecord 0

Breakpoint set at Microsoft.PowerShell.Commands.ConvertFromSecureStringCommand.ProcessRecord().
*** WARNING: Unable to verify checksum for C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\Microsoft.PowerShel#\2529703d3e0d2f9fd06cc0230f2bda3f\Microsoft.PowerShell.Security.ni.dll
0:003> !mbl
0 e : *!MICROSOFT.POWERSHELL.COMMANDS.
CONVERTFROMSECURESTRINGCOMMAND.PROCESSRECORD ILOffset=0: pass=1 oneshot=false thread=ANY
    Microsoft.PowerShell.Security!Microsoft.PowerShell.Commands.
ConvertFromSecureStringCommand.ProcessRecord()
        0 e 2242ffd4
0:003> g

Wykonujemy polecenia

C:\PS>$secure = Read-Host -AsSecureString

Oraz

C:\PS> $encrypted = ConvertFrom-SecureString -SecureString $secure

I po wykonaniu drugiego uaktywnia się nasza pułapka
W tym momencie dodajemy kolejne pułapki:

0:003> !mbm *!Microsoft.PowerShell.SecureStringHelper.Encrypt 0
Breakpoint set at Microsoft.PowerShell.SecureStringHelper.Encrypt(System.Security.SecureString, System.Security.SecureString).
Breakpoint set at Microsoft.PowerShell.SecureStringHelper.Encrypt(System.Security.SecureString, Byte[]).
0:003> !mbm *!Microsoft.PowerShell.SecureStringHelper.Protect 0
Breakpoint set at Microsoft.PowerShell.SecureStringHelper.Protect(System.Security.SecureString).
0:003> g

I puszczamy wykonanie dalej. Po chwili nasza pułapka uaktywnia się, więc znowu lądujemy w debuggerze:

0:003> !clrstack
OS Thread Id: 0x694 (3)
ESP       EIP    
03e1ee70 22430470 Microsoft.PowerShell.SecureStringHelper.Protect(System.Security.SecureString)
03e1eea4 2243007e Microsoft.PowerShell.Commands.ConvertFromSecureStringCommand.ProcessRecord()
03e1eedc 20612648 System.Management.Automation.Cmdlet.DoProcessRecord()
03e1eee4 2064e798 System.Management.Automation.CommandProcessor.ProcessRecord()
03e1ef24 205e8f1d System.Management.Automation.CommandProcessorBase.DoExecute()
03e1ef54 2061c287 System.Management.Automation.Internal.PipelineProcessor.Inject(System.Object, Boolean)

Na początek podejrzyjmy stos. Aha, to była jednak metoda Protect, a nie Encrypt. Przeglądamy pobieżnie reflektorem kod metody Protect i trafiamy szybko na wywołanie:

data = ProtectedData.Protect(userData, null, DataProtectionScope.CurrentUser);

A zaglądając głębiej trafiamy na

if (!CAPI.CryptProtectData(new IntPtr((void*) &cryptoapi_blob2), string.Empty, new IntPtr((void*) &cryptoapi_blob3), IntPtr.Zero, IntPtr.Zero, dwFlags, new IntPtr((void*) &cryptoapi_blob)))

co z kolei dosyć szybko sprowadza nas do wywołania:

[DllImport("crypt32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
internalstaticexternbool CryptProtectData([In] IntPtr pDataIn, [In] string szDataDescr, [In] IntPtr pOptionalEntropy, [In] IntPtr pvReserved, [In] IntPtr pPromptStruct, [In] uint dwFlags, [In, Out] IntPtr pDataBlob);

Ustawiamy zatem kolejną pułapkę, puszczamy wykonanie i patrzymy co będzie dalej:

0:003> !mbm *!System.Security.Cryptography.ProtectedData.Protect
Breakpoint set at System.Security.Cryptography.ProtectedData.Protect(Byte[], Byte[], System.Security.Cryptography.DataProtectionScope).
0:003> g
Breakpoint 4 hit
eax=00000000 ebx=00000000 ecx=01403418 edx=00000000 esi=01403418 edi=03e1ee54
eip=67982e82 esp=03e1ee10 ebp=03e1ee64 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
System_Security_ni+0x62e82:
67982e82 85f6            test    esi,esi
0:003> !clrstack -a
OS Thread Id: 0x694 (3)
ESP       EIP    
03e1ee10 67982e82 System.Security.Cryptography.ProtectedData.Protect(Byte[], Byte[], System.Security.Cryptography.DataProtectionScope)
    PARAMETERS:
        userData = 0x0142e7a4
        optionalEntropy = 0x00000000
        scope = 0x00000000
    LOCALS:
        <no data>
        <no data>
        <no data>
        <no data>
        <no data>
        <no data>
        <no data>
        0x03e1ee10 = 0x00000000

Jak widać, drugi i trzeci parametr to po prostu 0, a pierwszy parametr to tablica bajtów, co szybko możemy udowodnić:

0:003> !do 0x0142e7a4
Name: System.Byte[]
MethodTable: 79333470
EEClass: 790eeb6c
Size: 36(0x24) bytes
Array: Rank 1, Number of elements 24, Type Byte
Element Type: System.Byte
Fields:
None

OK, 24 bajty - brzmi znajomo. Z klawiatury wprowadziłem bowiem 12 znaków…
Robimy zatem zrzut tablicy:

0:003> !dumparray -details 0x0142e7a4
Name: System.Byte[]
MethodTable: 79333470
EEClass: 790eeb6c
Size: 36(0x24) bytes
Array: Rank 1, Number of elements 24, Type Byte
Element Methodtable: 79333520
[0] 0142e7ac
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance       97 m_value
[1] 0142e7ad
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[2] 0142e7ae
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance      108 m_value
[3] 0142e7af
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[4] 0142e7b0
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance       97 m_value
[5] 0142e7b1
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[6] 0142e7b2
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance      109 m_value
[7] 0142e7b3
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[8] 0142e7b4
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance       97 m_value
[9] 0142e7b5
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[10] 0142e7b6
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance      107 m_value
[11] 0142e7b7
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[12] 0142e7b8
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance      111 m_value
[13] 0142e7b9
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[14] 0142e7ba
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance      116 m_value
[15] 0142e7bb
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[16] 0142e7bc
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance       97 m_value
[17] 0142e7bd
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[18] 0142e7be
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance       50 m_value
[19] 0142e7bf
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[20] 0142e7c0
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance       53 m_value
[21] 0142e7c1
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value
[22] 0142e7c2
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance       54 m_value
[23] 0142e7c3
    Name: System.Byte
    MethodTable 79333520
    EEClass: 790eebe0
    Size: 12(0xc) bytes
     (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
    Fields:
          MT    Field   Offset                 Type VT     Attr    Value Name
    79333520  4000216        0          System.Byte  1 instance        0 m_value

I wszystko jasne - tablica bajtów zawiera łańcuch unikodowy zawierający wpisane przeze mnie hasło. Tak przygotowana tablica opakowywana jest w strukturę DATA_BLOB, która następnie trafia do funkcji CryptProtectData z biblioteki crypt32.dll.
Czas zatem zajrzeć do dokumentacji. Z pozostałych parametrów ustalamy jeszcze, że skoro optionalEntropy = null, to do funkcji trafia blob z domyślnymi polami, ponadto scope = 0, więc dwFlags = 1. Zaglądając do pliku nagłówkowego szybko ustalamy, że jest to

//Wincrypt.h
#define CRYPTPROTECT_UI_FORBIDDEN        0x1

W uwagach do funkcji CryptProtectData znajdujemy kilka ciekawych informacji:

"Typically, only a user with logon credentials that match those of the user who encrypted the data can decrypt the data. In addition, decryption usually can only be done on the computer where the data was encrypted. However, a user with a roaming profile can decrypt the data from another computer on the network.
If the CRYPTPROTECT_LOCAL_MACHINE flag is set when the data is encrypted, any user on the computer where the encryption was done can decrypt the data.
The function creates a session key to perform the encryption. The session key is derived again when the data is to be decrypted."

Wygląda zatem na to, że nasze dane mogą zostać odszyfrowane wyłącznie przez użytkownika, który je wygenerował i to wyłącznie po zalogowaniu na tej, lub innej należącej do jednej domeny maszynie, o ile dla tego użytkownika jest ustawiony profil mobilny.

DPAPI Internals

Funkcja CryptProtectData oraz bliźniacza CryptUnprotectData stanowią podstawę DPAPI (Data Protection API), czyli usługi ochrony danych opartych o hasła. - Jakie hasło? - ktoś spyta. - Hasło używane podczas logowania do systemu - odpowie dokumentacja. Na jego podstawie wyprowadzane są kolejno: MasterKey, a z niego oraz garści entropii i przypadkowych danych dalej klucze sesyjne. I to za ich pomocą szyfrowane są dane przekazane jawnym tekstem do funkcji CryptProtectData, a zaszyfrowany ciąg bajtów zwracany jest przez funkcję do aplikacji. Tym samym DPAPI zajmuje się wyłącznie szyfrowaniem danych, przechowywanie ich pozostawia jednak samej aplikacji. W konsekwencji zaszyfrowane dane można znaleźć w wielu miejscach systemu: w rejestrze, w katalogach aplikacji, profilu użytkownika, etc, zależnie od wyobraźni i kreatywności programisty :)
Wywołane funkcje DPAPI (z biblioteki crypt32.dll) kontaktują się poprzez RPC z LSA, które to z kolei odwołuje się do właściwych funkcji szyfrujących biblioteki crypt32.dll, jednak już w kontekście samego LSA.
Hasło użytkownika podawane jest raz - podczas logowania i na jego podstawie generowany jest MasterKey, który zmieniany raz na 3 miesiące, (przy kolejnym skorzystaniu DPAPI), lub bezpośrednio po zmianie hasła użytkownika. Dodatkowo, każdy MasterKey zaszyfrowany jest z użyciem hasła użytkownika i dopiero w takiej postaci zostaje zapisany na dysku. Spytacie pewnie: czy tylko ostatni MasterKey jest zapisany na dysku? A ja odpowiem: nie, wszystkie, ponieważ danych zaszyfrowanych z użyciem poprzednich MasterKeys nie da się odszyfrować wyłącznie z użyciem ostatniego MasterKey. Powiem więcej: do odszyfrowania poprzednich MasterKeys potrzebne są hasła użytkownika, które również przechowywane są na dysku w postaci skrótów zaszyfrowanych ostatnim hasłem w pliku CREDHIST.
Fizycznie katalogi, w których przechowywane są MasterKeys oraz plik CREDHIST znajdują się w:

MasterKeys: %APPDATA%\Microsoft\Protect\{UserSID}
CREDHIST: %APPDATA%\Microsoft\Protect\

Każdy MasterKey ma swój unikalny GUID i dla usprawnienia całego procesu plik zawierający zaszyfrowany MasterKey ma nazwę taką, jaki ma GUID. Dodatkowo, w katalogu zawierającym MasterKeys jest plik Preferred zawierający GUID ostatniego MasterKey i to właśnie on wybierany jest do szyfrowania danych.
W przypadku danych, które mają być dostępne dla wszystkich użytkowników systemu, używane są klucze systemowe, które można znaleźć w katalogu %SystemRoot%\System32\Microsoft\Protect\S-1-5-18
Konto systemu nie ma hasła, wobec czego nie ma konieczności przechowywania pliku CREDHIST.
Z tego krótkiego opisu wynika, że każda aplikacja uruchamiana w kontekście zalogowanego użytkownika będzie miała dostęp do wszystkich danych. Żeby się przed tym ustrzec, aplikacja może przekazać do CryptProtectData własny kluczyk (entropia), bez którego nie będzie można danych odszyfrować. Jak widzieliśmy jednak wcześniej, nasz PowerShell jednak z tej możliwości nie korzysta (optionalEntropy = 0x00000000 w parametrach funkcji System.Security.Cryptography.ProtectedData.Protect), co umożliwia każdej aplikacji uruchomionej przez tego samego użytkownika na odszyfrowanie danych. Sprawdźmy to!
W tym celu skorzystajmy z kodu (zamieszczam pełen, ponieważ nie jest aż taki długi, a dzięki temu każdy będzie mógł z niego skorzystać od razu):

using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Security.Cryptography;

namespace pl.net.zine.Articles.DPAPI
{
  publicclass SecureStringPSDecrypter
  {
    publicstaticvoid Main(string[] args)
    {
      if (args.Length > 0)
      {
        byte[] encryptedData = BlobFileUtils.ReadBlobFromTextFile(args[0]);
        byte[] originalData = ProtectedData.Unprotect(encryptedData, null, DataProtectionScope.CurrentUser);
        Console.WriteLine("The original data is:");
        Console.WriteLine(BlobFileUtils.StringFromByteArray(originalData));
      }
      else
        Console.WriteLine("Usage: {0} encrypted_file.txt", System.Diagnostics.Process.GetCurrentProcess().ProcessName);
    }
  }

  publicclass BlobFileUtils
  {
    publicstaticstring StringFromByteArray(byte[] arr)
    {
      int len = arr.Length;
      StringBuilder sb =new StringBuilder();
      for(int i = 0; i < len; i+=2)
      {
        sb.Append((char)arr[i]);
      }
      return sb.ToString();
    }

    publicstaticbyte[] ReadBlobFromTextFile(string fileName)
    {
      return ConvertToByteArr(ReadSecureStringEncrypted(fileName));
    }

    privatestaticbyte[] ConvertToByteArr(string text)
    {
      int num = text.Length / 2;
      byte[] byteArr =newbyte[num];
      if (text.Length > 0)
      {
        for (int i = 0; i < num; i++)
        {
          byteArr[i] =byte.Parse(text.Substring(2 * i, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
        }
      }
      return byteArr;
    }

    privatestaticstring ReadSecureStringEncrypted(string file)
    {
      string text ="";
      using (TextReader reader =new StreamReader(file))
      {
        text = reader.ReadToEnd();
      }
      return text;
    }
   }
}

Kompilujemy i uruchamiamy dla naszych przykładowych danych:

>decryptPSSS.exe encrypted.txt
The original data is:
alamakota256
>decryptPSSS.exe encrypted_pwd.txt
The original data is:
P@ssW0rD!

Cała magia sprowadza się do linijki:

byte[] originalData = ProtectedData.Unprotect(encryptedData, null, DataProtectionScope.CurrentUser);

w której następuje właściwe odszyfrowanie danych, a zgodnie z naszymi wcześniejszymi obserwacjami drugi parametr (optionalEntropy) wystarczyło ustawić na null.

Proste? :)

Tekst zrobił się przepotwornie długi, więc w tym miejscu dokonuję cięcia. Dalsza część poświęcona głębszemu zajrzeniu do DPAPI pojawi się w następnym wpisie, gdzie spróbuję dodatkowo odpowiedzieć na pytanie 'czy jest możliwe offline’owe odszyfrowanie danych zapisanych przez DPAPI?' i jak to się ma do EFS-a, haseł Internet Explorera, etc.

Zainteresowani ciągiem dalszym?


DPAPI Internals, a SecureString w PowerShellu (cz. 2)

$
0
0

Kolejna część naszych zabaw z DPAPI i jeszcze głębsze wejrzenie w mechanizmy nim rządzące.

W poprzednim odcinku znęcaliśmy się nieco nad PowerShellem, z którego pomocą wygenerowaliśmy kilka plików z zaszyfrowanymi danymi. Celowo poprzednio zamieściłem na początku tekstu zrzuty dwóch moich danych, dorzućmy do tej potrawy dodatkowo zaszyfrowany ciąg z przykładu ze strony MSDN i zamieszajmy nieco :)

Przypomnę zatem zaszyfrowane dane z poprzedniego wpisu:
1.
01000000d08c9ddf0115d1118c7a00c04fc297eb010000008727d6117f2b0c429d84f2b9f7e9ea35
0000000002000000000003660000a8000000100000009403327f1497e1a4266ca7d45f84266a0000
000004800000a000000010000000ed0b8a5247be59846e80ca735a1a3f9720000000a5025c6e7ee4
cad037fcd99a5cbbe4aef20d54e685352fa67c1deec0999779c7140000002f96e55035c6760aba6f
4bb9aeffc52616cb300c

2.
01000000d08c9ddf0115d1118c7a00c04fc297eb010000008727d6117f2b0c429d84f2b9f7e9ea35
0000000002000000000003660000a80000001000000037f547b0feab4a12ab792d809a63fd0a0000
000004800000a000000010000000426ad5282726129ddce3675e82c68ca018000000b00bf7525cca
296dc3753d4aaaf867deec089bcbc94c73c21400000091011d6139ce2150b304f0cabaa7dc8b21fb
65d3

3.
01000000d08c9ddf0115d1118c7a00c04fc297eb010000001a114d45b8dd3f4aa11ad7c0abdae980
0000000002000000000003660000a8000000100000005df63cea84bfb7d70bd6842e7efa79820000
000004800000a000000010000000f10cd0f4a99a8d5814d94e0687d7430b100000008bf11f196015
8405b2779613e9352c6d14000000e6b7bf46a9d485ff211b9b2a2df3bd6eb67aae41

Po chwili bliższego przyglądania się, możemy powyższe ciągi podzielić na kilka grup:

A. pierwsze 48 znaków jest identyczne w przypadku wszystkich trzech zrzutów:
01000000d08c9ddf0115d1118c7a00c04fc297eb01000000

B. następne 32 znaki są identyczne w przypadku moich zrzutów i różne od przykładowych z MSDN.
8727d6117f2b0c429d84f2b9f7e9ea35

C. kolejne 44 znaki są identyczne w przypadku wszystkich 3 zrzutów
0000000002000000000003660000a800000010000000

D. dalej kolejne 32 znaki różne we wszystkich 3 przypadkach.
9403327f1497e1a4266ca7d45f84266a

E. następne 32 znaki są identyczne we wszystkich 3 zrzutach
0000000004800000a000000010000000

F. i potem następuje dosyć długi ciąg różnej długości dla wszystkich 3 zrzutów.
ed0b8a5247be59846e80ca735a1a3f9720000000a5025c6e7ee4cad037fcd99a5cbbe4aef20d54e6
85352fa67c1deec0999779c7140000002f96e55035c6760aba6f4bb9aeffc52616cb300c

Spróbujmy rzucić nieco światła na powyższe dane.  Zacznijmy nasze poszukiwania jakichś wskazówek od zajrzenia do katalogów zawierających klucze MasterKey. W moim przypadku mogę tam znaleźć pliki o następujących nazwach:

>dir %APPDATA%\Microsoft\Protect\{SID}
11d62787-2b7f-420c-9d84-f2b9f7e9ea35
298da325-e2d2-454a-9a76-5846710f328e
fabbaf43-0835-4f00-9469-26f28b767c34
[CIACH pozostałe pliki]

Plik Prefered zawiera ciąg bajtów:
87 27 D6 11 7F 2B 0C 42 9D 84 F2 B9 F7 E9 EA 35 C0 BD E3 CB 49 0B CC 01
który zgodnie z tym, o czym pisałem w poprzednim odcinku, zawiera GUID klucza Master, którego ma aktualnie używać DPAPI + dodatkowe informacje.

I tak, pierwsze 16 bajtów to ciąg:
87 27 D6 117F 2B0C 429D 84F2 B9 F7 E9 EA 35
który odpowiada GUIDowi:
11d62787-2b7f-420c-9d84-f2b9f7e9ea35

W tym momencie bez trudu stwierdzamy, który plik z katalogu kluczy zawiera preferowany MasterKey. Bez większych problemów ustalamy również, że blok B z naszego zaszyfrowanego ciągu to po prostu GUID MasterKey użytego do zaszyfrowania danych.

Dochodzimy do miejsca, w którym głos należy oddać dwóm panom: Elie Bursztein oraz Jean-Michel Picod i ich prezentacji o internalsach DPAPI, która została przedstawiona w ubiegłym roku na konferencji BlackHat DC 2010 , a do której materiały dostępne są na stronie www.dpapick.com.
Znajdziemy tam między innymi względnie kompletny opis bloba, a co za tym idzie również naszego bloku A.

I tak, nasz blok A po podzieleniu na części to:

01000000 - liczba użytych dostawców usług kryptograficznych (crypto provider), w naszym i praktycznie każdym przypadku - 1;

d08c9ddf0115d1118c7a00c04fc297eb - GUIDy użytych crypto providers, w naszym przypadku jest to df9d8cd0-1501-11d1-8c7a-00c04fc297eb i odpowiada bibliotece psbase.dll (zgodnie z opisem, znajdziemy tę informację w kluczu HKLM\Software\Microsoft\Cryptography\Protect\Providers\df9d8cd0-1501-11d1-8c7a-00c04fc297eb);

01000000 - liczba kluczy MasterKeys użytych do szyfrowania.

Cała struktura bloba przedstawia się następująco [ze wspomnianej wyżej pracy E.B. i J-M.P]:

struct dpapi_blob_t {
DWORD cbProviders; // Num of crypto providers
GUID *arrProviders; // Crypto Providers GUIDs
DWORD cbKeys;
GUID *arrKeys; // Keys GUIDs (tu zaczyna się nasz blok B)
DWORD ppszDataDescrSize; // WARNING: in bytes
WCHAR *ppszDataDescr;
DWORD idCipherAlgo;
DWORD idHashAlgo;
BYTE *pbSalt; //Salt
BYTE *pbCipher; //Encrypted data
BYTE *pbHMAC; //HMAC
};

Ale co jest takiego wyjątkowego w ciągu z bloku A? Mogliśmy już to przyuważyć porównując dane z przykładu MSDN oraz mojego, choć jeszcze nie mieliśmy tej pewności. Odpowiedź jest prosta i po powyższych wywodach powinniśmy już ją znać: otóż jest to 'odcisk buciora' DPAPI, znak rozpoznawalny praktycznie zawsze i wszędzie. Jeśli kiedykolwiek będziecie czesali dysk, rejestr, pamięć, cokolwiek, w poszukiwaniu danych zaszyfrowanych z użyciem DPAPI, to wystarczy, że znajdziecie ten ciąg, a reszta będzie już tylko formalnością. :)

"Ale jaka ‘reszta’?" - ktoś spyta.
"Offline’owe odszyfrowanie tych danych" - odpowiedzą zgodnie panowie Bursztein i Picod.
"Zaraz, zaraz - przecież pisałeś, że dane dostępne są wyłącznie dla zalogowanego użytkownika, do tego LSA i… w ogóle to niemożliwe!" - słyszę kolejny oburzony głos.
Wszystko prawda. A przynajmniej w dokumentacji na stronach MSDN. Ale nie dla panów Picod i Bursztein :)
Na stronie dpapick.com możemy znaleźć przykładową aplikację wykorzystującą przygotowaną przez autorów bibliotekę libDPAPIck.dll pozwalającą na offline’owy dostęp do danych zaszyfrowanych z użyciem DPAPI. Wszystko, czego potrzebujemy, to:
- zestaw plików MasterKeys z katalogu kluczy użytkownika, w imieniu którego dane zostały zaszyfrowane;
- zaszyfrowany ciąg danych;
- tekstowe hasło użytkownika.

"Eee" - ktoś się skrzywi - "Hasło?"
Tak, cudów nie ma. Do odszyfrowania klucza MasterKey musi zostać wygenerowany Pre-Key, który wyprowadzany jest właśnie z hasła użytkownika, obejść się tego nie da. Oczywiście możemy zajrzeć do bazy sam i spróbować znaleźć hasło, dla którego mamy skrót, ale jest to pracochłonne i niekoniecznie najprostsze.

Są jednak sytuacje, kiedy znamy hasło i chcielibyśmy dostać się bez podnoszenia systemu do zaszyfrowanych danych i ta biblioteka nam to umożliwia. Autorzy podają jako przykład dostęp do plików szyfrowanych z użyciem EFS, haseł Internet Explorera i innych aplikacji z poziomu Wine’a, czyli ogólnie wsparcie interoperacyjności, lecz nie tylko. Tak na marginesie dodam, że przechowywaniem haseł z użyciem Credential Managera (opartym rzecz jasna na DPAPI) zajmiemy się w jednym z kolejnych wpisów.
Ja oczywiście podam jako przykład aplikację, która odczytuje offline’owo hasła zapisane do pliku w PowerShellu, naturalnie z wykorzystaniem biblioteki libDPAPIck.dll. Nie będę przedstawiał całego kodu źródłowego, jednak w oryginalnym przykładzie zabrakło paru drobiazgów, które mogą uniemożliwić nam odszyfrowanie danych, i nimi się teraz zajmiemy.

Do deszyfrowania danych skorzystamy z wyeksportowanej funkcji:

[DllImport("libDPAPIck.dll", CharSet = CharSet.Auto)]
publicstaticexternbool DPAPIBlobDecrypt(StringBuilder blobPath, ref DATA_BLOB blobEntropy, ref DATA_BLOB blobStrongPassword, ref DATA_BLOB blobMasterkey, ref DATA_BLOB blobResult);

Po pierwsze - do oryginału (tu uwaga: kodu źródłowego nie ma, ale wystarczy użyć reflectora, lub innego dezasemblera .net i możemy to i owo dodać) należy dodać funkcję odczytującą GUID MasterKey użytego do zaszyfrowania danych, co na podstawie powyższego opisu sprowadza się mniej-więcej do czegoś takiego:

privatestring retrieveMasterGuidFromBlob(byte[] blob)
{
  int startGuidPos = 24; // początek bloku B
  int guidLen = 16; // 16 bajtów na guid
  byte[] byteGuid =newbyte[guidLen];
  Array.Copy(blob, startGuidPos, byteGuid, 0, guidLen);
  returnnew Guid(byteGuid).ToString();
}

Tak odczytany Master-Guid należy wykorzystać w funkcji deszyfrującej MasterKey, gdzie w oryginale wykorzystywany jest pierwszy Master z listy, zamiast tego użytego do zaszyfrowania danych. Pozostawiam to jako ćwiczenie.
Nasze dane zapisane są w postaci tekstowej, należy więc dokonać konwersji ich na ciąg bajtów, co pozostawiam jako proste ćwiczenie. Oczywiście w przypadku danych z innych aplikacji taka konwersja nie musi być konieczna, więc myśląc ‘przyszłościowo’ należałoby to uwzględnić.
W samej funkcji deszyfrującej blob należy jeszcze pamiętać o tym, że zaszyfrowany przez PowerShell ciąg znaków był unikodowy, a nie ASCII i to już właściwie daje nam możliwość uruchomienia aplikacji na dowolnym komputerze, na który skopiowaliśmy katalog z kluczami oraz zaszyfrowany plik:

>decryptDPAPIOffline.exe masterDirectory Password encrypted.txt
The original data is:
alamakota256

Pierwszym parametrem jest nazwa katalogu zawierającego MasterKeys, drugim - hasło użytkownika, a jako trzeci podaję nazwę pliku txt zawierającego zaszyfrowany ciąg. I gotowe! :)

Jakiś czas temu zgłosiłem Eliemu i Jean-Michelowi drobne uwagi (w tym powyższe) i następna wersja, planowana na kolejną edycję konferencji BlackHat miała je zawierać. Dodatkowo (co wiem nieoficjalnie z wymiany mailowej z Elie i Jean-Michelem) DPAPIck prawdopodobnie będzie wspierać Windows 7 i środowisko domenowe, a do pobrania będzie kilka przykładowych aplikacji do odszyfrowywania danych konkretnych aplikacji.

Całość badań nad DPAPI wygląda bardzo obiecująco, więc warto co jakiś czas sprawdzać postęp prac nad biblioteką.

Przyznaję, że początkowo tekst miał wyglądać nieco inaczej, jednak ostatecznie stwierdziłem, że pewne rzeczy - jak np. odniesienia do EFSa, czy Credential Managera zostawię na inne wpisy. Zrezygnowałem również z publikacji większych kawałków kodu - pomimo tego, że dyskutowałem z Elie i Jean-Michelem o kodzie, to jednak nigdy nie spytałem wprost, czy mogę fragmenty swojej przeróbki opublikować. Tak więc - należy uzbroić się w cierpliwość - już za kilka tygodni kolejna edycja BlackHata, a tym samym publikacja poprawionej wersji DPAPIck :)

$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 :)