Windows 认证理论
我们在之前已经讨论了 NTLM 认证、Kerberos 认证、LSA 与 LSASS、SAM 等概念。在这小节,我们将对 Windows 认证理论做一个整合、归纳以及延伸。
Windows 认证类型与形式
发生在 Windows 环境中的认证过程,可以是本地认证,以及域认证。
本地认证
本地认证,例如是使用本地账户登陆主机。我们对于个人用的主机进行登陆往往就是本地登陆。本地用户只存在于特定系统,即只有该系统才知道有哪些本地用户,例如 SRV01\David。尽管系统与系统之间是互相独立的,但是不同的系统却可能有着相似甚至相同的帐号密码。例如 SRV01\David,SRV02\David。我们之前知道了本地系统的信息存储于 SAM 之中,当用户以本地账户认证的时候,Windows 会根据 SAM 中的记录验证凭证。
域认证
域认证,例如是员工通过分发的域账户登陆工作站并访问内部资源。总之,域认证与本地认证是不同的机制。域用户和群组呈现在具体的 AD 域中,所有的域系统, (或在受信任的域中) 知道如何应对认证,它们会将认证的流程委派给域控制器。域用户和主机数据存储于 NTDS (NT 目录服务) 数据库中,当有用户尝试认证,域控制器则根据记录来验证凭证。
而认证的形式,可以笼统地分为物理认证以及远程认证。
物理认证
当用户在电脑面前,输入凭证并登陆,那么这是物理认证,适用于本地用户和域用户,只要目标系统知道认证的帐号。在 AD 中,默认情况下,任何域用户可以物理登陆任何域主机。但是,绝大多数情况下,我们没有机会在目标的环境中实现物理登陆。
远程认证
与物理认证有所不同的是,默认情况下需要特定权限才能进行远程登陆,例如属于本地管理员分组,远程桌面用户分组。因此,我们应该关心的还是远程认证。
为了理解接下来的概念,我们需要对 Windows 认证机制很熟悉。
Windows 认证重要概念
认证包/安全支持提供程序 AP/SSP
虽然在前面的小节有讨论过了,让我们再回顾一下。AP/SSP 通过分析登陆数据来认证 Windows 用户,不同的 AP/SSP 对多种登陆过程以及认证协议提供支持。AP/SSP 以 DLL 形式存在,被 LSA 所加载和使用。常见的 AP/SSP 有 NTLM,Kerberos,WDigest,Credman 等。微软所提供的 AP 有 CSSP,Microsoft Negotiate,Microsoft NTLM,Microsoft Kerberos,Microsoft Digest SSP,以及 Secure Channel。
AP 提供了 Windows 充当客户端和认证服务器所需的逻辑。对于客户端,想要通过 Windows 认证连接到特定服务。对于服务器,想要让服务或应用程序支持 Windows 认证
SSP 接口
微软提供 SSP 接口以简单地将应用程序与认证系统无缝继承。
SSPI 的作用主要有这些:安全包管理、凭证管理、安全上下文管理、消息支持。
交互式登陆 vs 非交互式登陆
本地认证与域认证、物理认证与远程认证,这样的分类还不够。认证还有交互式与非交互式之分。
交互式登陆,通常需要指定明文凭证,例如通过 Windows 的登陆页面进行认证。最重要的一点是用户凭证会缓存在 LSA 进程的内存中,为各个 AP 做准备。缓存的凭证还能让 Windows 给用户提供 SSO 体验。
而非交互式认证,应用程序代表用户使用缓存的凭证,也就是非交互式认证只应在交互式认证之后发生,也就是缓存凭证可用时。一些应用通过 SSPI 来实现这些认证。
登陆会话
登陆会话会在成功的认证之后创建,无论是物理认证、远程认证、域认证、本地认证、交互式登陆还是非交互式登陆。AP 缓存的凭证绑定在了登陆会话上。而在用户登出后结束。
当认证成功时,被选中的 AP 将执行这两个重要任务:创建一个新的登陆会话,以及向 LSA 提供认证用户的安全信息。
LSA 使用该信息创建访问令牌来代表用户在系统中的的安全上下文。
通常来说,登陆会话通常在交互式认证之后存在缓存的凭证,而非交互式的认证普遍会导致不缓存凭证的登陆会话。
登陆类型
在 Windows 中,所有的认证都应当作为以下登陆类型之一处理,无论是何种认证协议以及认证形式。
登陆类型 | 编号 | 认证形式 | 凭证是否存储于 LSA 中 | 案例 |
交互式 | 2 | 密码,智能卡,其他 | 是 |
物理登陆 runas IIS 6 之前的基本认证 |
网络 | 3 | 密码,NT 哈希,Kerberos 票据 | 否 (除非委派被启用) |
NET USER RPC 调用 远程注册表 集成 Windows 认证的 IIS SQL Windows 认证 |
批处理 | 4 | 存储于 LSA 机密中的密码 | 是 | 计划任务 |
服务 | 5 | 同上 | 是 | Windows 服务 |
网络明文 | 8 | 密码 | 是 |
IIS 6 及之后的基本认证 带有 CredSSP 的 Windows PowerShell |
新凭证 | 9 | 密码 | 是 | runas /network |
远程交互式 | 10 | 密码,智能卡,其他 | 是 | 远程桌面 |
导入 Get-LogonSession.ps1 脚本 (https://github.com/leechristensen/Random/blob/master/PowerShellScripts/Get-LogonSession.ps1)。查看 Web02 目前的会话列表 (大家自己操作得到的结果可能有所不同),仅筛选出 white-bird 域中的用户
get-logonsession | where-object {$_.Domain -eq 'WHITE-BIRD'}|select UserName, LogonType, AuthenticationPackage
因为 Web02 设置了 serveradm 的自动登陆,那么 serveradm 的交互式登陆自然是预期之中的。而 Web02 上同样运行着 SQL 服务,服务账号是 sql_service,因此服务登陆以及网络登陆同样是预期之中的。
令牌
当一个登陆会话被创建后,返回给 LSA 的信息被用于创建一个访问令牌。访问令牌是被保护的对象,它包含了一个认证用户的本地安全上下文。每个访问令牌被绑定给了一个登陆会话,访问令牌与进程或线程有关。
访问令牌包含了用户与其上下文的重要数据:用户 SID、用户组归属、特权列表、会话 ID、完整度等级、令牌类型等。
在 Windows 中,相同的用户可以有着不同的上下文,例如 UAC 区分了中等和高完整度等级。这是因为 Windows 允许相同用户在同一个系统中有着不同的访问令牌和登陆会话。
Windows 使用访问令牌实施访问控制决定。Windows 对象有着访问控制列表,访问这样的对象的进程或线程有着访问令牌,而令牌信息被拿来与对象的 DACL 进行比较从而决定是否允许访问。
令牌的类型有主要令牌与模仿令牌。主要令牌又被称为进程令牌,每个进程都有关联的主要令牌,当有新的进程被创建,默认是继承父进程的主要令牌。
模仿令牌又被称为线程令牌,得以让一个线程运行在与父进程所不同的安全上下文中,通常用于客户端服务端的情景。当每个客户端连接到服务,新线程得以创建。正是因为模仿令牌,线程得以运行在不同客户端的上下文中,这使得服务可以通过 ACL 控制访问。
支持 Windows 认证的服务执行被称为客户端模仿的操作。当一个客户端连接到服务,客户端的凭证会被验证,以这个客户端为安全上下文的访问令牌被创建,服务在新的线程中置入令牌的备份,进而该线程可以代表这个客户端,并且受 ACL 的限制。因为一些服务只需要客户端的部分信息,即不需要完全的模仿。根据服务的配置,模仿令牌可以有着不同的模仿等级:
SecurityAnonymous:服务器不能模仿客户端的身份
SecurityIdentification:服务器可以获得客户端的身份和特权但不能模仿
SecurityImpersonation:服务器可以在本地主机上模仿用户的安全上下文
SecurityDelegation:服务器可以在远程主机上模仿用户的安全上下文
用户模仿
用户模仿即创建或劫持另一个用户的安全上下文以在网络中代表这个用户。创建一个安全上下文通常需要凭证,而劫持一个安全上下文则需要特权。用户模仿可以通过这些途径实现:令牌操纵、密码、NT 哈希、Kerberos 票据等。
请回顾跟登陆会话绑定的凭证,通常是交互式认证的产物。如果想通过使用一个令牌来访问网络资源,这个令牌必须与一个有着凭证的会话相关联。
Windows API 提供了能用于操纵令牌的功能,例如复制令牌。但是,我们需要特权:本地管理员或者 SYSTEM,服务账号 (土豆家族)。而低权限用户只能操纵自己的令牌。
如果是劫持一个已有令牌的方法,有着令牌模仿与进程注入的方法。前者复制目标的令牌并且将其用于已有进程或者新进程,后者将载荷注入到目标令牌所寄居的进程。
SharpToken (https://github.com/BeichenDream/SharpToken) 是一款可以通过操纵令牌来实现用户模仿的工具,在 本地管理员或者 SYSTEM 权限下,我们可以列举所有可用令牌,并窃取想要的令牌。
Cobalt Strike 中的内置命令 steal_token 也可实现令牌窃取
而通过进程注入实现令牌劫持的流程图如下:
有明文密码!
runas.exe
Windows 原生程序 runas.exe 可以通过提供额外的凭证来创建进程,例如用户想以另一个账户的身份运行特定进程。runas.exe 默认 (不提供其他选项) 会通过 LSA 验证提供的凭证的正确与否,类似于交互式认证。
对本地账户以及域账户都成立。
如果没有提供正确的凭证,则会失败。
在 Cobalt Strike 中,runas 命令则是对 Windows 系统中 runas.exe 的实现。
C# 工具 runascs (https://github.com/antonioCoco/RunasCs) 也有着相似的功能和实现,并且自定义程度更高,用户可以指定登陆类型。例如,指定 -l 为 2,则是交互式认证。
在使用 runas.exe 创建了以 white-bird\condrey 身份创建的 cmd.exe 进程以及使用 runascs.exe 以 wanh 身份创建 cmd.exe 进程后,我们可以用之前的脚本命令发现新增了用户的登陆会话。
runas.exe /netonly
runas.exe 程序有着 /netonly 的选项,它告诉 runas.exe 这个指定的凭证仅用于远程访问,而 LSA 并不会验证凭证的正确与否,也就是即便输入错误的凭证也不会有报错信息。
指定了 /netonly 选项后,假设提供的凭证是有效的,那么新的进程在本地层面保持原来的身份,但在网络层面却是新的身份。prod\john 对于 Srv01 具有 WinRM 访问。通过 /netonly 创建的新 powershell.exe 进程中,我们发现身份还是 prod\alice,但如果使用 john 的访问在 Srv01 上远程执行命令,身份则是 john 了。
当新的登陆会话随着指定的凭证创建,当前的令牌被复制了,而登陆会话 ID 被更新为新的。新的进程以这样的令牌运行。
runas.exe 使用的是 CreateProcessWithLogonW API,即以提供的凭证的安全上下文创建新的进程。/netonly 选项使用了 LOGON_NETCREDENTIALS_ONLY 登陆选项,创建和使用新的登陆会话,但是令牌还是原来的。
BOOL CreateProcessWithLogonW(
[in] LPCWSTR lpUsername,
[in, optional] LPCWSTR lpDomain,
[in] LPCWSTR lpPassword,
[in] DWORD dwLogonFlags,
[in, optional] LPCWSTR lpApplicationName,
[in, out, optional] LPWSTR lpCommandLine,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCWSTR lpCurrentDirectory,
[in] LPSTARTUPINFOW lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);