前言
作为一个脚本小子,当我们在进行内网渗透的过程中,要去抓取内网机器的账号密码、还有一堆hash值、还有什么黄金票据、白银票据,搞到这些东西以后就是一顿无脑梭哈,就能拿到机器了。现在!我不想再做一个脚本小子了!为什么呢?为什么就能抓到机器的明文账号密码了呢?抓了一堆的hash值干嘛的呢?什么是黄金票据什么是白银票据?为什么用这些票据就能拿到机器的权限?本章节带你来解答这些问题。
NTLM协议
先来介绍一下,在windows中进行身份认证使用的NTLM协议。
NTLM Hash算法
NTLM Hash算法是微软为了在提高安全性的同时保证兼容性而设计的散列加密算法,他是基于MD4加密算法进行加密的。
具体的NTLM Hash算法这里就不详细演示了,直接使用一行python代码进行NTLM加密:
1 | import binascii |
运行结果如下:

Windows系统存储的NTLM Hash
前面介绍了NTLM Hash算法,这个NTLM Hash算法加密的结果就是我们在进行内网渗透的过程中,抓取的Hash值。这些Hash值是干什么的呢?用于比对进行身份验证用的。
用户在Windows登录过程中,用户的密码经过NTLM Hash加密后存储在C:\Windows\system32\config\SAM文件中。

在用户输入密码进行本地认证的过程中,所有操作都是在本地进行的。系统将用户输入的密码转换为NTLM Hash,然后与SAM文件中的NTLM Hash进行比较,如果相同,说明密码正确,反之则错误。
当用户注销、重启、锁屏后,操作系统会让winlogon.exe显示登录界面,也就是输入框。当winlogon.exe接收输入后,将密码交给lsass.exe进程,lsass.exe进程中会存一份明文密码,将明文密码加密成NTLM Hash,与SAM数据库进行比较和认证。
我们使用mimikatz就是从lsass.exe进程中抓取明文密码或Hash密码。

Mimikatz原理[1]:Mimikatz通过逆向获取存储在lsass.exe进程中的明文登录密码。(lsass.exe用于本地安全和登陆策略)。首先使用Mimikatz抓取时必须是管理员权限,在win10,win11,win2012等版本中,系统会开启LSA保护,明文密码字段会显示null。
我抓取密码的环境是win10,想要抓到密码首先是要关闭LSA保护。
管理员权限对注册表进行修改,随后使用脚本或者任意方法重启系统,使受害机的管理员重新登陆,此次登录的明文密码将会保存在lsass.exe 进程中,使用Mimikatz再次抓取可显示明文密码。若恢复注册表可直接将1改为0。
修改注册表命令如下:
1 | reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 1 /f |
此时再去获取密码,就可行了。
使用MSF或CobaltStrike通过转储Hash抓到的密码格式如下,第一部分是用户名,第二部分是用户的SID值,第三部分是LM Hash,第四部分是NTLM Hash,其余部分为空。
1 | 用户名:用户SID值:LM Hash:NTLM Hash::: |
从Windows Vista和Windows Server 2008开始,由于默认禁用了LM Hash,因此第三部分的LM Hash固定为空值,第四部分的NTLM Hash才是用户密码加密后的凭据。
NTLM协议认证[2]
NTLM协议是一种基于Challenge/Response(质询/响应)的验证机制,由三种类型消息组成。
- Type 1(协商,Negotiate)
- Type 2(质询,Challenge)
- Type 3(认证,Authentication)
NTLM协议有NTLM v1和NTLM v2两个版本,目前使用最多的是NTLM v2。NTLM v1与NTLM v2最显著的区别是Challenge值与加密算法不同,共同之处就是都使用NTLM Hash进行加密。
工作组环境下的NTLM认证
工作组下的NTLM认证流程如下:

