Add USER option to maq module to enumerate per-user machine join count#1184
Add USER option to maq module to enumerate per-user machine join count#1184PvUL00 wants to merge 5 commits into
Conversation
Adds a USER option that queries how many computers a given user has already joined to the domain, compared against the MachineAccountQuota. Detection covers both SAMR domain joins (ms-DS-CreatorSID) and direct LDAP creation such as addcomputer.py (nTSecurityDescriptor owner SID).
Signed-off-by: PvUL00 <166558777+PvUL00@users.noreply.github.com>
Signed-off-by: PvUL00 <166558777+PvUL00@users.noreply.github.com>
| # Method 1: ms-DS-CreatorSID — set by DC for SAMR domain joins | ||
| raw_sid = comp.get("ms-DS-CreatorSID") | ||
| if raw_sid is not None: | ||
| creator_sid = _sid_to_str(raw_sid) if isinstance(raw_sid, bytes) else raw_sid |
There was a problem hiding this comment.
Did you get raw bytes for the sid? I think this should have been parsed by the ldap attribute parser
There was a problem hiding this comment.
The isinstance check has been removed and replaced with a direct call to sid_to_str (imported from nxc.parsers.ldap_results) which is the same function the attribute parser uses for objectSid.
There was a problem hiding this comment.
Actually, since this attribute should always be an SID we should add that to the attribute parser itself, so that it does not have to be called by external scripts (the module in this case).
There was a problem hiding this comment.
Done, ms-DS-CreatorSID has been added to parse_result_attributes in nxc/parsers/ldap_results.py and the manual sid_to_str call removed from the module
Description
Adds a USER option that queries how many computers a given user has already joined to the domain, compared against the MachineAccountQuota. Detection covers both SAMR domain joins (ms-DS-CreatorSID) and direct LDAP creation such as addcomputer.py (nTSecurityDescriptor owner SID).
Two detection methods are used to cover all creation paths:
The LDAP query uses LDAP_SERVER_SD_FLAGS_OID (1.2.840.113556.1.4.801) with OWNER_SECURITY_INFORMATION=1 to retrieve only the owner portion of the security descriptor.
Usage:
MAQ only (unchanged behaviour)
nxc ldap <DC> -u <user> -p <pass> -M maqMAQ + machine join count for a specific user
nxc ldap <DC> -u <user> -p <pass> -M maq -o USER=<username>This module was built with the assistance of Claude Code (claude-sonnet-4-6). All code was reviewed and tested against a live AD lab.
Type of change
Insert an "x" inside the brackets for relevant items (do not delete options)
Setup guide for the review
addcomputer.py -computer-name 'SHUTDOWN$' -computer-pass 'Password1!' -dc-host DC -domain-netbios LAB lab.local/user:pass
nxc ldap -u user -p pass -M maq -o USER=user
Expected:
Machines joined by 'user': 1/10 (remaining quota: 9)
Computer: SHUTDOWN
Screenshots (if appropriate):
Before :
After :

Checklist:
Insert an "x" inside the brackets for completed and relevant items (do not delete options)
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)