netexec-mcp 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- netexec_mcp-1.0.0/.github/workflows/publish.yml +57 -0
- netexec_mcp-1.0.0/.gitignore +21 -0
- netexec_mcp-1.0.0/.mcp.json.example +20 -0
- netexec_mcp-1.0.0/LICENSE +23 -0
- netexec_mcp-1.0.0/PKG-INFO +206 -0
- netexec_mcp-1.0.0/README.md +185 -0
- netexec_mcp-1.0.0/pyproject.toml +43 -0
- netexec_mcp-1.0.0/src/netexec_mcp/__init__.py +3 -0
- netexec_mcp-1.0.0/src/netexec_mcp/auth.py +156 -0
- netexec_mcp-1.0.0/src/netexec_mcp/config.py +219 -0
- netexec_mcp-1.0.0/src/netexec_mcp/dynamic.py +595 -0
- netexec_mcp-1.0.0/src/netexec_mcp/executor.py +388 -0
- netexec_mcp-1.0.0/src/netexec_mcp/guardrails.py +228 -0
- netexec_mcp-1.0.0/src/netexec_mcp/meta.py +212 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/__init__.py +39 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/ftp.py +133 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/ldap.py +910 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/mssql.py +619 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/nfs.py +174 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/rdp.py +251 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/smb.py +1687 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/ssh.py +209 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/vnc.py +96 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/winrm.py +329 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/wmi.py +259 -0
- netexec_mcp-1.0.0/src/netexec_mcp/modules/workspace.py +431 -0
- netexec_mcp-1.0.0/src/netexec_mcp/resources.py +319 -0
- netexec_mcp-1.0.0/src/netexec_mcp/results.py +829 -0
- netexec_mcp-1.0.0/src/netexec_mcp/server.py +242 -0
- netexec_mcp-1.0.0/uv.lock +1779 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Publishes netexec-mcp to PyPI when a GitHub Release is published.
|
|
4
|
+
# Uses PyPI Trusted Publishing (OIDC) — no API token is stored in the repo.
|
|
5
|
+
#
|
|
6
|
+
# One-time setup on PyPI (https://pypi.org/manage/account/publishing/):
|
|
7
|
+
# PyPI project name : netexec-mcp
|
|
8
|
+
# Owner : mpgn
|
|
9
|
+
# Repository : netexec-mcp
|
|
10
|
+
# Workflow filename : publish.yml
|
|
11
|
+
# Environment name : pypi
|
|
12
|
+
# For a brand-new project name, add it as a "pending publisher" before the first run.
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
release:
|
|
16
|
+
types: [published]
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build:
|
|
20
|
+
name: Build distribution
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
27
|
+
|
|
28
|
+
- name: Build sdist and wheel
|
|
29
|
+
run: uv build
|
|
30
|
+
|
|
31
|
+
- name: Check distribution metadata
|
|
32
|
+
run: uvx twine check dist/*
|
|
33
|
+
|
|
34
|
+
- name: Upload build artifacts
|
|
35
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
36
|
+
with:
|
|
37
|
+
name: dist
|
|
38
|
+
path: dist/
|
|
39
|
+
|
|
40
|
+
publish:
|
|
41
|
+
name: Publish to PyPI
|
|
42
|
+
needs: build
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
environment:
|
|
45
|
+
name: pypi
|
|
46
|
+
url: https://pypi.org/p/netexec-mcp
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write # required for Trusted Publishing (OIDC)
|
|
49
|
+
steps:
|
|
50
|
+
- name: Download build artifacts
|
|
51
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
52
|
+
with:
|
|
53
|
+
name: dist
|
|
54
|
+
path: dist/
|
|
55
|
+
|
|
56
|
+
- name: Publish to PyPI
|
|
57
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
|
|
7
|
+
# uv / venv
|
|
8
|
+
.venv/
|
|
9
|
+
|
|
10
|
+
# local secrets / audit artifacts
|
|
11
|
+
*.jsonl
|
|
12
|
+
.env
|
|
13
|
+
audit_out/ # parser_audit.py output: raw nxc stdout incl. real NTDS/LSA/gMSA secrets
|
|
14
|
+
tests/*
|
|
15
|
+
.mcp.json
|
|
16
|
+
PLAN.md
|
|
17
|
+
runtest.txt
|
|
18
|
+
audit_out/*
|
|
19
|
+
docs/*
|
|
20
|
+
scripts/*
|
|
21
|
+
.python-version
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"netexec": {
|
|
4
|
+
"command": "netexec-mcp", // with uv "command": "uv"
|
|
5
|
+
"args": [], // with uv ["run", "--directory", "/path/", "netexec-mcp"],
|
|
6
|
+
"env": {
|
|
7
|
+
"NXC_COMMAND": "netexec", // with uv "NXC_COMMAND": "uv run --directory /home/bonclay/NetExec netexec",
|
|
8
|
+
"NXC_PROTOCOLS": "smb,ldap,rdp,winrm,wmi,mssql",
|
|
9
|
+
"NXC_SCOPE": "10.0.0.0/24,192.168.56.0/24",
|
|
10
|
+
"NXC_MODE": "recon", // suggest, recon, loot, full
|
|
11
|
+
"NXC_TIMEOUT": "300",
|
|
12
|
+
"NXC_MAX_TARGETS": "256",
|
|
13
|
+
"NXC_AUDIT_LOG": "/home/bonclay/.netexec-mcp/audit.jsonl",
|
|
14
|
+
"NXC_WORKSPACE": "default",
|
|
15
|
+
"NXC_TOOL_MODE": "dynamic", // recommended
|
|
16
|
+
"NXC_PATH": "~/.nxc"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Copyright (c) 2026, Martial Puygrenier (mpgn)
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
8
|
+
list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
15
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
17
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
18
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
19
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
20
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
21
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
22
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
23
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: netexec-mcp
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: MCP server that drives NetExec (nxc) for authorized security testing.
|
|
5
|
+
Project-URL: Homepage, https://github.com/mpgn/netexec-mcp
|
|
6
|
+
Project-URL: Repository, https://github.com/mpgn/netexec-mcp
|
|
7
|
+
Project-URL: Issues, https://github.com/mpgn/netexec-mcp/issues
|
|
8
|
+
Author-email: Martial Puygrenier <mpgn@pm.me>
|
|
9
|
+
License-Expression: BSD-2-Clause
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: active-directory,ldap,mcp,netexec,nxc,pentest,security,smb
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Information Technology
|
|
14
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Security
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: fastmcp>=2.0
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# netexec-mcp
|
|
23
|
+
|
|
24
|
+
An [MCP](https://modelcontextprotocol.io) server that lets an AI agent drive
|
|
25
|
+
**[NetExec](https://github.com/Pennyw0rth/NetExec)** (`nxc`) for **authorized**
|
|
26
|
+
security testing only.
|
|
27
|
+
|
|
28
|
+
It is a **pure subprocess wrapper**: it shells out to the `nxc` CLI as an argv list
|
|
29
|
+
(never `shell=True`, never importing NetExec as a library) and declares **no** nxc
|
|
30
|
+
dependency. How nxc itself is installed (PATH / uv / pipx / docker) is entirely up to you.
|
|
31
|
+
|
|
32
|
+
<img width="1417" height="581" alt="image" src="https://github.com/user-attachments/assets/8c72cd0d-77bf-4ccf-a553-7c5ccce55513" />
|
|
33
|
+
<img width="1479" height="618" alt="image" src="https://github.com/user-attachments/assets/ae30ebcc-c613-47ec-a565-8a1247a67509" />
|
|
34
|
+
|
|
35
|
+
## Status
|
|
36
|
+
|
|
37
|
+
**v1 — all 10 nxc protocols, 131 tools** (117 protocol tools + 9 discovery/meta-tools +
|
|
38
|
+
5 offline workspace-DB readers). SMB, LDAP, WinRM, MSSQL, SSH, RDP, WMI, FTP, NFS, and VNC
|
|
39
|
+
are built out (native flags, credential dumping/gathering, command exec, file transfer, promoted
|
|
40
|
+
high-value `-M` modules, and structured output). The first seven are validated end-to-end against a
|
|
41
|
+
live AD lab; FTP/NFS/VNC are source-verified + unit-tested (no lab yet). A 3-level safety model gates
|
|
42
|
+
everything; the long tail of nxc `-M` modules is reachable via meta-tools.
|
|
43
|
+
|
|
44
|
+
## Built for small / local models
|
|
45
|
+
|
|
46
|
+
With 131 tools, listing every one to the model would spend ~46k tokens of context
|
|
47
|
+
before any work starts enough to overflow a small-context (≤8B / 32k) model and to
|
|
48
|
+
make every call on a large one needlessly expensive. So the default `NXC_TOOL_MODE`
|
|
49
|
+
is **`dynamic`**: only a handful of meta-tools are exposed (`nxc_catalog`,
|
|
50
|
+
`nxc_find_tool`, `nxc_describe_tool`, `nxc_call`, `nxc_health`), and the model
|
|
51
|
+
discovers and dispatches the real tools on demand. This keeps the tool surface out
|
|
52
|
+
of the context window required to run on small local models, and cheaper (same
|
|
53
|
+
quality) on large ones. Set `NXC_TOOL_MODE=static` to opt out and list every tool. See
|
|
54
|
+
the `NXC_TOOL_MODE` row under [Configuration](#configuration-env-vars) and the
|
|
55
|
+
step-budget note beneath it.
|
|
56
|
+
|
|
57
|
+
## Requirements
|
|
58
|
+
|
|
59
|
+
- Python ≥ 3.10, [`uv`](https://docs.astral.sh/uv/) or [`pipx`](https://pipx.pypa.io/)
|
|
60
|
+
- **NetExec installed separately** — it is not a pip dependency and is not on PyPI.
|
|
61
|
+
See the [official install guide](https://www.netexec.wiki/getting-started/installation).
|
|
62
|
+
Any install works as long as `nxc` / `netexec` is reachable on `PATH` or via
|
|
63
|
+
`NXC_COMMAND`; the server runs `nxc --version` on boot and refuses to start if it
|
|
64
|
+
can't find it.
|
|
65
|
+
|
|
66
|
+
## Quick start
|
|
67
|
+
|
|
68
|
+
### Option A: uv (run from a source checkout)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
uv sync
|
|
72
|
+
|
|
73
|
+
# Point at your nxc install (example: a uv-managed source checkout)
|
|
74
|
+
export NXC_COMMAND="uv run --directory ~/NetExec netexec"
|
|
75
|
+
export NXC_SCOPE="10.0.0.0/24" # required to target anything (fail-closed)
|
|
76
|
+
|
|
77
|
+
uv run netexec-mcp
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
MCP client config (`command`/`args` launch the server itself):
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"command": "uv",
|
|
85
|
+
"args": ["run", "--directory", "/path/to/netexec-mcp", "netexec-mcp"]
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Option B: install from PyPI (standalone `netexec-mcp` CLI)
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Pick one installer — all put `netexec-mcp` on PATH:
|
|
93
|
+
pipx install netexec-mcp # pipx
|
|
94
|
+
uv tool install netexec-mcp # uv
|
|
95
|
+
# or run without installing:
|
|
96
|
+
uvx netexec-mcp # uv, one-shot
|
|
97
|
+
|
|
98
|
+
export NXC_COMMAND="nxc" # or however your nxc install is invoked
|
|
99
|
+
export NXC_SCOPE="10.0.0.0/24"
|
|
100
|
+
|
|
101
|
+
netexec-mcp
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
MCP client config — `netexec-mcp` is on `PATH`, so no `uv`/`--directory` wrapper:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"command": "netexec-mcp",
|
|
109
|
+
"args": []
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
On boot the server runs `<base> --version` and **refuses to start** if it fails
|
|
114
|
+
(unless mode `suggest`, which downgrades that to a warning). See `.mcp.json.example`
|
|
115
|
+
for a ready-to-edit MCP client config (uv variant).
|
|
116
|
+
|
|
117
|
+
## Operating modes (`NXC_MODE`)
|
|
118
|
+
|
|
119
|
+
Four escalating levels of how much the server is allowed to *do*:
|
|
120
|
+
|
|
121
|
+
| Mode | What runs | Use when |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| `suggest` | **Nothing executes.** Tools return the resolved nxc command for a human (the auditor) to run. Scope still enforced; offensive commands *can* be previewed. | You want the agent to plan commands you run yourself. |
|
|
124
|
+
| `recon` _(default)_ | Read-only enumeration; executes, credential-dumping and state-changing actions are refused. | Day-to-day authorized recon. |
|
|
125
|
+
| `loot` | Recon **plus** read-only credential-dumping (sam/lsa/ntds/gpp/roasting) — no state change on the target, but it harvests credential material. | Authorized credential-harvesting. |
|
|
126
|
+
| `full` | Everything executes, including state-changing / privilege-escalation actions (exec, write, spray, coercion-with-listener, exploits). | Authorized active testing. |
|
|
127
|
+
|
|
128
|
+
`NXC_MODE` is the canonical control; it defaults to `recon` when unset.
|
|
129
|
+
|
|
130
|
+
## Configuration (env vars)
|
|
131
|
+
|
|
132
|
+
| Var | Purpose | Default |
|
|
133
|
+
| --- | --- | --- |
|
|
134
|
+
| `NXC_COMMAND` | Base command (shlex-parsed). Falls back to `nxc`/`netexec` on `PATH`. | autodetect |
|
|
135
|
+
| `NXC_PROTOCOLS` | Comma-separated protocols to enable (e.g. `smb,ldap`). | all implemented |
|
|
136
|
+
| `NXC_TOOL_MODE` | Tool-surface presentation — `dynamic` (**default**: expose only meta-tools; the ~100-tool surface is discovered via `nxc_find_tool`/`nxc_catalog` and run via `nxc_call`) or `static` (list every tool). Dynamic keeps the ~46k-token surface out of the context window — required for small-context models, ~8× cheaper (same quality) on large ones. Set `static` to opt out (best for a decisive model, or to avoid discovery round-trips on a big-context model). | `dynamic` |
|
|
137
|
+
| `NXC_SCOPE` | Comma-separated target allowlist (IP/CIDR/range/hostname). **Fail-closed**: targets with no scope are rejected. | _(none)_ |
|
|
138
|
+
| `NXC_MODE` | Operating level — `suggest` / `recon` / `loot` / `full`. | `recon` |
|
|
139
|
+
| `NXC_TIMEOUT` | Per-call timeout (seconds). | `300` |
|
|
140
|
+
| `NXC_MAX_TARGETS` | Max target tokens per call. | `256` |
|
|
141
|
+
| `NXC_AUDIT_LOG` | Path to an append-only JSONL audit log. | _(none)_ |
|
|
142
|
+
| `NXC_WORKSPACE` | nxc workspace to read for richer results. | _(none — reads nxc.conf's `workspace`, else `default`)_ |
|
|
143
|
+
| `NXC_PATH` | Override nxc's home dir (mirrors nxc's own `NXC_PATH`); where `nxc.conf` and `workspaces/` are read from. | `~/.nxc` |
|
|
144
|
+
|
|
145
|
+
> **Dynamic mode needs a bigger client-side step budget.** `max_steps` (how many tool
|
|
146
|
+
> calls the agent may make) is **not** an MCP setting — the server can't see or set it.
|
|
147
|
+
> It lives in your client's agent loop. In `dynamic` mode each action costs ~2 calls
|
|
148
|
+
> (`nxc_find_tool` → `nxc_call`), so a chain that needs 4 calls in `full` needs ~10 in
|
|
149
|
+
> `dynamic`. Set the budget high enough there, e.g. mcp-use:
|
|
150
|
+
> ```python
|
|
151
|
+
> MCPAgent(llm=ChatOllama(model="qwen3:14b"), client=client, max_steps=30) # default is 5 — too low
|
|
152
|
+
> ```
|
|
153
|
+
> Other clients (Claude Desktop, Cursor, Cline, …) have their own max-iterations
|
|
154
|
+
> setting. The MCP can only *hint* this via its startup `instructions`; it can't enforce it.
|
|
155
|
+
|
|
156
|
+
## Resources
|
|
157
|
+
|
|
158
|
+
The server publishes cheatsheets as MCP resources so an agent can ground itself:
|
|
159
|
+
|
|
160
|
+
- `netexec://guide/operating-modes`
|
|
161
|
+
- `netexec://guide/auth`
|
|
162
|
+
- `netexec://guide/workflows` (incl. the cross-protocol gMSA chain)
|
|
163
|
+
- `netexec://guide/workspace` (cached recall vs. live — when to use which)
|
|
164
|
+
- `netexec://guide/safety`
|
|
165
|
+
- `netexec://catalog/tools` (live, auto-generated tool inventory)
|
|
166
|
+
- `netexec://workspace/credentials`, `/admins`, `/loggedin`, `/hosts` (workspace DB data,
|
|
167
|
+
same source as the `workspace_*` tools, filterless)
|
|
168
|
+
|
|
169
|
+
## Safety & guardrails
|
|
170
|
+
|
|
171
|
+
Enforced at a single choke point before any command runs:
|
|
172
|
+
|
|
173
|
+
- **Scope allowlist** (`NXC_SCOPE`) — every target checked; **fail-closed**.
|
|
174
|
+
- **Mode gating** (`NXC_MODE`) — offensive actions refused outside `full`.
|
|
175
|
+
- **Target cap** + per-call **timeout**.
|
|
176
|
+
- **Audit log** — every command (executed / dry-run / rejected) appended as JSON.
|
|
177
|
+
- **No shell** — nxc is invoked as an argv list; nothing is shell-interpreted.
|
|
178
|
+
|
|
179
|
+
## Example: the gMSA credential chain
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
smb_lsa (full) -> secrets[] incl. { type: "gmsa_id", gmsa_id, ntlm }
|
|
183
|
+
ldap_gmsa_convert_id -> resolves the gmsa_id to an account name (gmsa-robin$)
|
|
184
|
+
<any tool> -> replay with username="gmsa-robin$", ntlm_hash=<nt>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Development
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
uv run pytest -q # mocked-subprocess unit tests (no nxc/network needed)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Architecture and milestone history live in `PLAN.md`. Tools/flags are verified against
|
|
194
|
+
the nxc source (`nxc/protocols/<proto>/proto_args.py`) — `--help` lists args the
|
|
195
|
+
handlers reject.
|
|
196
|
+
|
|
197
|
+
### nxc version
|
|
198
|
+
|
|
199
|
+
This MCP is a subprocess wrapper and pins no nxc dependency, but its tools/flags are
|
|
200
|
+
verified against a specific nxc build:
|
|
201
|
+
|
|
202
|
+
> **nxc 1.5.1 "Yippie-Ki-Yay", commit `738b842a`** (`738b842a…`, 2026-07-31, build 595)
|
|
203
|
+
|
|
204
|
+
Check your local build with `nxc --version` (it prints `version - codename - commit - build`).
|
|
205
|
+
nxc moves fast and occasionally moves/removes flags, so when you bump nxc, re-verify the
|
|
206
|
+
affected protocol's `proto_args.py` + handler and re-run the tests.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# netexec-mcp
|
|
2
|
+
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io) server that lets an AI agent drive
|
|
4
|
+
**[NetExec](https://github.com/Pennyw0rth/NetExec)** (`nxc`) for **authorized**
|
|
5
|
+
security testing only.
|
|
6
|
+
|
|
7
|
+
It is a **pure subprocess wrapper**: it shells out to the `nxc` CLI as an argv list
|
|
8
|
+
(never `shell=True`, never importing NetExec as a library) and declares **no** nxc
|
|
9
|
+
dependency. How nxc itself is installed (PATH / uv / pipx / docker) is entirely up to you.
|
|
10
|
+
|
|
11
|
+
<img width="1417" height="581" alt="image" src="https://github.com/user-attachments/assets/8c72cd0d-77bf-4ccf-a553-7c5ccce55513" />
|
|
12
|
+
<img width="1479" height="618" alt="image" src="https://github.com/user-attachments/assets/ae30ebcc-c613-47ec-a565-8a1247a67509" />
|
|
13
|
+
|
|
14
|
+
## Status
|
|
15
|
+
|
|
16
|
+
**v1 — all 10 nxc protocols, 131 tools** (117 protocol tools + 9 discovery/meta-tools +
|
|
17
|
+
5 offline workspace-DB readers). SMB, LDAP, WinRM, MSSQL, SSH, RDP, WMI, FTP, NFS, and VNC
|
|
18
|
+
are built out (native flags, credential dumping/gathering, command exec, file transfer, promoted
|
|
19
|
+
high-value `-M` modules, and structured output). The first seven are validated end-to-end against a
|
|
20
|
+
live AD lab; FTP/NFS/VNC are source-verified + unit-tested (no lab yet). A 3-level safety model gates
|
|
21
|
+
everything; the long tail of nxc `-M` modules is reachable via meta-tools.
|
|
22
|
+
|
|
23
|
+
## Built for small / local models
|
|
24
|
+
|
|
25
|
+
With 131 tools, listing every one to the model would spend ~46k tokens of context
|
|
26
|
+
before any work starts enough to overflow a small-context (≤8B / 32k) model and to
|
|
27
|
+
make every call on a large one needlessly expensive. So the default `NXC_TOOL_MODE`
|
|
28
|
+
is **`dynamic`**: only a handful of meta-tools are exposed (`nxc_catalog`,
|
|
29
|
+
`nxc_find_tool`, `nxc_describe_tool`, `nxc_call`, `nxc_health`), and the model
|
|
30
|
+
discovers and dispatches the real tools on demand. This keeps the tool surface out
|
|
31
|
+
of the context window required to run on small local models, and cheaper (same
|
|
32
|
+
quality) on large ones. Set `NXC_TOOL_MODE=static` to opt out and list every tool. See
|
|
33
|
+
the `NXC_TOOL_MODE` row under [Configuration](#configuration-env-vars) and the
|
|
34
|
+
step-budget note beneath it.
|
|
35
|
+
|
|
36
|
+
## Requirements
|
|
37
|
+
|
|
38
|
+
- Python ≥ 3.10, [`uv`](https://docs.astral.sh/uv/) or [`pipx`](https://pipx.pypa.io/)
|
|
39
|
+
- **NetExec installed separately** — it is not a pip dependency and is not on PyPI.
|
|
40
|
+
See the [official install guide](https://www.netexec.wiki/getting-started/installation).
|
|
41
|
+
Any install works as long as `nxc` / `netexec` is reachable on `PATH` or via
|
|
42
|
+
`NXC_COMMAND`; the server runs `nxc --version` on boot and refuses to start if it
|
|
43
|
+
can't find it.
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
### Option A: uv (run from a source checkout)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
uv sync
|
|
51
|
+
|
|
52
|
+
# Point at your nxc install (example: a uv-managed source checkout)
|
|
53
|
+
export NXC_COMMAND="uv run --directory ~/NetExec netexec"
|
|
54
|
+
export NXC_SCOPE="10.0.0.0/24" # required to target anything (fail-closed)
|
|
55
|
+
|
|
56
|
+
uv run netexec-mcp
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
MCP client config (`command`/`args` launch the server itself):
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"command": "uv",
|
|
64
|
+
"args": ["run", "--directory", "/path/to/netexec-mcp", "netexec-mcp"]
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Option B: install from PyPI (standalone `netexec-mcp` CLI)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Pick one installer — all put `netexec-mcp` on PATH:
|
|
72
|
+
pipx install netexec-mcp # pipx
|
|
73
|
+
uv tool install netexec-mcp # uv
|
|
74
|
+
# or run without installing:
|
|
75
|
+
uvx netexec-mcp # uv, one-shot
|
|
76
|
+
|
|
77
|
+
export NXC_COMMAND="nxc" # or however your nxc install is invoked
|
|
78
|
+
export NXC_SCOPE="10.0.0.0/24"
|
|
79
|
+
|
|
80
|
+
netexec-mcp
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
MCP client config — `netexec-mcp` is on `PATH`, so no `uv`/`--directory` wrapper:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"command": "netexec-mcp",
|
|
88
|
+
"args": []
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
On boot the server runs `<base> --version` and **refuses to start** if it fails
|
|
93
|
+
(unless mode `suggest`, which downgrades that to a warning). See `.mcp.json.example`
|
|
94
|
+
for a ready-to-edit MCP client config (uv variant).
|
|
95
|
+
|
|
96
|
+
## Operating modes (`NXC_MODE`)
|
|
97
|
+
|
|
98
|
+
Four escalating levels of how much the server is allowed to *do*:
|
|
99
|
+
|
|
100
|
+
| Mode | What runs | Use when |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| `suggest` | **Nothing executes.** Tools return the resolved nxc command for a human (the auditor) to run. Scope still enforced; offensive commands *can* be previewed. | You want the agent to plan commands you run yourself. |
|
|
103
|
+
| `recon` _(default)_ | Read-only enumeration; executes, credential-dumping and state-changing actions are refused. | Day-to-day authorized recon. |
|
|
104
|
+
| `loot` | Recon **plus** read-only credential-dumping (sam/lsa/ntds/gpp/roasting) — no state change on the target, but it harvests credential material. | Authorized credential-harvesting. |
|
|
105
|
+
| `full` | Everything executes, including state-changing / privilege-escalation actions (exec, write, spray, coercion-with-listener, exploits). | Authorized active testing. |
|
|
106
|
+
|
|
107
|
+
`NXC_MODE` is the canonical control; it defaults to `recon` when unset.
|
|
108
|
+
|
|
109
|
+
## Configuration (env vars)
|
|
110
|
+
|
|
111
|
+
| Var | Purpose | Default |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| `NXC_COMMAND` | Base command (shlex-parsed). Falls back to `nxc`/`netexec` on `PATH`. | autodetect |
|
|
114
|
+
| `NXC_PROTOCOLS` | Comma-separated protocols to enable (e.g. `smb,ldap`). | all implemented |
|
|
115
|
+
| `NXC_TOOL_MODE` | Tool-surface presentation — `dynamic` (**default**: expose only meta-tools; the ~100-tool surface is discovered via `nxc_find_tool`/`nxc_catalog` and run via `nxc_call`) or `static` (list every tool). Dynamic keeps the ~46k-token surface out of the context window — required for small-context models, ~8× cheaper (same quality) on large ones. Set `static` to opt out (best for a decisive model, or to avoid discovery round-trips on a big-context model). | `dynamic` |
|
|
116
|
+
| `NXC_SCOPE` | Comma-separated target allowlist (IP/CIDR/range/hostname). **Fail-closed**: targets with no scope are rejected. | _(none)_ |
|
|
117
|
+
| `NXC_MODE` | Operating level — `suggest` / `recon` / `loot` / `full`. | `recon` |
|
|
118
|
+
| `NXC_TIMEOUT` | Per-call timeout (seconds). | `300` |
|
|
119
|
+
| `NXC_MAX_TARGETS` | Max target tokens per call. | `256` |
|
|
120
|
+
| `NXC_AUDIT_LOG` | Path to an append-only JSONL audit log. | _(none)_ |
|
|
121
|
+
| `NXC_WORKSPACE` | nxc workspace to read for richer results. | _(none — reads nxc.conf's `workspace`, else `default`)_ |
|
|
122
|
+
| `NXC_PATH` | Override nxc's home dir (mirrors nxc's own `NXC_PATH`); where `nxc.conf` and `workspaces/` are read from. | `~/.nxc` |
|
|
123
|
+
|
|
124
|
+
> **Dynamic mode needs a bigger client-side step budget.** `max_steps` (how many tool
|
|
125
|
+
> calls the agent may make) is **not** an MCP setting — the server can't see or set it.
|
|
126
|
+
> It lives in your client's agent loop. In `dynamic` mode each action costs ~2 calls
|
|
127
|
+
> (`nxc_find_tool` → `nxc_call`), so a chain that needs 4 calls in `full` needs ~10 in
|
|
128
|
+
> `dynamic`. Set the budget high enough there, e.g. mcp-use:
|
|
129
|
+
> ```python
|
|
130
|
+
> MCPAgent(llm=ChatOllama(model="qwen3:14b"), client=client, max_steps=30) # default is 5 — too low
|
|
131
|
+
> ```
|
|
132
|
+
> Other clients (Claude Desktop, Cursor, Cline, …) have their own max-iterations
|
|
133
|
+
> setting. The MCP can only *hint* this via its startup `instructions`; it can't enforce it.
|
|
134
|
+
|
|
135
|
+
## Resources
|
|
136
|
+
|
|
137
|
+
The server publishes cheatsheets as MCP resources so an agent can ground itself:
|
|
138
|
+
|
|
139
|
+
- `netexec://guide/operating-modes`
|
|
140
|
+
- `netexec://guide/auth`
|
|
141
|
+
- `netexec://guide/workflows` (incl. the cross-protocol gMSA chain)
|
|
142
|
+
- `netexec://guide/workspace` (cached recall vs. live — when to use which)
|
|
143
|
+
- `netexec://guide/safety`
|
|
144
|
+
- `netexec://catalog/tools` (live, auto-generated tool inventory)
|
|
145
|
+
- `netexec://workspace/credentials`, `/admins`, `/loggedin`, `/hosts` (workspace DB data,
|
|
146
|
+
same source as the `workspace_*` tools, filterless)
|
|
147
|
+
|
|
148
|
+
## Safety & guardrails
|
|
149
|
+
|
|
150
|
+
Enforced at a single choke point before any command runs:
|
|
151
|
+
|
|
152
|
+
- **Scope allowlist** (`NXC_SCOPE`) — every target checked; **fail-closed**.
|
|
153
|
+
- **Mode gating** (`NXC_MODE`) — offensive actions refused outside `full`.
|
|
154
|
+
- **Target cap** + per-call **timeout**.
|
|
155
|
+
- **Audit log** — every command (executed / dry-run / rejected) appended as JSON.
|
|
156
|
+
- **No shell** — nxc is invoked as an argv list; nothing is shell-interpreted.
|
|
157
|
+
|
|
158
|
+
## Example: the gMSA credential chain
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
smb_lsa (full) -> secrets[] incl. { type: "gmsa_id", gmsa_id, ntlm }
|
|
162
|
+
ldap_gmsa_convert_id -> resolves the gmsa_id to an account name (gmsa-robin$)
|
|
163
|
+
<any tool> -> replay with username="gmsa-robin$", ntlm_hash=<nt>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
uv run pytest -q # mocked-subprocess unit tests (no nxc/network needed)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Architecture and milestone history live in `PLAN.md`. Tools/flags are verified against
|
|
173
|
+
the nxc source (`nxc/protocols/<proto>/proto_args.py`) — `--help` lists args the
|
|
174
|
+
handlers reject.
|
|
175
|
+
|
|
176
|
+
### nxc version
|
|
177
|
+
|
|
178
|
+
This MCP is a subprocess wrapper and pins no nxc dependency, but its tools/flags are
|
|
179
|
+
verified against a specific nxc build:
|
|
180
|
+
|
|
181
|
+
> **nxc 1.5.1 "Yippie-Ki-Yay", commit `738b842a`** (`738b842a…`, 2026-07-31, build 595)
|
|
182
|
+
|
|
183
|
+
Check your local build with `nxc --version` (it prints `version - codename - commit - build`).
|
|
184
|
+
nxc moves fast and occasionally moves/removes flags, so when you bump nxc, re-verify the
|
|
185
|
+
affected protocol's `proto_args.py` + handler and re-run the tests.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "netexec-mcp"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "MCP server that drives NetExec (nxc) for authorized security testing."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = "BSD-2-Clause"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Martial Puygrenier", email = "mpgn@pm.me" },
|
|
11
|
+
]
|
|
12
|
+
keywords = ["mcp", "netexec", "nxc", "pentest", "security", "active-directory", "smb", "ldap"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Information Technology",
|
|
16
|
+
"License :: OSI Approved :: BSD License",
|
|
17
|
+
"Topic :: Security",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"fastmcp>=2.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/mpgn/netexec-mcp"
|
|
27
|
+
Repository = "https://github.com/mpgn/netexec-mcp"
|
|
28
|
+
Issues = "https://github.com/mpgn/netexec-mcp/issues"
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
netexec-mcp = "netexec_mcp.server:main"
|
|
32
|
+
|
|
33
|
+
[build-system]
|
|
34
|
+
requires = ["hatchling"]
|
|
35
|
+
build-backend = "hatchling.build"
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.wheel]
|
|
38
|
+
packages = ["src/netexec_mcp"]
|
|
39
|
+
|
|
40
|
+
[dependency-groups]
|
|
41
|
+
dev = [
|
|
42
|
+
"pytest>=8.0",
|
|
43
|
+
]
|