Skip to content

feat(winrm): add ntds_shadow module - #1373

Open
0xbaas wants to merge 1 commit into
Pennyw0rth:mainfrom
0xbaas:research/ntds-shadow-clean
Open

feat(winrm): add ntds_shadow module#1373
0xbaas wants to merge 1 commit into
Pennyw0rth:mainfrom
0xbaas:research/ntds-shadow-clean

Conversation

@0xbaas

@0xbaas 0xbaas commented Aug 21, 2026

Copy link
Copy Markdown

Description

This adds an ntds_shadow module for WinRM.

With Backup Operators privileges on a domain controller, NetExec’s backup_operator module can retrieve hashes from the local SAM. To retrieve domain account hashes, we also need NTDS.dit and the SYSTEM hive.

Because NTDS.dit is locked while the domain controller is running, getting these files through DiskShadow normally requires several manual steps: SeBackupPrivilege manual steps.

The module checks for SeBackupPrivilege, creates and exposes a DiskShadow snapshot, and downloads NTDS.dit and the SYSTEM hive. It checks if the snapshot was exposed correctly, verifies the downloaded files, removes the remote files and snapshot, and prints the impacket-secretsdump command needed to process them locally.

Hash extraction is not part of this module. It only gets the files and shows the user the command needed for the next step.

The module does not require extra software, DLLs, registry changes or GPO changes on the target. The module only uses built-in Windows tools.

I tried to keep the module as small as possible by looking at existing modules such as backup_operator.py, ntdsutil.py and ntds-dump-raw.py and reusing parts of the same structure and patterns where possible.

The snapshot parsing, exposure checks, download checks and cleanup are kept in this module because they are specific to this workflow. Reusing the other module logic here would mean losing some of the checks and cleanup handling.

Closes #1366

AI assistance

I used Codex with gpt-5.6-sol mainly for the unit tests and an extra review of the module. Claude Sonnet 4.6 helped with some smaller refactoring changes. I checked the changes myself, ran Ruff and the local tests and tested the full workflow in my own labs.

Type of change

  • 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
  • This PR was created with the assistance of AI

Setup guide for the review

I tested the module from Kali Linux against:

  • Windows Server 2019, build 17763
  • Windows Server 2022, build 20348

The target must be a domain controller with WinRM enabled. The account needs SeBackupPrivilege, for example through membership of the Backup Operators group.

No extra software, DLLs, registry changes or GPO changes are needed on the target.

Example:

netexec winrm <target> -u <username> -p <password> -M ntds_shadow

A successful run downloads ntds.dit and SYSTEM.hive, checks both files, removes the remote files and snapshot, and prints the impacket-secretsdump command for offline processing.

Local checks:

  • All local tests passed
  • Ruff passed
  • Formatting passed
  • git diff --check passed

I also tested the full workflow in the lab, including processing the files with impacket-secretsdump and checking that the remote cleanup worked.

Cleanup only uses the exact snapshot GUID created during the run. The module does not use a broad command such as delete shadows all.

Relevant sources:

Screenshots

Successful ntds_shadow execution and cleanup on Windows Server 2019

Successful ntds_shadow execution and cleanup on Windows Server 2022

Checklist

  • I have run Ruff against my changes
  • I have added or updated tests/e2e_commands.txt
  • If reliant on changes of third-party dependencies, I have linked the relevant PRs
  • I have linked relevant sources describing the added technique
  • I have performed a self-review of my own code
  • I have commented my code where needed
  • I have made corresponding changes to the documentation

@0xbaas

0xbaas commented Aug 21, 2026

Copy link
Copy Markdown
Author

Sorry for opening a new PR. Something went wrong while cleaning up the previous one. This PR contains the final version. Looking forward to your feedback!

@NeffIsBack

NeffIsBack commented Aug 22, 2026

Copy link
Copy Markdown
Member

Sorry for opening a new PR. Something went wrong while cleaning up the previous one. This PR contains the final version. Looking forward to your feedback!

No worries about that :) Thanks for the PR

@NeffIsBack

Copy link
Copy Markdown
Member

So looking at the code it is much better than #1369, but there is still a lot of overhead that could be removed. E.g. what is the reason for the custom command execution implementation?

I uploaded an AGENTS.md file so I will give codex reviews a try to see how well it can detect the overhead and how good it is at reviewing stuff.

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T06:07:02.988043Z c7086db Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c7086dba6d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +309 to +313
if parts[2].upper() == "ENABLED":
log.success("SeBackupPrivilege confirmed")
return True
log.fail("SeBackupPrivilege is present but disabled")
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Accept a present backup privilege even when disabled

For the intended Backup Operators scenario, whoami commonly reports SeBackupPrivilege as disabled because it remains in the token but is enabled by the operation that needs it. Rejecting that state aborts before DiskShadow or robocopy /b can run, so valid Backup Operators accounts cannot use the module; check that the privilege is present and enable it when necessary rather than requiring the whoami state to already be ENABLED.

Useful? React with 👍 / 👎.

lines = [
"set verbose on",
"set context persistent nowriters",
f"set metadata {metadata_path}",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve the configured NTDS database volume

On domain controllers where the NTDS database has been relocated, this always snapshots C: and later looks only for \Windows\NTDS\ntds.dit, so the acquisition fails even though the account has the required privilege. Read the configured NTDS database path, snapshot its containing volume, and derive the path within that snapshot instead of assuming the default installation layout.

Useful? React with 👍 / 👎.

Comment on lines +110 to +113
ps = (
"$p=[System.Diagnostics.Process]::new();"
"$p.StartInfo.FileName='cmd.exe';"
f"$p.StartInfo.Arguments='/c {safe}';"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reuse the WinRM command execution primitive

Every simple command is unnecessarily wrapped in a PowerShell-created Process, asynchronous stream tasks, and JSON serialization even though the underlying WinRM client's execute_cmd already returns (stdout, stderr, returncode). This adds an extra remote process and serialization round trip to every operation and introduces bespoke quoting and error handling that diverges from the protocol implementation; call the existing command primitive directly.

Useful? React with 👍 / 👎.

@NeffIsBack

Copy link
Copy Markdown
Member

Hmm okay that was underwhelming

@0xbaas

0xbaas commented Aug 31, 2026

Copy link
Copy Markdown
Author

So looking at the code it is much better than #1369, but there is still a lot of overhead that could be removed. E.g. what is the reason for the custom command execution implementation?

I uploaded an AGENTS.md file so I will give codex reviews a try to see how well it can detect the overhead and how good it is at reviewing stuff.

@codex review

iirc this wrapper was already part of my initial implementation because direct execute_cmd returned a WSMan fault with code 5 for the Backup Operators session. So I used it again when simplifying the module.

I’ll test it with execute_cmd sometime over the next couple of days

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

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.

Add a DiskShadow module to automate NTDS.dit dumping via DiskShadow over WinRM

2 participants