Skip to content

Add safe_shares SMB module: share permission enumeration module that does not write files to disk#1138

Open
e-nzym3 wants to merge 3 commits intoPennyw0rth:mainfrom
e-nzym3:add-safe-shares-module
Open

Add safe_shares SMB module: share permission enumeration module that does not write files to disk#1138
e-nzym3 wants to merge 3 commits intoPennyw0rth:mainfrom
e-nzym3:add-safe-shares-module

Conversation

@e-nzym3
Copy link

@e-nzym3 e-nzym3 commented Mar 9, 2026

Preface

On a recent engagement, I stumbled upon a situation where Netexec's --shares flag
generated a bunch of artifacts on shares where my user did not possess DELETE permissions.

Artifact:
Screenshot_2026-03-08_19-49-03

Perms on share folder:
Screenshot_2026-03-08_19-48-30

First time I ran into this, but nonetheless, it sent me down a path of identifying
a better (safer) way of gathering share permissions, even with some slight downsides.

This proposed module comes with a slight downside, since share ACL and NTFS ACL may
differ, some WRITE permissions may be missed. Here's what output looks like when ran
against a share where my authenticating user has a DENY on DELETE permissions:

enzym3@pop-os:~/$ nxc smb 10.0.0.190 -u jsmith -p 'Passw0rd!' -M safe_shares
SMB         10.0.0.190      445    MORDOR-DC        [*] Windows 11 / Server 2025 Build 26100 x64 (name:MORDOR-DC) (domain:mordor.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.0.0.190      445    MORDOR-DC        [+] mordor.local\jsmith:Passw0rd!
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        [*] Enumerated shares (no disk writes)
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        Share                Access          Remark
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        -----                ------          ------
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        ADMIN$               NO ACCESS       Remote Admin
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        C$                   NO ACCESS       Default share
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        NETLOGON             READ            Logon server share
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        SYSVOL               READ            Logon server share
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        test_share           READ

And here's the output from --shares, correctly reporting WRITE permissions, but
at the same time leaving an artifact behind on disk (pictured in the first screenshot above).

enzym3@pop-os:~/$ nxc smb 10.0.0.190 -u jsmith -p 'Passw0rd!' --shares
SMB         10.0.0.190      445    MORDOR-DC        [*] Windows 11 / Server 2025 Build 26100 x64 (name:MORDOR-DC) (domain:mordor.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.0.0.190      445    MORDOR-DC        [+] mordor.local\jsmith:Passw0rd!
SMB         10.0.0.190      445    MORDOR-DC        [*] Enumerated shares
SMB         10.0.0.190      445    MORDOR-DC        Share           Permissions     Remark
SMB         10.0.0.190      445    MORDOR-DC        -----           -----------     ------
SMB         10.0.0.190      445    MORDOR-DC        ADMIN$                          Remote Admin
SMB         10.0.0.190      445    MORDOR-DC        C$                              Default share
SMB         10.0.0.190      445    MORDOR-DC        IPC$            READ            Remote IPC
SMB         10.0.0.190      445    MORDOR-DC        NETLOGON        READ            Logon server share
SMB         10.0.0.190      445    MORDOR-DC        SYSVOL          READ            Logon server share
SMB         10.0.0.190      445    MORDOR-DC        test_share      READ,WRITE

My thought: I'd rather have the option of running a safer check for WRITE permissions
and miss some, rather than potentially risk leaving a bunch of artifacts behind for clean-up.

In normal cases, it will report WRITE access as expected:

enzym3@pop-os:~/$ nxc smb 10.0.0.190 -u administrator -p 'Passw0rd!' -M safe_shares
SMB         10.0.0.190      445    MORDOR-DC        [*] Windows 11 / Server 2025 Build 26100 x64 (name:MORDOR-DC) (domain:mordor.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.0.0.190      445    MORDOR-DC        [+] mordor.local\administrator:Passw0rd! (Pwn3d!)
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        [*] Enumerated shares (no disk writes)
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        Share                Access          Remark
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        -----                ------          ------
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        ADMIN$               READ,WRITE      Remote Admin
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        C$                   READ,WRITE      Default share
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        NETLOGON             READ,WRITE      Logon server share
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        SYSVOL               READ,WRITE      Logon server share
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        test_share           READ,WRITE

Description

Adds a new SMB module safe_shares that enumerates share READ/WRITE permissions
without writing any files to disk — an OPSEC-safe alternative to the built-in
--shares flag. Effectively, an NXC implementation of SharpShares (https://github.com/djhohnstein/SharpShares).

Problem with existing --shares: Write access is tested by creating a
temporary file/directory, then deleting it. This leaves artifacts during
the creation-deletion window.

This module's approach (inspired by SharpShares):

  • READ check: listPath() — same non-destructive approach NXC already uses
  • WRITE check: Opens the share root directory (\) with GENERIC_WRITE desired
    access and FILE_OPEN (0x1) create disposition — opens the existing directory
    handle, never creates any object on disk. If the server grants the handle,
    write access is confirmed.

No third-party dependencies beyond impacket (already bundled with NXC).

AI Assistance: This module was developed with the assistance of Claude Code
(Anthropic). The technique research, impacket API usage, and module architecture
were AI-assisted; the code has been manually reviewed and tested.

Type of change

  • New feature (non-breaking change which adds functionality)
  • This PR was created with the assistance of AI (Claude Code by Anthropic —
    used for implementation and impacket API research)

Setup guide for the review

Any authenticated SMB session against a Windows target with shares is sufficient.
No special configuration required. Tested against:

  • Windows Server 2025 domain member
  • Python 3.12, Debian Linux
# Basic enumeration
nxc smb <target> -u <user> -p <pass> -M safe_shares

# Show only shares with WRITE access
nxc smb <target> -u <user> -p <pass> -M safe_shares -o FILTER=WRITE

# Exclude additional shares
nxc smb <target> -u <user> -p <pass> -M safe_shares -o EXCLUDE=IPC$,ADMIN$,C$

Screenshots

See Preface above for inline screenshots. Terminal output:

enzym3@pop-os:~/claude$ nxc smb 10.0.0.190 -u jsmith -p 'Passw0rd!' -M safe_shares
SMB         10.0.0.190      445    MORDOR-DC        [*] Windows 11 / Server 2025 Build 26100 x64 (name:MORDOR-DC) (domain:mordor.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.0.0.190      445    MORDOR-DC        [+] mordor.local\jsmith:Passw0rd!
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        [*] Enumerated shares (no disk writes)
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        Share                Access          Remark
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        -----                ------          ------
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        ADMIN$               NO ACCESS       Remote Admin
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        C$                   NO ACCESS       Default share
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        NETLOGON             READ            Logon server share
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        SYSVOL               READ            Logon server share
SAFE_SHARES 10.0.0.190      445    MORDOR-DC        test_share           READ

Checklist

  • I have ran Ruff against my changes
  • I have added or updated the tests/e2e_commands.txt file
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
    (will open NetExec-Wiki PR after this is merged)

@mpgn
Copy link
Collaborator

mpgn commented Mar 9, 2026

@e-nzym3
Copy link
Author

e-nzym3 commented Mar 9, 2026

--no-write-check will omit checking write permissions altogether:

enzym3@pop-os:~/$ nxc smb 10.0.0.190 -u jsmith -p 'Passw0rd!' --shares --no-write-check
SMB         10.0.0.190      445    MORDOR-DC        [*] Windows 11 / Server 2025 Build 26100 x64 (name:MORDOR-DC) (domain:mordor.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.0.0.190      445    MORDOR-DC        [+] mordor.local\jsmith:Passw0rd!
SMB         10.0.0.190      445    MORDOR-DC        [*] Enumerated shares
SMB         10.0.0.190      445    MORDOR-DC        Share           Permissions     Remark
SMB         10.0.0.190      445    MORDOR-DC        -----           -----------     ------
SMB         10.0.0.190      445    MORDOR-DC        ADMIN$                          Remote Admin
SMB         10.0.0.190      445    MORDOR-DC        C$                              Default share
SMB         10.0.0.190      445    MORDOR-DC        IPC$            READ            Remote IPC
SMB         10.0.0.190      445    MORDOR-DC        NETLOGON        READ            Logon server share
SMB         10.0.0.190      445    MORDOR-DC        SYSVOL          READ            Logon server share

enzym3@pop-os:~/$ nxc smb 10.0.0.190 -u administrator -p 'Passw0rd!' --shares --no-write-check
SMB         10.0.0.190      445    MORDOR-DC        [*] Windows 11 / Server 2025 Build 26100 x64 (name:MORDOR-DC) (domain:mordor.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.0.0.190      445    MORDOR-DC        [+] mordor.local\administrator:Passw0rd! (Pwn3d!)
SMB         10.0.0.190      445    MORDOR-DC        [*] Enumerated shares
SMB         10.0.0.190      445    MORDOR-DC        Share           Permissions     Remark
SMB         10.0.0.190      445    MORDOR-DC        -----           -----------     ------
SMB         10.0.0.190      445    MORDOR-DC        ADMIN$          READ            Remote Admin
SMB         10.0.0.190      445    MORDOR-DC        C$              READ            Default share
SMB         10.0.0.190      445    MORDOR-DC        IPC$            READ            Remote IPC
SMB         10.0.0.190      445    MORDOR-DC        NETLOGON        READ            Logon server share
SMB         10.0.0.190      445    MORDOR-DC        SYSVOL          READ            Logon server share
SMB         10.0.0.190      445    MORDOR-DC        test_share      READ

whereas this module will check for WRITE permissions safely, and still report them where possible

Signed-off-by: e-nzym3 <jack@enzym3.io>
@NeffIsBack
Copy link
Member

NeffIsBack commented Mar 10, 2026

Thanks for the PR!

I think we should either integrate it into the default --share command or not integrate it at all, a module just for share enumeration is kinda the wrong place imo. This change would only result in false positives because we would show write privs although the NTFS privs might not allow the write, correct? Or am i missing something?

Replaces the single GENERIC_WRITE check with a multi-mask approach covering
FILE_ADD_FILE, FILE_ADD_SUBDIRECTORY, WRITE_DAC, and WRITE_OWNER. Plain WRITE
suppresses the more granular labels when redundant. Dynamic column widths added.
@e-nzym3
Copy link
Author

e-nzym3 commented Mar 11, 2026

Actually, as I was digging more into the SharpShares logic, I realized it had some gaps that could be tightened down to make this proposed module less prone to false positives by adding checks for some other permissions as well, such as FILE_ADD_FILE or FILE_ADD_SUBDIRECTORY. It only checked for GENERIC_WRITE prior. Not sure how useful writing subdirectories may be, so that one is up for debate, currently it is reported as WRITE (subdir) to distinguish.

Another bonus I added was WRITE_DAC and WRITE_OWNER checks, which would check if the authenticating user can modify the permissions to grant themselves WRITE if it isn't already present (WRITE reporting takes precedence). I can provide a PoC script for this if interested.

To clarify on the false positive concern: the module is not reading the DACL directly, it's calling openFile() with FILE_OPEN which goes through the full Windows access check (share perms + NTFS + MIC). If NTFS denies it, the open fails and we report nothing. There are edge cases where it won't be accurate (disk quotas, Dynamic Access Control) but those are pretty niche.

Worth flagging if you're considering merging the logic:

  • False positives: quota exhaustion and conditional ACEs can fool an ACL probe but not an actual write attempt, --shares wins here
  • False negatives in --shares: WRITE_DAC and WRITE_OWNER don't allow immediate file creation so --shares won't flag them, but both are reliable escalation paths to full write. The module flags these as WRITE (ACL)

I created a test share setup to display when this module would trigger WRITE. Each individual share was only granted a subset of permissions for demonstration purposes. Here is the output of the share check script:
check_shares-output

  • In green, you will see permissions that would trigger the module's WRITE reporting, and the last column shows what the module would actually report.
  • The file/folder write was only implemented in this test script for validation purposes (it is not part of the module)

Here is what the module ran through nxc would report:
safe_shares-output

@NeffIsBack
Copy link
Member

Very interesting, thanks for the deep dive! I still don't see this as a new module, but perhaps we could integrate it into --shares, so that we use the file handle checks by default and only when requested do the current actual "write-file/delete-file" tests.

For that i would need to assess how common these false negatives/positives are. So you say you only have false negatives with implicit write privs? Otherwise your implementation is as good as the current --shares implementation? I don't mind false positives that much (although annoying), but having false negatives would probably a deal breaker.

@e-nzym3
Copy link
Author

e-nzym3 commented Mar 17, 2026

The false negatives are actually present in the current implementation of --shares (not in the proposed new method) where it doesn't verify whether WRITE permissions could be attained through ACL abuse. Here's a screenshot of what the --shares output looks like for the same shares as I tested above with -M safe_shares:
Screenshot_2026-03-17_11-19-40

The output is nearly identical, with the exception that it misses the two shares, test_write_dac and test_write_owner, where there is an opportunity to have WRITE access by modifying the permissions of the share folder. The proposed module implementation would report those, increasing the potential WRITE access scope.

To expand on the possible false positive scenarios:

  • Since we're only checking DACL, if there are some EDR/AV solutions that monitor and block I/O operations, the module may report WRITE, but when the user goes to write a file, they get stopped by the EDR (not that common)
  • If NTFS quota is enforced and the authenticating user can no longer create files because they've met their quota, the tool will still report WRITE but the write action will not be possible. This is because the quota is charged at FILE_CREATE. We're checking with FILE_OPEN. (not common either)

Other than that, I believe the proposed solution would work as well as the current implementation of --shares without the need to write files. Ultimately, the best comparison would be in a live environments.

Perhaps, as you've proposed, we implement this as the default behavior for --shares and instead of having --no-write-check, we implement a --file-write-check which then shifts to performing the file write + delete method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants