Skip to content

fix(ldap): resolve group members via 'member' fallback for missing 'm… - #1362

Open
amikuss wants to merge 1 commit into
Pennyw0rth:mainfrom
amikuss:fix/ldap-groups-memberof-fallback
Open

fix(ldap): resolve group members via 'member' fallback for missing 'm…#1362
amikuss wants to merge 1 commit into
Pennyw0rth:mainfrom
amikuss:fix/ldap-groups-memberof-fallback

Conversation

@amikuss

@amikuss amikuss commented Aug 14, 2026

Copy link
Copy Markdown

…emberOf' backlinks

Description

Added a fallback checking "members" attribute for the --groups "GROUP" argument.

If a user's "memberOf" backlink has not been populated, the current memberOf/primaryGroupID check does not return the user when running --groups "GROUP", even though --groups returns the correct number of group members.

I ran into this issue in a HTB CAPE environment, where a Foreign Security Principal's memberOf was not set, even though it was a member of a group. See screenshots below.

AI disclosure: Claude Sonnet 5 was used for some coding help.

Type of change

Insert an "x" inside the brackets for relevant items (do not delete options)

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Deprecation of feature or functionality
  • This change requires a documentation update
  • This requires a third party update (such as Impacket, Dploot, lsassy, etc)
  • This PR was created with the assistance of AI (list what type of assistance, tool(s)/model(s) in the description)

Setup guide for the review

I have been unable to reproduce this issue as the memberOf attribute is dynamically maintained, including FSPs.
I was unable to figure out why this was not the case in the CAPE lab environment and honestly unsure if this PR is necessary given that this seems like a very environment-specific quirk.

Screenshots (if appropriate):

Before:
image

After:
image

Checklist:

Insert an "x" inside the brackets for completed and relevant items (do not delete options)

  • I have ran Ruff against my changes (poetry: poetry run ruff check ., use --fix to automatically fix what it can)
  • I have added or updated the tests/e2e_commands.txt file if necessary (new modules or features are required to be added to the e2e tests)
  • If reliant on changes of third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
  • I have linked relevant sources that describes the added technique (blog posts, documentation, etc)
  • I have performed a self-review of my own code (not an AI review)
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (PR here: https://github.com/Pennyw0rth/NetExec-Wiki)

@NeffIsBack NeffIsBack left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the bug fix PR!

So far, the search preparation and display logic was separated because there were only small differences in both code branches. Since this is much more logic now, please just make a big if/else to separate between searching all groups and looking at a specific group.

Comment thread nxc/protocols/ldap.py
Comment on lines +784 to +786
original_scope = self.scope
try:
self.scope = ldapasn1_impacket.Scope("baseObject")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for the change in scope?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't be strictly necessary given that it is querying users/groups which do not have children, but I added it as a defensive precaution since I am assuming it will always only return the 1 desired DN on line 796.

Comment thread nxc/protocols/ldap.py
Comment on lines +799 to +800
finally:
self.scope = original_scope

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need yet another try/except/finally block here if there isn't a specific reason (error) that should be caught.

Comment thread nxc/protocols/ldap.py
Comment on lines +788 to +792
try:
member_resp = self.search("(objectClass=*)", member_attributes, baseDN=member_dn)
except Exception as e:
self.logger.debug(f"Failed to resolve member DN '{member_dn}': {e}")
member_resp = []

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for the try&except? LDAPSearchError are usually caught in the search itself. Did you notice any other?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not realize they got caught in the search, try/except will be removed.

@NeffIsBack NeffIsBack added the bug-fix This Pull Request fixes a bug label Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug-fix This Pull Request fixes a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants