Skip to content
Conduit

mcp server

secagent

Autonomous security assessment agent built on MCP — explicit planning, parallel tool calls, tiered memory, MITRE ATT&CK mapping.

RicardoMLu777/secagentcommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 26, 2026

SecAgent 🔐

基于 MCP (Model Context Protocol) 的自主安全评估 Agent —— 把真实的 Kali 安全工具封装成 MCP Server,让 LLM 显式规划、并/串行调用工具、分层记忆发现、映射 MITRE ATT&CK、自动生成报告。

安全优先:侵入式操作默认需要人工批准,工具调用全程审计,支持目标白名单与私网访问管控。

Python 3.11+ MCP tests License: MIT changelog

[!WARNING] 仅限授权测试。 SecAgent 会执行真实的端口扫描、目录爆破与 SQL 注入检测。请只在自有资产、持有书面授权的目标或本地靶场中使用。未授权扫描在多数司法辖区属于违法行为。详见文末 Disclaimer。


效果演示

给一个目标,Agent 自己规划、自己决定每一步做什么:

$ python secagent.py --target 192.168.1.10 --report out/report.md --policy allow

这里用 --policy allow 是因为演示环境是明确授权的本地靶场。默认模式是 ask,会对每一步侵入式操作弹出人工确认——见下方安全模型。

╭────────────────────────────── plan ──────────────────────────────╮
│ Plan for 192.168.1.10:                                           │
│   ○ 1. Probe the target to confirm it is live (http_probe)        │
│   ○ 2. Inspect HTTP security headers (check_security_headers)     │
│   ○ 3. Review robots.txt for hidden paths (check_robots)          │
│   ○ 4. Enumerate sensitive directories (ffuf_dirs) [intrusive]    │
│   ○ 5. Port/service scan (nmap_scan) [intrusive]                  │
│   ○ 6. Consolidate into a risk assessment                         │
╰──────────────────────────────────────────────────────────────────╯

step 1 · reasoning
  → nmap_server__nmap_scan {"target": "192.168.1.10"}
     ✓ 22/tcp open ssh OpenSSH 8.2p1 / 80/tcp open http nginx 1.18.0

step 2 · reasoning
  running 3 tool calls in parallel          ← 并行执行
  → httpx_server__http_probe {"url": "http://192.168.1.10"}
  → httpx_server__check_security_headers {"url": "http://192.168.1.10"}
  → httpx_server__check_robots {"url": "http://192.168.1.10"}
     ✓ status 200, server nginx/1.18.0
     ✓ missing: content-security-policy, x-frame-options (4/6)
     ✓ interesting: /admin, /backup

step 3 · reasoning
  → ffuf_server__ffuf_dirs {"url": "http://192.168.1.10/FUZZ"}
     ✓ 7 hits: /admin (301), /backup.zip (200), ...

ATT&CK: T1046, T1592, T1083
report written: out/report.md

生成的报告包含 执行计划 / ATT&CK 映射 / 配置弱点 / Agent 结论:

# SecAgent Assessment Report

**Steps used:** 5   **Tool calls:** 11

## Summary
- Open ports: 22, 80
- Services: ssh, nginx 1.18.0
- Missing security headers: content-security-policy, x-frame-options

## Plan execution
| # | Task | Status | Intrusive |
|---|------|--------|-----------|
| 1 | Probe the target | done | no |
...

## MITRE ATT&CK techniques observed
| ID | Technique | Tactic |
|----|-----------|--------|
| T1046 | Network Service Discovery | Discovery |
| T1592 | Gather Victim Host Information | Reconnaissance |
| T1083 | File and Directory Discovery | Discovery |

## Configuration weaknesses
- **content-security-policy** — No CSP: reflected XSS has no browser-level mitigation.

## Agent report
### Findings
...

架构

┌──────────────────────────────────────────────────────────┐
│                      SecAgent                            │
│                                                          │
│   ┌──────────────┐        ┌──────────────────────────┐   │
│   │   Planner    │───────▶│   ReAct Execution Loop   │   │
│   │              │        │                          │   │
│   │ 1. plan()    │        │  think → call → observe  │   │
│   │ 2. revise()  │◀───3────│  (parallel tool calls)   │   │
│   └──────────────┘  steps └────────────┬─────────────┘   │
│                                        │                 │
│                          ┌─────────────┴─────────────┐   │
│                          │      Tiered Memory        │   │
│                          │  raw observations +       │   │
│                          │  distilled findings       │   │
│                          └───────────────────────────┘   │
│                                        │                 │
│            ┌─────────────┴─────────────┐                 │
│            │  Policy Gate              │                 │
│            │  deny / ask / allow       │                 │
│            └─────────────┬─────────────┘                 │
│            ┌─────────────┴─────────────┐                 │
│            │  ATT&CK Mapper + Report   │                 │
│            │  + SQLite History         │                 │
│            └───────────────────────────┘                 │
└──────────────────────────┬───────────────────────────────┘
                           │ MCP protocol (stdio)
     ┌────────────┬────────┴───────┬────────────┐
     ▼            ▼                ▼            ▼
┌──────────┐ ┌──────────┐ ┌───────────┐ ┌──────────┐
│  nmap    │ │  httpx   │ │  sqlmap   │ │   ffuf   │
│ 2 tools  │ │ 3 tools  │ │  3 tools  │ │  1 tool  │
└────┬─────┘ └────┬─────┘ └─────┬─────┘ └────┬─────┘
     ▼            ▼             ▼            ▼
   nmap       httpx+re     sqlmap       ffuf

模块职责

模块文件职责
Planneragent/planner.pyPlan-and-Execute:一次性分解目标 + 每 3 步重规划
ReAct Coreagent/core.py推理→调用工具→观察循环,支持并行工具调用
MCP Manageragent/mcp_client.pyMCP Server 生命周期、工具发现、命名空间隔离
Memoryagent/memory.py双层记忆:原始观察(有预算)+ 结构化发现
ATT&CK Mapperagent/attack_map.py发现 → MITRE ATT&CK 技术 ID + 配置弱点说明
Policyagent/policy.py工具分级、目标白名单、人工批准门控
Storeagent/store.pySQLite 持久化 + 历次扫描对比
Retryagent/retry.py瞬时错误指数退避重试
Reportagent/report.py渲染 Markdown / JSON 报告

安全模型

自主 Agent 执行真实的攻击工具,最大的风险是它自己决定去做什么。SecAgent 用三层约束解决这个问题。

1. 工具分级 — 侵入式操作默认需要人工批准

PASSIVE_TOOLS   = {"http_probe", "check_security_headers", "check_robots"}
INTRUSIVE_TOOLS = {"nmap_scan", "nmap_vuln_scan", "sqlmap_scan",
                   "sqlmap_dump", "ffuf_dirs", ...}
$ python secagent.py --target 192.168.1.10 --policy ask

step 2 · reasoning
  → sqlmap_server__sqlmap_scan {"url": "http://192.168.1.10/news.php?id=1"}

╭──────────────────── ⚠ approval required ────────────────────╮
│ sqlmap_scan                                                 │
│ {"url": "http://192.168.1.10/news.php?id=1"}                │
│                                                             │
│ 'sqlmap_scan' sends intrusive traffic and needs approval    │
╰─────────────────────────────────────────────────────────────╯
  Allow this action? [y/N]

三种模式:

模式被动工具侵入工具适用场景
passive✅ 自动放行❌ 一律拒绝生产环境的只读侦察
ask(默认)✅ 自动放行⚠️ 弹窗等人工确认常规渗透测试
allow✅✅明确授权的靶场 / 红队演练

未配置批准人时默认拒绝(fail closed)——这是刻意的,避免"配置漏了"变成"全放行"。

被拒绝的调用会以结构化 JSON 反馈给模型,并附上指引:

