+38 067 569 61 50

info@clapkey.com

KeePass2: Kradzież DLL i podłączanie Windows API

KeePass2: Kradzież DLL i podłączanie Windows API

Niedawno ujawniono KeePass2 CVE-2023-24055 i całe zamieszanie wokół niej skłoniło mnie do dokończenia małego projektu, który rozpocząłem w zeszłym roku. Moim celem było sprawdzić, czy uda mi się znaleźć sposób na przechwycenie hasła głównego bazy danych KeePass2. Oczywiście dla zabawy i nauki ;)

Zespół Clapkey przetłumaczył dosłownie notatkę pentestera dotyczącą podatności KeePass2. 

Materiał przedstawiono WYŁĄCZNIE W CELACH EDUKACYJNYCH. 

Wprowadzenie

Wyobraź sobie to. Masz uprawnienia administratora na stacji roboczej i właśnie zdobyłeś bazę danych KeePass2.

Problem: KeeFarce/KeeThief już nie działają.

Znajdźmy inny sposób na zdobycie tego hasła głównego.

DLL Hijacking

Was ist das?

Przejęcie DLL (DLL Hijacking) to rodzaj ataku, w którym wykorzystuje się kolejność wyszukiwania bibliotek przez program, aby załadować bibliotekę dowiązań dynamicznych. Gdy program próbuje załadować plik DLL, szuka go w określonej kolejności. Kolejność jest następująca:

  1. Katalog, z którego uruchamiany jest program
  2. Katalog systemowy: C:\Windows\System32
  3. 16-bitowy katalog systemowy: C:\Windows\System
  4. Katalog Windows: C:\Windows
  5. Bieżący katalog
  6. Katalogi wymienione w zmiennej środowiskowej PATH

Jeśli atakującemu uda się umieścić złośliwy plik DLL w jednym z tych katalogów pod tą samą nazwą co legalny plik DLL, program załaduje złośliwą DLL zamiast prawdziwej, umożliwiając atakującemu wykonanie dowolnego kodu w procesie.

Dotyczy to również KeePass?

DLL docelowa

Najprostszym sposobem na znalezienie potencjalnej DLL do przejęcia jest wyszukanie za pomocą promon. Znajdź CreateFile w bibliotece DLL, która zwraca błąd NAME NOT FOUND, taki jak ten tutaj:

Co się dzieje?

Tutaj KeePass próbuje utworzyć DLL o nazwie UxTheme.dll, ale próbuje ją załadować z własnego folderu instalacyjnego w C:\Program Files\KeePass Password Safe 2.

Zwykle jednak jest to DLL systemowa i znajduje się w C:\Windows\System32:

Dlatego ta DLL może być dobrym kandydatem do przejęcia.

Skompilujmy DLL i sprawdźmy, czy załaduje się w KeePass2, jeśli zmienimy jej nazwę. Użyjemy tego kodu:

BOOL APIENTRY DllMain(

HMODULE hModule,

DWORD ul_reason_for_call,

LPVOID lpReserved

)

{

WPP_INIT_TRACING(L"Test");

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

TraceEvents(TRACE_LEVEL_VERBOSE, GENERAL, "[+ dllmain] DLL_PROCESS_ATTACH\n");

}

return TRUE;

}

Zwróć uwagę na wywołanie TraceEvents zamiast printf. Dzieje się tak, ponieważ pracujemy z DLL i nie ma prostego sposobu na uzyskanie jej wyniku.

TraceEvents wykorzystuje programowego preprocesora śledzenia Windows (WPP), który jest komponentem ETW.

Będziemy mogli zobaczyć nasze zdarzenia w TraceView.

Teraz, gdy mamy naszą DLL, przenieśmy ją do C:\Program Files\KeePass Password Safe 2 i zmieniamy jej nazwę na UxTheme.dll:

I uruchommy KeePass:

Tak!! Zadziałało. Mamy dziennik w TraceView, pokazujący naszą DLL załadowaną w KeePass2.

To było łatwe. Co teraz?

Podłączanie Windows API

Przechwytywanie (hooking) pozwala przechwytywać i/lub zmieniać zachowanie funkcji wywoływanych przez dany program. W naszym przypadku chcemy móc rejestrować parametry i wartości zwracane przez wywołania Windows API.

Na poniższym diagramie widać zwykłe wywołanie API (na zielono) i przechwycone wywołanie API (na czerwono):

Ciekawostka: właśnie tak AV/EDR śledzą wywołania API.

Zacząłem pracować nad własnym mechanizmem przechwytywania, zanim natrafiłem na własną bibliotekę Microsoftu do tego celu: Detours

Nie będę wdawał się w szczegóły implementacji hooków, ponieważ dokumentacja jest dostępna w internecie.

Przykład MessageBoxW 

Zacznijmy od czegoś prostego — wywołania MessageBoxW.

MessageBoxW(

NULL,TEXT("Hello Twitter!"),

TEXT("SimpleEXE"),

MB_OK);

Jeśli spojrzymy na dokumentację dla MessageBoxW na MSDN, otrzymamy taki prototyp funkcji:

intMessageBoxW(

[in,optional]HWNDhWnd,

[in,optional]LPCWSTRlpText,

[in,optional]LPCWSTRlpCaption,

[in]UINTuType);

