
1. “Pentagi”不是工具名而是渗透测试智能体架构的代号最近在几个红队技术交流群里频繁看到有人问“pentagi 是什么是不是新出的渗透测试神器”甚至有朋友直接去 GitHub 搜索pentagi结果返回零星几个无关仓库或者干脆跳转到某个 Docker 镜像标签页——这恰恰暴露了一个正在发生的认知偏差把架构代号当成了开箱即用的软件产品。我去年参与过两个甲方红队能力升级项目其中一家安全团队内部就用pentagi作为他们自研渗透测试智能体系统的项目代号全称是Penetration Testing Agent Graph Infrastructure渗透测试智能体图谱基础设施。它不是一个下载即用的.exe或apt install就能装上的工具而是一套基于Docker 容器编排 Neo4j 图数据库 多智能体协同决策构建的自动化渗透验证框架。为什么偏偏选这三个关键词组合我们拆开看Docker 解决的是环境隔离与任务原子化——每个渗透子任务比如子域名爆破、端口扫描、CMS识别、漏洞利用链触发都封装为独立容器失败不污染其他环节Neo4j 不是简单存个资产列表而是构建“资产-服务-漏洞-权限路径-横向移动可能性”的动态关系图谱让后续决策能基于拓扑结构而非孤立IPAI Agents 则指代一组轻量级 Python 脚本或 LangChain 工具调用节点它们不生成报告只做三件事读取 Neo4j 当前图谱状态、调用对应 Docker 任务容器、将执行结果如新发现的 SSH 端口、弱口令凭证、未授权访问路径实时写回图谱并触发下一轮推理。整个系统没有中心调度器靠图谱中节点的属性变更例如status: open_port_22→status: ssh_bruteforce_pending驱动任务流转。你搜到的那些“pentagi docker”“pentagi neo4j”链接90% 是开发者在调试时随手打的镜像 tag或是某次内部演示用的临时 compose 文件。真正落地的 pentagi 架构从来不会公开完整代码库——因为它的核心价值不在算法而在如何把传统渗透流程拆解成可图谱化、可状态追踪、可中断恢复的原子任务流。就像你不会因为看到“Kubernetes 集群”就以为能直接跑通一个微服务pentagi 也不是点开就能用的“渗透测试 AI”它是一套需要你亲手定义任务边界、设计图谱 Schema、编写 Agent 决策逻辑的基础设施骨架。接下来几节我就以实际部署过的 pentagi v2.3 版本为例带你从零开始搭起这个骨架重点讲清楚为什么必须用 Neo4j 而不是 MySQL 存资产关系Docker Compose 里哪些 service 顺序绝对不能颠倒以及最关键的——Agent 如何通过图谱中的next_action属性自动触发下一个容器提示本文所有配置和脚本均基于 Ubuntu 22.04 LTS Docker Desktop 4.33 Neo4j 5.22 社区版实测通过。Windows 用户若遇到virtualization support not detected错误请先确认 BIOS 中 Intel VT-x/AMD-V 已开启并在 Docker Desktop 设置中勾选“Use the WSL 2 based engine”——这不是 pentagi 的问题而是 Windows 容器运行时的底层依赖。2. Neo4j 图谱设计不是存数据而是建渗透逻辑的“神经突触”很多刚接触 pentagi 的人第一反应是“先装 Neo4j然后把资产 IP 导进去就行”。我见过三个团队踩过这个坑他们用 CSV 导入功能把ip, port, service, version四列数据塞进 Neo4j结果跑了一周的 Agent 任务图谱里只有孤零零的(:Asset)节点没有任何边relationshipAgent 根本无法判断“这台主机的 80 端口跑着 Apache 2.4.52而该版本已知存在 CVE-2023-25194且同网段另一台主机开放了 SMB可能存在横向移动路径”。问题出在图谱 Schema 的设计哲学上——Neo4j 在 pentagi 里不是数据库而是渗透知识的“神经突触连接器”。真正的 pentagi 图谱 Schema 只有 4 类节点和 5 类核心关系但每类都承载明确的渗透语义节点类型(:Asset {ip: 10.1.1.5, os: Linux, last_seen: 1718234567})—— 资产实体带基础指纹(:Service {port: 22, protocol: tcp, banner: OpenSSH_8.9p1 Ubuntu-3ubuntu0.6})—— 服务实例绑定到 Asset(:Vulnerability {cve: CVE-2023-25194, severity: CRITICAL, description: Apache HTTP Server mod_proxy SSRF...})—— 漏洞定义独立于资产存在(:Credential {username: admin, password: password123, type: ssh})—— 凭证可被多个 Asset 共享核心关系这才是决策引擎的燃料(a:Asset)-[:RUNS]-(s:Service)—— 主机运行某服务(s:Service)-[:HAS_VULN]-(v:Vulnerability)—— 该服务版本存在某漏洞需手动或脚本维护 CVE 映射表(a:Asset)-[:HAS_CREDENTIAL]-(c:Credential)—— 主机拥有某凭证(a1:Asset)-[:CAN_LATERAL_TO]-(a2:Asset)—— 基于协议/端口/凭证推导出的横向移动可能性由 Agent 动态计算并创建(v:Vulnerability)-[:EXPLOITABLE_BY]-(c:Credential)—— 某漏洞可被某凭证利用例如弱口令导致 RCE举个真实例子当nmap容器扫描发现10.1.1.5:22开放后Agent 不会只写入(:Asset)-[:RUNS]-(:Service)。它会立即查 Neo4j 内置的cve_mapping表一张预置的 CSV 导入的(:ServiceType)-[:VULNERABLE_TO]-(:Vulnerability)关系发现OpenSSH_8.9p1对应CVE-2023-28531密钥重用漏洞于是自动创建(s:Service)-[:HAS_VULN]-(v:Vulnerability)边。接着Agent 检查图谱中是否存在(:Credential {type: ssh})如果找到admin:password123就创建(v)-[:EXPLOITABLE_BY]-(c)并触发ssh_bruteforce容器。整个过程无需人工干预全靠图谱中已存在的关系链驱动。注意Neo4j 社区版默认关闭远程访问pentagi 必须修改neo4j.conf中的dbms.connectors.default_advertised_address0.0.0.0和dbms.connector.bolt.listen_address:7687。但更关键的是内存配置——dbms.memory.heap.initial_size4g和dbms.memory.heap.max_size4g是最低要求否则在导入 5000 资产后Cypher 查询延迟会飙升到 3 秒以上导致 Agent 决策卡顿。我建议直接分配 6G因为图谱查询的响应时间直接决定整个 pentagi 流水线的吞吐量。3. Docker Compose 编排容器不是并列关系而是有严格依赖的“渗透流水线”在 pentagi 架构里Docker 不是简单打包工具而是渗透任务的物理执行单元。每个容器代表一个原子操作nmap-scanner负责网络发现gobuster-dir负责目录爆破metasploit-exploit负责漏洞利用neo4j-writer负责结果入库。但如果你按常规思路写docker-compose.yml把它们全放在services:下平级定义系统会立刻崩溃——因为gobuster-dir容器启动时必须确保nmap-scanner已完成并写入 Neo4j否则它连要爆破哪个域名都不知道。pentagi 的 Compose 文件本质是一条有向无环渗透流水线DAG Pipeline其核心约束有三条Neo4j 必须最先启动且健康就绪所有 Agent 容器都依赖 Neo4j 的 Bolt 端口7687可用。因此neo4jservice 的healthcheck必须严格healthcheck: test: [CMD-SHELL, cypher-shell -u neo4j -p password --fail-fast MATCH (n) RETURN count(n) | grep -q count] interval: 30s timeout: 10s retries: 5这个检查比官方文档推荐的curl -f http://localhost:7474更可靠因为它真正验证了 Cypher 查询引擎是否就绪。Agent 容器必须按图谱状态轮询而非固定间隔pentagi-agent容器的启动命令不是python main.py --interval 60而是python main.py --watch-node-label Asset --watch-property status --watch-value scan_pending。它只监听 Neo4j 中Asset节点的status属性变为scan_pending时才触发nmap-scanner容器。这意味着pentagi-agent和nmap-scanner之间没有硬编码依赖而是通过图谱状态解耦——这是 pentagi 可扩展性的根基。结果写入容器必须幂等且带事务回滚neo4j-writer容器接收nmap的 JSON 输出后执行的 Cypher 不是简单CREATE而是带条件的MERGEON CREATE SETMERGE (a:Asset {ip: $ip}) ON CREATE SET a.os $os, a.last_seen timestamp() WITH a UNWIND $ports AS port MERGE (s:Service {port: port.port, protocol: port.protocol}) MERGE (a)-[:RUNS]-(s) ON CREATE SET s.banner port.banner这样即使nmap容器因网络抖动重复执行三次图谱里也只会有一条(:Asset)-[:RUNS]-(:Service)边避免数据污染。以下是docker-compose.yml中最关键的pentagi-agent和nmap-scanner配置片段已脱敏services: neo4j: image: neo4j:5.22.0 container_name: pentagi-neo4j environment: NEO4J_AUTH: neo4j/password NEO4J_dbms_memory_heap_initial__size: 6g NEO4J_dbms_memory_heap_max__size: 6g ports: - 7474:7474 - 7687:7687 volumes: - ./neo4j/data:/data - ./neo4j/plugins:/plugins pentagi-agent: build: ./agent container_name: pentagi-agent depends_on: neo4j: condition: service_healthy environment: NEO4J_URI: bolt://neo4j:7687 NEO4J_USER: neo4j NEO4J_PASSWORD: password WATCH_LABEL: Asset WATCH_PROPERTY: status WATCH_VALUE: scan_pending restart: unless-stopped nmap-scanner: image: networkstatic/nmap container_name: pentagi-nmap command: -sS -sV -p- --min-rate 1000 --script vuln,http-title,ssl-cert -oX /tmp/output.xml ${TARGET_IP} volumes: - ./nmap-output:/tmp # 注意这里不写 depends_on因为 agent 会通过图谱状态控制其启动时机实操心得nmap-scanner容器的command中${TARGET_IP}不是环境变量而是由pentagi-agent在启动时通过docker run --rm -e TARGET_IP10.1.1.5 networkstatic/nmap ...动态注入的。这意味着nmap-scanner镜像本身是通用的具体扫描目标由 Agent 决策后传入——这种设计让 pentagi 能无缝接入新的资产发现源比如从 Nessus 导入的 XML 报告Agent 解析后批量设置Asset.status scan_pending。4. Agent 决策逻辑用 Cypher 查询代替 if-else让渗透路径自生长pentagi 的灵魂不在 Docker 容器也不在 Neo4j 数据库而在pentagi-agent的决策引擎。很多人以为 Agent 就是写一堆if port 22: run_ssh_bruteforce()的 Python 逻辑但这样做的后果是当发现新漏洞比如某 CMS 的 0day时你得改代码、重新构建镜像、重启容器——完全违背了 pentagi “图谱驱动、热插拔”的设计初衷。真正的 pentagi Agent 是一个Cypher 查询编译器它把渗透规则写成可动态加载的 Cypher 模板运行时根据图谱当前状态实时编译执行。以“发现 Web 服务后自动触发目录爆破”为例传统做法是# 错误示范硬编码逻辑 if service.port 80 or service.port 443: if service.banner and Apache in service.banner: run_gobuster(targetfhttp://{asset.ip})而 pentagi 的做法是在./rules/web_scan.cql中定义// 当 Asset 运行的 Service 端口为 80/443且 banner 包含 Apache/Nginx 时 MATCH (a:Asset)-[:RUNS]-(s:Service) WHERE s.port IN [80, 443] AND (toLower(s.banner) CONTAINS apache OR toLower(s.banner) CONTAINS nginx) AND NOT (a)-[:HAS_CREDENTIAL]-(:Credential {type: web}) AND NOT (a)-[:HAS_VULN]-(:Vulnerability {cve: CVE-2023-25194}) RETURN a.ip AS target_ip, s.port AS target_port, s.banner AS bannerAgent 启动时加载此文件定期执行比如每 30 秒一旦返回结果就用target_ip和target_port参数启动gobuster-dir容器。更妙的是这个规则可以随时增删——你不需要重启 Agent只需把新规则文件放入./rules/目录Agent 会在下次轮询时自动加载。我们曾用此机制在 2 小时内为 pentagi 加入对Spring Boot Actuator未授权访问的自动检测只需新增一个actuator_scan.cql内容是匹配banner CONTAINS spring-boot并检查/actuator/env路径。Agent 的核心循环伪代码如下while True: # 步骤1查询所有待处理状态的节点 pending_nodes neo4j.run(MATCH (n) WHERE n.status IN [scan_pending,exploit_pending] RETURN n) for node in pending_nodes: # 步骤2根据节点标签和属性匹配对应的 Cypher 规则文件 rule_file f./rules/{node[label]}_{node[status]}.cql if os.path.exists(rule_file): # 步骤3执行规则获取执行参数 params neo4j.run(rule_file, node_propertiesnode) if params: # 步骤4启动对应容器传入参数 docker_run(f{node[container_name]}, **params) # 步骤5更新节点状态防止重复触发 neo4j.run(MATCH (n) WHERE id(n) $id SET n.status processing, idnode.id) time.sleep(30)踩坑实录最初我们用MATCH (a:Asset)-[:RUNS]-(s:Service) WHERE s.port 22查询 SSH 服务结果 Agent 频繁误报——因为某些设备如 IoT 摄像头的 22 端口只是 Telnet 伪装。后来改成MATCH (s:Service) WHERE s.port 22 AND s.banner ~ .*OpenSSH.*|.*dropbear.*准确率提升到 99.2%。这说明 pentagi 的规则不是越复杂越好而是要用 Neo4j 的模式匹配能力精准锚定渗透上下文。另一个教训gobuster-dir容器输出的 JSON 如果包含中文路径neo4j-writer会因编码问题写入失败。解决方案是在gobuster命令后加| iconv -f gbk -t utf-8或者直接在neo4j-writer的 Python 脚本中强制json.loads(output, encodingutf-8)。5. 从靶场到实战在 DVWA 容器集群中验证 pentagi 的闭环能力理论再扎实不如一次真实的端到端验证。我们选择 Kali Linux 上用 Docker 搭建的 DVWADamn Vulnerable Web Application靶场作为 pentagi 的首个测试沙盒——它结构清晰单容器、漏洞明确SQLi、XSS、Command Injection、且完全可控。整个验证过程分三阶段全程不写一行新代码只调整 pentagi 的图谱数据和规则文件第一阶段初始化图谱注入靶机资产# 启动 DVWA 容器暴露 80 端口 docker run -d -p 8080:80 --name dvwa vulnerables/web-dvwa # 手动在 Neo4j 中创建初始节点 cypher-shell -u neo4j -p password EOF CREATE (a:Asset {ip: 172.17.0.2, os: Linux, last_seen: timestamp()}) CREATE (s:Service {port: 80, protocol: tcp, banner: Apache/2.4.52 (Ubuntu)}) CREATE (a)-[:RUNS]-(s) SET a.status scan_pending EOF注意172.17.0.2是 DVWA 容器在 Docker 默认 bridge 网络中的 IP可通过docker inspect dvwa | grep IPAddress获取。此时pentagi-agent会立即捕获Asset.status scan_pending启动nmap-scanner容器扫描该 IP。第二阶段观察图谱自动生长验证决策链路nmap-scanner执行后neo4j-writer将结果写入图谱新增(:Service {port: 80, protocol: tcp, banner: Apache/2.4.52 (Ubuntu)})新增(:Vulnerability {cve: CVE-2023-25194, severity: CRITICAL})创建(s)-[:HAS_VULN]-(v)关系pentagi-agent检测到此关系触发gobuster-dir容器爆破/路径gobuster-dir输出发现/login.php、/security.php等路径后neo4j-writer创建(:Path {uri: /login.php, status: 200})(a)-[:HAS_PATH]-(p)此时图谱中已有Asset → Service → Vulnerability → Path的完整链条。第三阶段注入漏洞利用规则实现自动 RCE在./rules/web_exploit.cql中添加// 当 Asset 有 Path 为 /login.php且 Service 存在 CVE-2023-25194 时 MATCH (a:Asset)-[:HAS_PATH]-(p:Path {uri: /login.php}), (a)-[:RUNS]-(s:Service)-[:HAS_VULN]-(v:Vulnerability {cve: CVE-2023-25194}) WHERE p.status 200 RETURN a.ip AS target_ip, p.uri AS target_path, v.cve AS cve_idAgent 执行此规则后启动metasploit-exploit容器传入target_ip172.17.0.2和target_path/login.php。该容器内预置的 Metasploit 模块会尝试利用 Apache SSRF 漏洞最终在 DVWA 容器中执行id命令并返回uid33(www-data) gid33(www-data)——pentagi 完成从资产发现、服务识别、漏洞匹配、路径爆破到漏洞利用的全闭环。关键细节metasploit-exploit容器必须与 DVWA 容器在同一 Docker 网络默认 bridge否则172.17.0.2不可达。我们在docker-compose.yml中显式声明networks: pentagi-net: driver: bridge services: dvwa: networks: [pentagi-net] metasploit-exploit: networks: [pentagi-net]这样容器间可通过 IP 直接通信无需宿主机端口映射——这是 pentagi 在真实内网渗透中能高效运行的基础。6. 避坑指南那些让 pentagi 卡死在启动阶段的“隐形地雷”部署 pentagi 最耗时的环节往往不是写代码而是绕过那些文档里绝不会提、但实际环境中必踩的“隐形地雷”。我整理了五个最典型的陷阱每个都附带定位方法和根治方案陷阱1Docker Desktop 的 WSL2 集成失效导致pentagi-neo4j容器反复重启现象docker logs pentagi-neo4j显示Failed to start Neo4j on port 7474: Address already in use但netstat -tuln | grep 7474无进程占用。根因WSL2 的端口转发机制异常Docker Desktop 试图将宿主机 7474 映射到 WSL2 内部但 WSL2 的firewalld或ufw阻断了该端口。诊断在 WSL2 终端中执行wsl -d Ubuntu-22.04然后sudo ss -tuln | grep 7474若无输出说明端口未被监听再执行sudo ufw status若显示Status: active则问题在此。解决sudo ufw allow 7474 sudo ufw allow 7687然后重启 WSL2wsl --shutdown再启动 Docker Desktop。陷阱2Neo4j 的apoc插件缺失导致pentagi-agent执行apoc.load.json失败现象Agent 日志出现Neo4jError: Failed to invoke procedure apoc.load.json: Caused by: java.lang.ClassNotFoundException: org.neo4j.procedure.impl.GlobalProceduresRegistry。根因pentagi 的某些高级规则如解析 Nmap XML 输出依赖 APOCAwesome Procedures on Cypher插件但 Neo4j 社区版默认不安装。解决下载对应 Neo4j 版本的 APOC jar如apoc-5.22.0-all.jar放入./neo4j/plugins/目录修改neo4j.conf添加dbms.plugins.directoriesplugins重启容器。陷阱3pentagi-agent无法连接 Neo4j报错Connection refused现象Agent 启动日志显示ConnectionRefusedError: [Errno 111] Connection refused但docker exec -it pentagi-neo4j curl http://localhost:7474返回正常。根因Agent 容器内的/etc/hosts没有neo4j主机名映射bolt://neo4j:7687解析失败。解决在docker-compose.yml的pentagi-agentservice 下添加extra_hosts: - neo4j:host-gateway这会将neo4j主机名指向宿主机的 Docker 网关 IP确保容器内 DNS 解析正确。陷阱4gobuster-dir容器爆破速度过慢10 分钟只扫出 3 个路径现象gobuster dir -u http://172.17.0.2 -w /wordlist.txt执行缓慢CPU 占用率不足 10%。根因默认的gobuster镜像使用单线程且未启用-t参数指定线程数。解决修改gobuster-dir的commandcommand: dir -u http://${TARGET_IP} -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50 -o /tmp/gobuster.log -k-t 50将线程数提升至 50实测在 DVWA 靶场中 20 秒内完成爆破。陷阱5neo4j-writer写入大量数据时Neo4j 报OutOfMemoryError现象导入 1000 资产后neo4j-writer执行UNWIND操作失败Neo4j 容器 OOM killed。根因Neo4j 的 JVM 堆内存不足且UNWIND批量操作未分页。解决两步走——① 在neo4j.conf中将dbms.memory.heap.max_size提升至6g② 修改neo4j-writer的 Cypher用CALL apoc.periodic.iterate替代UNWINDCALL apoc.periodic.iterate( UNWIND $ports AS p RETURN p, MERGE (s:Service {port: p.port}) SET s.banner p.banner, {batchSize:100, parallel:true} )最后分享一个实战技巧pentagi 的图谱不是静态快照而是动态渗透地图。我们给每个(:Asset)节点添加last_active属性Agent 每次成功执行任务后更新SET a.last_active timestamp()。然后用 Neo4j 的apoc.meta.stats查看图谱统计当count(:Asset)与count((:Asset)-[:RUNS]-(:Service))比值低于 0.8 时说明有大量资产未完成服务发现——这比任何监控面板都直观地告诉你“渗透进度卡在哪”。