{
  "blocked": true,
  "tool": "sqlmap_server__sqlmap_scan",
  "reason": "'sqlmap_scan' sends intrusive traffic and needs approval",
  "guidance": "This action was blocked by the engagement policy. Do not retry it."
}

模型拿到这个不会死循环重试,而是转向被动手段或在报告里说明需要授权。

2. 目标管控 — 白名单 + 私网保护

# 只允许指定域名,越界一律拒绝
python secagent.py --target x --scope 'example\.com' --scope 'testsite\.org'

# 禁止访问回环/内网地址(防 SSRF 式的误操作)
python secagent.py --target x --deny-private

# 连域名一起查:解析结果里只要有一个私网地址就拒绝
python secagent.py --target x --deny-private --resolve-targets

# 彻底禁用某个工具
python secagent.py --target x --deny-tools sqlmap_dump ffuf_dirs

--scope 按 hostname 全匹配,不是子串匹配:example\.com 不会放行 example.com.attacker.net 或 evilexample.com。要放行子域需显式写出 --scope '.*\.example\.com'。

--deny-private 拦截 127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、169.254.0.0/16 与 ::1。判断依据是 IP 字面量,因此 10.example.com 这类域名不会被误伤。这条规则在扫内网时要注意关掉。

默认只校验字面量——一个指向 192.168.x.x 的域名照样能过。加上 --resolve-targets 才会解析:解析出的任意一个地址落在上述网段即拒绝,解析失败或没有结果也拒绝(fail closed)。结果按 hostname 缓存在进程内,不会每次工具调用都查一遍。

这挡得住「域名静态指向内网」,挡不住 DNS rebinding:工具连接时会自己再解析一次,若那时答案变了,仍然会连到内网。真正的修法是让校验出的 IP 与实际连接绑定,那需要接管工具的 socket,当前没做——见已知局限。

3. 全链路审计

每次工具调用都记录决策与理由:

$ python secagent.py --target x --policy ask --json | jq '.tool_audit'
[
  {"step": 1, "tool": "httpx_server__http_probe",
   "decision": "allow", "reason": "passive reconnaissance"},
  {"step": 2, "tool": "sqlmap_server__sqlmap_scan",
   "decision": "ask-denied",
   "reason": "'sqlmap_scan' sends intrusive traffic and needs approval..."}
]

审计日志同时落库,可以回溯"这次评估到底跑过什么、谁批的"。


快速开始

环境要求

  • Python 3.11+
  • Linux(nmap / sqlmap / ffuf 需要,缺失的工具会被自动跳过)
  • 一个 OpenAI 兼容的 LLM API
sudo apt install nmap sqlmap ffuf

安装

git clone https://github.com/RicardoMLu777/secagent.git
cd secagent
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

用 Docker 跑(不想装工具链时用这个)

镜像里已经带好 nmap / sqlmap / ffuf,宿主机不需要任何安全工具:

docker build -t secagent .

# Linux:--network host 让扫描能到达你的靶场网段
docker run --rm --network host --env-file .env secagent --target 192.168.1.10

# 想留一份报告出来
docker run --rm --network host --env-file .env -v "$PWD/out:/app/out" \
  secagent --target 192.168.1.10 --report out/report.md

镜像内以非 root 用户运行。--deny-private 默认关闭,扫内网靶场不用额外处理。

配置

cp .env.example .env
# 编辑 .env,填入 DEEPSEEK_API_KEY

兼容任意 OpenAI 协议端点:

SECAGENT_BASE_URL=https://api.deepseek.com/v1   # DeepSeek
SECAGENT_BASE_URL=https://api.openai.com/v1     # OpenAI
SECAGENT_BASE_URL=http://localhost:11434/v1     # Ollama 本地

运行

# 给目标,自动规划
python secagent.py --target 192.168.1.10

# 自然语言目标
python secagent.py "审计 example.com 的安全头配置和敏感路径"

