调用InitializeSecurityContext(协商)时要使用什么TargetName? [英] What TargetName to use when calling InitializeSecurityContext (Negotiate)?

查看:160
本文介绍了调用InitializeSecurityContext(协商)时要使用什么TargetName?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

调用 ,我应将什么值传递给TargetName参数?

修改后的背景

我正在调用函数 :InitializeSecurityContextA(@pAS.hcred, phContext, "", ...);

  • "spn/HOSTNAME" :InitializeSecurityContextA(@pAS.hcred, phContext, "spn/HOSTNAME", ...);
  • spn/HOSTNAME.DOMAIN.COM :InitializeSecurityContextA(@pAS.hcred, phContext, "spn/HOSTNAME.DOMAIN.COM", ...);
  • "cargocult/PROGRAMMING" :InitializeSecurityContextA(@pAS.hcred, phContext, "cargocult/PROGRAMMING", ...);
  • "http/TFS.DOMAIN.COM" :InitializeSecurityContextA(@pAS.hcred, phContext, "http/TFS.DOMAIN.COM", ...);
  • "http/HOSTNAME" :InitializeSecurityContextA(@pAS.hcred, phContext, "http/HOSTNAME", ...);
  • "qwertyasdf" :InitializeSecurityContextA(@pAS.hcred, phContext, "qwertyasdf", ...);

  • "AuthSamp" :InitializeSecurityContextA(@pAS.hcred, phContext, "AuthSamp", ...);

  • 它们全部失败,或降级为NTLM.

    注意:我的计算机已加入域,但不是域名为domain.com甚至hostname.domain.com甚至qwertyasdf.因此,对于这些尝试失败我并不感到惊讶.但是人们说尝试使用http/HOSTNAME之类的东西,所以我输入了http/HOSTNAME.

    背景

    InitializeSecurityContext (协商)函数具有可选 TargetName参数:

    pszTargetName [in,可选]

    指向以空值结尾的字符串的指针,该字符串指示服务主体名称(SPN)或目标服务器的安全上下文.
    应用程序必须提供有效的SPN来减轻重放攻击.

    这应该是什么?

    更多背景

    我正在尝试验证一组用户凭据,例如:

    Boolean ValidateCredentials(String username, String password, String domain)
    {
       ...
    }
    

    验证一组用户凭据需要使用SSPI API.要调用的第一个函数是InitializeSecurityContext. InitializeSecurityContext的参数之一是"TargetName" 字符串.

    我尝试将其保留为 null ,但记录为:

    服务主体名称 (SPN)或如何在Microsoft操作系统上验证用户凭据

    ss = _InitializeSecurityContext(
            &pAS->hcred,
            pAS->fInitialized ? &pAS->hctxt : NULL, 
            NULL,        //<-------pszTargetName
            0, 
            0,
            SECURITY_NATIVE_DREP, 
            pAS->fInitialized ? &sbdIn : NULL,
            0, 
            &pAS->hctxt, 
            &sbdOut, 
            &fContextAttr, 
            &tsExpiry);
    

    但是没有任何内容(即NULL)不起作用.

    注意:该知识库文章在2007年被大规模重写.在其最初的1999年化身中,他们通过了"AuthSamp"作为目标,但也失败了.

    奖励聊天:

    服务主体名称
    (SPN)客户端用来唯一标识服务实例的名称.如果您在整个林中的计算机上安装服务的多个实例,则每个实例必须具有自己的SPN.如果客户端可以使用多个名称进行身份验证,则给定的服务实例可以具有多个SPN

    安全上下文
    当前有效的安全属性或规则.例如,当前用户登录到计算机或智能卡用户输入的个人标识号.对于SSPI,安全上下文是不透明的数据结构,其中包含与连接有关的安全数据,例如会话密钥或会话持续时间的指示.

    奖励聊天2

    从应用程序验证程序文档中:

    Verifier插件检测到以下错误:

    • NTLM包直接在对AcquireCredentialsHandle(或更高级别的包装器API)的调用中指定.

    • 对InitializeSecurityContext的调用中的目标名称为NULL.

    • 对InitializeSecurityContext的调用中的目标名称不是格式正确的SPN,UPN或NetBIOS样式的域名.

    • 后两种情况将迫使协商"直接(第一种情况)或间接退回到NTLM(域控制器将在第二种情况下返回未找到主要"错误,导致协商"退回) .

    • 当插件检测到降级为NTLM时,它还会记录警告.例如,当域控制器未找到SPN时.由于它们通常是合法情况,例如,在对未加入域的系统进行身份验证时,它们仅作为警告记录.

    在我的情况下,我要验证的域是null(因为我不知道计算机的域名,或者即使一个域).但是,如果硬编码我的开发计算机的域名,结果是相同的.

    更新3

    pszTargetName 的值会触发AppVerifier错误,但登录成功:

    • null
    • ""
    • "AuthSamp"
    • "qwertyasdf"
    • *我要验证的域名(例如"avatopia.com")
    • *计算机加入的域的名称(例如"avatopia.com")
    • *用户帐户所在的域的名称(例如"avatopia.com")

    pszTargetName 的值不触发AppVerifier错误,但登录失败:

    • "http/HOSTNAME"
    • "http/TFS.DOMAIN.COM"
    • "frob/GROBBER"
    • "cargocult/PROGRAMMING"
    • "spn/HOSTNAME"
    • "spn/HOSTNAME.DOMAIN.COM"

    pszTargetname 的值不触发AppVerifier错误,登录成功:

    更新4

    我正在尝试做的事情:弄清楚用户名/密码是否有效.

    • 我有一个用户名:例如"ian"
    • 我有一个密码:例如"pass1"

    现在还有一个问题,帐户ian可以是本地帐户,也可以是帐户.您需要先确定ian是本地帐户还是域帐户,然后才能进行询问.这是因为ian可以具有两个帐户:

    • ian在域stackoverflow.com
    • ian在本地计算机上

    所以我需要指定是否要:

    • 询问特定域(例如stackoverflow.com),或
    • 询问本地计算机(我将其表示".")

    现在,我们可以提出一个交叉引用:

    Username  Password  Domain             Machine on domain?  Validate as
    ========  ========  =================  ==================  ==============
    iboyd     pass1     .                  No                  Local account
    iboyd     pass1     (empty)            No                  Local account
    iboyd     pass1     stackoverflow.com  No                  Domain account
    
    iboyd     pass1     .                  Yes                 Local account
    iboyd     pass1     (empty)            Yes                 Domain account
    iboyd     pass1     stackoverflow.com  Yes                 Domain account
    

    更新5

    这可能有助于解释我要做什么,然后如何会变得更容易.假设我走进市区的随机办公楼,走进随机的隔间,然后输入随机的用户名和密码:

    我将尝试登录到域TURBOENCABULATOR.我指定了我想要通过对TURBOENCABULATOR域进行身份验证的方式,将我的用户名添加为前缀:

    TURBOENCABULATOR\ian
    

    注意:我强烈怀疑该网络具有一个名为 turboencabulator 的域,因为该名称本身仅来自

    尽管Kerberos已经使用了许多年了,但许多应用程序 仍然只写为使用NTLM.这不必要地减少了 应用程序的安全性.但是,Kerberos不能全部替换NTLM 方案–主要是那些客户需要进行身份验证的方案 未加入域的系统(可能是家庭网络 其中最常见的).协商安全软件包允许 向后兼容的折衷方案,尽可能使用Kerberos 并且仅在没有其他选择时才恢复为NTLM.切换码 使用协商而不是NTLM将大大增加 为客户提供安全保障,而很少或根本不引入应用 兼容性.谈判本身不是灵丹妙药–在那里 在某些情况下,攻击者可以强制降级为NTLM,但这些都是 更加难以利用.但是,立即 改进之处在于,编写为正确使用协商"的应用程序 自动抵御NTLM反射攻击.

    最后警告不要使用NTLM: Windows版本将可以在以下位置禁用NTLM: 操作系统.如果应用程序对NTLM有严格的依赖性 禁用NTLM时,它们将完全无法通过身份验证.

    插件的工作方式

    Verifier插件检测到以下错误:

    • NTLM包直接在对AcquireCredentialsHandle(或更高级别的包装器API)的调用中指定.

    • 对InitializeSecurityContext的调用中的目标名称为NULL.

    • 对InitializeSecurityContext的调用中的目标名称不是格式正确的SPN,UPN或NetBIOS样式的域名.

    后两种情况将迫使协商直接(第一种情况)或间接地退回到NTLM(域控制器将在第二种情况下返回未找到主要"错误,导致协商后退).

    当插件检测到降级为NTLM时,它还会记录警告.例如,当域控制器未找到SPN时.由于它们通常是合法情况,例如,在对未加入域的系统进行身份验证时,它们仅作为警告记录.

    NTLM停止

    5000 –应用程序已明确选择NTLM软件包

    严重性-错误

    应用程序或子系统在对AcquireCredentialsHandle的调用中显式选择了NTLM而不是协商".即使客户端和服务器可能使用Kerberos进行身份验证,这也可以通过显式选择NTLM来防止.

    如何解决此错误

    此错误的解决方法是选择Negotiate程序包代替NTLM.如何完成此操作将取决于客户端或服务器使用的特定网络子系统.下面给出一些示例.您应该查阅所使用的特定库或API集上的文档.

    APIs(parameter) Used by Application    Incorrect Value  Correct Value  
    =====================================  ===============  ========================
    AcquireCredentialsHandle (pszPackage)  NTLM           NEGOSSP_NAME Negotiate
    

    另请参见

    解决方案

    简短回答

    TargetName 服务器" 代码将以其运行的用户名.

    • 我以ian@stackoverflow.com
    • 的身份登录
    • 我想向steve@stackoverflow.com
    • 证明我的身份
    • TargetName 设置为steve@stackoverflow.com

    背景

    Negotiate身份验证程序包将尝试使用Kerberos.如果不能,它将尝试回退到NTLM.

    • 想要使用Kerberos.
    • 不想使用NTLM;它是旧的,过时的,脆弱的,破裂的,不应使用.
    • 要使用Kerberos,您必须提供 TargetName
    • 没有 TargetName Kerberos根本无法使用该功能

    考虑到参与我的所有参与者( Ian )已通过 Steve 进行身份验证,问题变成了,我要指定什么 TargetName ? >

    在这里重要的是要知道 TargetName 对Kerberos的含义:

    • 我想向steve@stackoverflow.local
    • 证明我的身份
    • 域控制器递给我一个加密的blob,以证明我的身份
    • 该blob已加密,因此steve@stackoverflow.local是唯​​一能够对其解密的人
    • 域控制器知道要为steve@stackoverflow.local进行加密,因为我在 TargetName
    • 中指定了steve@stackoverflow.local
    • 史蒂夫目标

    这就是史蒂夫(Steve)知道Blob有效的原因,它已加密,因此只有他才能解密.

    我必须告诉Kerberos ,我将把加密的blob提供给谁,以便域控制器知道要为其加密的对象.

    因此在上面的可能名称列表中,三个值起作用:

    InitializeSecurityContext(credHandle, context, "steve@stackoverflow.local", ...);    
    InitializeSecurityContext(credHandle, context, "stackoverflow.local\steve", ...);    
    InitializeSecurityContext(credHandle, context, "steve", ...); //if we're in the same forest
    

    由于灵活性(欢迎使用SPN)而造成的混乱

    SPN的简短版本

    • 使用Kerberos时,您必须将用户名指定为您的 TargetName
    • SPN是用户名的别名"
    • 因此,您可以将SPN指定为您的 TargetName

    SPN的长版本

    在上述情况下,我必须知道服务器" 代码将以steve@stackoverflow.local运行.

    这很痛苦.我的意思是说我知道史蒂夫就很好.但是,如果我正在与远程计算机通信,我必须找出代码运行所使用的用户帐户?

    • 我必须弄清楚IIS是否以iisagent@stackoverflow.local的身份运行?
    • 我必须弄清楚SQL Server是否以sqldaemon@stackoverflow.local的身份运行?
    • 以及如果该服务在 Local Service 上运行该怎么办;根本不是域用户

    幸运地(?),Kerberos创建了别名(称为服务主体名称-或SPN):

    • 如果我需要向Web服务器进行身份验证http://bugtracker.stackoverflow.local
    • 并且IIS服务在域帐户iisagent@stackoverflow.local
    • 下运行
    • 不必指定iisagent@stackoverflow.local
    • 的目标名称
    • 我可以指定HTTP/bugtracker.stackoverflow.local
    • 那是因为IIS在域控制器中注册了一个别名
    • HTTP/bugtracker.stackoverflow.local-> iisagent@stackoverflow.local

    如果要使用SPN作为 TargetName ,所有这些都要求您知道SPN.各种标准的Microsoft产品在安装时都会注册SPN:

    • IIS :HTTP/[servername]
    • SQL Server :MSSQLSvc/[servername]:1433
    • SMTP :SMTPSVC/[servername]
    • 文件共享:HOST/[servername]

    这些都是未记录的,如果配置不正确,会使您的生活陷于瘫痪.

    但是您不必提供 SPN . SPN只是一个别名,旨在使您的生活更加困难.

    这大致相当于尝试指定"stackoverflow.com" ,而不是简单地使用"35.186.238.101" .

    SSPI如何工作

    SSPI被设计为围绕不同安全算法的通用包装.使用API​​的方法非常简单:

    • 客户端:呼叫InitializeSecurityContext并被赋予一个斑点
    • 客户端将该Blob发送到服务器
    • 服务器:调用AcceptSecurityContext(blob),并获得一滴斑点
    • 服务器将该Blob发送回客户端
    • 客户:呼叫InitializeSecurityContext(blob),并获得一个Blob
    • 客户端将该Blob发送回服务器
    • 服务器:调用AcceptSecurityContext(blob),并获得一滴斑点
    • ...一直重复直到被告知要停止...

    双方一直来回移动,直到函数停止返回需要发送到另一方的Blob:

    因此,使用SSPI,您可以前后往复执行此操作,直到被告知停止操作为止.这样,他们便可以将所有身份验证方案塞入那种乒乓球直至停止的高级抽象中.

    如何传输斑点?

    您可以通过所使用的任何通信渠道来传输blob.

    如果您正在通过TCP/IP与删除服务器通信,那么您可能会使用它:

    // Open connection to server
    sockConnect(162.210.196.166, 1433);
    
    blob = null;
    
    Boolean bContinue = InitializeSecurityContext(ref blob);
    
    while (bContinue)
    {
       sockWrite(blob); //send the blob to the server
    
       blob = sockRead(); //wait for the server to return a blob 
    
       bContinue = InitializeSecurityContext(ref blob);
    }
    

    如果您通过http:

    blob = null;
    Boolean bContinue = InitializeSecurityContext(ref blob);
    
    while (bContinue)
    {
        http = new HttpRequest("http://4chan.org/default.aspx");
        http.AddHeader("X-SSPI-Blob", blob.ToBase64());
        http.Send();
    
        blob = http.ReasponseHeader["X-SSPI-Blob"];
        if (blob.IsEmpty())
           break;
    
        bContinue = InitializeSecurityContext(ref blob);
    }
    

    The Question

    When calling InitializeSecurityContext, what value do i pass to the TargetName parameter?

    Revised Background

    I'm calling the function InitializeSecurityContext:

    InitializeSecurityContextA(
          @pAS.hcred,           //[in] credentials
          phContext,            //[in] optional] Context handle structure
          pszTargetName,        //[in, optional] Target name
          0,                    //[in] context requirements
          0,                    //[in] reserved1, must be zero
          SECURITY_NATIVE_DREP, //[in] target data representation
          pInput,               //[in] optional] SecBufferDescription
          0,                    //[in] reserved2, must be zero
          @pAS.hctxt,           //[in, out] pointer to context handle structure
          @OutBuffDesc,         //[in, out] pointer to SecBufferDesc
          ContextAttributes,    //[out] context attributes
          @lifetime);           //[out] expiration timestamp
    

    What do i pass to pszTargetName?

    I've tried

    • null: InitializeSecurityContextA(@pAS.hcred, phContext, null, ...);
    • "": InitializeSecurityContextA(@pAS.hcred, phContext, "", ...);
    • "spn/HOSTNAME": InitializeSecurityContextA(@pAS.hcred, phContext, "spn/HOSTNAME", ...);
    • spn/HOSTNAME.DOMAIN.COM: InitializeSecurityContextA(@pAS.hcred, phContext, "spn/HOSTNAME.DOMAIN.COM", ...);
    • "cargocult/PROGRAMMING": InitializeSecurityContextA(@pAS.hcred, phContext, "cargocult/PROGRAMMING", ...);
    • "http/TFS.DOMAIN.COM": InitializeSecurityContextA(@pAS.hcred, phContext, "http/TFS.DOMAIN.COM", ...);
    • "http/HOSTNAME": InitializeSecurityContextA(@pAS.hcred, phContext, "http/HOSTNAME", ...);
    • "qwertyasdf": InitializeSecurityContextA(@pAS.hcred, phContext, "qwertyasdf", ...);

    • "AuthSamp": InitializeSecurityContextA(@pAS.hcred, phContext, "AuthSamp", ...);

    They all either fail, or downgrade to NTLM.

    Note: My machine is domain joined, but the domain is not named domain.com, or even hostname.domain.com, or even qwertyasdf. So i'm not surprised that those attempts fail. But people said try things like http/HOSTNAME, so i put in http/HOSTNAME.

    Background

    The InitializeSecurityContext (Negotiate) function has an optional TargetName parameter:

    pszTargetName [in, optional]

    A pointer to a null-terminated string that indicates the service principal name (SPN) or the security context of the destination server.
    Applications must supply a valid SPN to help mitigate replay attacks.

    What is this supposed to be?

    More Background

    i am trying to validate a set of user's credentials, e.g.:

    Boolean ValidateCredentials(String username, String password, String domain)
    {
       ...
    }
    

    Validating a set of user's credentials requires using the SSPI API. The first function to call is InitializeSecurityContext. One of the parameters to InitializeSecurityContext is a "TargetName" string.

    i've tried leaving it null, but the Application Verifier triggers a breakpoint, writing out the error:

    VERIFIER STOP 00005003: pid 0xF08:
    InitializeSecurityContext uses NULL target or malformed target for Kerberos service.
    Please see pszTargetName for the value of the target.
    00000000 : Not used.
    00000000 : Not

    At this point it would be helpful to remember that the Negotiate provider will attempt to use Kerberos, but fallback to NTLM. In the case of Negotiate, Kerberos or NTLM, the TargetName parameter is documented to be:

    Service principal name (SPN) or the security context of the destination server.

    But then what should i pass?

    i tried doing what the SSPI Knowledge Base article does, nothing (i.e. pass NULL):

    How to validate user credentials on Microsoft operating systems

    ss = _InitializeSecurityContext(
            &pAS->hcred,
            pAS->fInitialized ? &pAS->hctxt : NULL, 
            NULL,        //<-------pszTargetName
            0, 
            0,
            SECURITY_NATIVE_DREP, 
            pAS->fInitialized ? &sbdIn : NULL,
            0, 
            &pAS->hctxt, 
            &sbdOut, 
            &fContextAttr, 
            &tsExpiry);
    

    But nothing (i.e. NULL) doesn't work.

    Note: The KB article was massivly rewritten in 2007. In its original 1999 incarnation they passed "AuthSamp" as the target, but that also fails.

    Bonus Chatter:

    service principal name
    (SPN) The name by which a client uniquely identifies an instance of a service. If you install multiple instances of a service on computers throughout a forest, each instance must have its own SPN. A given service instance can have multiple SPNs if there are multiple names that clients might use for authentication

    security context
    The security attributes or rules that are currently in effect. For example, the current user logged on to the computer or the personal identification number entered by the smart card user. For SSPI, a security context is an opaque data structure that contains security data relevant to a connection, such as a session key or an indication of the duration of the session.

    Bonus Chatter 2

    From the application verifier documentation:

    The Verifier plug detects the following errors:

    • The NTLM package is directly specified in the call to AcquireCredentialsHandle (or higher level wrapper API).

    • The target name in the call to InitializeSecurityContext is NULL.

    • The target name in the call to InitializeSecurityContext is not a properly-formed SPN, UPN or NetBIOS-style domain name.

    • The latter two cases will force Negotiate to fall back to NTLM either directly (the first case) or indirectly (the domain controller will return a "principal not found" error in the second case causing Negotiate to fall back).

    • The plug-in also logs warnings when it detects downgrades to NTLM; for example, when an SPN is not found by the Domain Controller. These are only logged as warnings since they are often legitimate cases – for example, when authenticating to a system that is not domain-joined.

    In my case the domain i am validating against is null (since i don't know the machine's domain name, or even if there is a domain). But the results are the same if the hard-code my development machine's domain name.

    Update 3

    Values of pszTargetName that trigger AppVerifier error, but logon succeeds:

    • null
    • ""
    • "AuthSamp"
    • "qwertyasdf"
    • *the name of the domain i'm validating against (e.g. "avatopia.com")
    • *the name of the domain the machine is joined to (e.g. "avatopia.com")
    • *the name of the domain the user account is located in (e.g. "avatopia.com")

    Values of pszTargetName that do not trigger an AppVerifier error, but logon fails:

    • "http/HOSTNAME"
    • "http/TFS.DOMAIN.COM"
    • "frob/GROBBER"
    • "cargocult/PROGRAMMING"
    • "spn/HOSTNAME"
    • "spn/HOSTNAME.DOMAIN.COM"

    Values of pszTargetname that do not trigger an AppVerifier error, and logon succeeds:

    • none

    Update 4

    What i'm trying to do: figure out if a username/password is valid.

    • i have a username: e.g. "ian"
    • i have a password: e.g. "pass1"

    Now there's the further wrinkle that the account ian could be a local account or a domain account. And you need to decide if ian is a local or domain account before you can ask. This is because ian can have two accounts:

    • ian on domain stackoverflow.com
    • ian on local machine

    So i need to specify if i want to:

    • ask a particular domain (e.g. stackoverflow.com), or
    • ask the local machine (which i'll represent as ".")

    Now we can come up with a cross reference:

    Username  Password  Domain             Machine on domain?  Validate as
    ========  ========  =================  ==================  ==============
    iboyd     pass1     .                  No                  Local account
    iboyd     pass1     (empty)            No                  Local account
    iboyd     pass1     stackoverflow.com  No                  Domain account
    
    iboyd     pass1     .                  Yes                 Local account
    iboyd     pass1     (empty)            Yes                 Domain account
    iboyd     pass1     stackoverflow.com  Yes                 Domain account
    

    Update 5

    It might help to explain what i'm trying to do, then maybe how to do it will become easier. Lets say i walk into a random office building downtown, walk into a random cubicle, and type in a random username and password:

    i'm going to try to login to the domain TURBOENCABULATOR. i specified i want to try to authenticate against the TURBOENCABULATOR domain by prefixing my username as:

    TURBOENCABULATOR\ian
    

    Note: i highly doubt the network has a domain called turboencabulator, since the name itself only comes from Rockwell automation. The attempt to login will almost certainly fail. But how does Windows check them?

    How does Windows attempt to validate these credentials? How does Windows validate the credentials:

    • Username: ian
    • Password: pass1
    • Domain: TURBOENCABULATOR

    Does Windows use the Security Support Package Interface? Assuming windows uses Negotiate or Kerberos for authentication, what does Windows pass as the pszTarget parameter? Almost certainly the credentials i enter will not be valid. How will Windows determine if they are valid? What API will Windows call to validate the credentails?

    Windows is able to validate credentails. I want to also validate credentials.

    Perhaps instead of trying to connect to the TURBOENCABULATOR domain, i try to connect to the turboencabulator.com domain by prepending the domain to my username as turboencabulator.com\ian:

    Same question applies. How does Windows validate credentials? i want to do what Windows does. Assuming Windows uses kerberos for authorization, what does Windows pass as the pszTargetName parameter in SSPI?

    Perhaps instead of trying to connect to the turboencabulator.com domain, i try to connect to the turboencabulator.net domain:

    Note that in this example i've appended the domain name to my username, rather than prepending it.

    Perhaps instead of trying to connect to the turboencabulator.net domain, i try to validate the user as a local (machine) account by prefixing my username with .\ as:

    How does Windows validate the username and password against the local account database? Does it use SSPI with Negotiate package? If so what value does it pass as the pszTargetName?

    People are talking about web servers, http, team foundation server. i really don't know where they're getting that from. Or they talk about editing a user in active directory to ensure something is present - i don't see why i need to edit anything: Windows doesn't edit anything.

    What TargetName do i used when calling InitializeSecurityContext in order to validate a set of credentials?

    Bonus Chatter

    Here's a chapter from the Application Verifier documentation about why they have a test if someone is mistakenly using NTLM:

    Why the NTLM Plug-in is Needed

    NTLM is an outdated authentication protocol with flaws that potentially compromise the security of applications and the operating system. The most important shortcoming is the lack of server authentication, which could allow an attacker to trick users into connecting to a spoofed server. As a corollary of missing server authentication, applications using NTLM can also be vulnerable to a type of attack known as a "reflection" attack. This latter allows an attacker to hijack a user’s authentication conversation to a legitimate server and use it to authenticate the attacker to the user’s computer. NTLM’s vulnerabilities and ways of exploiting them are the target of increasing research activity in the security community.

    Although Kerberos has been available for many years many applications are still written to use NTLM only. This needlessly reduces the security of applications. Kerberos cannot however replace NTLM in all scenarios – principally those where a client needs to authenticate to systems that are not joined to a domain (a home network perhaps being the most common of these). The Negotiate security package allows a backwards-compatible compromise that uses Kerberos whenever possible and only reverts to NTLM when there is no other option. Switching code to use Negotiate instead of NTLM will significantly increase the security for our customers while introducing few or no application compatibilities. Negotiate by itself is not a silver bullet – there are cases where an attacker can force downgrade to NTLM but these are significantly more difficult to exploit. However, one immediate improvement is that applications written to use Negotiate correctly are automatically immune to NTLM reflection attacks.

    By way of a final word of caution against use of NTLM: in future versions of Windows it will be possible to disable the use of NTLM at the operating system. If applications have a hard dependency on NTLM they will simply fail to authenticate when NTLM is disabled.

    How the Plug-in Works

    The Verifier plug detects the following errors:

    • The NTLM package is directly specified in the call to AcquireCredentialsHandle (or higher level wrapper API).

    • The target name in the call to InitializeSecurityContext is NULL.

    • The target name in the call to InitializeSecurityContext is not a properly-formed SPN, UPN or NetBIOS-style domain name.

    The latter two cases will force Negotiate to fall back to NTLM either directly (the first case) or indirectly (the domain controller will return a "principal not found" error in the second case causing Negotiate to fall back).

    The plug-in also logs warnings when it detects downgrades to NTLM; for example, when an SPN is not found by the Domain Controller. These are only logged as warnings since they are often legitimate cases – for example, when authenticating to a system that is not domain-joined.

    NTLM Stops

    5000 – Application Has Explicitly Selected NTLM Package

    Severity – Error

    The application or subsystem explicitly selects NTLM instead of Negotiate in the call to AcquireCredentialsHandle. Even though it may be possible for the client and server to authenticate using Kerberos this is prevented by the explicit selection of NTLM.

    How to Fix this Error

    The fix for this error is to select the Negotiate package in place of NTLM. How this is done will depend on the particular Network subsystem being used by the client or server. Some examples are given below. You should consult the documentation on the particular library or API set that you are using.

    APIs(parameter) Used by Application    Incorrect Value  Correct Value  
    =====================================  ===============  ========================
    AcquireCredentialsHandle (pszPackage)  "NTLM"           NEGOSSP_NAME "Negotiate"
    

    See also

    解决方案

    Short Answer

    The TargetName is the username that the "server" code will be running as.

    • I'm logged in as ian@stackoverflow.com
    • I want to prove my identity to steve@stackoverflow.com
    • Set TargetName to steve@stackoverflow.com

    Background

    The Negotiate authentication package will attempt to use Kerberos. If it cannot, it will attempt to fallback to NTLM.

    • You want to use Kerberos.
    • You don't want to use NTLM; it is old, deprecated, weak, broken, and should not be used.
    • In order to use Kerberos you must supply a TargetName
    • Without a TargetName, Kerberos is fundamentally unable the function

    The question becomes, given all the parties involved in me (Ian) authenticating with Steve, what TargetName do i specify?

    This is where it's important to know what TargetName means to Kerberos:

    • i want to prove my identity to steve@stackoverflow.local
    • the domain controller hands me an encrypted blob that proves my identity
    • the blob is encrytped so steve@stackoverflow.local is the only one able to decrypt it
    • the domain controller knews to encrypt it for steve@stackoverflow.local because i specified steve@stackoverflow.local in the TargetName
    • Steve is the target

    That's how Steve knows the blob is valid, it was encrypted so only he can decrypt it.

    I have to tell Kerberos who i will be giving the encrypted blob to, so the domain controller knows who to encrypt it for.

    So in the above list of possible names, three values work:

    InitializeSecurityContext(credHandle, context, "steve@stackoverflow.local", ...);    
    InitializeSecurityContext(credHandle, context, "stackoverflow.local\steve", ...);    
    InitializeSecurityContext(credHandle, context, "steve", ...); //if we're in the same forest
    

    Extra confusion because flexibility (welcome to SPN)

    SPNs Short version

    • when using Kerberos you must specify a username as your TargetName
    • an SPN is an "alias" for a username
    • therefore you can specify an SPN as your TargetName

    SPNs Long Version

    In the case above i had to know that the "server" code will be running as steve@stackoverflow.local.

    That's a pain. I mean it's fine when i know it's steve. But if i'm talking to a remote machine, i have to find out the user account that the code is running as?

    • i have to figure out that IIS is running as iisagent@stackoverflow.local?
    • i have to figure out that SQL Server is running as sqldaemon@stackoverflow.local?
    • and what if the service is running at Local Service; that not a domain user at all

    Fortunately(?), Kerberos created aliases (called Service Principle Names - or SPNs):

    • if i need to authenticate to the web server http://bugtracker.stackoverflow.local
    • and IIS service is running under the domain account iisagent@stackoverflow.local
    • rather than have to specify the targetname of iisagent@stackoverflow.local
    • i can specify HTTP/bugtracker.stackoverflow.local
    • that's because IIS registered an alias with the domain controller
    • HTTP/bugtracker.stackoverflow.local --> iisagent@stackoverflow.local

    All this requires that you know the SPN if you wish to use it as a TargetName. Various standard Microsoft products register SPNs when they install:

    • IIS: HTTP/[servername]
    • SQL Server: MSSQLSvc/[servername]:1433
    • SMTP: SMTPSVC/[servername]
    • File sharing: HOST/[servername]

    These are all undocumented, and make your life hell when one isn't configured correctly.

    But by no means do you have to supply a SPN. An SPN is simply an alias designed to make your life more difficult.

    It's roughly equivalent to attempting to specify "stackoverflow.com", rather than simply using "35.186.238.101".

    How does SSPI work

    SSPI was designed as a generic wrapper around different security algorithms. The way to use the API is pretty simple:

    • Client: calls InitializeSecurityContext and is given a blob
    • client sends that blob to the server
    • Server: calls AcceptSecurityContext(blob), and is given a blob back
    • server sends that blob back to the client
    • Client: calls InitializeSecurityContext(blob), and is given back a blob
    • client sends that blob back to the server
    • Server: calls AcceptSecurityContext(blob), and is given a blob back
    • ...keep repeating until told to stop...

    Both sides keep going back and forth until the function stops returning a blob that needs to be sent to the other side:

    And so the with SSPI you do this ping-ponging back and forth until you're told to stop. And so they were able to shoe-horn every authentication scheme into that ping-pong-until-told-to-stop high level abstraction.

    How do I transmit the blobs?

    You transmit the blobs over whatever communication channel you're using.

    If you're talking to a remove server over TCP/IP, then you'd probably use that:

    // Open connection to server
    sockConnect(162.210.196.166, 1433);
    
    blob = null;
    
    Boolean bContinue = InitializeSecurityContext(ref blob);
    
    while (bContinue)
    {
       sockWrite(blob); //send the blob to the server
    
       blob = sockRead(); //wait for the server to return a blob 
    
       bContinue = InitializeSecurityContext(ref blob);
    }
    

    If you're doing it over http:

    blob = null;
    Boolean bContinue = InitializeSecurityContext(ref blob);
    
    while (bContinue)
    {
        http = new HttpRequest("http://4chan.org/default.aspx");
        http.AddHeader("X-SSPI-Blob", blob.ToBase64());
        http.Send();
    
        blob = http.ReasponseHeader["X-SSPI-Blob"];
        if (blob.IsEmpty())
           break;
    
        bContinue = InitializeSecurityContext(ref blob);
    }
    

    这篇关于调用InitializeSecurityContext(协商)时要使用什么TargetName?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