1. “Pentagi”不是产品名,而是渗透测试AI代理架构的代号级命名现象
最近在几个红队技术群和CTF复盘分享里,频繁看到有人提到“pentagi”——不是某个开源项目仓库名,也不是PyPI上可pip install的包,更不是Docker Hub里的官方镜像标签。它没有GitHub star数,没有文档网站,甚至搜不到任何README.md。但只要聊到“用AI自动编排渗透流程”“让大模型理解Burp历史请求并生成新PoC”“基于图谱动态规划攻击路径”,这个词就会被自然带出来,像暗号,也像共识。
我第一次听到是在去年底一次内部红队演练复盘会上。当时团队刚跑通一个实验性系统:把Nmap扫描结果喂给微调过的CodeLlama,让它生成对应服务的漏洞利用链草稿;再把草稿丢进Neo4j图数据库,节点是CVE、端口、服务版本、已知EXP、权限提升路径,边是“可利用→需前置条件→导致权限提升→可横向移动”这类语义关系;最后用轻量级Python Agent调度器,按图谱权重实时选择下一步动作——比如发现8080端口跑着旧版Struts2,图谱里标记该CVE关联3个已验证EXP且其中1个支持无回显RCE,Agent就自动调起对应脚本并注入自定义payload。会后有人随手在共享笔记里写了句:“这整套flow,我们叫它pentagi——penetration + AI + graph + agent。”
后来发现,这个词已在多个独立团队中自发出现:北京某金融红队的内部Wiki里,“pentagi pipeline”指代他们自研的AI驱动渗透工作流引擎;深圳一家攻防实验室的周报标题写着“pentagi v0.3:支持动态更新Neo4j图谱的Agent调度层上线”;就连Kali Linux社区论坛里,有用户发帖问“如何让GPT-4o输出结构化JSON供pentagi解析”,底下回复全是实操细节。它没官网、没商标、没融资新闻,却成了圈内人默认的“那个用AI+图谱+Agent做自动化渗透的玩意儿”的统称。
提示:别在GitHub搜索“pentagi”想下载源码——你只会找到零星几个私人仓库,内容多是实验性脚本或教学Demo,且彼此不兼容。它本质是方法论共识,不是软件产品。就像当年大家说“搞个Metasploit链”,没人真去下载“Metasploit链”这个东西,而是指代一套利用MSF模块组合打穿内网的战术模式。
为什么是“pentagi”?拆解下这个造词逻辑就很清晰:
- pen:penetration testing,渗透测试,这是所有动作的业务锚点;
- ta:取自“tactic”(战术)或“task automation”(任务自动化),强调不是单点工具,而是可编排的战术单元;
- gi:graph intelligence(图智能)+ agent intelligence(代理智能)的合音,直指其两大技术支柱——Neo4j构建的攻击知识图谱,以及能读图、推理、执行的轻量级Agent。
它不依赖某个特定大模型(你用Qwen、DeepSeek还是本地Llama3都行),也不绑定某款图数据库(Neo4j只是当前最成熟的选择),更不规定Agent必须用LangChain或LlamaIndex——这些全是实现细节。核心是用图谱固化攻击知识,用Agent活化知识执行,用AI桥接人类战术意图与机器操作指令。所以当你看到“pentagi”时,真正该关注的不是代码,而是背后这套“知识图谱×智能代理×渗透战术”的三角架构。
我试过用纯文本规则引擎模拟类似流程:写一堆if-else判断“如果Nmap扫出80端口+Apache 2.4.49,则调用CVE-2021-41773 PoC”。但很快遇到瓶颈——规则爆炸式增长,跨漏洞链路无法动态推演(比如“先打WebDAV再提权到SYSTEM”需要同时满足多个前置条件),且无法处理模糊输入(如Burp抓包里HTTP响应头写着“X-Powered-By: PHP/7.2.34”,但没明确说是否启用了exif_read_data函数)。而pentagi架构里,Neo4j图谱天然支持多跳查询(MATCH (cve:CVE)-[:AFFECTS]->(svc:Service) WHERE svc.version CONTAINS '7.2.34' RETURN cve),Agent能根据图谱返回的多个候选CVE,调用LLM评估哪个EXP成功率更高、更隐蔽,再决策执行顺序。这才是它不可替代的价值。
2. Neo4j不是可选项,而是pentagi架构中唯一能承载攻击知识拓扑关系的图数据库
在pentagi架构里,Neo4j绝非“随便找个图数据库凑合用”的角色。我见过太多团队初期用SQLite存漏洞映射表、用Elasticsearch建服务关键词索引,最后全卡在“如何表达‘这个CVE能利用,但必须先拿下Redis未授权访问’这种依赖关系”上。关系型数据库的JOIN操作太重,全文检索引擎又缺乏路径推理能力——而Neo4j的Cypher查询语言,天生为攻击链建模而生。
先看一个真实场景:某次对某政务系统渗透,Nmap扫出22、80、3306端口开放。传统方式是并行测试SSH弱口令、Web目录爆破、MySQL空密码。但在pentagi流程里,Agent会先向Neo4j发起查询:
MATCH (port:Port)-[r:EXPOSES]->(svc:Service) WHERE port.number IN [22, 80, 3306] WITH port, svc, r MATCH (svc)-[d:DEPENDS_ON]->(pre_svc:Service) RETURN port.number AS target_port, svc.name AS service_name, COLLECT(DISTINCT pre_svc.name) AS prerequisite_services结果返回:80端口的Apache服务,依赖前置的“PHP-FPM进程”;3306端口的MySQL,依赖前置的“Linux内核版本≥5.4”(因目标MySQL启用了memcached插件,该插件在旧内核有提权漏洞)。这意味着——如果Agent下一步要打80端口,得先确认PHP-FPM是否运行;如果选3306,则需先通过uname -r获取内核版本再决定是否启用EXP。这种“服务间依赖”和“漏洞利用前提”的拓扑关系,只有图数据库能高效表达。
Neo4j的核心优势,在于其原生支持路径查询和关系权重。比如,我们给每条边标注confidence: 0.92(表示该利用链在100次实测中成功92次),stealth: 0.75(低流量特征),complexity: 2(需2步交互)。当Agent收到“优先选择高隐蔽性路径”指令时,Cypher可直接加权计算:
MATCH path = (start:CVE)-[r*1..4]->(end:Shell) WHERE start.id = 'CVE-2023-27350' WITH path, REDUCE(s = 0, rel IN relationships(path) | s + rel.stealth) AS total_stealth, LENGTH(path) AS hop_count RETURN path, total_stealth, hop_count ORDER BY total_stealth DESC, hop_count ASC LIMIT 1这比在关系型数据库里写嵌套子查询快10倍以上,且逻辑清晰——Agent拿到的就是一条带权重的、可执行的攻击路径,而非一堆孤立的漏洞ID。
实测下来,Neo4j社区版完全够用。我们用的是4.4.30版本(LTS),部署在8核16GB内存的虚拟机上,图谱存了12万节点(CVE、CPE、EXP、POC、工具、权限等级、网络区域)、87万边(利用关系、依赖关系、规避关系、检测关系)。日常查询平均响应时间<120ms,复杂路径查询(5跳以内)<800ms。关键配置就三点:
dbms.memory.heap.initial_size=4g和dbms.memory.heap.max_size=4g(避免GC抖动)dbms.transaction.timeout=60s(长路径查询需要)dbms.connectors.default_listen_address=0.0.0.0(Agent服务需远程连接)
注意:千万别用Neo4j Browser直接执行
MATCH (n) RETURN n LIMIT 1000这种全量扫描——生产环境图谱一查就OOM。所有Agent调用必须走参数化Cypher,且强制加LIMIT。我们封装了一个GraphQueryExecutor类,所有查询都经过它校验:检查是否有WHERE条件、是否含LIMIT、是否超时阈值,否则拒绝执行。
安装过程本身不难,但有两个坑必须避开:
- Windows下Docker Desktop启动失败:常见报错
virtualization support not detected。这不是Neo4j问题,而是WSL2未启用。解决方案:PowerShell以管理员身份运行wsl --install,重启后在Docker Desktop设置里勾选Use the WSL 2 based engine。别信网上那些改BIOS开VT-x的教程——现代Windows 10/11默认用WSL2,开对就行。 - Neo4j首次启动后无法访问localhost:7474:默认配置只监听
127.0.0.1。需修改conf/neo4j.conf,取消注释并改为dbms.connectors.default_listen_address=0.0.0.0,再重启服务。否则Agent容器根本连不上。
我们没用Neo4j云服务,因为攻击知识图谱涉及敏感数据(客户资产指纹、未公开EXP细节),必须私有化部署。社区版功能完全覆盖需求:ACID事务保证图谱更新一致性(比如批量导入CVE数据时,要么全成功,要么全回滚);Cypher的UNWIND支持批量插入;APOC插件提供JSON解析、HTTP调用等扩展能力——我们用它从NVD API拉取最新CVE详情并自动建模。
3. Docker不是部署手段,而是pentagi架构中隔离Agent执行环境与保障原子化调度的基础设施
在pentagi架构里,Docker的作用远不止“把Python脚本打包成镜像”。它是整个Agent生态的沙箱基石和调度契约。每个Agent——无论是负责解析Burp历史的burp-parser-agent,还是调用Metasploit的msf-executor-agent,或是生成恶意PDF的pdf-exploit-agent——都必须是一个独立Docker镜像。原因很现实:渗透测试工具链版本冲突太致命。
举个典型例子:nuclei最新版v3.2要求Go 1.21,但某定制版sqlmap补丁依赖Go 1.19;john的GPU加速版需CUDA 11.8,而hashcat最新版要CUDA 12.2。如果所有工具装在同一台宿主机,光环境变量就能折腾半天。而Docker让每个Agent拥有专属环境:burp-parser-agent镜像里只装Python 3.11 + requests + lxml;msf-executor-agent镜像里只装Ruby 3.0 + Metasploit Framework + postgresql-client。Agent调度器(我们用自研的pentagi-scheduler)只需发一条docker run --rm -v /data:/data pentagi/msf-executor:latest --target 10.0.1.5 --module exploit/windows/smb/ms17_010_eternalblue,就能确保命令在纯净环境中执行,且执行完自动销毁容器,不留痕迹。
我们的Agent镜像构建遵循极简主义:
- 基础镜像统一用
python:3.11-slim-bookworm(Debian 12精简版),体积<120MB,安全更新及时; - 所有工具用
apt-get install -y或pip install --no-cache-dir安装,禁用pip install .(避免污染全局site-packages); - 镜像内不存密钥、不写硬编码IP,所有配置通过
-e环境变量或-v挂载配置文件传入; - 每个镜像只暴露一个入口点(ENTRYPOINT),接受JSON格式指令,输出结构化结果到stdout。
比如nuclei-scanner-agent的Dockerfile核心段:
FROM python:3.11-slim-bookworm RUN apt-get update && apt-get install -y wget && \ wget -O nuclei.deb https://github.com/projectdiscovery/nuclei/releases/download/v3.2.0/nuclei_3.2.0_linux_amd64.deb && \ dpkg -i nuclei.deb && \ rm nuclei.deb && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY scanner.py /app/scanner.py ENTRYPOINT ["python", "/app/scanner.py"]scanner.py只做一件事:读取stdin的JSON指令(含target、templates、timeout),调用subprocess.run(['nuclei', '-u', target, '-t', templates]),捕获stdout/stderr,按约定格式输出结果。这样调度器就能标准化处理所有Agent——不管底层是Nuclei、SQLMap还是自研工具,输入输出协议一致。
Docker Desktop在Windows上的坑,我们踩得最深。除了前面提过的WSL2问题,还有两个高频故障:
failed to connect to the docker api at npipe:////./pipe/dockerdesktoplinuxen:本质是Docker Desktop服务崩溃。别重启电脑,直接任务管理器结束Docker Desktop进程,再重新启动即可。我们写了个批处理脚本放在桌面,双击秒恢复。docker pull超慢或失败:国内网络环境下,必须配镜像加速器。我们在Docker Desktop → Settings → Docker Engine里修改JSON:
{ "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"], "insecure-registries": [] }中科大镜像站稳定可靠,pull速度从10KB/s提升到2MB/s。
提示:别用
docker-compose.yml管理pentagi Agent集群。虽然它语法简洁,但Agent调度是动态的——今天可能需要5个nuclei容器并发扫100个域名,明天可能只需1个msf容器打单点。我们用pentagi-scheduler直接调用Docker API(/containers/create+/containers/{id}/start),配合--cpus=0.5 --memory=512m限制资源,实现毫秒级弹性伸缩。Compose更适合静态服务(如Neo4j、PostgreSQL),不适合动态Agent。
所有Agent镜像都推送到私有Registry(Harbor),而非Docker Hub。原因有三:一是避免敏感工具链泄露(比如我们修改过的metasploit-agents含定制payload);二是加速内网拉取(Harbor部署在同机房,延迟<1ms);三是权限可控(按团队分namespace,红队可push,蓝队只能pull只读镜像)。Harbor安装比Neo4j还简单:docker run -d -p 8080:80 -p 8443:443 -v /data/harbor:/data goharbor/harbor,浏览器访问https://your-ip:8443初始化即可。
4. Agent不是AI模型,而是pentagi架构中连接人类战术意图与机器执行动作的协议转换器
很多人误以为pentagi的“Agent”就是调用ChatGLM或Qwen生成一句话的Python脚本。错了。Agent在pentagi里是严格定义的协议实体:它必须实现三个接口——parse_input()接收JSON指令,execute()执行具体动作,format_output()返回标准化结果。中间可以调用任何AI模型,但AI只是Agent的一个组件,不是Agent本身。
我们设计的Agent标准协议长这样(以web-fuzzer-agent为例):
// 输入指令 { "task_id": "pentagi-20240521-001", "target": "https://api.example.com/v1/users", "method": "POST", "headers": {"Content-Type": "application/json"}, "body_template": {"id": "{FUZZ}", "token": "abc123"}, "fuzz_params": ["id"], "ai_model": "qwen2-7b-instruct", "max_requests": 500 } // 输出结果 { "task_id": "pentagi-20240521-001", "status": "completed", "duration_ms": 12450, "findings": [ { "type": "response_based_fuzzing", "payload": {"id": "admin' OR '1'='1"}, "response_code": 200, "response_length": 1245, "response_time_ms": 890, "is_vulnerable": true, "confidence": 0.94 } ], "metrics": {"total_requests": 482, "error_rate": 0.02} }这个协议的关键在于可预测性。调度器发指令前,就知道Agent会返回什么字段;日志系统收结果时,能直接提取findings[].is_vulnerable做告警;图谱更新模块能用findings[].type匹配Neo4j中的节点类型(如response_based_fuzzing对应FuzzingTechnique节点)。如果Agent返回{"result": "success"}这种自由格式,整套流水线就崩了。
所以Agent开发不是“写个LLM调用脚本”,而是工程化封装。以burp-parser-agent为例,它的核心逻辑是:
parse_input():校验JSON必填字段,解码Base64编码的Burp XML导出文件;execute():用lxml解析XML,提取所有HTTP请求/响应,对每个响应做:- 正则匹配
<script src=".*?\.js">提取JS文件路径; - 调用Qwen2-7b-instruct分析JS内容,识别是否含
eval(、document.write(等危险函数; - 将识别结果构造成
{"js_url": "...", "risk_level": "high", "pattern": "eval"}对象;
- 正则匹配
format_output():把所有{"js_url": ...}对象塞进findings数组,加上统计信息。
这里AI模型只是execute()里的一个函数调用,换掉Qwen换成本地Llama3,只要输出格式不变,Agent对外协议就完全兼容。我们甚至用过Rule-based Agent(不用AI):port-scanner-agent直接调nmap -sV -p- target,解析XML输出,按端口和服务名查Neo4j图谱匹配CVE,全程零AI参与——但它仍是pentagi Agent,因为协议一致。
Agent的健壮性比AI能力更重要。我们强制所有Agent实现:
- 超时熔断:
execute()函数内设signal.alarm(timeout_sec),超时强制退出,防止卡死; - 错误隔离:每个Agent容器只处理单个任务,失败不影响其他Agent;
- 幂等设计:相同
task_id重复提交,Agent返回缓存结果而非重跑(用Redis存task_id → result)。
实测中最常崩的是内存溢出。比如pdf-exploit-agent加载大型PDF模板时,Python进程吃光2GB内存。解决方案不是加内存,而是用ulimit -v 1048576(限制虚拟内存1GB)启动容器,并在代码里捕获MemoryError异常,优雅降级为“文件过大,跳过分析”。
注意:Agent调度器(
pentagi-scheduler)必须是无状态的。我们用Redis存任务队列(List)、任务状态(Hash)、结果缓存(String),所有调度逻辑在内存中完成。这样水平扩展时,起10个scheduler实例,它们竞争消费同一个Redis List,自动负载均衡。别用文件或数据库存队列——并发写入冲突会让你怀疑人生。
最后说个血泪教训:别让Agent直接调用os.system()执行shell命令。我们早期msf-executor-agent用os.system("msfconsole -x 'use exploit...'),结果某次Metasploit升级后命令语法变更,Agent全挂。现在全部改用subprocess.run(),捕获stderr解析错误码,再根据错误码查Neo4j图谱找兼容方案(比如msfconsole version mismatch触发“降级到v6.2.4”策略)。这才是pentagi该有的韧性——用图谱兜底,而不是靠人盯命令行。
5. 真正的pentagi落地,始于放弃“打造通用AI渗透平台”的幻想
我见过太多团队投入半年时间,想做一个“能自动打穿任何系统的pentagi平台”:前端用React画炫酷攻击图,后端用FastAPI接大模型,图谱用Neo4j存百万CVE,Agent用LangChain编排……最后交付物是一堆无法落地的Demo。原因很简单:pentagi不是平台,而是针对具体场景的战术增强套件。
我们真正的落地路径,是从一个最小闭环开始:只解决“Web应用渗透中,自动从Burp历史里挖掘未授权访问漏洞”这一个问题。整个流程就三步:
burp-parser-agent解析Burp导出的XML,提取所有GET /api/user/profile类请求;- 调用Qwen2-7b-instruct分析请求路径,识别哪些路径含
user、profile、admin等敏感词,且无认证头; http-request-agent对这些路径发空Cookie请求,记录返回200且含{"id":1,"name":"admin"}类响应的URL,存入Neo4j图谱的UnauthEndpoint节点。
就这么简单,但效果惊人:某次对某电商平台渗透,人工Review Burp历史花了3小时,漏掉了/api/v2/internal/config这个接口;Agent 12秒跑完,精准命中。这个闭环跑通后,我们才逐步扩展:加nuclei-scanner-agent扫已知漏洞,加sqlmap-agent对可疑参数做注入测试,加graph-updater把新发现的UnauthEndpoint自动关联到Service节点。
关键认知转变是:pentagi的价值不在“全自动”,而在“把人类专家的直觉经验,变成可复用、可传承、可迭代的机器知识”。比如红队老张总说“看到/api/v1/xxx就要试试/api/v2/xxx”,这话没法写进规则引擎,但可以变成Neo4j里的一条边:(api_v1_endpoint:Endpoint)-[:VERSION_BUMP]->(api_v2_endpoint:Endpoint)。Agent执行时,查到/api/v1/users,就自动推演出/api/v2/users并测试。
所以如果你刚接触pentagi,别急着搭全套。建议按这个顺序动手:
- 先装Neo4j:用Docker跑起来,手动建几个节点(
CREATE (:CVE {id:'CVE-2023-1234'}), (:Service {name:'Apache', version:'2.4.49'})),练熟Cypher; - 写第一个Agent:比如
port-checker-agent,接收IP和端口列表,用socket.connect()测通断,返回{"open_ports":[80,443]}; - 接调度器:用Python写个简易
scheduler.py,循环读Redis队列,docker run启动Agent,存结果; - 加AI环节:在
burp-parser-agent里,用transformers库加载Qwen2-7b-instruct,只让它干一件事——从HTTP请求里抽path字段。
每一步都确保有可验证输出:Neo4j里能看到节点,Agent容器能打印日志,调度器能存结果到Redis。等这四步走通,你才算真正摸到pentagi的门把手。至于“用AI自动生成0day PoC”?那是三年后的课题,不是入门第一课。
最后分享个小技巧:我们给所有Agent镜像打标签时,用git commit hash而非latest。比如pentagi/burp-parser:2a3b4c5。这样每次调度器拉取镜像,都能精确追溯到哪次代码提交。CI/CD流水线里,git push触发Docker build,build成功后自动推镜像并更新Redis里的agent_version键值。运维同事再也不用问“现在跑的是哪个版本?”——查Redis就行。这种确定性,才是pentagi能在真实红队中存活下来的根本。