# 输出报告文件
python secagent.py --target example.com --report out/example.md

# 加载全部 MCP Server
python secagent.py --target x --all-servers

# 关闭规划/并行(对比实验用)
python secagent.py --target x --no-planner --no-parallel

# JSON 输出(便于集成到 CI)
python secagent.py --target x --json

MCP 工具清单

nmap_server.py

工具说明ATT&CK
nmap_scan端口扫描 + 服务版本识别T1046
nmap_vuln_scanNSE 漏洞脚本扫描T1595

httpx_server.py

工具说明ATT&CK
http_probe状态码、响应头、标题、技术指纹T1592
check_security_headers6 个关键安全头缺失审计—
check_robotsrobots.txt 解析,标记敏感路径T1590

sqlmap_server.py

工具说明ATT&CK
sqlmap_scanSQL 注入检测T1190
sqlmap_tables枚举数据库表—
sqlmap_dump导出表数据(需显式指定库表)T1213

ffuf_server.py

工具说明ATT&CK
ffuf_dirs目录/文件爆破(自带迷你字典)T1083

设计要点

1. 显式规划 vs 纯 ReAct

纯 ReAct 的问题:模型边想边做,容易在细节里打转。SecAgent 拆成两阶段:

# 阶段一:无工具环境下的纯规划(模型不会忍不住直接开火)
plan = planner.plan(objective, available_tools)

# 阶段二:带计划执行
result = await agent.run(objective, plan=plan)

# 阶段三:每 3 步让 planner 复核,可 continue / revise / done
action, plan = planner.revise(objective, plan, findings_so_far)

规划调用不挂工具,这是关键——模型只能思考 做什么,不能直接 做。

2. 并行工具调用

模型一轮返回多个 tool_calls 时,用 asyncio.gather 并发执行:

if self.parallel and len(parsed) > 1:
    results = await asyncio.gather(
        *(mcp.call(tc.function.name, args) for tc, args in parsed),
        return_exceptions=True,
    )

无依赖的调用(http_probe + check_headers + check_robots)一轮搞定,实测省 40%+ 时间。

3. 双层记忆

大多数 Agent 把全部工具输出塞进 context,很快就爆了:

# 第一层:原始观察(有长度预算,超长截断)
observations = [Observation(step=1, tool="nmap_scan", result="...")]

# 第二层:结构化事实(用正则从原始输出抽取)
findings = {
    "open_ports": {"22", "80"},
    "services": {"ssh", "nginx 1.18.0"},
    "missing_headers": {"content-security-policy"},
    "interesting_paths": {"/admin", "/backup"},
    "flags": set(),
}

每轮只注入 摘要 + 最近 2 条观察,而非全量历史。

4. 工具命名空间

多个 Server 可能有同名工具,统一加前缀:

nmap_server__nmap_scan
httpx_server__http_probe

5. 安全加固

  • 所有系统命令用 list 形式调用(不经 shell),并在参数层面过滤元字符:
    if re.search(r"[;&|`$><\n]", target):
        return "error: invalid characters in target"
    
  • sqlmap_dump 的库名/表名/列名全部正则白名单校验
  • System prompt 明确 Rules of Engagement:只对授权目标操作、优先非侵入、不伪造输出

历史与对比

每次评估自动落库(SQLite,默认 ~/.secagent/engagements.db),可以回溯和对比同一个目标的历次扫描。

# 查看历史
$ python secagent.py history --target example.com

         Engagement history
┏━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
┃ ID ┃ Target     ┃ Started             ┃ Steps ┃ Calls ┃ Flags              ┃
┡━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
│ 7  │ example.com│ 2026-09-17T14:20:11 │ 6     │ 14    │ flag{new_finding}  │
│ 3  │ example.com│ 2026-09-10T09:02:44 │ 5     │ 11    │ -                  │
└────┴────────────┴─────────────────────┴───────┴───────┴────────────────────┘

# 对比两次扫描
$ python secagent.py diff 3 7

