Skip to content

feat(smbtakeover): add smbtakeover.py module#1153

Open
sttlr wants to merge 6 commits intoPennyw0rth:mainfrom
sttlr:feat-add-smbtakeover-module
Open

feat(smbtakeover): add smbtakeover.py module#1153
sttlr wants to merge 6 commits intoPennyw0rth:mainfrom
sttlr:feat-add-smbtakeover-module

Conversation

@sttlr
Copy link
Copy Markdown

@sttlr sttlr commented Mar 14, 2026

Description

Added smbtakeover.py module to unbind/rebind SMB port 445/tcp. Useful when conducting NTLM relay attacks.

Usually Windows hosts already use port 445/tcp for SMB. And to conduct an NTLM relay attack you'd need port 445 to be available to bind to it. There are ways to do it via loading a driver, loading a module into LSASS, rebooting the target machine - which are all bad for OPSEC.

This module solves this problem in an OPSEC safe way.

Original research - https://specterops.io/blog/2024/08/01/relay-your-heart-away-an-opsec-conscious-approach-to-445-takeover/
Original code - https://github.com/zyn3rgy/smbtakeover

Used Gemini Pro to generate the NXCModule class. Validated after it.

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

NOTE: User running the module has to have Administrator level access on the machine (to start/stop the services).
NOTE: WMI ports need to be accessible (check firewall).

I used Kaiju chain from Vulnlab to validate the module.

Screenshots (if appropriate):

Pasted Graphic 2

Unbinding and then rebinding works ok.

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)

Copy link
Copy Markdown
Member

@NeffIsBack NeffIsBack left a comment

Choose a reason for hiding this comment

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

Thanks for the PR.

This code looks much better than #1152, but there are still a few things to do.

Comment thread nxc/modules/smbtakeover.py Outdated
Comment thread nxc/modules/smbtakeover.py Outdated
Comment thread nxc/modules/smbtakeover.py Outdated
Comment thread nxc/modules/smbtakeover.py Outdated
Comment thread nxc/modules/smbtakeover.py Outdated
Comment thread nxc/modules/smbtakeover.py Outdated
@sttlr
Copy link
Copy Markdown
Author

sttlr commented Mar 17, 2026

Now

Screenshot 2026-03-17 at 19 41 29

@sttlr sttlr requested a review from NeffIsBack March 17, 2026 17:59
Comment thread nxc/modules/smbtakeover.py Outdated
@sttlr sttlr requested a review from NeffIsBack March 18, 2026 11:48
Comment thread nxc/modules/smbtakeover.py Outdated
Comment thread nxc/modules/smbtakeover.py Outdated
Comment thread nxc/modules/smbtakeover.py Outdated
@sttlr sttlr requested a review from azoxlpf April 10, 2026 19:58
self.start_service(svc_obj, svc_name) if is_start else self.stop_service(svc_obj, svc_name)

failed = False
statuses = self.check()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

process() already calls check_service() in the loop, then calls self.check() at the end which queries the same services again and prints highlights. It works, but it duplicates WMI calls and can be noisy. Consider reusing one pass of results for both the actions and the final check

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.

process() calls check_service() in the loop to only change services that don't have target state set.

if i remove check_service() check - it would try to change all the services.

self.check() is used to validate results after changing.

so a service needs to be checked at least 2 times: first to check if it needs changing, second time to check if it was changed successfully.

self.logger.fail(f"SMB {msg} sequence failed.")
else:
status = "restored" if is_start else "free"
self.logger.success(f"SMB {msg} sequence completed. Port 445/tcp {status}.")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are we only validating WMI service state here, and not that TCP 445 is actually closed or listening? If so, should the success line say that the services are in the expected state rather than that port 445/tcp is free/restored?

return self._i_wbem_services

def get_service_or_driver(self, name):
svc_name_to_table = {"srv2": "Win32_SystemDriver", "srvnet": "Win32_SystemDriver", "LanmanServer": "Win32_Service"}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

svc_name_to_table is built inside get_service_or_driver() on every call. Please move it to a class-level constant so the mapping isn’t recreated each time same behavior, slightly cleaner

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants