ldapsearch
is a extremely powerful tool, especially for Windows Active Directory enumeration. It’s one of my primary tools when performing pentesting or red teaming against an environment with Active Directory, but also comes in quiet handy to know as many times it can come default installed or part of a base image, so its a bit Living-Off-The-Land-esq. Another point towards ldapsearch is that it’s easy to forget that Active Directory isn’t the only LDAP server in most environments and the ability to utilize a tool like this can come in extremely handy.
If you want to find Active Directory LDAP servers, use the following command:
$ dig -t SRV _ldap._tcp.dc._msdcs.sittingduck.info
Basic Usage
-x
Basic Authentication, you usually use this if you are going to include a username and password (instead of something like a kerberos ticket)-h
IP address or hostname-H
URL to force protocol and/or add port number-H ldaps://dc1.b.com
or-H ldaps://dc1.b.com:6636
(636 is the default port for LDAPS, but can be connected to on 389 and is sometimes found with an additional 6 in the front as shown).3269
is the Global Catalog port and can also accept LDAPS queries sometimes (you may have to ignore cert errors for this to work, depending on your setup).-b
Base, generally this is Distinguished Name format for exampledc=google,dc=com
-D
Username and Domain (not FQDN)"sittingduck\uberuser"
. For maximum Kerberos based authentication you can also specify your user inuberuser@SITTINGDUCK.INFO
form, but ldapsearch will auto user the current user in Kerberos tickets available, so this can be omitted-LLL
This removes all of the extra log output of the search-Y GSSAPI
Used for Kerberos based authentication, however this is on by default and not needed (if installed), but you can specify it just to test to see if SASL is installed.apt install libsasl2-modules-gssapi-mit
if it isn’t. Once installed, you do not need to keep this on the command line.-E
is for extended controls which are generally OIDs, but can also be:-E pr=1000/noprompt
By defaultldapsearch
will pull 1000 records maximum. Adding this it will pull 1000 records at a time until it has printed all results to the screen.-O minssf=0,maxssf=0
This option will fix some errors during SASL (Kerberos) authentication. Technically this shouldn’t do anything based on the RFC, but it’s essentially telling the LDAP protocol that there isn’t a min or a max size for the authentication packets.-o ldif-wrap=no
This options makes the output non-wrapped, which is the best format to pull out things like certificates and other very long strings.-W
and-w
--W
will prompt for the password, with-w
you can specify the password on the command line. If you do not specify one of theseldapsearch
will assume no password for the account. You can lock accounts out this way.LDAPTLS_REQCERT=never ldapsearch {arguments}
This is used when you are connecting to a self signed, or SSL enabled LDAP that your system cannot verify the certificate for.-N
Do not try to have the host name of your target DC attempted to perform a reverse DNS on the IP to match them. This is needed when the Domain Controller doesn’t have a valid reverse DNS record from your standpoint on the network and you are using Kerberos.
LDAP Search Filters
After the arguments comes the filter, then the attributes. Filters are like search terms. For example "(ms-Mcs-AdmPwdExpirationTtime=*)"
is a search that says if the LDAP object has the ms-Mcs-AdmPwdExpirationTtime
attribute and it’s not empty. LDAP filters are a bit odd, as they need to be within parentheses completely. In order to do a search for x
AND y
you have to do something like this (&(x=*)(y=*))
. This search says only return results that have x
and y
and both are not empty. Here is a real world example:
"(&(&(servicePrincipalName=*)(UserAccountControl:1.2.840.113556.1.4.803:=512))(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))"
This says all objects that have servicesPrincipalName
attributes, it is a normal account (512) see more here AND is NOT !
disabled (2).
Note: 1.2.840.113556.1.4.803
is an LDAP feature that says LDAP_MATCHING_RULE_BIT_AND
which is basically a bitwise AND
. For example, a standard user account is 512
but would be 514
if disabled, but a computer account is 4096
and 4098
if disabled. Used the bitwise function you can account for both scenarios since it’s looking in the 2
binary location.
Everything after the filter is selection of attributes. (also known as “parameters” if you are used to Powershell and AD Objects)
Interesting Attributes
msDS-Approx-Immed-Subordinates
- Gives you a count of objects directly below the specified “Base”-b
NTSecurityDescriptor
- This is the SDSF in binary and base64 format, and will only be displayed if you request it specifically for the object.msDS-ManagedPassword
- This is a at-runtime built attribute that is the current NT hash of the Group Managed Service Account GMSA. !WARNING! See below before querying.msFVE-RecoveryPassword
- This is also an at-runtine built attribute that is the current BitLocker secret key for the computer account you query. !WARNING! See below before querying.
Null Session Starting
This can be done without any authentication and will give you a ton of information about the LDAP server in question (usually Active Directory):
$ ldapsearch -h 192.168.80.100 -x -b "" -s base \* +
Example Output
# extended LDIF## LDAPv3# base <> with scope baseObject# filter: (objectclass=*)# requesting: * + ##dn:currentTime: 20220513212748.0ZsubschemaSubentry: CN=Aggregate,CN=Schema,CN=Configuration,DC=sittingduck,DC=i nfodsServiceName: CN=NTDS Settings,CN=DC2,CN=Servers,CN=Default-First-Site-Name,C N=Sites,CN=Configuration,DC=sittingduck,DC=infonamingContexts: DC=sittingduck,DC=infonamingContexts: CN=Configuration,DC=sittingduck,DC=infonamingContexts: CN=Schema,CN=Configuration,DC=sittingduck,DC=infonamingContexts: DC=DomainDnsZones,DC=sittingduck,DC=infonamingContexts: DC=ForestDnsZones,DC=sittingduck,DC=infodefaultNamingContext: DC=sittingduck,DC=infoschemaNamingContext: CN=Schema,CN=Configuration,DC=sittingduck,DC=infoconfigurationNamingContext: CN=Configuration,DC=sittingduck,DC=inforootDomainNamingContext: DC=sittingduck,DC=infosupportedControl: 1.2.840.113556.1.4.319supportedControl: 1.2.840.113556.1.4.801supportedControl: 1.2.840.113556.1.4.473supportedControl: 1.2.840.113556.1.4.528supportedControl: 1.2.840.113556.1.4.417supportedControl: 1.2.840.113556.1.4.619supportedControl: 1.2.840.113556.1.4.841supportedControl: 1.2.840.113556.1.4.529supportedControl: 1.2.840.113556.1.4.805supportedControl: 1.2.840.113556.1.4.521supportedControl: 1.2.840.113556.1.4.970supportedControl: 1.2.840.113556.1.4.1338supportedControl: 1.2.840.113556.1.4.474supportedControl: 1.2.840.113556.1.4.1339supportedControl: 1.2.840.113556.1.4.1340supportedControl: 1.2.840.113556.1.4.1413supportedControl: 2.16.840.1.113730.3.4.9supportedControl: 2.16.840.1.113730.3.4.10supportedControl: 1.2.840.113556.1.4.1504supportedControl: 1.2.840.113556.1.4.1852supportedControl: 1.2.840.113556.1.4.802supportedControl: 1.2.840.113556.1.4.1907supportedControl: 1.2.840.113556.1.4.1948supportedControl: 1.2.840.113556.1.4.1974supportedControl: 1.2.840.113556.1.4.1341supportedControl: 1.2.840.113556.1.4.2026supportedControl: 1.2.840.113556.1.4.2064supportedControl: 1.2.840.113556.1.4.2065supportedControl: 1.2.840.113556.1.4.2066supportedControl: 1.2.840.113556.1.4.2090supportedControl: 1.2.840.113556.1.4.2205supportedControl: 1.2.840.113556.1.4.2204supportedControl: 1.2.840.113556.1.4.2206supportedControl: 1.2.840.113556.1.4.2211supportedControl: 1.2.840.113556.1.4.2239supportedControl: 1.2.840.113556.1.4.2255supportedControl: 1.2.840.113556.1.4.2256supportedLDAPVersion: 3supportedLDAPVersion: 2supportedLDAPPolicies: MaxPoolThreadssupportedLDAPPolicies: MaxPercentDirSyncRequestssupportedLDAPPolicies: MaxDatagramRecvsupportedLDAPPolicies: MaxReceiveBuffersupportedLDAPPolicies: InitRecvTimeoutsupportedLDAPPolicies: MaxConnectionssupportedLDAPPolicies: MaxConnIdleTimesupportedLDAPPolicies: MaxPageSizesupportedLDAPPolicies: MaxBatchReturnMessagessupportedLDAPPolicies: MaxQueryDurationsupportedLDAPPolicies: MaxTempTableSizesupportedLDAPPolicies: MaxResultSetSizesupportedLDAPPolicies: MinResultSetssupportedLDAPPolicies: MaxResultSetsPerConnsupportedLDAPPolicies: MaxNotificationPerConnsupportedLDAPPolicies: MaxValRangesupportedLDAPPolicies: MaxValRangeTransitivesupportedLDAPPolicies: ThreadMemoryLimitsupportedLDAPPolicies: SystemMemoryLimitPercenthighestCommittedUSN: 2830648supportedSASLMechanisms: GSSAPIsupportedSASLMechanisms: GSS-SPNEGOsupportedSASLMechanisms: EXTERNALsupportedSASLMechanisms: DIGEST-MD5dnsHostName: dc2.sittingduck.infoldapServiceName: sittingduck.info:dc2$@SITTINGDUCK.INFOserverName: CN=DC2,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configura tion,DC=sittingduck,DC=infosupportedCapabilities: 1.2.840.113556.1.4.800supportedCapabilities: 1.2.840.113556.1.4.1670supportedCapabilities: 1.2.840.113556.1.4.1791supportedCapabilities: 1.2.840.113556.1.4.1935supportedCapabilities: 1.2.840.113556.1.4.2080supportedCapabilities: 1.2.840.113556.1.4.2237isSynchronized: TRUEisGlobalCatalogReady: TRUEdomainFunctionality: 3forestFunctionality: 3domainControllerFunctionality: 6# search resultsearch: 2result: 0 Success# numResponses: 2# numEntries: 1
You can learn the dnsHostName
of the LDAP server you pinged, the dn
which is useful for picking your base search if you don’t know it, the domainFunctionality
level (aka the Domain Functional Level) will give you a sense of what capabilities and GPOs can be set. The supportedControl
and supportedCapabilities
are more advanced topics, but these basically say what you can and can’t do on the server.
Interesting Searches
The following are searches that I’ve found interesting or impactful in my experience.
LAPs Passwords
This search will only show computers with LAPs enabled, and if you can read any passwords, what the password currently is.
Notice that this user can read every password except SDLEGACY2K3
and SDEXCHANGE
$ ldapsearch -LLL -h 192.168.80.10 -b "dc=sittingduck,dc=info" -x -D "sittingduck\uberuser" -w "ASDqwe123"'(ms-Mcs-AdmPwdExpirationtime=*)' ms-Mcs-AdmPwddn: CN=DC1,OU=Domain Controllers,DC=sittingduck,DC=infoms-Mcs-AdmPwd: f/0HG3)7d5P+e;/IE5wW8I/o//5)}Wdn: CN=SDCLIENT_DAWIN7,OU=LabComputers,OU=Lab,DC=sittingduck,DC=infoms-Mcs-AdmPwd: /B+%R26cVSA1c0zOi[-Z/#n1Q2sA,,dn: CN=SDLEGACY2K3,OU=LabComputers,OU=Lab,DC=sittingduck,DC=infodn: CN=SD_WSUS_2012,OU=LabComputers,OU=Lab,DC=sittingduck,DC=infoms-Mcs-AdmPwd: }b+BC@p,7))6pqxj3y]MxBm9@h9Hy-dn: CN=SDEXCHANGE,OU=LabComputers,OU=Lab,DC=sittingduck,DC=infodn: CN=WIN-PM0ID6F0AHN,OU=LabComputers,OU=Lab,DC=sittingduck,DC=infoms-Mcs-AdmPwd: ]]3-%AN574#{/%t55!S+0!/10OJC;Vdn: CN=DC2,OU=Domain Controllers,DC=sittingduck,DC=infoms-Mcs-AdmPwd: kMo,85{N(uhUmWj183,3#T&d[Aeddv
BitLocker Recovery Passwords
src: https://stackoverflow.com/a/50833203
The key on this one is narrowing down the search base to a small list or a single computer. The reason this is important is that the Domain Controller you are asking the password from has to do a bunch of decryption to calculate this attribute on the fly, if you ask for too many (like every machine in the Domain) the connection will be closed out due to timeout. Don’t be greedy, just get the one you need.
$ ldapsearch -h dc1.sittingduck.info -b "CN=WIN10COMP1,OU=Workstations,DC=sittingduck,DC=info" msfve-recoverypassword
GMSA NT Hash
- src: https://stealthbits.com/blog/securing-gmsa-passwords/
- src: https://github.com/SecureAuthCorp/impacket/pull/770
Group Managed Service Accounts (GMSA) have randomly generated passwords that are default/generally pretty long. The msDS-ManagedPassword
in an at-runtime decrypted value that the Domain Controller has to construct in order to return, much like the BitLocker keys. However, unlike with BitLocker, generally there aren’t very many GMSAs so it’s less of a chance to knock over the DC if you get greedy and query them all.
$ LDAPTLS_REQCERT=never ldapsearch -LLL -H ldaps://192.168.80.10 -x -D uberuser -w ASDqwe123 -o ldif-wrap=no -b 'dc=sittingduck,dc=info''(&(ObjectClass=msDS-GroupManagedServiceAccount))' msDS-ManagedPassworddn: CN=TestGMSA,CN=Managed Service Accounts,DC=sittingduck,DC=infomsDS-ManagedPassword:: AQAAACQCAAAQABIBFAIcAq/tlcA3PziSdtjxwKwcpM9VAteZa9No6et8yDWzgEYj5yFDhzT0OMFtTA4qAI4M+sWjiUVqYhDwFItW+5FlhaJp8tm/fcAfIDc5MAsYOXgq3AIc6ofCpeJga6Jw4SX/EKmnQdsS7lGmXd/yh27qJGj6SS0vwk//D8cr1+6s43YB0TxUxyLrruupZmd4Ndx2PZ4L4zkWa/vlRlDzadyeGwji70q9sfjH5x7+l7V+w5JK7/hKfG48swD8koEK/bHyLxZa8eDMEoscAmnuC/CA/pTvdF6jfIYmAYMGZujOVkbXJjaCEPGA1+nBDqkOQ1NlLZu4Y6Es1bB3YVIv6agDICYAAJ3USvAd5t267x9PfdetWSfLGRt4qxMCmOwXO0nVWMJTSYfjC+Phc0QX/rrTimPptYvx2NnuAclbgj0Fvg0iZfOvHW0SwdKTyKCvGeL+UPz4Jwr2VAd+PX43GoUPD8uBhDrtzVSwUhtScDIvzGJYgvHrREnTUqaf9pENqkVpbdXHtpE/buIphVrPEg84e9fgNB2a/r6sZaPX+1C8e/5pbNO7qoQ4fqXSDPM3qh6ymlh2ouNRoP0kN4sLVUvW+cNX+YQk7UJDSnG3fANPtgquhi6B4OVqU2fpypUbFtd91Ju/9xZeuod508FqYHPT1aX86tBdLj4yq2/65ROupERLVhAAADPE7gxzEgAAM2YeWnISAAA=
Once you have this blob of text, you need to convert it into the NT hash using this tool:
(this is a slightly modified version of the script that agsolino posted)
#!/usr/bin/env python3from impacket.structure import hexdump, Structureimport base64import sys, getopt# Source from: https://github.com/SecureAuthCorp/impacket/pull/770#issuecomment-589243865classMSDS_MANAGEDPASSWORD_BLOB(Structure): structure = ( ('Version','<H'), ('Reserved','<H'), ('Length','<L'), ('CurrentPasswordOffset','<H'), ('PreviousPasswordOffset','<H'), ('QueryPasswordIntervalOffset','<H'), ('UnchangedPasswordIntervalOffset','<H'), ('CurrentPassword',':'), ('PreviousPassword',':'),#('AlignmentPadding',':'), ('QueryPasswordInterval',':'), ('UnchangedPasswordInterval',':'), )def __init__(self, data =None): Structure.__init__(self, data = data)deffromString(self, data): Structure.fromString(self,data)if self['PreviousPasswordOffset'] ==0: endData = self['QueryPasswordIntervalOffset']else: endData = self['PreviousPasswordOffset'] self['CurrentPassword'] = self.rawData[self['CurrentPasswordOffset']:][:endData - self['CurrentPasswordOffset']]if self['PreviousPasswordOffset'] !=0: self['PreviousPassword'] = self.rawData[self['PreviousPasswordOffset']:][:self['QueryPasswordIntervalOffset']-self['PreviousPasswordOffset']] self['QueryPasswordInterval'] = self.rawData[self['QueryPasswordIntervalOffset']:][:self['UnchangedPasswordIntervalOffset']-self['QueryPasswordIntervalOffset']] self['UnchangedPasswordInterval'] = self.rawData[self['UnchangedPasswordIntervalOffset']:]defmain(argv): b64 ="" verbose =Falsetry: opts, args = getopt.getopt(argv,"hvb:",["base64="])except getopt.GetoptError: print ('gmsa.py -b <msDS-ManagedPassword base64> -v (optional verbose)') sys.exit(2)for opt, arg in opts:if opt =='-h': print ('gmsa.py -b <msDS-ManagedPassword base64> -v (optional verbose)') sys.exit()elif opt in ("-v"): verbose =Trueelif opt in ("-b", "--base64"): b64 = argif b64 =="": print ('gmsa.py -b <msDS-ManagedPassword base64> -v (optional verbose)') sys.exit() test = base64.b64decode(b64) blob = MSDS_MANAGEDPASSWORD_BLOB() blob.fromString(test)if verbose ==True: blob.dump() print("="*80) print("Cleartext Password") hexdump(blob['CurrentPassword'][:-2])from Cryptodome.Hash import MD4 hash = MD4.new () hash.update (blob['CurrentPassword'][:-2]) print("="*80) print("NTHash: {}".format(hash.hexdigest())) print("="*80)if __name__ =="__main__": main(sys.argv[1:])
$ python3 ./gmsa.py -b AQAAACQCAAAQABIBFAIcAq/tlcA3PziSdtjxwKwcpM9VAteZa9No6et8yDWzgEYj5yFDhzT0OMFtTA4qAI4M+sWjiUVqYhDwFItW+5FlhaJp8tm/fcAfIDc5MAsYOXgq3AIc6ofCpeJga6Jw4SX/EKmnQdsS7lGmXd/yh27qJGj6SS0vwk//D8cr1+6s43YB0TxUxyLrruupZmd4Ndx2PZ4L4zkWa/vlRlDzadyeGwji70q9sfjH5x7+l7V+w5JK7/hKfG48swD8koEK/bHyLxZa8eDMEoscAmnuC/CA/pTvdF6jfIYmAYMGZujOVkbXJjaCEPGA1+nBDqkOQ1NlLZu4Y6Es1bB3YVIv6agDICYAAJ3USvAd5t267x9PfdetWSfLGRt4qxMCmOwXO0nVWMJTSYfjC+Phc0QX/rrTimPptYvx2NnuAclbgj0Fvg0iZfOvHW0SwdKTyKCvGeL+UPz4Jwr2VAd+PX43GoUPD8uBhDrtzVSwUhtScDIvzGJYgvHrREnTUqaf9pENqkVpbdXHtpE/buIphVrPEg84e9fgNB2a/r6sZaPX+1C8e/5pbNO7qoQ4fqXSDPM3qh6ymlh2ouNRoP0kN4sLVUvW+cNX+YQk7UJDSnG3fANPtgquhi6B4OVqU2fpypUbFtd91Ju/9xZeuod508FqYHPT1aX86tBdLj4yq2/65ROupERLVhAAADPE7gxzEgAAM2YeWnISAAA=================================================================================NTHash: 78450c9611e6c9515e2a489f9b31cc3e================================================================================
If you want to learn more about GMSAs and a newer attack called the “Golden GMSA” check out the Sempris blog here: https://www.semperis.com/blog/golden-gmsa-attack/
Certificate Authorities and Templates
This is a list of certificate templates that are enabled on each certificate authority in the domain.
$ ldapsearch -LLL -b "CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=sittingduck,DC=info" -h 192.168.80.10 -w "ASDqwe123" -D "uberuser" certificateTemplatesdn: CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration ,DC=sittingduck,DC=infodn: CN=sittingduck-W2KCA-CA,CN=Enrollment Services,CN=Public Key Services,CN=S ervices,CN=Configuration,DC=sittingduck,DC=infocertificateTemplates: ExchangeUsercertificateTemplates: ExchangeUserSignaturecertificateTemplates: EnrollmentAgentOfflinecertificateTemplates: MachineEnrollmentAgentcertificateTemplates: EnrollmentAgentcertificateTemplates: CrossCAcertificateTemplates: CodeSigningcertificateTemplates: CEPEncryptioncertificateTemplates: CAExchangecertificateTemplates: ClientAuthcertificateTemplates: WorkstationcertificateTemplates: UserSignaturecertificateTemplates: CTLSigningcertificateTemplates: SmartcardUsercertificateTemplates: SmartcardLogoncertificateTemplates: OfflineRoutercertificateTemplates: RASAndIASServercertificateTemplates: OCSPResponseSigningcertificateTemplates: KeyRecoveryAgentcertificateTemplates: IPSECIntermediateOfflinecertificateTemplates: IPSECIntermediateOnlinecertificateTemplates: DirectoryEmailReplicationcertificateTemplates: DomainControllerAuthenticationcertificateTemplates: KerberosAuthenticationcertificateTemplates: EFSRecoverycertificateTemplates: EFScertificateTemplates: DomainControllercertificateTemplates: WebServercertificateTemplates: MachinecertificateTemplates: UsercertificateTemplates: SubCAcertificateTemplates: Administrator
Nested Group Membership
A query like this:
$ ldapsearch -x -LLL -h dc1.sittingduck.info -w ASDqwe123 -D uberuser -b "dc=sittingduck,dc=info"'(memberOf=cn=Domain Admins,cn=Users,dc=SittingDuck,dc=info)' cn
will result in all of the accounts that have a memberOf
attribute of Domain Admins. This will show you the first level of membership, but LDAP/AD actually has a way to identify nested membership using the 1.2.840.113556.1.4.1941OID:
$ ldapsearch -x -LLL -h dc1.sittingduck.info -w ASDqwe123 -D uberuser -b "dc=sittingduck,dc=info"'(memberOf:1.2.840.113556.1.4.1941:=cn=Domain Admins,cn=Users,dc=sittingduck,dc=info)' cn
This will show you all Domain Admins even if they are in a group in a group in a group in a group.
Find Exchange Servers
$ ldapsearch -LLL -h 192.168.80.10 -o ldif-wrap=no -x -D uberuser -w ASDqwe123 -b "cn=Configuration,dc=sittingduck,dc=info""(objectCategory=msExchExchangeServer)" dndn: CN=SDEXCHANGE,CN=Servers,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=First Organization,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=sittingduck,DC=info
Deleted / Tombstoned objects
Looking at deleted and tombstoned objects won’t always get you much information you can directly use, but sometimes there are passwords or other sensitive data stored in objects. (I don’t believe deleting objects changes their permissions but I could be wrong here)
$ ldapsearch -h 192.168.80.10 -x -w ASDqwe123 -D uberuser -b "CN=Deleted Objects,DC=sittingduck,DC=info" -E '!1.2.840.113556.1.4.417'
For example here is a Computer account that was deleted:
# blah1DEL:2f7b19b1-9dee-4a48-8bb0-ad62e6a287ef, Deleted Objects, sittingduck. infodn: CN=blah1\0ADEL:2f7b19b1-9dee-4a48-8bb0-ad62e6a287ef,CN=Deleted Objects,DC= sittingduck,DC=infoobjectClass: topobjectClass: personobjectClass: organizationalPersonobjectClass: userobjectClass: computercn:: YmxhaDEKREVMOjJmN2IxOWIxLTlkZWUtNGE0OC04YmIwLWFkNjJlNmEyODdlZg==distinguishedName: CN=blah1\0ADEL:2f7b19b1-9dee-4a48-8bb0-ad62e6a287ef,CN=Dele ted Objects,DC=sittingduck,DC=infoinstanceType: 4whenCreated: 20220512041425.0ZwhenChanged: 20220512045921.0ZuSNCreated: 2663983isDeleted: TRUEuSNChanged: 2664044name:: YmxhaDEKREVMOjJmN2IxOWIxLTlkZWUtNGE0OC04YmIwLWFkNjJlNmEyODdlZg==objectGUID:: sRl7L+6dSEqLsK1i5qKH7w==userAccountControl: 4128objectSid:: AQUAAAAAAAUVAAAAfS9vpuKLWG5ZpW7N9RUAAA==sAMAccountName: BLAH1$lastKnownParent: CN=Computers,DC=sittingduck,DC=infoisRecycled: TRUE
Replication Metadata
src: https://posts.specterops.io/hunting-with-active-directory-replication-metadata-1dab2f681b19 src: https://stackoverflow.com/a/38710484
This is one place where ldapsearch isn’t quite as cool as other tools, all of the base64 output. I did find a great way to decode most of it on StackOverflow:
| perl -MMIME::Base64 -n -00 -e 's/\n +//g;s/(?<=:: )(\S+)/decode_base64($1)/eg;print'
Basically you are pipeing the output of ldapsearch
into a bit of perl that Base64 decodes anything that looks like Base64.
Back on track, there is a ton of great info in Replication metadata but the blog post from SpecterOps above.
$ ldapsearch -h 192.168.80.10 -o ldif-wrap=no -x -w ASDqwe123 -D sittingduck\\uberuser -b "CN=Administrator,CN=Users,DC=sittingduck,DC=info" msDS-ReplAttributeMetaData # extended LDIF## LDAPv3# base <CN=Administrator,CN=Users,DC=sittingduck,DC=info> with scope subtree# filter: (objectclass=*)# requesting: msDS-ReplAttributeMetaData## Administrator, Users, sittingduck.infodn: CN=Administrator,CN=Users,DC=sittingduck,DC=infomsDS-ReplAttributeMetaData:: PERTX1JFUExfQVRUUl9NRVRBX0RBVEE+Cgk8cHN6QXR0cmlidXRlTmFtZT5tc0RTLVN1cHBvcnRlZEVuY3J5cHRpb25UeXBlczwvcHN6QXR0cmlidXRlTmFtZT4KCTxkd1ZlcnNpb24+MTwvZHdWZXJzaW9uPgoJPGZ0aW1lTGFzdE9yaWdpbmF0aW5nQ2hhbmdlPjIwMTUtMTAtMTRUMDM6Mjg6MTRaPC9mdGltZUxhc3RPcmlnaW5hdGluZ0NoYW5nZT4KCTx1dWlkTGFzdE9yaWdpbmF0aW5nRHNhSW52b2NhdGlvbklEPjhkMmExZjVjLTU4ZDQtNGVmYS1iY2QyLWY3ZDg1ZWY1MzAxMzwvdXVpZExhc3RPcmlnaW5hdGluZ0RzYUludm9jYXRpb25JRD4KCTx1c25PcmlnaW5hdGluZ0NoYW5nZT4xMjc3NjwvdXNuT3JpZ2luYXRpbmdDaGFuZ2U+Cgk8dXNuTG9jYWxDaGFuZ2U+MTI3NzY8L3VzbkxvY2FsQ2hhbmdlPgoJPHBzekxhc3RPcmlnaW5hdGluZ0RzYUROPjwvcHN6TGFzdE9yaWdpbmF0aW5nRHNhRE4+CjwvRFNfUkVQTF9BVFRSX01FVEFfREFUQT4KAA==msDS-ReplAttributeMetaData:: PERTX1JFUExfQVRUUl9NRVRBX0RBVEE+Cgk8cHN6QXR0cmlidXRlTmFtZT5sYXN0TG9nb25UaW1lc3RhbXA8L3BzekF0dHJpYnV0ZU5hbWU+Cgk8ZHdWZXJzaW9uPjI8L2R3VmVyc2lvbj4KCTxmdGltZUxhc3RPcmlnaW5hdGluZ0NoYW5nZT4yMDE3LTA2LTIwVDAwOjE3OjIyWjwvZnRpbWVMYXN0T3JpZ2luYXRpbmdDaGFuZ2U+Cgk8dXVpZExhc3RPcmlnaW5hdGluZ0RzYUludm9jYXRpb25JRD4yOWJkZWQ4MS0xMGRmLTQ0YTMtOGVkNi0xOWY1ODJjYjhiNGU8L3V1aWRMYXN0T3JpZ2luYXRpbmdEc2FJbnZvY2F0aW9uSUQ+Cgk8dXNuT3JpZ2luYXRpbmdDaGFuZ2U+MzQzOTQ2PC91c25PcmlnaW5hdGluZ0NoYW5nZT4KCTx1c25Mb2NhbENoYW5nZT4zNDM5NDY8L3VzbkxvY2FsQ2hhbmdlPgoJPHBzekxhc3RPcmlnaW5hdGluZ0RzYUROPjwvcHN6TGFzdE9yaWdpbmF0aW5nRHNhRE4+CjwvRFNfUkVQTF9BVFRSX01FVEFfREFUQT4KAA==
With the Perl trick:
$ ldapsearch -h 192.168.80.10 -o ldif-wrap=no -x -w ASDqwe123 -D sittingduck\\uberuser -b "CN=Administrator,CN=Users,DC=sittingduck,DC=info" msDS-ReplAttributeMetaData | perl -MMIME::Base64-n -00 -e 's/\n +//g;s/(?<=:: )(\S+)/decode_base64($1)/eg;print'# extended LDIF## LDAPv3# base <CN=Administrator,CN=Users,DC=sittingduck,DC=info> with scope subtree# filter: (objectclass=*)# requesting: msDS-ReplAttributeMetaData## Administrator, Users, sittingduck.infodn: CN=Administrator,CN=Users,DC=sittingduck,DC=infomsDS-ReplAttributeMetaData:: <DS_REPL_ATTR_META_DATA><pszAttributeName>msDS-SupportedEncryptionTypes</pszAttributeName><dwVersion>1</dwVersion><ftimeLastOriginatingChange>2015-10-14T03:28:14Z</ftimeLastOriginatingChange><uuidLastOriginatingDsaInvocationID>8d2a1f5c-58d4-4efa-bcd2-f7d85ef53013</uuidLastOriginatingDsaInvocationID><usnOriginatingChange>12776</usnOriginatingChange><usnLocalChange>12776</usnLocalChange><pszLastOriginatingDsaDN></pszLastOriginatingDsaDN></DS_REPL_ATTR_META_DATA>msDS-ReplAttributeMetaData:: <DS_REPL_ATTR_META_DATA><pszAttributeName>lastLogonTimestamp</pszAttributeName><dwVersion>2</dwVersion><ftimeLastOriginatingChange>2017-06-20T00:17:22Z</ftimeLastOriginatingChange><uuidLastOriginatingDsaInvocationID>29bded81-10df-44a3-8ed6-19f582cb8b4e</uuidLastOriginatingDsaInvocationID><usnOriginatingChange>343946</usnOriginatingChange><usnLocalChange>343946</usnLocalChange><pszLastOriginatingDsaDN></pszLastOriginatingDsaDN></DS_REPL_ATTR_META_DATA>
References
For futher info, the LDAP Wiki is an amazing resource.
Ropnop’s Talk at Troopers 2019
You should absolutely check out Ropnop (and follow him on Twitter) if you want to learn more.