example.com  #3 (2026-09-10T09:02:44) → #7 (2026-09-17T14:20:11)
  + ports: 8080
  - headers: x-frame-options
  + flags: flag{new_finding}
  = services: no change (4)

# 所有评估过的目标
$ python secagent.py targets

对比维度:开放端口、服务版本、缺失的安全头、敏感路径、Flag。

关掉持久化用 --no-store,或指定库路径 --db /path/to.db。


容错

工具调用遇到网络抖动、超时、5xx 时自动重试(指数退避 + 抖动),最多 3 次:

TRANSIENT_MARKERS = (
    "timeout", "connection refused", "connection reset",
    "502 bad gateway", "503 service unavailable", "rate limit", ...
)

只有瞬时错误才重试——遇到"目标不可达"这类确定性失败直接返回,不做无谓等待。重试过程会打印 ↻ retry 1 让使用者看到。

调整次数:retry_attempts=5(构造参数)。


CI

推送到 main 自动触发 GitHub Actions:

matrix: python 3.11 / 3.12 / 3.13
  - pytest tests/          # 单元测试
lint:
  - ruff check .           # E/F/W 规则
  - compileall             # 字节码编译检查

CI 曾经抓到过一个真 bug:ffuf_server.py 里 ext 变量名写错(应为 exts),静态检查直接暴露。这就是把 lint 放进 CI 的价值。


扩展新的 MCP Server

在 mcp_servers/ 下按模板新建即可,运行时会自动发现:

from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool

app = Server("myserver")

@app.list_tools()
async def list_tools() -> list[Tool]:
    return [Tool(
        name="my_tool",
        description="What this tool does",
        inputSchema={"type": "object", "properties": {...}, "required": [...]},
    )]

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    return [TextContent(type="text", text=json.dumps(result))]

async def main():
    async with stdio_server() as (r, w):
        await app.run(r, w, app.create_initialization_options())
python secagent.py --target X --servers nmap_server.py myserver.py

推荐扩展方向:nuclei_server.py、nikto_server.py、gobuster_server.py、hydra_server.py


项目结构

secagent/
├── agent/
│   ├── __init__.py
│   ├── core.py           # ReAct 主循环 + 并行执行
│   ├── planner.py        # Plan-and-Execute 规划层
│   ├── mcp_client.py     # MCP Server 生命周期与工具发现
│   ├── memory.py         # 双层记忆:原始观察 + 结构化发现
│   ├── attack_map.py     # 发现 → MITRE ATT&CK 映射
│   ├── policy.py         # 工具分级、白名单、审批门控
│   ├── store.py          # SQLite 持久化与历次对比
│   ├── retry.py          # 瞬时错误指数退避
│   └── report.py         # Markdown / JSON 报告渲染
├── mcp_servers/
│   ├── mcp_guard.py      # argv 形状校验(防参数注入)
│   ├── nmap_server.py    # 端口扫描 / NSE
│   ├── httpx_server.py   # HTTP 探测 / 安全头 / robots
│   ├── sqlmap_server.py  # 注入检测 / 库表枚举 / 导出
│   └── ffuf_server.py    # 目录爆破
├── tests/                # 单元测试
├── .github/workflows/tests.yml
├── secagent.py           # CLI 入口
├── requirements.txt
├── .env.example
├── Dockerfile
├── CHANGELOG.md
├── LICENSE
└── README.md

安全修复记录

记录本项目自己被审计出来的问题及修复方式。

nmap 工具存在参数注入(已修复 · 0.3.2)

nmap_scan / nmap_vuln_scan 的 target 曾被直接追加到 argv 末尾。因为参数不经过 shell,直觉上会以为没风险——但开头的 - 才是问题:

模型给出的 targetnmap 实际执行
-iL /etc/passwd把该文件当扫描目标列表读进来
-oN /tmp/out把扫描结果写到任意路径
--script=vuln悄悄切换成漏洞脚本扫描