Our hook function to log the lpText and lpCaption parameter would look something like this:

int(*real_MessageBoxW)(HWNDhWnd,LPCWSTRlpText,LPCWSTRlpCaption,UINTuType)=MessageBoxW;inthook_MessageBoxW(HWNDhWnd,LPCWSTRlpText,LPCWSTRlpCaption,UINTuType){TraceEvents(TRACE_LEVEL_VERBOSE,GENERAL,"[+ Hook] MessageBoxW(lpText=%ls, lpCaption=%ls, uType=%u)",lpText,lpCaption,uType);returnreal_MessageBoxW(hWnd,lpText,lpCaption,uType);}

Ten kod jest dość prosty. Gdy wywoływany jest MessageBoxW, najpierw rejestruje argumenty za pomocą TraceEvents, zanim przejdzie do wykonania „prawdziwego” MessageBoxW.

Spróbujmy!

Hook the box

Zostało to przetestowane za pomocą specjalnego loadera, który wstrzykuje DLL podczas uruchamiania procesu. A gdy pojawi się MessageBox:

Wywołanie funkcji jest rejestrowane w TraceView! Z argumentami i to wszystko.

Teraz wyobraź sobie, że możesz uruchamiać kod w poufnym procesie i przechwytywać funkcje obsługujące poufne dane.

Automatyczne generowanie kodu

Ale najpierw generowanie kodu.

Teraz, gdy mamy pierwszy działający przykład podłączenia, musimy zwiększyć jego skalę. W Windows API jest wiele funkcji. Pamiętaj, naszym celem jest przechwycenie hasła głównego bazy danych w momencie jego wprowadzania.

Oczywiście nie piszę kodu dla każdej funkcji Windows API samodzielnie. Nikt nie ma na to czasu. Użyłem skryptu Python i pliku Json, aby wygenerować kod do przechwytywania DLL.

MSDN — Twój najlepszy przyjaciel do dokumentacji, ewentualnie możesz też sprawdzić pliki nagłówkowe w Visual Studio.

Oto krótki fragment pliku json:

I generacja: 

Domyślne generowanie kodu wywołuje TraceEvents w celu rejestrowania wywołań funkcji i argumentów. Oto przykład wygenerowanego kodu:

Jest bardzo podobny do kodu w naszym pierwszym przykładzie, ale generowany jest automatycznie, gdy funkcja zostanie dodana do pliku json. Dodałem też specjalne fragmenty kodu, aby rozszerzyć funkcjonalność w razie potrzeby. Dzięki temu możemy rejestrować zwracane wartości, wypisywać argumenty w określony sposób itd.

I tak oto mamy działające generowanie kodu. Możemy przechwycić dowolną funkcję Windows API, dodając jej prototyp do pliku json!

Połączmy to wszystko razem: 

Podczas testów zawęziłem poszukiwania do funkcji obsługujących ciągi znaków i pracę ze schowkiem. Po krótkim researchu znalazłem dwa interesujące wywołania API dla naszego przypadku użycia:

- SetClipboardData: umieszcza dane w schowku w podanym formacie

HANDLE SetClipboardData(

[in] UINT uFormat,

[in, optional] HANDLE hMem

);

- ToUnicodeEx: konwertuje podany kod klawisza wirtualnego i stan klawiatury na odpowiedni symbol lub symbole Unicode

int ToUnicodeEx(

[in] UINT wVirtKey,

[in] UINT wScanCode,

[in] const BYTE *lpKeyState,

[out] LPWSTR pwszBuff,

[in] int cchBuff,

[in] UINT wFlags,

[in, optional] HKL dwhkl

);

Po wygenerowaniu hooków dla tych dwóch funkcji kopiujemy DLL do C:\Program Files\KeePass Password Safe 2 i zmieniamy jej nazwę na UxTheme.dll.

A teraz uruchommy ponownie KeePass2 i wprowadźmy hasło, aby odblokować bazę danych:

Jak to fajnie? Hasło jest rejestrowane w TraceView!

Wpisy w bazie danych można również przechwycić po naciśnięciu CTRL+C, dzięki hookowi SetClipboardData:

Podsumowanie

Muszę jeszcze trochę popracować, aby zapisać hasło do pliku, ale ideę już rozumiesz. Mam nadzieję, że podobało Ci się to, co dziś przeczytałeś, i że dowiedziałeś się czegoś nowego.

Nie mam sekcji komentarzy, ale wyślij mi wiadomość na Twitter, zanim platforma zniknie, hehe.

Przydatne linki

https://keepass.info/

https://learn.microsoft.com/en-us/sysinternals/

https://github.com/microsoft/Detours

https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/traceview https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/wpp-software-tracing

Ta strona została przetłumaczona częściowo lub w całości przy użyciu SI. Uwagi dotyczące tłumaczenia prosimy przesyłać na adres info@clapkey.com.

#StandWithUkraine

Wesprzyj Siły Zbrojne Ukrainy podczas rosyjskiej inwazji

Kontakty

Biuro

4 piętro. TC Metropoliten (ul. Hoholia 38), m. Połtawa, Ukraina, 36000