Add rbcd module to abuse Resource-Based Constrained Delegation#1236
Add rbcd module to abuse Resource-Based Constrained Delegation#1236AhmadAlawneh3 wants to merge 2 commits into
Conversation
Implements read/write/remove of msDS-AllowedToActOnBehalfOfOtherIdentity on the LDAP protocol. Complements the existing --find-delegation enumeration by allowing pentesters to actually configure RBCD without leaving nxc. Closes Pennyw0rth#1219
|
Thanks for the PR, the detailed setup guide is much appreciated! Ticked all the boxes how a great PR description should look like :) Looks really great so far, but please don't use the I think I also ran into this "empty SD" problem as well, which can also be problematic when enumerating these users (crashed on my once because of that resulting empty SD). Since the default is that users don't even have any value set for the
|
Per maintainer review on PR Pennyw0rth#1236: drop the ldap3 dependency and use the impacket-native SDFlagsControl (same pattern as PR Pennyw0rth#1163). Verified end-to-end against GOAD-Mini after the change.
|
Thanks for the quick review! Replaced the ldap3 On the empty-SD point: the module already handles this in REMOVE. When the last delegation entry is removed (either via Let me know if you'd like any other changes. |
Description
Adds a new LDAP module
rbcdthat completes the existing RBCD enumeration story in nxc.nxc already supports reading
msDS-AllowedToActOnBehalfOfOtherIdentityvia--find-delegation, but there has been no way to write the attribute. Pentesters who find a misconfigured DACL (GenericWrite/GenericAll/WriteDACL on a computer object) currently have to leave nxc and switch to ntlmrelayx, BloodyAD, or impacket-rbcd to perform the actual delegation write. This module fills that gap.The module supports three actions on the LDAP protocol:
It chains naturally with the existing
add-computermodule: create a machine account via MAQ, then use this module to configure RBCD, then usegetST.py -impersonatefor the S4U2Self/S4U2Proxy step.Closes #1219
Technique references
ntlmrelayx.attacks.ldapattack.delegateAttackandcreate_empty_sd/create_allow_acehelpersAI assistance disclosure
Per the AI policy: I used Claude Code (Opus 4.7) as an assistant during this work. Specifically:
add-computer/badsuccessorhandle LDAP writes, howparse_result_attributesandconnection.search()work)I personally wrote/reviewed every line, ran each test case in a real GOAD-Mini lab against
sevenkingdoms.local, and caught one significant bug during testing (see "Setup guide" below, the Owner SID stripping issue) that required redesigning how the SD is reconstructed before write. The PR body and issue are written by me.Type of change
Insert an "x" inside the brackets for relevant items (do not delete options)
Setup guide for the review
Lab
Tested against GOAD-Mini (
sevenkingdoms.local, single DC =KINGSLANDING/ Windows Server 2019 Build 17763). Python 3.12 on Linux. nxc installed viapip install -e .from this branch.Realistic scenario tested
Rather than testing only as Domain Admin, I provisioned a realistic scenario:
rbcd_lowprivVICTIM$rbcd_lowprivGenericWrite onVICTIM$(the kind of misconfig pentesters find in the wild)This is the "compromised low-priv user with a misconfigured ACL" attack scenario.
Tests run
Functional (as
rbcd_lowpriv, not Domain Admin):End-to-end attack chain: Followed by
getST.py -spn cifs/VICTIM.sevenkingdoms.local -impersonate Administrator -dc-ip 10.10.10.10 'sevenkingdoms.local/PWNPC$:PwnPC123!'. The KDC successfully issued the Administrator service ticket, confirming the SD is valid.Negative case: Attempting to write RBCD on a target the user has no rights on (
KINGSLANDING$) returns:Auth methods: Verified read/write/remove cycle works under password auth, NT hash auth (
-H), Kerberos auth (-k --use-kcache), and over LDAPS (--port 636).Idempotency: Running WRITE with the same SID twice does not create a duplicate ACE.
Append: Adding a second SID preserves the existing ACE.
Bug found & fixed during testing
When AD returns
msDS-AllowedToActOnBehalfOfOtherIdentitywithsdflags=0x04, the server strips OwnerSid/GroupSid/Sacl from the response. Naively reusing that parsed SD for a write back fails withconstraintViolationbecause AD requires a valid Owner. The module always rebuilds a fresh SD viacreate_empty_sd()(withBUILTIN\Administratorsas Owner) and copies the existing ACEs over. There's a docstring comment oncreate_empty_sdexplaining this so future maintainers don't reintroduce the regression.Setup needed by the reviewer
To reproduce:
dacledit.pyor by running it as a Domain Adminadd-computer(MAQ >= 1)Screenshots (if appropriate)
All screenshots taken from the realistic low-priv lab scenario (
rbcd_lowprivuser with GenericWrite onVICTIM$, GOAD-Mini).1. Precondition:
rbcd_lowprivhas GenericWrite onVICTIM$daclreadconfirms the misconfigured ACE that the attack relies on.2. Low-priv user creates attacker computer and configures RBCD
add-computer(via MAQ) followed by the newrbcdmodule performing the write.3. Verify RBCD entry + S4U produces Administrator service ticket
The
rbcdmodule READ confirms the entry was set, thengetST.pyperforms S4U2Self/S4U2Proxy and the KDC issues the Administrator ticket. This is the cryptographic proof that the RBCD config is valid.4. Negative case: graceful error on insufficient rights
rbcd_lowprivcannot modify RBCD on a target it has no rights on (the DC).Checklist:
poetry run ruff check ., use--fixto automatically fix what it can)tests/e2e_commands.txtfile if necessary (new modules or features are required to be added to the e2e tests)