也就是说,一个被污染的提示词足以让「扫一个主机」变成「读一个文件」。

现在 target 必须匹配保守字符集、不能以 - 开头、不能含 ..、长度上限 255;ports 同样限制为 [0-9,-]。规则集中在 mcp_servers/mcp_guard.py,回归测试见 tests/test_mcp_guard.py。

教训:参数不经过 shell 不等于安全。 shell 注入只是其中一个平面,argv 注入是另一个,而后者常被"我用了 list 形式"这句话掩盖掉。

--scope 白名单可被绕过(已修复 · 0.3.1)

早期实现用 re.search(pattern, target) 对整个目标字符串做子串匹配,--scope 'example\.com' 会放行以下目标:

目标为什么能绕过
http://example.com.attacker.net授权域名作为攻击者域名的前缀出现
http://evilexample.com授权域名作为子串出现
http://example.com@attacker.net@ 前是 userinfo,真实 host 是 attacker.net
https://attacker.net/?next=example.com子串出现在 query 里

现在改为先 urlsplit 提取 hostname,再 re.fullmatch 全匹配,回归测试见 tests/test_policy_scope.py。

同一次审计还发现 --deny-private 用字符串正则判断,会把 10.example.com、notlocalhost.com 误判为私网地址;已改用 ipaddress 判断 IP 字面量。

教训:校验点选错位置,比不做校验更危险——它给你的是虚假的安全感。


已知局限

诚实记录边界,不做过度承诺。

  1. DNS 校验有 TOCTOU 窗口 —— --deny-private --resolve-targets 会解析域名并拒绝指向私网的答案,但校验与实际连接之间存在时间差:nmap / sqlmap 连接时会自己再解析一次,攻击者控制的域名(TTL 设 0、应答随请求变化)可以在两次解析之间换掉答案。彻底解决要把校验出的 IP 钉进连接,需要接管工具的 socket,当前未做。不加 --resolve-targets 时,指向内网的域名完全不设防。
  2. 工具输出解析依赖正则 —— memory.py 从原始输出中抽取端口、服务、路径等事实。工具版本升级改变输出格式时抽取可能失败,降级策略是保留原始观察、放弃结构化字段,不中断执行。
  3. 未对抗 prompt injection —— 如果被扫描目标返回的 banner 或页面内容里嵌入了指令,模型的规划可能被污染。目前依赖 policy 层兜底(越界目标会被拒绝),但没有输入侧的隔离与净化。
  4. 单机单进程 —— 没有任务队列、没有并发目标隔离。定位是单次授权评估的辅助工具,不是扫描平台。
  5. 规划质量受模型能力影响 —— 换用小模型时计划可能过于保守或重复。--no-planner 可以退回纯 ReAct 做对比实验。

后续计划

  • 解析后 IP 校验(0.3.3,--resolve-targets)
  • 把校验出的 IP 钉进实际连接,关闭 DNS 校验与连接之间的 TOCTOU 窗口
  • 目标响应内容与指令上下文隔离,做 prompt injection 防护
  • 新增 nuclei_server.py(模板化漏洞检测)
  • 报告支持 SARIF 输出,便于接入 CI
  • 附一份本地靶场(DVWA / OWASP Juice Shop)的端到端复现指南

Disclaimer

本项目仅用于获得明确授权的安全测试、教学与安全研究。

使用者须确保:

  • 对目标拥有所有权,或持有书面的渗透测试授权;
  • 遵守所在地法律法规——在中国境内,未经授权对计算机信息系统进行扫描、探测或获取数据,可能违反《中华人民共和国网络安全法》及《刑法》第二百八十五条;
  • 不将本工具用于任何未授权目标。

工具内置的审批门控、目标白名单与私网管控是降低误操作风险的设计,不构成使用授权的替代。作者不对任何滥用行为及由此产生的后果承担责任。

在本地靶场练习,永远是正确的第一步。

↑↓ navigate openesc close