Skip to content

stephenbradshaw/ad_ldap_dumper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Introduction

Security focused tool for dumping information from Active Directory via LDAP

This tool seeks to dump a similar set of information from Active Directory Domain Controllers as is retrieved by the LDAP component of Bloodhound collectors such as SharpHound.

Originally written because I wanted something that worked from *nix systems, which worked reliably under more restrictive circumstances than Bloodhound collectors. e.g. if you can talk to the relevant LDAP port on a Domain Controller, this will grab all the useful data you have the permissions to see, regardless of the state of DNS resolution.

This tool also allows me to very strictly control how and what LDAP queries are executed for environments that have LDAP query based detections/alerting in place.

In its current state, this tool writes its output to a single large indented JSON file. There is also BETA level support for converting the output to Bloodhound format, discussed below.

Requirements

This tool depends on ldap3 and impacket Python modules to work. For kerberos you also need gssapi.

Authentication options

You can authenticate using either NTLM (password or pass the hash), simple bind, Kerberos or anonymous.

Provide the username (-u USERNAME, --username USERNAME) in the DOMAIN\username format for NTLM or as username@domain.com for simple bind. Provide the LMHASH:NTHASH hash in place of a password if you wish to use this. :NTHASH works too if you dont have a LM hash. you can either specify a password with -password or you will get prompted for one if you have attempted an authentication method that requires one.

Use -k for Kerberos authentication. On *nix a ccache ticket cache must exist and be referenced by the KRB5CCNAME environment variable. Similar to the way Kerberos authentication works for Impacket. I havent tested this on Windows. You will need to provide the domain controller to connect to (-d option) as a domain name for this to work. This can work without DNS if you use your host file, but it will be finicky when it comes to case. Try and match the servers SPN. You might also need to specify a realm (e.g. short domain name) and a dc-ip with those options. Best results usually come from using upper case for the realm.

The -no-password option can be used when attempting to logon as a user with an emtpy password set. You need to specify the username in NTLM format for this to work - the ldap3 module requires that some password be specified for all non anonymous binds, so we set a blank NT hash in this case. It seems to work.

Connect without specifying any authentication details for anonymous access to get some basic server information (and perhaps more if the server is misconfigured).

The ssl option is available to use SSL for the LDAP connection for servers that require this.

Information collected

The tool collects information on the following categories of objects. The LDAP query used by default for each category is provided:

  • certauthorities - (objectClass=certificationAuthority)
  • certenrollservices - (objectClass=pKIEnrollmentService)
  • certtemplates - (objectClass=pKICertificateTemplate)
  • containers - (objectClass=container)
  • computers - (objectClass=computer)
  • domains - (objectClass=domain)
  • forests - (objectClass=crossRefContainer)
  • gpos - (objectClass=groupPolicyContainer)
  • groups - (objectClass=group)
  • ous - (objectClass=organizationalUnit)
  • trusted_domains - (objectClass=trustedDomain)
  • users - (&(objectClass=user)(|(objectCategory=person)(objectCategory=msDS-GroupManagedServiceAccount)(objectCategory=msDS-ManagedServiceAccount)))
  • info - server.info as available to anonymous binds

If any of the certificate categories are collected, the following query will also be run in the configuration naming context to obtain certificate object parent containers:

  • containers (|(objectClass=container)(objectClass=configuration))

All the attributes that the user you connect with can see will be collected for each object. Attribute names are case sensitive, and largely match the LDAP names.

The exceptions to this naming approach are parsed flag entries, which will have Flags appended to the name (e.g. userAccountControlFlags) and raw copies of parsed binary types which have _raw appended (e.g. nTSecurityDescriptor_raw).

There is interpretation or parsing on security descriptor type attributes (nTSecurityDescriptor and msDS-GroupMSAMembership), date attributes and certain other interesting flag style attributes such as userAccountControl, but in general attributes are returned as is.

The nTSecurityDescriptor attribute for each object has the Dacls, owner, group and a few of the control fields parsed, and Sids resolved to a friendly name where possible. The Sacl component are currently not being retrieved. The raw version of the attribute is also still returned, as hexlified binary data.

There is an option (-custom-query <query>) to run an alternate custom LDAP query as opposed to multiple queries that collect the previous info, or to run only a subset of the built in query categories from the list above (-methods <comma-seperated-list>).

The LDAP schema is also queried by default to help with some of the internal lookups when parsing ACL entries. You can choose to avoid the schema lookup or only perform this as required (-only-schema or -no-schema). Expect the quality of the output to be negatively affected if the schema collection is omitted.

Output

The output will be stored in an automatically named json file, unless an output filename is provided with -o. There are options to increase logging level (-loglevel) and exclude raw output for fields that the tool does parsing on (-exclude-raw).

There is a (BETA quality) option there to output in a Bloodhound compatible format, discussed below.

The JSON represents an object with the following high level keys by default (although this can change when run with non default options):

  • schema
  • certauthorities
  • certenrollservices
  • certtemplates
  • containers
  • computers
  • domains
  • forests
  • gpos
  • groups
  • ous
  • trusted_domains
  • users
  • info
  • meta

The key names that match that of a category from the previous section contain lists of each collected object of that type. e.g. users in the users key. The schema section contains a dump of the LDAP schema and the meta section contains various information about the operation of the tool.

Even when run against small environments, this is A LOT of information. You will likely need to have a good approach to make sense out of this - I use iPython, and an overview of how to explore the data was covered in a post on my blog here.

Evasions

The tool includes the option to introduce delays (-sleep <time_seconds>), with optional jitter (-jitter <jitter_max_seconds>), between each query it performs in order to avoid detection by tools that correlate queries over time from particular sources.

You can also provide a JSON file specifying custom queries and/or attributes for each category of information (-query-config <filename>), if you have specific queries to use instead of the default ones for evasion. File should be in the format of a simple lookup mapping the category name to a new query - the default query will be used for any unmapped values.

Heres a very simple example file overriding the query only:

{
    "users": {
        "query": "(objectClass=user)"
    }
}

Here is an example overriding the query and the attributes. The minimum attributes of "objectSid,distinguishedName,name" will be added to any in the configured list to prevent errors in the tool.

{
    "users": {
        "query": "(objectClass=user)",
        "attributes": [
            "ntSecurityDescriptor",
            "objectSid",
            "distinguishedName",
            "name"
        ]
    }
}

Controlling attributes returned

As well as the previously mentioned -query-config <filename> option, you can also specify -attributes <attributes_list> to specify the attributes for each object that will be returned for queries. The provided value should be a comma seperated list of attributes to query.

The following attributes are needed for the tool to operate and will be added to the list of provided attributes if not already provided.

objectSid,distinguishedName,name

Interpreted attributes for domain and domainShort will also be added for objects that have an objectSid regardless when domain information is collected.

Bloodhound output

The tool now has BETA level support for Bloodhound output.

The best approach to use for the moment while any kinks in the output are resolved is to run the tool as normal, outputting to json, and then converting the contents to Bloodhound output as a seperate step.

For an example output file of 20240410185809_192.168.1.100_AD_Dump.json created using a normal execution of the tool, you can do the conversion by running the tool similar to the following:

./ad_ldap_dumper.py -b -loglevel DEBUG -i 20240410185809_192.168.1.100_AD_Dump.json

The individual Bloodhound output files will be written individually to the present working directory (these files will not be added to a zip archive like SharpHound does).

Global Catalog Servers

You should be querying a Global Catalog LDAP server in order to maximise the quantity/quality of information collected. Theres a few ways to identify Global Catalog servers:

Do an NSLookup against the domains DNS servers for gc._msdcs.<domain_name>. For example for domain example.com.

dig gc._msdcs.example.com

Do a SRV lookup for _gc._tcp.<domain_name>. For domain example.com.

dig SRV _gc._tcp.example.com

The tool will now also warn you if you are NOT talking to a Global Catalog by checking the info.other.isGlobalCatalogReady property during connection. You can do this using anonymous binds (e.g. without authentication). If you bump the loglevel to at least INFO the tool will also specifically tell you that the server is a Global Catalog or not.

Something like the following is a lightweight approach (e.g. minimally invasive on the target server) that will allow you to work out if a given server 192.168.1.100 is a Global Catalog server or not.

./ad_ldap_dumper.py -d 192.168.1.100 -methods info -loglevel INFO

About

Security focused tool for dumping information from Active Directory via LDAP

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages