ローカルシステムアカウントとしてログインしているサービスを使用して、Windowsサービスでドメインユーザーを偽装しようとしています。
これまでのところ、次のように、サービスをログに記録し、ユーザー資格情報を使用してプロセスを設定することによってのみ、これを機能させることができます。
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = CommandDetails.Command;
startInfo.WorkingDirectory = Settings.RoboCopyWorkingDirectory;
startInfo.Arguments = commandLine;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
// Credentials
startInfo.Domain = ImperDomain;
startInfo.UserName = ImperUsername;
startInfo.Password = ImperPasswordSecure;
process = Process.Start(startInfo);
私の目標は、ドメインアカウントのパスワードがリセットされるため、ドメインユーザーではなくローカルシステムとしてサービスログを取得することです。
ローカルシステムを使用すると、アクセスが拒否されました
これを達成する方法はありますか?
StackTace
Access is denied
at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
at Ace.WindowsService.ProcessCmd.ProcessCommand.StartProcess(ProcessStartInfo startInfo) in
以下にリストされている偽装コードでコードをラップしようとしましたが、成功しませんでした。
偽装コード
public class Impersonation2 : IDisposable
{
private WindowsImpersonationContext _impersonatedUserContext;
// Declare signatures for Win32 LogonUser and CloseHandle APIs
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonSessionType logonType,
LogonProvider logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool RevertToSelf();
// ReSharper disable UnusedMember.Local
enum LogonSessionType : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
// ReSharper disable InconsistentNaming
enum LogonProvider : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
// ReSharper restore InconsistentNaming
// ReSharper restore UnusedMember.Local
/// <summary>
/// Class to allow running a segment of code under a given user login context
/// </summary>
/// <param name="user">domain\user</param>
/// <param name="password">user's domain password</param>
public Impersonation2(string domain, string username, string password)
{
var token = ValidateParametersAndGetFirstLoginToken(username, domain, password);
var duplicateToken = IntPtr.Zero;
try
{
if (DuplicateToken(token, 2, ref duplicateToken) == 0)
{
throw new Exception("DuplicateToken call to reset permissions for this token failed");
}
var identityForLoggedOnUser = new WindowsIdentity(duplicateToken);
_impersonatedUserContext = identityForLoggedOnUser.Impersonate();
if (_impersonatedUserContext == null)
{
throw new Exception("WindowsIdentity.Impersonate() failed");
}
}
finally
{
if (token != IntPtr.Zero)
CloseHandle(token);
if (duplicateToken != IntPtr.Zero)
CloseHandle(duplicateToken);
}
}
private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password)
{
if (!RevertToSelf())
{
throw new Exception("RevertToSelf call to remove any prior impersonations failed");
ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, "");
}
IntPtr token;
var result = LogonUser(domain, username,
password,
LogonSessionType.Interactive,
LogonProvider.Default,
out token);
if (!result)
{
var errorCode = Marshal.GetLastWin32Error();
ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user. LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, "");
throw new Exception("Logon for user " + username + " failed.");
}
return token;
}
public void Dispose()
{
// Stop impersonation and revert to the process identity
if (_impersonatedUserContext != null)
{
_impersonatedUserContext.Undo();
_impersonatedUserContext = null;
}
}
更新
実行しているだけで実行している場合、これは正常に機能します。ただし、サービスとして実行している場合は機能しません
更新2
偽装ログインをLogonSessionType.NewCredentialsに変更し、プロセスから資格情報を削除すると、Process.Startからアクセスが拒否されません。しかし、robocopyコマンドを実行するとエラーが表示されます。プロセスに資格情報がある場合、robocopyコマンドからログファイルを生成しません
エラー
2016/07/16 09:19:12 ERROR 5 (0x00000005)
Accessing Source Directory \\[server]\[path]\
Access is denied.
変更
var result = LogonUser(domain, username,
password,
LogonSessionType.NewCredentials,
LogonProvider.Default,
out token);
アップデート3
コピーおよび移動機能は機能しています。しかし、サブプロセスの作成はそうではありません。 Hary Johnstonが示唆したように、私はCreateProcessAsUserで遊んでいます。
私はそれを機能させることができました。
通常の偽装には、次のコードを使用しました
public class Impersonation : IDisposable
{
private WindowsImpersonationContext _impersonatedUserContext;
#region FUNCTIONS (P/INVOKE)
// Declare signatures for Win32 LogonUser and CloseHandle APIs
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonSessionType logonType,
LogonProvider logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool RevertToSelf();
#endregion
#region ENUMS
enum LogonSessionType : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
enum LogonProvider : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
#endregion
/// <summary>
/// Class to allow running a segment of code under a given user login context
/// </summary>
/// <param name="user">domain\user</param>
/// <param name="password">user's domain password</param>
public Impersonation(string domain, string username, string password)
{
var token = ValidateParametersAndGetFirstLoginToken(username, domain, password);
var duplicateToken = IntPtr.Zero;
try
{
if (DuplicateToken(token, 2, ref duplicateToken) == 0)
{
throw new InvalidOperationException("DuplicateToken call to reset permissions for this token failed");
}
var identityForLoggedOnUser = new WindowsIdentity(duplicateToken);
_impersonatedUserContext = identityForLoggedOnUser.Impersonate();
if (_impersonatedUserContext == null)
{
throw new InvalidOperationException("WindowsIdentity.Impersonate() failed");
}
}
finally
{
if (token != IntPtr.Zero)
CloseHandle(token);
if (duplicateToken != IntPtr.Zero)
CloseHandle(duplicateToken);
}
}
private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password)
{
if (!RevertToSelf())
{
ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, "");
throw new InvalidOperationException("RevertToSelf call to remove any prior impersonations failed");
}
IntPtr token;
var result = LogonUser(domain, username,
password,
LogonSessionType.NewCredentials,
LogonProvider.Default,
out token);
if (!result)
{
var errorCode = Marshal.GetLastWin32Error();
ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user. LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, "");
throw new InvalidOperationException("Logon for user " + username + " failed.");
}
return token;
}
public void Dispose()
{
// Stop impersonation and revert to the process identity
if (_impersonatedUserContext != null)
{
_impersonatedUserContext.Undo();
_impersonatedUserContext = null;
}
}
}
それを実行するには、次のことを行います。
FileInfo fi = new FileInfo(logfile);
using (var imp = new Impersonation(Settings.ImpersonateUser.AccountDomain, Settings.ImpersonateUser.AccountName, Settings.ImpersonateUser.AccountPassword))
{
if (File.Exists(filename))
File.Delete(filename);
fi.MoveTo(filename);
}
コンソールコマンドの実行には、次のコードを使用しました。
public class CreateProcess
{
#region Constants
const UInt32 INFINITE = 0xFFFFFFFF;
const UInt32 WAIT_FAILED = 0xFFFFFFFF;
#endregion
#region ENUMS
[Flags]
public enum LogonType
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK = 3,
LOGON32_LOGON_BATCH = 4,
LOGON32_LOGON_SERVICE = 5,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
LOGON32_LOGON_NEW_CREDENTIALS = 9
}
[Flags]
public enum LogonProvider
{
LOGON32_PROVIDER_DEFAULT = 0,
LOGON32_PROVIDER_WINNT35,
LOGON32_PROVIDER_WINNT40,
LOGON32_PROVIDER_WINNT50
}
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public Int32 cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 dwProcessId;
public Int32 dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public unsafe byte* lpSecurityDescriptor;
public int bInheritHandle;
}
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
#endregion
#region FUNCTIONS (P/INVOKE)
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool RevertToSelf();
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean LogonUser
(
String UserName,
String Domain,
String Password,
LogonType dwLogonType,
LogonProvider dwLogonProvider,
out IntPtr phToken
);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CreateProcessAsUser
(
IntPtr hToken,
String lpApplicationName,
String lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
Boolean bInheritHandles,
Int32 dwCreationFlags,
IntPtr lpEnvironment,
String lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern UInt32 WaitForSingleObject
(
IntPtr hHandle,
UInt32 dwMilliseconds
);
[DllImport("kernel32", SetLastError = true)]
public static extern Boolean CloseHandle(IntPtr handle);
#endregion
#region Functions
public static int LaunchCommand(string command, string domain, string account, string password)
{
int ProcessId = -1;
PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
STARTUPINFO startInfo = new STARTUPINFO();
Boolean bResult = false;
UInt32 uiResultWait = WAIT_FAILED;
var token = ValidateParametersAndGetFirstLoginToken(domain, account, password);
var duplicateToken = IntPtr.Zero;
try
{
startInfo.cb = Marshal.SizeOf(startInfo);
// startInfo.lpDesktop = "winsta0\\default";
bResult = CreateProcessAsUser(
token,
null,
command,
IntPtr.Zero,
IntPtr.Zero,
false,
0,
IntPtr.Zero,
null,
ref startInfo,
out processInfo
);
if (!bResult) { throw new Exception("CreateProcessAsUser error #" + Marshal.GetLastWin32Error()); }
// Wait for process to end
uiResultWait = WaitForSingleObject(processInfo.hProcess, INFINITE);
ProcessId = processInfo.dwProcessId;
if (uiResultWait == WAIT_FAILED) { throw new Exception("WaitForSingleObject error #" + Marshal.GetLastWin32Error()); }
}
finally
{
if (token != IntPtr.Zero)
CloseHandle(token);
if (duplicateToken != IntPtr.Zero)
CloseHandle(duplicateToken);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
return ProcessId;
}
private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password)
{
if (!RevertToSelf())
{
ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, "");
throw new Exception("RevertToSelf call to remove any prior impersonations failed");
}
IntPtr token;
var result = LogonUser(username,
domain,
password,
LogonType.LOGON32_LOGON_INTERACTIVE,
LogonProvider.LOGON32_PROVIDER_DEFAULT,
out token);
if (!result)
{
var errorCode = Marshal.GetLastWin32Error();
ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user. LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, "");
throw new Exception("Logon for user " + username + " failed.");
}
return token;
}
#endregion
}
そして、それを次のように実行します
string commandLine = "Robocopy " + args;
ProcessId = CreateProcess.LaunchCommand(commandLine, ImperDomain, ImperUsername, ImperPassword);
また、robocopyでアクセス許可をコピーできるようにするため、ローカルポリシーにいくつかの変更を加える必要がありました。
すべてのコメントと助けてくれてありがとう。