Parte 1: http://blogs.ugidotnet.org/raffaele/archive/2009/01/27/windows-vista-integrity-levels-parte-1.aspx
Parte 2: http://blogs.ugidotnet.org/raffaele/archive/2009/01/29/windows-vista-integrity-levels-parte-2.aspx
La parte divertente è quella di scrivere codice, vediamo quindi le API per usare gli Integrity Levels.
Creazione di un processo via API con integrity level impostato a low
- Prendere l'handle del token del processo corrente (che di default è "medium")
- Duplicare l'handle (API DuplicateHandle)
- Impostare l'Integrity Level a "Low" grazie a SetTokenInformation
- Creare il processo con CreateProcessAsUser usando il nuovo token modificato
1: #include <windows.h>
2: #include <Sddl.h>
3:
4: #include <string>
5: using namespace std;
6:
7:
8: // restituisce zero se tutto è ok, oppure l'errore win32
9: DWORD CreateLowProcess(wstring PathName, wstring CommandLine)
10: {
11: // Low integrity SID
12: WCHAR IntegritySid[20] = L"S-1-16-4096";
13: DWORD LastError = 0;
14:
15: HANDLE hToken = NULL;
16: // Legge il token del processo attuale
17: if(OpenProcessToken(GetCurrentProcess(),
18: TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT |
19: TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY,
20: &hToken))
21: {
22: HANDLE hNewToken = NULL;
23: // duplica il token per poterlo modificare
24: if(DuplicateTokenEx(hToken,
25: MAXIMUM_ALLOWED,
26: NULL,
27: SecurityImpersonation,
28: TokenPrimary,
29: &hNewToken))
30: {
31: PSID pIntegritySid = NULL;
32: // converte la stringa del sid in struttura binaria
33: if(ConvertStringSidToSid(IntegritySid, &pIntegritySid))
34: {
35: TOKEN_MANDATORY_LABEL TIL = {0};
36: TIL.Label.Attributes = SE_GROUP_INTEGRITY;
37: TIL.Label.Sid = pIntegritySid;
38:
39: // Modifica l'integrity level del nuovo token
40: if(SetTokenInformation(hNewToken, TokenIntegrityLevel, &TIL,
41: sizeof(TOKEN_MANDATORY_LABEL)))
42: {
43: PROCESS_INFORMATION ProcInformation = {0};
44: STARTUPINFO StartupInfo = {0};
45: StartupInfo.cb = sizeof(StartupInfo);
46:
47: // crea il processo con il nuovo token
48: if(CreateProcessAsUser(hNewToken,
49: PathName.c_str(),
50: const_cast<wchar_t*>(CommandLine.c_str()),
51: NULL,
52: NULL,
53: FALSE,
54: 0,
55: NULL,
56: NULL,
57: &StartupInfo,
58: &ProcInformation))
59: {
60: if (ProcInformation.hProcess != NULL)
61: CloseHandle(ProcInformation.hProcess);
62:
63: if (ProcInformation.hThread != NULL)
64: CloseHandle(ProcInformation.hThread);
65: }
66: else
67: LastError = ::GetLastError();
68: }
69: else
70: LastError = ::GetLastError();
71: LocalFree(pIntegritySid);
72: }
73: else
74: LastError = ::GetLastError();
75: CloseHandle(hNewToken);
76: }
77: else
78: LastError = ::GetLastError();
79: CloseHandle(hToken);
80: }
81: else
82: LastError = ::GetLastError();
83:
84: return LastError;
85: }
Il listato è in C++ perché non ci sono chiamate pronte nel Framework.NET. Ovviamente le stesse cose si possono fare creando le corrispondenti chiamate PInvoke.
In alternativa è sufficiente impostare il file dell'exe con un Integrity Level "low" e di conseguenza al suo avvio il token sarà automaticamente a quello stesso livello.
Impostare a low un file
Per fare le prove consiglio di copiare il file notepad.exe in una cartella temporanea.
Si può cambiare l'integrity level di un file con l'utility ICACLS:
icacls notepad.exe /setintegritylevel L
Per leggere l'integrity level non è necessario il prompt elevato e il risultato è:
C:\Temp>icacls notepad.exe
notepad.exe XYZ\Raf:(I)(F)
BUILTIN\Administrators:(I)(F)
NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Users:(I)(RX)
Mandatory Label\Low Mandatory Level:(NW)
Successfully processed 1 files; Failed processing 0 files
Io mi sono costruitio una mia utility per modificare i token e posso impostare l'integrity level ad un numero arbitrario, non solo ai livelli prefissati nell'sdk.
Se un file viene impostato ad un valore non predeterminato nell'SDK, ICACLS non mostra il numero esatto.
Per esempio uso la mia utility RafToken per impostare il livello 4095 (Low è 4096), ma poi ICACLS mi dice che non è riuscito a mappare il numero 4095 in uno degli Integrity Level noti.
C:\Temp>debug\raftoken -i 4095 notepad.exe
C:\Temp>icacls notepad.exe
notepad.exe XYZ\Raf:(I)(F)
BUILTIN\Administrators:(I)(F)
NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Users:(I)(RX)
No mapping between account names and security IDs was done.
(NW,NR,NX)
Successfully processed 1 files; Failed processing 0 files
Questa operazione non è affatto illegale. È solo l'utility ICACLS che non mostra il valore numerico. L'integrity Level alla fine dei conti è solo un numero a 16 bit e quindi qualsiasi valore è valido. Ovviamente il sistema operativo ha dei valori noti a cui fa riferimento.
Impostare a low un oggetto kernel (come un file) via API
- Creare una stringa SDDL con le opzioni desiderate
Per esempio "S:(ML;;NWNRNX;;;S-1-16-4096)" imposta
- no-write-up (NW), no-read-up (NR), no-execute-up (NX) e level 4096 cioè medium
(ML è l'acronimo di Mandatory Label, usato in MSDN per indicare la struttura che contiene le informazioni relative agli Integriry Level e Policy)
- Aprire il file per ottenerne l'handle (GENERIC_READ | GENERIC_WRITE | WRITE_DAC | WRITE_OWNER)
- Chiamare ConvertStringSecurityDescriptorToSecurityDescriptor per creare un security descriptor binario a partire dalla stringa SDDL
- Chiamare GetSecurityDescriptorSacl per ottenere la SACL a partire dal security descriptor appena ottenuto
- Chiamare SetSecurityInfo specificando come object type "SE_KERNEL_OBJECT", come security information "LABEL_SECURITY_INFORMATION" e infine la SACL nell'ultimo parametro.
Le SACL sono nate per ospitare le informazioni sull'auditing degli oggetti e sono accessibili solo dagli administrators.
Il mio target era invece quello di poter modificare gli integrity level / policy senza richiedere l'elevazione con UAC.
Il "trucco" sta nell'API SetSecurityInfo con parametro "LABEL_SECURITY_INFORMATION". Impostando le SACL con altre API l'operazione fallirebbe con un accesso negato e sarebbe perciò necessario elevare il processo ad administrator.
Il listato per questa sequenza di chiamate è più lungo e il blog non mi sembra il posto più indicato dove farlo. Ad ogni modo le chiamate sopra citate fanno parte della mia utility (scritta in C++) e quindi sono assolutamente collaudate. Basta un minimo di pazienza e il gioco è fatto.
La lettura degli integrity level è del tutto analoga e consiste nel chiamare GetSecurityInfo con "LABEL_SECURITY_INFORMATION" ottenendo le Sacl da cui estrarre le ACE di tipo "SYSTEM_MANDATORY_LABEL_ACE_TYPE" che contengono le informazioni su Integrity Level e Policy.
Tool: AccessChk
AccessChk di Mark Russinovich permette di verificare l'accesso ad un oggetto e mostrare la relativa security. Per esempio nel caso del file di notepad a cui abbiamo modificato l'Integrity Level a Low, ecco il risultato:
C:\Temp>c:\util\SysinternalsSuite\accesschk -e notepad.exe
Accesschk v4.20 - Reports effective permissions for securable objects
Copyright (C) 2006-2008 Mark Russinovich
Sysinternals - www.sysinternals.com
C:\Temp\notepad.exe
Low Mandatory Level [No-Write-Up, No-Read-Up, No-Execute-Up]
RW XYZ\Raf
RW BUILTIN\Administrators
RW NT AUTHORITY\SYSTEM
R BUILTIN\Users
Tool: Process Explorer
Process Explorer è il famoso tool sempre della suite SysInternals, che forinisce centinaia di informazioni interessanti. Tra queste possiamo vedere l'Integrity Level nel token del processo di Notepad che abbiamo lanciato grazie al primo listato del post.
Lo stesso "Low Mandatory Label" sarebbe visibile nel processo di Internet Explorer in Protected Mode.
Gli Integrity Levels sono il mattoncino per un'altra novità fondamentale in Vista che è UIPI.
[more to come ...]
Parte 4: http://blogs.ugidotnet.org/raffaele/archive/2009/02/09/windows-vista-uipi-parte-4.aspx