posts - 644, comments - 2003, trackbacks - 137

My Links

News

Raffaele Rialdi website

Su questo sito si trovano i miei articoli, esempi, snippet, tools, etc.

Archives

Post Categories

Image Galleries

Blogs

Links

Windows Vista Integrity Levels (parte 3)

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

  1. Prendere l'handle del token del processo corrente (che di default è "medium")
  2. Duplicare l'handle (API DuplicateHandle)
  3. Impostare l'Integrity Level a "Low" grazie a SetTokenInformation
  4. 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

  1. 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)
  2. Aprire il file per ottenerne l'handle (GENERIC_READ | GENERIC_WRITE | WRITE_DAC | WRITE_OWNER)
  3. Chiamare ConvertStringSecurityDescriptorToSecurityDescriptor per creare un security descriptor binario a partire dalla stringa SDDL
  4. Chiamare GetSecurityDescriptorSacl per ottenere la SACL a partire dal security descriptor appena ottenuto
  5. 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.

image

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.

image

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

Print | posted on martedì 3 febbraio 2009 14:25 |

Feedback

Gravatar

# re: Windows Vista Integrity Levels (parte 3)

Lo immagino ma devo aspettare che la mia azienda decida "il passo" anche se ormai è inevitabile :-)

La tua informazione mi conforta avevo letto che CreateProcessAsUser su W7 non funzionava, ti spiego, nel tempo libero lavoro per un progetto italiano Open Source simile a portable apps (in una nutshell: programmi portabili che si installano un momento prima di essere utilizzati e si disintallano al termine) e abbiamo il problema di scrivere nel registro con W7 la soluzione potrebbe essere chiedere all'utente le credenziali di Admin, e se le ha può usare il programma.

Grazie infinite della disponibilità.
09/04/2010 14:54 | Roberto Bragaglia
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET