Add SeMachineAccountPrivilege to maq module#1083
Conversation
|
Nice addition |
|
Looks cool, thanks for the PR! Probably the better way to integrate the GPO parsing. |
|
Hey ! Really nice, good trick to enumerate which GPO is relevant, I will close my PR since yours looks perfect. |
|
This looks like it breaks on the first GPO it found with the privilege. We should probably iterate through all existing GPOs and check if there are multiple occurrences, so we don't miss any. |
|
Interesting, could be usefull to get the level of the GPO |
|
Since the current scope only includes GPOs linked to the Domain Controllers OU,
|
| sessions.setdefault(sid, {"Username": ""}) | ||
|
|
||
| try: | ||
| # Handle RPC connection |
There was a problem hiding this comment.
Is there a reason you're using LSA RPC here instead of LDAP for SID resolution? This module is already ldap-only, (objectSid={sid}) + sAMAccountName would be simpler (see badsuccessor.py)
e.g :
resp = connection.search(searchFilter=f"(objectSid={sid})", attributes=["sAMAccountName"])| return | ||
|
|
||
| # Extract GUIDS from the output using regex | ||
| guids = re.findall(r"(?i)cn=\{([0-9a-f\-]{36})\}", entries[0]["gPLink"]) |
There was a problem hiding this comment.
If the Domain Controllers OU has no linked GPO this will raise KeyError. Please use entries[0].get("gPLink", "") and bail out early if it's empty.
|
|
||
| for guid in guids: | ||
| # Accessing the GPO in the SYSVOL share to parse GptTmpl.inf | ||
| path = ntpath.join(connection.targetDomain, "Policies", f"{{{guid}}}", "MACHINE", "Microsoft", "Windows NT", "SecEdit", "GptTmpl.inf",) |
There was a problem hiding this comment.
Please drop the trailing comma, it's not needed and looks like a leftover from a tuple
| @@ -19,10 +29,157 @@ def options(self, context, module_options): | |||
| """No options available""" | |||
|
|
|||
| name = "maq" | |||
There was a problem hiding this comment.
Please move name, description, supported_protocols and category above options() to match the rest of the modules (see raisechild.py)
| return "" | ||
|
|
||
| # Just handle smb connection | ||
| def connect_smb(connection): |
There was a problem hiding this comment.
Please move connect_smb (and resolve_gpo) out of get_SeMachineAccountPrivilege and make them methods of the class easier to read.
|
|
||
| else: | ||
| context.log.fail("No SID(s) found in SeMachineAccountPrivilege") | ||
|
|
There was a problem hiding this comment.
Please call smb.logoff() after you're done reading SYSVOL.
| entries = parse_result_attributes(ldap_response) | ||
| except Exception as e: | ||
| context.logger.debug(f"Exception raised while looking for gPLink: {e}") | ||
| exit(1) |
There was a problem hiding this comment.
Hmm imo should simply be a return

Description
I recently spent some time working on SeMachineAccountPrivilege enumeration, following the discussion and initial work introduced in PR #1029 by @Blatzy.
Based on that, I’m proposing this PR, which integrates the check directly into the MAQ module and makes it especially useful during internal penetration tests.
The idea is to keep everything dynamic and reliable, by:
This avoids hardcoded assumptions and provides a clearer picture of how the privilege is actually configured on Domain Controllers.
Type of change
Setup guide for the review
The feature relies on:
Screenshots:
Debug
Checklist:
poetry run python -m ruff check . --preview, 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)