(1)当客户端需要访问服务器的某个服务时,就需要进行身份认证。于是,在客户端输入服务器的用户名和密码进行验证之后,就会缓存服务器密码的NTLM Hash值。然后,客户端会向服务器发送一个请求,该请求利用NTLM SSP生成NTLMSSP_NEGOTIATE消息(被称为Type 1协商消息)。
(2)服务器接收到客户端发送过来的Type 1消息后,读取其中的内容,并从中选择自己所能接受的服务内容、加密等级、安全服务等,然后传入NTLM SSP,得到NTLMSSP_CHALLENGE消息(被称为Type 2 质询消息),并将此Type 2消息发回给客户端。在此Type 2消息中包含一个由服务器生成的16位随机值,被称为Challenge值,服务器会将该Challenge值进行缓存。
(3)客户端收到服务器返回的Type 2消息后,读取服务器所支持的内容,并取出其中的Challenge值,用缓存的服务器密码的NTLM Hash对其进行加密得到Response消息。最后将Response和一些其他信息封装到NTLMSSP_AUTH消息中(被称为 Type 3 认证消息),发往服务器。
(4)服务器收到认证消息后,从中取出Net-NTLM Hash,然后用自己密码的NTLM Hash对Challenge值进行一系列加密运算,得到自己计算的Net-NTLM Hash,并比较自己计算出的Net-NTLM Hash和客户端发送的Net-NTLM Hash是否相等。如果相等,则证明客户端输入的密码正确,从而认证成功,反之则认证失败。
以上是完整的整个流程,可以看到认证的成功的关键就是**Net-NTLM Hash比对的成功**。而Net-NTLM Hash的值是由NTLM Hash和服务器生成的Challenge值进行一系列加密运算得到的,所以这里的NTLM Hash和Challege非常关键。
域环境下的NTLM认证
域环境下的NTLM认证流程如下:

(1)客户端想要访问服务器的某个服务,需要进行身份认证。于是,在输入服务器的用户名和密码进行验证之后,客户端会缓存服务器密码的NTLM Hash值。然后,客户端会向服务器发送一个请求,该请求利用NTLM SSP生成NTLMSSP_NEGOTIATE消息(被称为Type 1 协商消息)。
(2)服务器接收到客户端发送过来的Type 1消息,会读取其中的内容,并从中选择自己所能接受的服务内容、加密等级、安全服务等。然后传入NTLM SSP,得到NTLMSSP_CHALLENGE消息(被称为Type 2 质询消息),并将此Type 2消息发回给客户端。在此Type 2消息中包含一个由服务器生成的16位随机值,被称为Challenge值,服务端将该Challenge值缓存起来。
(3)客户端收到服务端返回的Type 2消息后,读取服务端所支持的内容,并取出其中的Challenge值,用缓存的服务器密码的NTLM Hash对其进行加密得到Response消息,Response消息中可以提取出Net-NTLM Hash。最后将Response和一些其他信息封装到NTLMSSP_AUTH消息中(被称为Type 3 认证消息),发往服务器。
(4)服务器接收到客户端发送来的NTLMSSP_AUTH认证消息后,通过Netlogon协议与域控制器(Domain Controller,DC,域控)建立一个安全通道,将验证消息(其中包含了服务器生成的Challenge)发给域控。
(5)域控收到服务器发送来的验证消息后,从中取出Net-NTLM Hash。然后从数据库中找到该用户的NTLM Hash,对Challenge进行一系列加密运算,得到自己计算的Net-NTLM Hash,比较自己计算出的Net-NTLM Hash和服务器发送的Net-NTLM Hash是否相等,如果相等,则证明客户端输入的密码正确,认证成功,反之认证失败,域控将验证结果发给服务器。
(6)服务器根据域控返回的结果,对客户端进行回复。
对于步骤(1)、(2)、(3)和工作组环境下的NTLM认证一致。在(4)、(5)、(6)将认证成功的任务转交给了域控,其中在服务器和域控交互的过程中使用到Netlogon协议。认证成功的关键,还是比较Net-NTLM Hash是否相等。
Kerberos协议
Kerberos协议是由MIT提出的一种网络身份验证协议,是一种在开放的非安全网络中认证并识别用户身份信息的方法。它旨在使用密钥加密技术为客户端/服务端应用程序提供强身份验证。目前使用的主流Kerberos版本为2005年RFC4120[3]标准定义的Kerberos v5,Windows、Linux和MacOS均支持Kerberos协议。
Kerberos基础
在Kerberos协议中,主要有以下三个角色:
- 访问服务的客户端:Kerberos客户端代表需要访问资源的用户进行操作的应用程序,例如打开文件、查询数据库和打印文件等。每个Kerberos客户端在访问资源之前都会请求身份验证。
- 提供服务的服务端:域内提供服务的服务端,服务端都有唯一的SPN(服务主体名称)。
- 提供认证服务的KDC(Key Distribution Center,密钥分发中心):KDC是一种网络服务,它向活动目录域内的用户和计算机提供会话票据和临时会话密钥,其服务账户为
krbtgt。KDC作为活动目录域服务的一部分运行在每个域控制器上。
krbtgt账户,在我们后面进行域渗透的时候会多次提到的账户,比较重要。该用户是在创建活动目录时系统自动创建的一个账户,其作用是KDC(密钥发行中心)的服务账户,其密码是系统随机生成的,无法正常登录主机。

Kerberos是一种基于票据(Ticket)的认证方式。客户端想要访问服务端的某个服务,首先需要购买服务端认可的ST(Service Ticket,服务票据)。也就是说,客户端在访问服务之前需要先买好票,等待服务验票之后才能访问。但是这张票并不能直接购买,需要一张TGT(Ticket Granting Ticket,认购权证)。也就是说,客户端在买票之前必须先获得一张TGT,TGT和ST均是由KDC发放的,因为KDC运行在域控上,所以说TGT和ST均是由域控发放的。
Kerberos协议有两个基础认证模块——AS_REQ & AS_REP 和TGS_REQ & TGS_REP,以及微软扩展的两个认证模块S4U和PAC。SU4是微软为了实现委派而扩展的模块,分为S4u2Self和S4u2Proxy。在Kerberos最初设计的流程里只说明了如何证明客户端的真实身份,并没有说明客户端是否有权限访问该服务,因为在域中,不同权限的用户能够访问的资源是不同的。因此微软为了解决权限问题,引入了PAC(Privilege Attribute Certificate,特权属性证书)的概念。
Kerberos实验环境
为了后续的更深入的学习Kerberos协议,我搭建了一个域环境,如下:
- 攻击机 (Kali Linux):
192.168.233.5 - 普通域用户:
bob/qwer@1234 - 域控制器 (DC):
- 主机名:
dc.test.mydomain - IP地址:
192.168.233.131
- 主机名:
- 域成员服务器:
- 主机名:
bob.test.mydomain - IP地址:
192.168.233.132
- 主机名:
我在kali中利用普通域用户bob使用Impacket工具[4]请求bob.test.mydomain机器的CIFS服务票据,然后进行远程SMB连接,在该过程中使用Wireshark抓包用于后续分析。
Impacket工具使用命令如下:
1 | 使用 test.mydomain/bob 账户/密码 请求 bob.test.mydomain 的 cifs 服务的 ST(Service Ticket) |
结果如下:

在执行上面的命令之前,打开Wireshark抓取数据包就好。

下面就是介绍整个Kerberos认证流程如下图所示:

更清晰的理解票据生成的图,如下:

PAC
在分析AS_REQ & AS_REP和TGS_REQ & TGS_REP之前,我们先来看看什么是PAC?
AS-REQ & AS-REP
首先看一下AS-REQ & AS-REP的部分。

下面展示一个简要的AS-REQ & AS-REP请求过程图,便于直观的了解一下AS-REQ & AS-REP请求过程。

客户端是如何获得TGT的?TGT是由KDC的AS(Authentication Service,认证服务)发放的。下面具体分析AS-REQ & AS-REP请求过程中的数据包细节。
1、AS-REQ包分析
当域内某个用户想要访问域内某个服务时,输入用户名和密码,本机就会向KDC的AS发送一个AS-REQ(认证请求)。该请求的内容如AS-REQ & AS-REP图片左侧内容一样,时间的包内容如下:


下面就来详细的介绍一下各个字段:
- PA-DATA pA-ENC-TIMESTAMP:
预认证,就是用用户密码Hash加密时间戳,将其作为value发送给KDC的AS。然后KDC从活动目录中查询出用户密码的Hash,使用用户密码Hash进行解密,获得时间戳,如果能解密,且时间戳在一定的范围内,则证明认证通过,由于是使用用户密码Hash加密的时间戳,因此也就造就了哈希传递攻击(Pass The Hash,PTH)。哈希传递攻击是这么来的!!!⚠️⚠️⚠️。
- PA-DATA pA-PAC-REQUEST:
启用PAC支持的扩展。这里value对应的值为True或False,KDC根据include-pac的值来确定返回的票据中是否需要携带PAC。

- include-pac:
是否包含PAC,这里为True,说明包含PAC。
- kdc-options:
用于与KDC协商一些选项设置。
- cname:
请求的用户名,这个用户名存在与否,返回的包是有差异的,因此可以用于枚举域内用户名⚠️⚠️⚠️。当用户名存在时,密码正确与否会影响返回包,因此也可以进行**密码喷洒(Password Spraying)**⚠️⚠️⚠️。
- realm:
域名。
- sname:
请求的服务,包含type和value。在AS-REQ中sname始终为krbtgt。
着重介绍一下PA-DATA pA-ENC-TIMESTAMP字段。在AS-REQ包中,只有PA-DATA pA-ENC-TIMESTAMP部分是加密的,这一部分属于预认证,称为Authenticator。
如下是impacket/impacket/krb5/kerberosv5.py脚本中的代码,这里可以看到是使用用户的密码Hash或用户的密码AES Key来加密时间戳的。

下面就是对使用Wireshark抓取的PA-DATA pA-ENC-TIMESTAMP字段中的cipher进行解密了,解密是使用test.mydomain/bob的密钥来进行解密。
Wireshark自带有解密的功能,要导入解密的keytab文件。生成解密的keytab文件,如下
1 | 启动 ktutil |
导入keytab可以注意到解密后的数据,patimestamp和pausec显示的是解密后的值。

2、AS-REP包分析
当KDC的AS接收到客户端发来的AS-REQ后,AS会从活动目录数据库中取出该用户的密钥Hash,然后用该密钥Hash对请求包中的预认证部分进行解密。如果解密成功,并且时间戳在有效的范围内,则证明请求者提供的用户密钥正确。KDC的AS在成功认证客户端的身份之后,发送AS-REP包给客户端。AS-REP包中主要包含的信息如AS-REQ & AS-REP图片右侧的内容一样。
真实的数据包如下图:

下面对AS-REP包中部分字段进行解释,具体如下:
- **ticket:**认购权证据票。
- enc-part(ticket中的):TGT中的加密部分,这部分是用
krbtgt的密码Hash加密的。因此,如果我们拥有krbtgt的Hash,就可以自己制作一个ticket,这就造成了黄金票据传递攻击⚠️⚠️⚠️。 - enc-part(最外层的):
Logon Session Key,这部分是用请求的用户密码Hash加密的,作为下一阶段的认证密钥。
AS-REP包中最重要的就是TGT和加密的Logon Session Key。TGT中加密部分是使用krbtgt密钥加密的,而Logon Session Key是使用请求的用户密钥加密的。下面通过解密Wireshark来看看TGT和Logon Session Key中包含哪些内容。
(1)TGT
AS-REP包中的ticket便是TGT了。TGT中包含一些明文显示的信息,如版本号tkt-vno、域名realm、请求的服务名sname,但最重要的还是加密部分。加密部分是使用krbtgt账户密钥加密的,主要包含Logon Session Key、请求的用户名cname、域名crealm、认证时间authtime、认证到期时间endtime、authorization-data等信息。其中authorization-data部分包含客户端的身份权限等信息,这些信息包含在PAC中。
先去解密加密的部分,使用mimikatz抓取krbtgt账户的Hash用于去生成解密的keytab。抓取Hash的命令如下:
1 | 将整个 mimikitz 会话输出到文件 |
获取到对应的Hash值如下:
1 | aes256_hash = "bc61d540e046302787e8bbdab97d6801deebaaf38d4753deda056f6165eb71ae" |
生成keytab文件。
1 | ktutil |
将生成的keytab文件导入到wireshark中去解密流量。解密后的流量如下,这里就是TGT:

中间部分:

最后的部分:

TGT中的authorization-data字段下代表用户身份权限的PAC是什么样的呢?下面只查看PAC的凭证信息部分PAC_LOGON_INFO。如下图,主要还是通过User RID和Group RID来辨别用户权限的。

KDC生成PAC的过程如下:
KDC在收到客户端发来的AS-REQ后,从请求中取出cname字段,然后查询活动目录数据库,找到sAMAccountName属性为cname字段的值的用户,用该用户的身份生成一个对应的PAC。
(2)Logon Session Key
AS-REP包最外层的那部分便是加密的Logon Session Key,用于确保客户端和KDC下一阶段的通信安全,它使用请求的用户密钥加密。
我们对最外层的enc-part部分进行解密,如下图,可以看到使用的是普通域用户bob的密钥对其进行解密的。

后半部分。

解密结果如下:主要包含认证时间authtime、认证到期时间endtime、域名srealm、请求的服务名sname、协商标志flags等信息。需要说明的是,在TGT中也包含Logon Session Key。