Олицетворение и доступ к реестру CurrentUser - PullRequest
11 голосов
/ 08 декабря 2010

Среда: Windows XP SP3, C #, .Net 4.0

Проблема:

Я пытаюсь добавить доступ к кусту реестра олицетворенных пользователей в классе олицетворения, и у меня возникают проблемы, связанные с типом олицетворяемого пользователя (или, точнее, кажется, что ограничение олицетворяет пользователя).

Первоначально я следовал примеру олицетворения из CodeProject , в котором был показан вызов LoadUserProfile(), произошедший после того, как было начато олицетворение с использованием сгенерированного дубликатом токена вызова DuplcateToken() из исходного токена, полученного из LogonUser(). Мне не удалось заставить этот пример работать в моей среде, выдавая себя за пользователя с ограниченными правами из учетной записи администратора (из снимков экрана, включенных в пример, кажется, что это было сделано в системе Windows Vista \ 7, и никаких подробностей о типы учетных записей).

При вызове LoadUserProfile() возникла ошибка «Отказано в доступе». При просмотре userenv.log показывается строка «LoadUserProfile: не удалось включить привилегию восстановления. Ошибка c0000022». Документация LoadUserProfile в MSDN показывает, что вызывающий процесс должен обладать привилегиями SE_RESTORE_NAME и SE_BACKUP_NAME, которые по умолчанию имеют только члены групп «Администраторы» и «Операторы резервного копирования». (В качестве примечания, когда я позже попытался добавить эти две привилегии в группу «Пользователи», я все еще получил «Отказ в доступе», но журнал userenv.log показал, что «DropClientContext: Client [number] не имеет достаточных прав. Ошибка 5» », Я не могу найти информацию о)

Учитывая, что пользователь, под которым я действовал, не имел этих привилегий, я переместил вызов на LoadUserProfile() до, прежде чем начать олицетворение, и на этот раз он загрузился без проблем, и я смог прочитать и записать его в этом тесте , Думая, что я нашел свой ответ, я создал условную проверку для типа учетной записи, чтобы LoadUserProfile() вызывался до олицетворения, если текущий пользователь был членом Администраторов, или ждал до окончания после олицетворения, если участник не был членом Администраторов. (в более позднем случае я буду полагаться на пользователя, выдавшего себя за него, имеющего эти привилегии). К сожалению, я был неправ; Я не обнаружил свой ответ. Когда я тестировал вызов с перевернутой ролью (Пользователь> Администратор) Вызов LoadUserProfile() все еще не удался снова с ошибкой «Отказано в доступе», и файл userenv.log показал то же самое «LoadUserProfile: не удалось включить привилегию восстановления. Ошибка c0000061"но на этот раз с другим номером ошибки.

Считая, что привилегии не могут быть включены по умолчанию для токенов, возвращенных с LogonUser() и \ или DuplicateToken() Я добавил два вызова к AdjustTokenPrivilege() на токене текущих пользователей (происходящем после олицетворения), полученном с WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token. TokenAccessLevels.AdjustPrivileges и TokenAccessLevels.Query были указаны, поскольку в документации для AdjustTokenPrivilege на MSDN указано, что они необходимы для корректируемого токена (я также пытался получить токен через вызов OpenProcessToken() с помощью дескриптора, полученного из System.Diagnostics.Process.GetCurrentProcess().Handle но это не удалось при вызове от пользователя как внутри, так и за пределами олицетворения, при этом GetCurrentProcess() - это функция, выдавшая отказ в доступе)

AdjustTokenPrivilege() успешно возвращено при использовании с WindowsIdentity...Token, но LoadUserProfile() по-прежнему приводит к отказу в доступе (привилегия восстановления).В этот момент я не был убежден, что AdjustTokenPrivilege() выполняет свою работу, поэтому я решил определить, какие привилегии были доступны и в каком состоянии они находились для конкретного токена с GetTokenInformation(), что привело к его собственной небольшой сумке веселья.После изучения некоторых новых вещей я смог вызвать GetTokenInformation() и распечатать список привилегий и их текущее состояние, но результаты оказались несколько неубедительными, так как Restore и Backup показывали атрибут 0 до и после вызова AdjustTokenPrivilege() какадминистратор и выдавая себя за администратора (как ни странно, три других привилегии изменились с 2 до 1 на токене при вызове AdjustTokenPrivilege(), но не те, которые были фактически настроены и остались на значении 0)

Я удалил вызовDuplicateToken() и заменил все места, которые он использовал, токеном, возвращенным из LogonUser(), чтобы посмотреть, поможет ли это, хотя при тестировании привилегий на токенах токены LogonUser() и DuplicateToken() были идентичны.Когда я изначально писал класс подражания, я без проблем использовал основной токен в своем вызове WindowsImpersonationContext.Impersonate() и решил, что это стоит попробовать.

В приведенном ниже примере кода я могуолицетворять и получать доступ к реестру пользователя при запуске от имени администратора, но не наоборот.Любая помощь будет принята с благодарностью.

Редактирование перед публикацией:

Я также пытался использовать RegOpenCurrentUser() API вместо LoadUserProfile() и добился успеха с администратором> self и Administrator> Олицетворение пользователя, но при олицетворении Администратора из другой учетной записи Администратора или Пользователя RegOpenCurrentUser() возвращает указатель на HKEY_USERS \ S-1-5-18 (что бы это ни было) вместо куста фактической учетной записи.Я предполагаю, потому что он на самом деле не загружен, что возвращает меня к исходной точке с необходимостью использования LoadUserProfile()

Из документации RegOpenCurrentUser (MSDN):

RegOpenCurrentUser использует потоктокен для доступа к соответствующему ключу или по умолчанию, если профиль не загружен.

Фрагмент кода:

