ntobjmanager-mcp: a stateful MCP server for Windows RPC research
A Model Context Protocol (MCP) server that gives an AI agent live, stateful access to Windows RPC attack-surface research, built on James Forshaw's NtObjectManager (NtCoreLib).
Why it exists
Most "let an LLM drive PowerShell" setups are stateless: every tool call spawns a fresh shell, so the RpcServer object you just parsed, the client you just connected, and any session variables are gone when the call returns. That breaks the workflows RPC research depends on: auth handshakes, context-handle chains, anything where step two needs the object from step one.
ntobjmanager-mcp keeps a single PowerShell engine alive for the whole session and exposes it as 22 fixed tools.
Two things a generic PowerShell MCP cannot do
1. Stateful RPC connections. A persistent powershell.exe keeps parsed RpcServer objects and connected RPC clients alive across tool calls: rpc_connect once, rpc_call many times. Auth handshakes, context-handle chains, and session variables survive.
2. CVE methodology as fixed tools. The standard hunting workflows from 2024-2026 public research ship as one-click tools instead of prompt-engineering.
Architecture
AI Agent (Claude Code / OpenCode / any MCP client)
| MCP (stdio, 22 tools)
v
server.py -- snippets.py (PS templates, @@TOKEN@@ + ps_str escape)
|
v
ps_engine.py -- persistent powershell.exe (base64 + __MCP_DONE__)
| state: $RPCMCP = @{ Servers; Clients; vars }
v
NtObjectManager / NtCoreLib --> RPC runtime (ALPC / pipe / TCP)
Tool matrix (22)
Core stateful pipeline
| Tool | Purpose |
|---|---|
rpc_parse(file, symbol_path?) |
Parse a PE for RPC servers, cache (keys file_N) |
rpc_state() |
Cached servers + live sessions |
rpc_get_interface(key) |
Procedures, NDR params, context handles, strictness |
rpc_query_endpoints(ifid?, search_binding?, find_alpc_port?) |
Endpoint-mapper query (local or remote) |
rpc_running_servers(pid?/service?) |
Live process/service enumeration |
rpc_connect(session, key, binding?, auth?) |
Generate + connect a client (stateful) |
rpc_methods(session) |
Signatures with opnum mapping |
rpc_call(session, method, args_json, store_as?) |
Reflection invoke; {"__var__"} passes stored objects |
rpc_disconnect(session) |
Drop session |
2024-2026 CVE methodology tools
| Tool | Methodology source |
|---|---|
rpc_scan_context_handles(paths) |
Context-handle type confusion - CVE-2025-48815 pattern (whereisk0shl 2026) |
rpc_inventory(paths?, limit?) |
Attack-surface inventory + EPM cross-check - MS-RPC-Fuzzer phase 1 (CVE-2025-26651) |
rpc_fuzz(session, dry_run=True) |
Primitive-only default-value fuzzing with ok/denied/error classification - dry-run by default |
rpc_find_hijackable() |
Unregistered interfaces of stopped services - EPM poisoning / RPC-Racer (CVE-2025-49760 / 59200 / 59230) |
rpc_etw_unreachable(duration, trigger_script?) |
Clients calling dead servers - PhantomRPC (Kaspersky 2026), admin required |
rpc_interface_security(key) |
ALPC security descriptor / anonymous-ACE audit - MS-NRPC null session (SafeBreach / Securelist 2025) |
rpc_decode_flags(flags) |
RpcServerRegisterIf3 flag bitmask decoding |
rpc_format_client(key) |
Export the generated C# client source (offline grep workflow) |
rpc_new_struct(session, type, store_as) |
Build NDR complex types as session vars |
rpc_alpc_squat(name, duration) |
ALPC port squat + connection capture (race validation primitive) |
rpc_accessible_tasks() |
User-startable tasks (Dark-Elevator chain material, CVE-2026-66804 pattern) |
rpc_vars / rpc_clear_cache
|
Session-variable and cache management (eviction cap 150) |
Every tool call is appended to output/mcp_audit.log, so at the end you have a full trace of what the agent actually did.
Quick start
# 1) Prerequisites (one-time)
Install-Module NtObjectManager -Scope CurrentUser -Force
pip install -r requirements.txt # mcp>=1.2.0 (1.x / 2.x compatible)
# 2) Verify - three suites
python tests\smoke_test.py # 17 checks (live MCP stdio round-trip)
python tests\var_test.py # 10 checks (store_as / __var__ mechanics)
python tests\audit.py # 43 checks (edge cases, hostile paths, concurrency)
# 3) Run the server
python server.py # stdio MCP
Add it to an MCP client
Claude Code:
claude mcp add ntobjectmanager-rpc -- python C:\path\to\ntobjmanager-mcp\server.py
Any MCP client (OpenCode opencode.json):
{
"mcp": {
"ntobjectmanager-rpc": {
"type": "local",
"command": ["python", "C:\\path\\to\\ntobjmanager-mcp\\server.py"],
"enabled": true
}
}
}
How it works
The agent speaks MCP over stdio to server.py, which compiles PowerShell snippets and hands them to a persistent powershell.exe (base64-encoded, with a completion sentinel). All state lives in one PowerShell variable, $RPCMCP = @{ Servers; Clients; vars }. NtObjectManager and NtCoreLib do the actual RPC work underneath, over ALPC, named pipe, or TCP.
Repo and docs: https://github.com/lupingQAQ/ntobjmanager-mcp
Top comments (0)