系统相关
首页 > 系统相关> > 托管Active Directory RSAT Powershell脚本的ASP.NET-DC挂断

托管Active Directory RSAT Powershell脚本的ASP.NET-DC挂断

作者:互联网

我正在开发一个ASP.NET(C#)应用程序,该应用程序基本上是运行Powershell脚本以执行常规管理任务的网关.其中一些脚本使用ActiveDirectory RSAT模块,我发现其中一些cmdlet在通过网关调用时将无法正确运行,并且跟踪似乎暗示与域控制器的连接已成功,但随后被关闭了由DC.

以下代码是ASP.NET Web表单,该表单具有一个输入文本以指定用户名.基本上,它执行以下操作:

>假定Web用户的身份(确认已由Powershell继承)
>创建一个Powershell运行空间和该运行空间内的管道
>调用Get-ADUser cmdlet并将用户名作为Identity参数传递
>通过将用户名读入表单上的输出元素来确认成功.

protected void LookupButton_Click( object sender, EventArgs e ) {
  WindowsImpersonationContext impersonationContext = ((WindowsIdentity)User.Identity).Impersonate();
  Runspace runspace;
  Pipeline pipe;
  try {
    runspace = new_runspace();
    runspace.Open();
    pipe = runspace.CreatePipeline();
    Command cmd = new Command("Get-ADUser");
    cmd.Parameters.Add(new CommandParameter("Identity", text_username.Text));
    pipe.Commands.Add(cmd);

    PSObject ps_out = pipe.Invoke().First();
    output.Text = ps_out.Properties["Name"].Value.ToString();
  }
  catch( Exception ex ) {
    error.Text = ex.ToString();
  }
  finally {
    impersonationContext.Undo();
  }
}

private Runspace new_runspace( ) {
  InitialSessionState init_state = InitialSessionState.CreateDefault();
  init_state.ThreadOptions = PSThreadOptions.UseCurrentThread;
  init_state.ImportPSModule(new[] { "ActiveDirectory" });
  return RunspaceFactory.CreateRunspace(init_state);
}

有趣的部分是在catch块中暴露的错误消息中的特定措辞(强调我的意思):

System.Management.Automation.CmdletInvocationException: Unable to
contact the server. This may be because this server does not exist, it
is currently down, or it does not have the Active Directory Web
Services running. —>
Microsoft.ActiveDirectory.Management.ADServerDownException: Unable to
contact the server. This may be because this server does not exist, it
is currently down, or it does not have the Active Directory Web
Services running. —> System.ServiceModel.CommunicationException: The
socket connection was aborted. This could be caused by an error
processing your message or a receive timeout being exceeded by the
remote host, or an underlying network resource issue. Local socket
timeout was ’00:01:59.6870000′. —> System.IO.IOException: The read
operation failed, see inner exception. —>
System.ServiceModel.CommunicationException: The socket connection was
aborted. This could be caused by an error processing your message or a
receive timeout being exceeded by the remote host, or an underlying
network resource issue. Local socket timeout was ’00:01:59.6870000′.
—> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host

较高级别的异常表明存在超时,但较低的异常没有指示超时(从命令返回仅花费了几秒钟).在服务器无法访问的情况下,最低级别的异常消息会说得足够多,但是这种特定的措辞使我认为此处存在某种身份验证(或其他安全性)问题.

2013年2月19日更新:
当使用描述的模拟方法here时,脚本将按预期运行.这使我认为问题可能是Windows身份验证提供的WindowsIdentity对象可能不适用于本质上针对AD进行RPC调用的脚本.不幸的是,我真的不希望放弃Windows身份验证,因为我必须在我的应用程序代码中处理用户的密码(这不是我想要的责任).

我还没有找到任何有关Windows auth到底在做什么的文件或使用它可能导致的模拟的任何文档.使用Windows身份验证时是否可以执行此操作,还是我需要要求用户提供密码?

解决方法:

原因

此问题的根本原因是,当您在IIS中使用Windows身份验证时,安全令牌仅对将Web客户端计算机向Web服务器计算机进行身份验证有效.相同的令牌对于将Web服务器计算机向任何其他计算机进行身份验证均无效,这就是我的应用程序正在尝试执行的操作:

>客户端获取安全令牌并将其发送到Web服务器.
> IIS要求DC验证令牌,并且令牌已验证.此时,Web客户端已通过Web服务器的身份验证.
> IIS根据应用程序的授权规则检查已验证的身份.
> Web应用程序使用IIS接收的令牌模拟身份,并运行脚本,该脚本随后将继承相同的安全令牌.
>该脚本尝试使用相同的令牌对远程RPC服务进行身份验证.
>域控制器将身份验证尝试识别为重播攻击(该令牌用于其他服务),并关闭连接.

称它为Kerberos的“副作用”并不完全正确,但起初对我来说并不明显,尽管事后看来很明显.我希望有人可以从此信息中受益.

解决方案是让应用程序生成自己的安全令牌,然后通过对LogonUser()进行API调用,将其用作Web用户身份验证其他机器上的服务.您的应用程序代码将需要访问用户的明文密码和可以通过仅在IIS中启用HTTP Basic身份验证来使其可用. Web服务器仍将强制执行相同的身份验证和授权规则,但是用户名和密码都可用于您的应用程序代码.请记住,这些凭据是明文传输到Web服务器的,因此在生产环境中使用SSL之前,您将需要SSL.

我根据here所述的过程创建了一个小的帮助程序类,该类有助于该过程.这是一个简短的演示:

登录助手:

public class IdentityHelper {
  [DllImport("advapi32.dll")]
  private static extern int LogonUserA( String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken );

  [DllImport("advapi32.dll",
    CharSet = CharSet.Auto,
    SetLastError = true)]
  private static extern int DuplicateToken( IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken );

  [DllImport("kernel32.dll",
    CharSet = CharSet.Auto)]
  private static extern bool CloseHandle( IntPtr handle );

  public const int LOGON32_LOGON_INTERACTIVE = 2;
  public const int LOGON32_PROVIDER_DEFAULT = 0;
  public const int IMPERSONATION_LEVEL_IMPERSONATE = 2;

  public static WindowsIdentity Logon( string username, string password, string domain = "" ) {
    IntPtr token = IntPtr.Zero;
    WindowsIdentity wid = null;

    if( domain == "" ) {
      split_username(username, ref username, ref domain);
    }

    if( LogonUserA(username, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0 ) {
      wid = WIDFromToken(token);
    }
    if( token != IntPtr.Zero ) CloseHandle(token);
    return wid;
  }

  public static WindowsIdentity WIDFromToken( IntPtr src ) {
    WindowsIdentity wid = null;
    IntPtr token = IntPtr.Zero;
    if( DuplicateToken(src, IMPERSONATION_LEVEL_IMPERSONATE, ref token) != 0 ) {
      wid = new WindowsIdentity(token);
    }
    if( token != IntPtr.Zero ) CloseHandle(token);
    return wid;
  }

  private static void split_username( string username, ref string username_out, ref string domain_out ) {
    string[] composite_username = username.Split(new char[] { '\\' });
    if( composite_username.Length == 2 ) {
      domain_out = composite_username[0];
      username_out = composite_username[1];
    }
  }
}

Powershell助手类:

public class PSHelper {
  public static Runspace new_runspace() {
    InitialSessionState init_state = InitialSessionState.CreateDefault();
    init_state.ThreadOptions = PSThreadOptions.UseCurrentThread;
    init_state.ImportPSModule(new[] { "ActiveDirectory" });
    return RunspaceFactory.CreateRunspace(init_state);
  }
}

ASP.NET表单处理程序:

protected void LookupButton_Click( object sender, EventArgs e ) {
  string outp = "";
  WindowsIdentity wid = IdentityHelper.Logon(Request["AUTH_USER"], Request["AUTH_PASSWORD"]);
  using( wid.Impersonate() ) {
    Runspace runspace;
    Pipeline pipe;
    runspace = PSHelper.new_runspace();
    runspace.Open();
    pipe = runspace.CreatePipeline();
    Command cmd = new Command("Get-ADUser");
    cmd.Parameters.Add(new CommandParameter("Identity", text_username.Text));
    pipe.Commands.Add(cmd);
    outp = pipe.Invoke().First().Properties["Name"].Value.ToString();
  }
  output.Text = outp;
}

标签:impersonation,active-directory,powershell,asp-net,c
来源: https://codeday.me/bug/20191031/1974277.html