// Private variables used by class
private IntPtr tokenHandle;
private PROFILEINFO pInfo;
private WindowsImpersonationContext thisUser;
private string sDomain = string.Empty;
private string sUsername = string.Empty;
private string sPassword = string.Empty;
private bool bDisposed = false;
private RegistryKey rCurrentUser = null;
private SafeRegistryHandle safeHandle = null;

//Constants used for privilege adjustment
private const string SE_RESTORE_NAME = "SeRestorePrivilege";
private const string SE_BACKUP_NAME = "SeBackupPrivilege";
private const UInt32 SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001;
private const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
private const UInt32 SE_PRIVILEGE_REMOVED = 0x00000004;
private const UInt32 SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000;

[StructLayout(LayoutKind.Sequential)]
protected struct PROFILEINFO {
  public int dwSize;
  public int dwFlags;
  [MarshalAs(UnmanagedType.LPTStr)]
  public String lpUserName;
  [MarshalAs(UnmanagedType.LPTStr)]
  public String lpProfilePath;
  [MarshalAs(UnmanagedType.LPTStr)]
  public String lpDefaultPath;
  [MarshalAs(UnmanagedType.LPTStr)]
  public String lpServerName;
  [MarshalAs(UnmanagedType.LPTStr)]
  public String lpPolicyPath;
  public IntPtr hProfile;
}

protected struct TOKEN_PRIVILEGES {
  public UInt32 PrivilegeCount;
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
  public LUID_AND_ATTRIBUTES[] Privileges;
}

[StructLayout(LayoutKind.Sequential)]
protected struct LUID_AND_ATTRIBUTES {
  public LUID Luid;
  public  UInt32 Attributes;
}

[StructLayout(LayoutKind.Sequential)]
protected struct LUID {
  public uint LowPart;
  public int HighPart;
}


// Private API calls used by class
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
protected static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); 

[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
protected static extern bool LoadUserProfile(IntPtr hToken, ref PROFILEINFO lpProfileInfo);

[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
protected static extern bool UnloadUserProfile(IntPtr hToken, IntPtr hProfile);

[DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
protected static extern bool CloseHandle(IntPtr hObject);

[DllImport("advapi32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
protected static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 Zero, IntPtr Null1, IntPtr Null2);

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)][return: MarshalAs(UnmanagedType.Bool)]
protected static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid);


[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Start() {

  tokenHandle = IntPtr.Zero; // set the pointer to nothing
  if (!LogonUser(sUsername, sDomain, sPassword, 2, 0, ref tokenHandle)) {
    throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
  } // end if !LogonUser returned false

  try { //All of this is for loading the registry and is not required for impersonation to start
    LUID LuidRestore = new LUID();
    LUID LuidBackup = new LUID();
    if(LookupPrivilegeValue(null, SE_RESTORE_NAME, ref LuidRestore) && LookupPrivilegeValue(null, SE_BACKUP_NAME, ref LuidBackup)) {
      //Create the TokenPrivileges array to pass to AdjustTokenPrivileges
      LUID_AND_ATTRIBUTES[] LuidAndAttributes = new LUID_AND_ATTRIBUTES[2];
      LuidAndAttributes[0].Luid = LuidRestore;
      LuidAndAttributes[0].Attributes = SE_PRIVILEGE_ENABLED;
      LuidAndAttributes[1].Luid = LuidBackup;
      LuidAndAttributes[1].Attributes = SE_PRIVILEGE_ENABLED;

      TOKEN_PRIVILEGES TokenPrivileges = new TOKEN_PRIVILEGES();
      TokenPrivileges.PrivilegeCount = 2;
      TokenPrivileges.Privileges = LuidAndAttributes;

      IntPtr procHandle = WindowsIdentity.GetCurrent(TokenAccessLevels.AdjustPrivileges | TokenAccessLevels.Query).Token;

      if(AdjustTokenPrivileges(procHandle, false, ref TokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero)) {
        pInfo = new PROFILEINFO();
        pInfo.dwSize = Marshal.SizeOf(pInfo);
        pInfo.lpUserName = sUsername;
        pInfo.dwFlags = 1;

        LoadUserProfile(tokenHandle, ref pInfo); //this is not required to take place
        if(pInfo.hProfile != IntPtr.Zero) {
          safeHandle = new SafeRegistryHandle(pInfo.hProfile, true);
          rCurrentUser = RegistryKey.FromHandle(safeHandle);
        }//end if pInfo.hProfile
      }//end if AdjustTokenPrivileges
    }//end if LookupPrivilegeValue 1 & 2
  }catch{
    //We don't really care that this didn't work but we don't want to throw any errors at this point as it would stop impersonation
  }//end try

  WindowsIdentity thisId = new WindowsIdentity(tokenHandle);
  thisUser = thisId.Impersonate();

} // end function Start

Ответы [ 2 ]

7 голосов
/ 21 декабря 2011

Из документов LoadUserProfile :

Начиная с Windows XP с пакетом обновления 2 (SP2) и Windows Server 2003, вызывающая сторона должна быть администратором или учетной записью LocalSystem. Для вызывающего абонента недостаточно просто олицетворять учетную запись администратора или LocalSystem.

Если ваш процесс начинается как обычный пользователь, вам не повезло. Вы можете запустить новый процесс (под учетными данными администратора) для загрузки профиля.

0 голосов
/ 10 апреля 2014

Я обнаружил, что тип входа, установленный в вызове LogonUser (), может быть фактором.Даже при работе в качестве администратора я не мог обойти ошибку, если не переключился с LOGON32_LOGON_INTERACTIVE на LOGON32_LOGON_BATCH.Вы должны быть уверены, что групповая политика «Вход в систему как пакетное задание» не мешает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...