ntobjmanager-mcp:把 Windows RPC 攻击面研究接进 AI agent
原创 侠盗鲁平 2026-09-30 08:00 日本

GitHub: https://github.com/lupingQAQ/ntobjmanager-mcp
WeChat: IndexSec
获取
git clone https://github.com/lupingQAQ/ntobjmanager-mcp.git
# 依赖装一次
Install-Module NtObjectManager -Scope CurrentUser -Force
pip install -r requirements.txt # mcp>=1.2.0
# 自检:三套测试,全绿再接客户端
python tests\smoke_test.py # 17 项,MCP stdio 全链路
python tests\var_test.py # 10 项,store_as / __var__ 机制
python tests\audit.py # 43 项,恶意输入、并发、引擎杀死重启
# 起服务
python server.py # stdio MCPClaude Code 一行接入:
claude mcp add ntobjectmanager-rpc -- python C:\path\to\ntobjmanager-mcp\server.pyOpenCode / 其它 MCP 客户端在配置里把 command 指到 python server.py 即可。
0x00 这是什么
一个 MCP server,22 个工具,底层是 James Forshaw 的 NtObjectManager(NtCoreLib)。AI agent 通过它拿到的是活的 Windows RPC 攻击面研究能力:解析 DLL 里的 RPC 服务器、查端点映射、生成客户端、真连上去调方法。
做两件事:
有状态的 RPC 连接。常驻 PowerShell 引擎把解析出的
RpcServer对象、连上的客户端、调用产生的返回值全部留在进程里——rpc_connect连一次,rpc_call调多次,认证握手、上下文把手、会话变量跨工具调用不丢。把固定流程做成固定工具。解析、连端点、找把手、查劫持面这类每次研究都要走的步骤,是一个工具调用,不用靠提示词现场拼 PowerShell。
每次工具调用都追加写入 output/mcp_audit.log,事后可审计。
0x01 一次工具调用经过什么
AI agent(Claude Code / OpenCode / 任意 MCP 客户端)
└─ MCP stdio(server.py,22 个工具)
└─ snippets.py(PowerShell 模板:@@TOKEN@@ 渲染 + ps_str 转义)
└─ ps_engine.py(常驻 powershell.exe:base64 命令 + __MCP_DONE__ 完成标记 + 超时)
└─ wrapper.ps1(状态全部放在 $RPCMCP = @{ Servers; Clients; vars })
└─ NtObjectManager / NtCoreLib → RPC 运行时(ALPC / 命名管道 / TCP)引擎协议很简单:Python 侧把 PowerShell 命令 base64 编码后写进常驻进程的标准输入,等 __MCP_DONE__ 标记回来说明执行完,超时可杀可重启。状态模型三样东西——解析过的服务器、连着的客户端、store_as 存下来的对象——全挂在 $RPCMCP 上,进程活着就在。
0x02 工具清单
核心有状态管线(9 个):
工具 | 作用 |
|---|---|
rpc_parse | 解析 PE 里的 RPC 服务器,缓存(键 |
rpc_state | 列缓存的服务器 + 活着的会话 |
rpc_get_interface | 过程、NDR 参数、上下文把手、strict 标记 |
rpc_query_endpoints | 查端点映射,本地或远程( |
rpc_running_servers | 按进程 / 服务枚举在跑的 RPC 服务器 |
rpc_connect | 生成 C# 客户端并真连上(可走 EPM 自动发现绑定) |
rpc_methods | 方法签名带 opnum 映射 |
rpc_call | 反射调用; |
rpc_disconnect | 断开会话 |
方法论工具(13 个):
工具 | 干什么 |
|---|---|
rpc_scan_context_handles | 上下文把手扫描,标出生产者([out] ctx)/ 消费者([in] ctx)对——CVE-2025-48815 同型面 |
rpc_inventory | 攻击面清单 + 与端点映射交叉核对 |
rpc_fuzz | 原语参数默认值试探,按 ok / 拒绝 / 报错分类;默认 dry-run |
rpc_find_hijackable | 找已停服务未注册的接口(EPM 投毒 / 抢注面) |
rpc_etw_unreachable | ETW 抓"客户端在调不存在的服务"(要管理员) |
rpc_interface_security | 查 ALPC 端口安全描述符、匿名 ACE |
rpc_decode_flags | RpcServerRegisterIf3注册标志位解码 |
rpc_format_client | 导出生成的 C# 客户端源码,离线 grep 用 |
rpc_new_struct | 构造 NDR 复杂类型存成会话变量 |
rpc_alpc_squat | ALPC 端口抢占 + 连接捕获(验证竞态用) |
rpc_accessible_tasks | 列当前用户可启动的 SYSTEM 计划任务 |
rpc_vars / | 会话变量与缓存管理(上限 150) |
0x03 核心原语:返回值跨调用直传
rpc_call 带 store_as="h" 会把返回对象原样存进会话;下一次调用用 {"__var__":"h"} 直接引用。RPC 返回的对象不做序列化往返——上下文把手的生产者→消费者链测试正需要这个:拿到 A 类型的裸句柄,原样喂给收 B 类型把手的函数。
一套 CVE-2025-48815 同型的完整流程:
1. rpc_parse C:\Windows\System32\target.dll
2. rpc_scan_context_handles ["C:\\Windows\\System32\\target.dll"]
3. rpc_get_interface target_0 → 看 [out]/[in] 把手配对
4. rpc_connect s1 target_0 → EPM 自动发现绑定并连上
5. rpc_methods s1 → 带 opnum 的签名
6. rpc_call s1 <开把手方法> store_as="h"
7. rpc_call s1 <关把手方法> [{"__var__":"h"}]第 6 步拿到的句柄对象,第 7 步原样传进去,中间没有任何转换。
0x04 能力边界
说法 | 实际情况 |
|---|---|
跨工具调用的有状态客户端 | 是,常驻引擎 + |
生产者→消费者把手链 | 是, |
自动确认类型混淆 | 否 ——NDR 证明不了把手的多个类型,要靠逆向验证(见 XactSrv 那条) |
完整恶意 RPC 服务端托管 | 否 ——NtObjectManager 2.0.1 没有服务端构建器, |
ETW 追踪 / ALPC SDDL 读取 | 要管理员权限 |
符号解析的函数名 | 看环境(symsrv 链),不行就落到启发式命名 |
0x06 免责声明
仅用于合法的安全研究、教学与获得明确授权的渗透测试。rpc_call 会真实调用 RPC 方法、可能打崩服务——只在隔离虚拟机上跑,不要对生产机或日常主机使用。
本文转载自微信公众号「Security丨Art」,查看原文。版权归原作者所有,如有侵权请联系删除。