ai挖洞的自动化流程以及自动化渗透测试工具的研究
这将是很长的一篇技术文章,没耐心的可以划走了。
1.ai挖洞的基础理论
(1)什么是LLM
LLM = Large Language Model,大语言模型。
LLM 是一种用海量文本/代码训练出来的、参数量极大的神经网络模型,核心能力是“根据上下文预测下一个 token”,从而表现出理解、生成、推理、写代码、分析文本等通用能力。
但依然有局限性,可能生成看似合理实际不存在的漏洞,所以需要手工验证,而且会出现缺乏理解,对于复杂的逻辑漏洞可能出现遗漏。
(2)AI挖洞的局限性:
1.无中生有,尤其是deep seek这种的模型,在挖到那种没有漏洞的那种强网站的时候,特别容易给你随便的蹦出一堆,根本就不存在的那种垃圾,连漏洞都算不上。原本都不是漏洞,但AI觉得它是漏洞,而且这种漏洞评级还特别高,在代码审计进行的时候,就是它也比较的容易进行那种假的阳性报告,也需要进行二次验证。
建议:暂时不用规避,自己去判断,让AI把这个它觉得这是漏洞的详情告诉你,或者生成BURP对应的数据包,然后你去进行复现。
2.无法深入业务逻辑,在面对业务逻辑强的那种站套站的时候,容易不知所措,缺乏对业务逻辑一个深入的理解。
建议:手挖。
3上下文文本容易爆炸,由于渗透测试是极其复杂的一个任务,经常容易长文本,AI容易挖到后面,忘了前面。这样的话更容易产生AI幻觉。GPT它的模型能力就比较强, 但就是贵 ,而且道德感强,需要你绕一下。
GLM国内比较强的, Deepseek 分为flashpro pro/3=flash,需要你多去和它交互,只是简单的一句话的话,它会偷懒,没有体现出skill,它同样不会去使用skill约束自己。
Skill 名称 站点 这个是正确使用方式。
对 某某站点 进行漏洞挖掘 这是错误使用方式。
建议:新开会话,并且让AI不断的保持一个记录的习惯,无论是绕waf没绕过去的思考,还是新的思路的总结,还是阶段性的测试。这样的话新开的agent可以从你之前的记录文档里面找到线索,去进行接下来的挖掘。
4.主agent与子agent的分工不均,可能出现各种问题。就比如我在使用美团的龙猫模型的时候,这个模型不推荐用,推理能力太弱,容易死循环。之前暑假拿它测试的时候已经死循环了两次,就是它的主agent可能会收到一个对一个网站进行渗透测试的一个任务,它的子agent进行相关的,辅助进行加解密拆分分析数据包的时候,就特别的容易陷入死循环。就是它本身这个模型的能力不强,喜欢在这个自己不行的地方死磕。需要用提示词让它去,换个方向,换个思路。最有钱的解决办法是,直接换成gpt能解决垃圾模型90%的问题,Gpt的话,需要一定的绕过,你就给他说我这是授权的。不要用弱agent去指挥弱agent,达到资源的一个最大化的利用,主agent的一定要是强agent,因为它要根据指子agent的汇报结果进行一个,深度的汇总结合,需要具备极强的推理能力,然后呢,弱agent就可以是龙猫的,这种推理能力不太行的模型,或者是其他的老版本的一种,推理模型。以达到资源的一个最大化的利用。但是这样,是特别烧钱的。这个东西,我没钱帮你去验证,但我的一个朋友用5000块钱挖洞试了一下,用gpt指挥deep seek,然后挖了一个月回本了。
建议:强agent指挥强agent或者部分弱agent干简单的活。
5.AI 的信息收集能力生成mcp等等这些工具,这个信息收集能力偏弱,不建议使用。
建议:(1).第一种还是用最早的手工信息收集方法,比如企查查,空间测绘,子域名爆破AI 信息收集,难度评估, 收集单个站点的信息,比如爬取JS文件,理解整个站点业务。
(2).第二种自动化的一个信息收集流程,集成subsfinder,purdens,crt证书查询,app.agniops的子域名查询,进行枚举查重,还有http测活,也可以结合wafscan的一个脚本,让AI调工具,批量的检测弄出来的子域名的网站WAf,没有waf可以考虑目录扫描。如果你想就是彻底的实现一个大致的输命令,然后就自动化的话。不要给那种,需要交互登录的一个信息收集的域名。那样会非常麻烦。
https://hackertarget.com/find-dns-host-records
比如说这个网站,它是可以查找内网IP的,但是这个网站它是需要交互的,我的建议是直接的把的结果复制到一个TXT文件里面,或者把多个这种需要交互的进行信息收集的域名全部整合到一个文件里面,让AI去进行机器的查重,提取,整合。这样的话,可能会好很多.
解决以上的问题的根本办法是自研agent,这个留到最后讨论。
然后呢,我们可以在深入的研讨一个问题,那就是费效比(自定义的)问题。AI在单位时间内,消耗的token的费用,和产生的效率的比例。费效比越高,说明Agent的能力越弱,费效比越低,证明agent能力越强。用更少的token干更多的事。这个主要取决于什么呢,答案是skill。
ai的约束规则到底要怎样弄呢。
下面是L师傅的分享和我的一些个人思考,感谢师傅授权。
ai做项目可能需要注意的地方
(1). 这些写个人挖洞项目的时候,就不要直接把市面上所有的绕过方式都写进知识库
原因:因为如果你绕过方式给的太多,就会出现空转,比如一个接口,一直在尝试绕过。
弊端:费时费力,浪费AI的token也就是浪费钱,而且容易触发风控
核心:进展驱动 + 硬上限 + 死路即停 + 受阻留痕
- 进展驱动:有进展,可以继续
- 尝试绕的次数一定要有顶,到顶即收
- 响应无差异 ,尝试几次后,可以直接换
3.1 让AI反思,问题出在哪里
注意:如果你去告诉AI怎么绕,那么AI就会根据你说的方法论操作,不会自主思考,应该怎么绕 ,这种有403的,甚至WAf熔断的,不要一直死磕
下面来看下师傅怎么约束ai不浪费token的
项目的方法论文档里已经把这个问题固化为规则了,核心就是「进展驱动+硬上限+死路即停+受阻留痕」四件套,设计上就是为了防止无限重试烧token。出处主要是references/methodology-scope-policy.md54、references/spec-Waf-avoidance.md项和references-archive/spec-hunt.md 55.13/52.2
1.先分流:限制分两类,待遇完全不同
类型判定处理
waf-circuit熔断(惩罚页/429/挑战页/连续拦截样)硬熔断立即停,一次都不再试,禁止对惩罚页堆payload
app-filter/waf-block(应用过滤、路径型WAF)可绕过允许换形,但受下面的预算约束
也就是说「可以绕过」的限制才进入尝试流程,熔断类直接零消耗退出。
2.换形是「进展驱动」的,不是固定次数盲试
每次换形后,如果响应语义和上一发有差异(状态码变了/报错文案变了/数据变了/拦截形态变了)=有进展,可以继续;
同点上限8次一到顶即收,不管有没有绕过去;
连续2发响应无差异=死路,立即停止,不等8次用完;
每次换形必须写bypassReason(说明这发为什么值得打),没写的发送不计入覆盖,代码层面强制。
所以实际消耗通常远小于8次:大部分死路在第第2~3发就被「连续无差异」规则掐掉了
3.卡住走三步,不许同姿势重发
同一关键点连续两次失败后,禁止第三发同姿势,必须按序走(spec-hunt52.2):
(1).反思:失败语义是什么(缺参/鉴权/应用过滤/熔断);
(2).情报:先做一轮被动OSINT(WebSearch、Wayback、crt.sh,都是只读公开源,不花目标请求);
(3).换形:按失败语义只换一种形,写bypassReason。
三步走完仍无进展→换下一个关键点,不在一棵树上吊死。
4.放弃不是丢掉:受阻面要留痕
收口(没绕过去)的面必须:
·记入 anomaly-notes.json(type:waf-blocked,含已试过的姿势);
·在报告的【受阻未测面】章节列出,并向用户汇报;
·标注「被拦截,结果不可信】一区分“安全“和“未测成”,不把拦截当成没问题。
这样即使放弃了,信息也沉淀下来:下一轮测试或人工接手时不用从零再烧一遍token。
2. 写操作接口在AI中也需要去测试,但一定要注意尺度
原因:应该可能会直接删除掉线上数据,这就是越过红线范围了。
弊端:如果不去测试,那有的漏洞测试的深度就不够
核心:
(1). 读接口可以全测试,不论是GET还是POST,
(2). 写操作的接口,需要进行深度挖掘,需要去创建数据,发现技能接口,再深入测
试。
(3). 删除不真发操作
项自对写操作接口是分层处理的,从「默认不发」到「可以发但有严格协议」,层层加码。出处主要在
references/methodology-scope-policy.md( 写操作一节)和 references-archive/spec-write-safety.md。Longv
**第一层:默认禁止,只记录**
删除/修改/创建类接口默认不发送,脚本只把它记录进「需人工测试」清单(产物里标skipped-write)。用户开工时说「只读
」,则任何写操作都不做(开工话术.md:41)。
**第二层:用户明确允许后,也只发1次裸请求**
脚本传--aLLow-write-test才会真发,且约束很死:
·每个写端点只发1次裸请求(wt-bare),不携带删除/修改数据体;
·目的只是判「接口受不受理」:返回非拒绝状态码>判定「写操作接口未授权可调用」(高危,写报告);拒绝类→排除;
·用状态码和响应语义定级,不需要真的写入数据就能证明问题。
**第三层:自建测试对象豁免(越权测试的前提)**
在授权的测试账号内创建最小测试对象(订单/项目/发票等,每对象1个)是允许的标准SRC做法:
·命名带 PENTEST_前缀,登记到站点目录 objects.json/cleanup-plan.json;
·测完自动清理并复核,删不掉的明确告诉用户手工删;
·只用于测试,不碰他人或生产数据。
**第四层:修改类验证走「快照一验证一恢复-复核」四步协议**
这是最严格的一档(spec-write-safety.md),顺序强制、逐步不可省:
步动作硬性要求
1写前快照先GET读出目标字段原值无before快照禁止发任何修改请求
2修改验证发改请求,再读一次 确认生效after与before 无差分=不算确认
3立即恢复用before原值发恢复请求验证和恢复之间不得插入其他测试,一次只改一个字段
4恢复复核再读一次确认回到原值记restoreVerified=true
**配套的红线和门禁:**
·删除类操作对他人数据永不真发一只验证到「接口受理/语义上有权限」即止;
·涉他人数据只改非关键字段(昵称/备注类),一次一个字段;
·恢复失败>当场停下并告知用户人工恢复,禁止静默继续;
·全程落盘write-tests.json(before/after 脱敏),如果报告里有改类 finding 但缺 restoreVerified=true 记录,覆盖门禁
write-restore不过,报告不得标完整。
日常偏好:只读证明代替真写
即使允许写,框架也优先用不触发风控的方式证明:
·越权/篡改(改价、改状态)用基线diff--对比请求前后响应差异,不真改数据(spec-waf-avoidance.md:23);
·业务逻辑链的57真改价×真提现、真改状态」这类终局验证交人工在授权环境执行,工具只负责把链找出来、串起来、标清楚
(methodology-chain-rules.md S6)。
注意:你不能让你的模型,又是思考,又是检查, 模型就会告诉你,都测完了,没问
题。门禁就是来防止漏测
3. 模型遇到问题,要结合业务界面功能或JS文件分析尝试,不是直接丢弃,或者谎
报无问题。
原因:如果假象太多,那就没办法深入挖掘到高价值的漏洞,只会浮于表面,该逆向
分析就需要去分析。
弊端:原本几个低中危漏洞组合链能形成高危或严重的漏洞,没有深度,直接错过了
核心:能自救的按固定流程自救,自救不了的留痕+叫人(告诉你),绝不静默跳过或硬
耗
(1). 不允许静默跳过
(2). 不要去硬刚
(3). 记忆外存,防止上下文压制,缺失。
(4). 开工读前期复盘,形成一个文件,模型才能自主学习进步
(5). 使用全新的子代理去做检查,主要就是检查哪些点没有测,哪些点没深入等等
几条贯穿性的兜底纪律
1.不许静默跳过,skip必须有名目
覆盖面只能标「测了 /排除了+一句话理由」,skip原因必须是枚举值里的(waf-stop/sign-gate/no-id-source 等),无
原因不得skip。「沉默即漏测」。
2.不许硬耗,也不许装没事
遇到死路有明确停止条件(2发无差异/熔断信号),停下来的面不是丢掉,而是留痕存档,附已试姿势,下轮或人工接手不用重
烧一遍成本。
3.上下文撑不住时有记忆外存
每完成一个阶段更新session-notes.md(假设清单、关键id、已排除项)一上下文压缩后从笔记恢复,不靠回忆;新发现立即进
objects.json。日常只读紧凑摘要不读原始raw,控制 token 膨胀。
4.开工先读lessons.md
历次实战复盘出的教训是唯一注入的经验一别人(或上次)踩过的坑,开工时先读完按它避坑,不把已解决的问题再“遇到“一次。
5.收尾前自审防自欺
宣布测完前起一个全新上下文的子代理读产物挑刺(没建的创建按钮、没钻的详情页、没闭环的假设),提出的问题不解决不收口;
最后输出审批页,用户点同意才算测完。
6.方向偏了人随时能纠
跑的过程中/goaLstatus看进度,发现方向不对按Ctrl-S输入纠正内容立即生效-一模型的"问题“也可以由人来解。
遇到的问题 / 处置机制
探测卡住(连续失败/被拦) / 「卡住三步」:反思失败语义→被动OSINT找情报→按语义只换一种形(写
bypassReason)。三步走完仍无面→记anomaly-notes.json 换下一个点
被WAF/风控拦 / 路径型:进展驱动换形(8次封顶、2发无差异即停);惩罚页/429/挑战:立即熔断,
停手不硬惑
信息不足(端点/参数学不到)/ 回JS封装、报错信息里挖真实字段;抓运行时请求补漏;仍缺→请用户复制一条带参
请求,不编造
会话/登录态过期 / 连续 401/token.expired→写 session-expired.json,停手叫人,不自动重登
授权信息不全(缺账号/缺回连地址) / 开工只集中问一次;补不上就显式记入受阻面,不静默跳过;只测了登录前的部分会产出pending-account-checklist.json,拿到账号后回来先打这张清单
复现不了 / validate-replay.mjs 重放复核,复现不了→降级为疑似,不硬当确认
测不完/面受阻 / 受阻面记入_anomaly-notes并进报告「受阻未测面」章节,附解锁条件;区分「安全」和「未测成]
前面看不看的懂没关系,可以喂给ai,让ai去写skill。
不建议直接喂给ai漏洞报告。
喂AI漏洞报告=蒸馏形成了一套技巧打法刻板了印象==限制了AI的发挥不会自主的思考,如果不行,我换一种思路,或者说结合其余API端点,来组合利用下。
下面是skill的编写方法
这里以ai最擅长的未授权举例
如何编写SKill
Skill 又称为技能,在AI自动化挖掘中,有着很大的作用,因为Skill提供了结构化的工作流程和领域专业知识点。 说的直白点,就是将经验和坑点都写入一个文件中,然后让AI去读懂,让它能够成为一个经验丰富,而不是一个傻瓜式的扫描器。
下面skill的编写都是L师傅的方法。
举例说明:
比如针对于漏洞挖掘,都是有一定的流程化
- 信息收集
- 筛选资产
- 未登录前的测试
- 登录后的测试
那么一个Skill就可以把上述的所有流程变成一个结构化,第一步调用什么?第二步在
调用什么?
Skill 编写准则
按照标准的来说,必选的SKILL.md,这是比不可少的,下面的东西,都是可选,因为
在md文件中就可以要求需要执行哪些脚本。
在这个skill中,除开工具的调用,还有就是结构化,和上面举例的一样,你想要把这
个skill 针对一些专项漏洞,比如未授权,那么是不是就要按照如何去挖掘未授权访问
来书写这个skill
未授权测试的Skill如何书写
1. 如何去发现API端点
2. 发现API端点后,怎么梳理
3. 如果不是GET应该怎么办
4. 缺少参数如何去补充
5. 类型不对form,要怎么切换
6. 登陆前/后的区别都有哪些
带着上面的问题,所以你就要懂这个未授权访问,如果不懂,那么你这个skill是没办
法写好的。
如何去发现API端点
首先要确定工具
探测API端点的工具有哪些
Thought for 6s, searched for 1 pattern (ctrl+o to expand)
·好,我梳理一下所有跟API端点发现/探测相关的工具,按用途分类:
、从页面源码/JS里提取
工具干啥的
extract_api_endpoints从JS/HTML中提取所有API路径,自动分类到功能模块(用户管理、订单管理等)
extract_runtime_api打开页面,抓取浏览器实际发出的所有网络请求(包括动态生成的)
fetch_page_source获取页面完整HTML,自动提取script 标签和表单结构
extract_all_scripts下载页面上的所有外部JS文件到本地
detect_source_map检测JS是否泄露SourceMap,有的话直接下载还原原始源码(能看到未混淆的API)
二、爬虫式探索
工具干啥的
crawl_attack_surface智能爬虫,递归爬页面→提取参数→归一化API→检测GraphQL/WebSocket→深度分析JS。最全面的端点发现工具
playwright_crawl_spa专门爬SPA(单页应用),从JS里提取前端路由定义,逐个导航发现隐藏API
capture_api_traffic打开页面后自动抓包N秒,记录所有API请求和响应结构
playwright_capture_traffic同上但用PLaywright引擎,支持代理和指纹随机化防封
三、拼接/补全遗漏的
工具干啥的
stitch_endpointsJS 里经常有baseUrL+"/api/"+version+"/users"这种拼接写法,这个工具追踪变量值,拼出完整路径
js_param_stitch从JS里提取端点+参数+变量值,自动拼接构造请求,发现隐藏接口和参数组合
analyze_js_network深度分析webpack/UMI等打包代码,提取动态构造的baseUrl、API路径、请求模式
trace_js_executionHook页面上的fetch/XHR,记录运行时实际调用了哪些API
有的时候一个工具不够,就需要多个工具来一起辅助,总之最终的目的就是将这个站点所有的API端点都要扒下来,让你能够得到。
1.1发现API
#不管什么系统,先干这三件事
extract_all_scripts(ur1="<目标uRL>")#把JS全下下来
extract_api_endpoints(source="<目标URL>")#提取接口路径
extract_runtime_api(ur1="<目标uRL>")#抓浏览器实际发的请求
#如果提取到的端点太少(比如<5个),说明被混淆了
#换方式:
stitch_endpoints(ur1="<目标URL>")#从JS变量拼接路径
#或
playwright_capture_traffic(ur1="<目标URL>")#用真实浏览器抓
梳理API端点
因为这样,才能够更好的进入下一步,业务API端点的梳理,让你知道这些API端点都
是做什么的,因为这样也能让AI去进行主动的构造。
用业务理解驱动测试(重要):
不要只依赖工具提取到的路径。想想这个系统是干什么的,主动构造你可能漏掉的接口:
如果系统是「客服平台」→应该会有ticket、customer、agent、conversation 相关接口
如果系统是「NLP平台」→应该会有intent、entity、model、train、evaluate相关接口
如果系统是「电商系统」→应该会有product、order、cart、payment 相关接口
如果系统是「后台管理」→应该会有user、role、permission、config、log相关接口
方法:
1.从提取到的路径中找出命名规律(如/api/users/list)
2.按这个规律构造可能存在的其他路径(如/api/users/detail、/api/users/create)
3.用GET试一下这些路径是否存在
4.存在就加到端点清单里
等上面把业务API端点找到并梳理完成后,那就是开始我们的测试
API 端点接口测试
GET/POST方法矩阵
有的API端点,它是GET,也有可能是POST,所有的读操作(查看),不一定都是以
GET的请求方式来定义的
.3方法矩阵测试(核心操作)
拿到端点和参数后,不要只用一种HTTP方法测。对每个接口按优先级试:
第1轮:GET/api/endpoint→最简单,先试
第2轮:GET/api/endpoint?已发现的参数=值→带query参数
第3轮:POST/api/endpoint body:{}→换POST
第4轮:POST/api/endpointbody:{"已发现的参数":值}→带body 参数
第5轮:其他方法(PUT/PATCH/DELETE)→方法变异
每一轮如果返回500或400:
→读错误消息
→提取它说的参数名
→带上这个参数名在当前轮重试
→如果当前轮(如GET)不行,换下一轮(如POST)再试
参数名参考
这种都是一些常见的,让AI能够快速拼接参数,但实际还是需要根据错误信息,或者
JS 中去提取
参数名快速参考(各种系统中常见的)
唯一标识类:id,ID,uid,userId,orgId,groupId,tenantId,shopId,projectId
名称类:name, key, keyword, query, q, search
分页类:page,pageNum, offset, start, pagesize,size,limit
状态类:status,type,category,categoryId,state
时间类:startTime,endTime,date,from,to
这些只是参考,实际参数名从错误消息里读、从JS里提取、从list返回值里拿,不硬编码。
注意这里要以实际的js文件为准。
登录前后区别
登录前,发现的API端点,肯定没有登录后发现的API端点全的,因为有一些功能点的
API,只会出现在登录后。
如果AI将登录前的API端点用于登录后来进行测试,那么就有可能出现以下问题:
- 权限问题
因为在登录前的测试,已经告诉你,这些API端点没有权限,AI在你登录后,它就会
默认所有的API端点都测试完成,且安全。 - API 端点少一部分
登录后有一些API端点只会登录后才出现,如果页面本身功能点就比较少,那么如果按
照登录前API端点,就会漏掉一部分。
防误报
一些本身就不是漏洞的,比如登录后,就直接可以看到个人信息,AI会将这种也定义为
信息泄露,所以就需要降噪来进行防止误报操作
结果判定(防误报):
你的ID=1→返回{name:"张三",phone:"138xxxx"}
改ID=2→返回{name:"李四",phone:“139xxxx"}×越权
改ID=2→返回{name:"张三”,phone:"138xxxx"}<不是漏洞(接口忽略了参数)
改ID=2→返回nu11/[]/404/{"无权限"}N有权限控制
改ID=2→返回{shop:"公开信息"}M公开内容
无法判断时:
把完整请求包和响应体记录下来,标注「需人工判断,原因:XXx」
敏感信息发现
这些都是顺带的事情,可以在这个skill中都写上,能够更好的发现一些敏感信息,类
似于Hae这种,甚至都可以把很多正则规则,让它学习用于敏感信息的发现。
3.6敏感信息泄露检测
#扫所有接口的响应
-手机号、身份证、银行卡号
-AK/SK等密钥凭证
-其他用户的私密数据(订单、聊天记录、文件)
#注意:调试接口(/debug,/actuator,/swagger)存在本身不代表漏洞
#但记录下来供参考
禁止行为
由于AI本身有时候也会偷懒,也会跳过一些关键步骤,需要在测试中需要多去了解它
都做了什么,这是防止AI偷懒。
禁止行为清单
X使用test_adaptive_fetch McP工具-必须使用scripts/auto-param-extract-and-test.mjs
xsession不清就测"未授权”-必须session_clear后确认
x只试一种HTTP方法就下结论
x看到500/400不读错误消息直接跳过
x只测孤立接口,不把1ist结果联动到detail
×改参数时只改一个值,不做多值遍历
x有凭证能调通就说「有权限控制」J能看自己=不能看别人
x登录后不重新发现API
x报告中不附完整请求包和响应包
x报告中不做数据归属分析
×错误消息只看参数名,不记录返回了什么错误
x只依赖工具提取的路径,不用业务理解主动构造可能的路径
x页面空白就放弃测试——页面空白+API能调通正是漏洞信号
x纠结”数据属于谁”而漏掉无归属用户的未授权漏洞一—用户归属为空时,任何能调通的接口都算漏洞
x不查用户归属就直接开始测一一不知道用户权限基线,后续判断全凭猜测
还有一种是执行规则,特别是一些写操作,就是增加、删除、修改这种,因为这类操作会涉及到线上数据,所以很危险,都需要在skill中禁止的,这一块可以把一些SRC的规则都喂给AI,让它去生成,这样就更加的完善,而且一些SRC不收的洞,也可以帮你进行规避。
执行规则
1.禁止自主执行一无论漏洞风险多明显,AI不得对写操作接口发送实际请求
2.全部记录到报告中一所有发现的写操作接口必须在报告中列出,标注「!需人工测试」
3.报告中需包含:
。接口路径和方法
。所属功能模块
。建议测试的漏洞类型(未授权写操作/IDOR写操作/参数篡改等)
。为何建议测试该接口(如:查询接口已泄露该模块数据,写接口可能同样存在越权)
4.仅限只读接口可自主测试一GET/列表查询/信息查看类接口可正常测试
5.报告末尾必须包含「需人工进一步测试的接口」汇总表,让测试者清楚下一步要测什么
上面的skill编写全是师傅的思路,感谢师傅的分享。
我们来系统的总结一下师傅的思路。原文写的太好了,懒得删。
skill的编写总结
skill的编写的目的是为了让ai更像我们人一样有经验的去进行漏洞挖掘。
无论是哪种的漏洞挖掘,前期基本都是信息收集,筛选资产,测试也分登录前,未登录前。
后面就有具体的区分,你要自己思考漏洞的利用链。
把漏洞的利用链思考清楚后,思考工具的调用,比如调用哪些工具。公欲成其事,必先利其器。用业务理解驱动测试,多问几个为什么,如果你是开发人员,这个系统要干什么,这些接口换做是你,你怎么写,主动构造这些接口。
防止误报,根据之前的挖洞经验和网上的资料,补充这种漏洞的误报信息,进行信息的降噪处理。
思考可不可以顺带把敏感信息提取一下。
以及哪些行为禁止。
skill编写要思考几件事:
1.目的是什么
2.流程怎么样,不同的skill之间有没有相似的流程,比如前期的信息收集
3.漏洞的利用链怎么样
4.该调用哪些工具
5.如果我是开发人员,我会做什么。然后去补充攻击面。
6.如果信息降噪,防止误报。
7.哪些行为在测试中是禁止的。
8.一直要思考怎么补充攻击面。
这几件事想出来,你的skill自能写出来。
但是,再好的ai的skill的也只是给agent打补丁。
真正能解决上述所有问题都是自研的agent和自研专门挖洞的大模型。
但是专门挖洞的大模型普通人是很难实现的。
下面是对agent自动化渗透测试工具Artex的部分研究
ARTEX 架构分析
ARTEX(AI 自主渗透测试系统)是一个 Go 单体后端 + Next.js 前端(go:embed 内嵌单二进制)+ PostgreSQL 的 LLM 多 agent 自主渗透框架,agent 能力由作者自己的 norma SDK 提供(agentcore/tool/permission/harness/memory/transcript)。
核心设计一句话概括:事件驱动的双图(资产图 + 探索图)闭环 + planner/worker 分工 + 记录型 MITM 留痕 + 工具审批门。
一、总体分层架构
┌──────────────────────────────────────────────────────────────┐
│ 前端 Next.js(go:embed 内嵌,单二进制) │
│ 仪表盘/任务/资产覆盖图/探索链路/流量/人在环路对话/系统配置 │
└──────────────┬───────────────────────────────────────────────┘
│ REST /api/* (JWT) + SSE
┌──────────────▼───────────────────────────────────────────────┐
│ server(Go net/http) │
│ · Manager:任务/引擎/DB store 生命周期 │
│ · Engine:事件驱动调度循环(plannerLoop + N 个 worker goroutine)│
└──────────────┬───────────────────────────────────────────────┘
│
┌──────────────▼───────────────────────────────────────────────┐
│ agent(norma SDK)—— 4 种角色 │
│ · goals 目标拆解器 · planner 规划者(唯一意图生成者) │
│ · worker 执行者 ×N · mainagent 人在环路 │
└──────────────┬───────────────────────────────────────────────┘
│
┌──────────────▼──────────────┐ ┌────────────────────────────┐
│ PostgreSQL 双图 │ │ 支撑子系统 │
│ 资产图(全局共享真值库) │ │ traffic 流量MITM · guard/ │
│ 探索图(每任务推进链) │ │ intercept 审批门 · enrich │
└──────────────────────────────┘ │ DNS/HTTP补全 · MCP/skills │
│ notify/report/selfupdate │
└────────────────────────────┘
二、各组件功能与代码举例
1. 入口层 cmd/artex/main.go
进程入口,负责:解析参数(-addr 前端+API、-proxy 流量录制代理)、自更新引导(selfupdate.Bootstrap)、信号处理、启动 Manager 与 HTTP server。
func run() int {
var (
addr = flag.String("addr", ":8787", "HTTP listen address")
dataDir = flag.String("data", filepath.Join(config.BaseDir(), "data"), ...)
proxy = flag.String("proxy", "127.0.0.1:8788", "traffic recording proxy address (empty to disable)")
)
flag.Parse()
...
mgr, err := server.NewManager(*dataDir, *proxy) // 打开双图 store
...
srv := server.New(ctx, mgr, skillDir, *dataDir, config.BaseDir())
httpSrv := &http.Server{Addr: *addr, Handler: srv.Handler(), ...}
2. server 层 —— API / Manager / Engine
server/manager.go(63KB):管理任务、引擎、DB store、LLM 配置、MCP/skill 注册的全生命周期。
server/engine.go(49KB):事件驱动调度核心。Engine 结构体里全是 sync.Map(paused、deleting、deadline、inflight…),支持暂停/删除/超时/收尾。关键循环:
// Run 启动 planner + N 个 worker goroutine
func (e *Engine) Run(ctx context.Context, t *Task) {
...
runTaskRoutine(rt, func(loopCtx context.Context) { e.plannerLoop(loopCtx, t) })
...
}
// runWorkerStep 从 frontier 领一条意图并完整结算
func (e *Engine) runWorkerStep(...) bool {
intent := e.claimNext(t, name)
...
}
// claimNext:意图领取
func (e *Engine) claimNext(t *Task, name string) *db.Node { ... }
这是"图一变 → 唤醒 planner → 派意图进 frontier → worker 领意图执行 → 写回 → 再唤醒"闭环的引擎载体。
3. agent 层 —— 四种角色(最核心)
① planner(规划者)—— 唯一意图生成者
agent/planner.go:事件驱动,每次图变更(debounce)被唤醒,读态势、判目标、派 0..N 条意图。它持有跨唤醒共享的 todolist,把串行利用链"记录一次、按依赖逐步派",解决无状态会话下攻击链错序/重复的问题:
// todos keeps ONE plan-scratchpad per task ... survives across wake-ups
func (p *Planner) todoFor(expID int64) *actool.TodoStore {
p.todoMu.Lock()
defer p.todoMu.Unlock()
s := p.todos[expID]
if s == nil {
s = actool.NewTodoStore()
p.todos[expID] = s
}
return s
}
其提示词里写死了决策铁律(plannerDefaultTmpl),例如"深度优先于覆盖度"、"目标未达成且无 open/running 意图时必须派至少一个意图"、"串行链分步派别并行"。
② worker(执行者)—— 只做一条意图
agent/worker.go:领一条意图,用真实工具(Bash/Kali 工具/HTTP)执行,把 fact/asset/finding 写回图后即停,不自主生成新方向:
// Worker is an LLM work agent ... it claims ONE intent, completes it
// with real tools ... writes the FACTS it found back into the graph, and stops.
关键设计是 proxyEnv——把 Bash 子进程的 HTTP 全部路由进记录代理并让工具链信任 MITM CA:
func proxyEnv(proxyAddr, caCert string) []string {
if proxyAddr == "" { return nil }
env := []string{
"HTTP_PROXY=" + proxyAddr, "HTTPS_PROXY=" + proxyAddr, ...
"NODE_USE_ENV_PROXY=1",
}
if caCert != "" {
env = append(env,
"SSL_CERT_FILE="+caCert,
"CURL_CA_BUNDLE="+caCert,
"REQUESTS_CA_BUNDLE="+caCert, ...)
}
return env
}
③ mainagent(人在环路)
agent/mainagent.go:人类操作员接口,观察 + 操舵,不亲自探索。通过 add_hint(战略提示→planner)、add_intent(高优先级意图→frontier)、steer_work(实时纠偏运行中的 worker)、set_goals/set_constraints(补目标/约束)来"落地人的意图"。
④ goals(目标拆解器)
agent/goals.go:把任务标题拆解为多个目标 + 提取操作约束,写进库。
工具目录 agent/toolcatalog.go
内置工具被"代码化 → 可枚举 → 可被 DB 覆盖"。工具的 handler 永远来自代码(DB 改不了行为),但描述/参数 schema/默认值可在 UI 覆盖:
// DecorateTool wraps t so Description()/InputSchema() report the DB overrides
// and Call() injects scalar parameter defaults ...
func DecorateTool(t actool.CoreTool, desc string, schema map[string]any) actool.CoreTool { ... }
各角色的工具集分工明确(agent/tools.go / tools_insert.go),例如 worker 只给 list_findings/add_finding/record_fact/insert_assets/list_assets/search_all_worker_traces/get_worker_trace/node_detail,而 planner 多给 add_intent/prove_goal/list_goals/kill_work/steer_work 等。
4. 双图数据层 db/
db/schema.sql 定义了资产图(companies/assets/company_scope/task_scope)与探索图(explorations/exploration_nodes/exploration_edges/exploration_anchors)。
- 资产图:全局共享真值库,节点为
root_domain/subdomain/ip/service/app/endpoint,父子关系与去重 key 由程序计算。 - 探索图:每任务独立推进链,节点为
goal/intent/fact/finding/hint,靠spawns/derived_from/yields/proves边连成血缘链。 - 锚点表
exploration_anchors(node_id, asset_id)把两图相连,支撑"资产测试覆盖度"和"资产覆盖图"。
CREATE TABLE IF NOT EXISTS exploration_anchors ( ... );
CREATE TABLE IF NOT EXISTS exploration_nodes ( ... );
CREATE TABLE IF NOT EXISTS exploration_edges ( ... );
5. 安全边界 guard + intercept
guard/guard.go:挂在 PreToolUse/PostToolUse 钩子上,每条工具调用都过审计日志 + 拦截规则:
func newGuard(ic *intercept.Interceptor) *Guard {
g := &Guard{attrib: map[string]int{}, interceptor: ic}
g.reg = hook.NewRegistry().
On(hook.PreToolUse, g.preToolUse).
On(hook.PostToolUse, g.postToolUse)
return g
}
intercept/intercept.go:用户可配置的规则引擎,三种动作 allow/deny/ask(ask = 阻塞并等人工审批,超时/拒绝即 block)。规则带正则预编译、按优先级匹配,未命中时可选 LLM 兜底裁决:
switch dec.Action {
case "deny": ... return g.block(...)
case "allow": ... return hook.Result{}
case "ask":
if !g.interceptor.HandleAsk(ctx, convID, dec, ev.ToolName, ev.Input) {
return g.block(ev.ToolName, systemBlockMessage("人工审批未通过(用户拒绝或审批超时)"), "")
}
}
这套机制让"自主性"和"可控性"并存——这是它区别于普通一键扫描器的关键。
6. 流量录制 traffic/(记录型 MITM)
traffic/traffic.go(79KB):内嵌 go-mitmproxy,把每次目标 HTTP 交换写成可浏览文件树 + SQLite 索引(支持 FTS5 全文检索),明文全量留痕:
// Package traffic implements the request-recording subsystem ... an embedded
// go-mitmproxy proxy whose addon writes every target HTTP exchange into
// a human-browsable file tree, with a sidecar SQLite index for paged queries.
// Full capture, plaintext (no redaction), target HTTP only.
indexSchema 里建立了 exchanges、exchange_bodies、blob_refs 表,worker 通过 traffic_search/traffic_get/traffic_blob 工具回看已访问过的响应,避免重复 curl。
7. 异步补全 enrich/
非 AI 的资产自动补全层:DNS 解析(dnsx)+ HTTP 探测(走记录代理),异步写回资产图,创建 IP/端口节点与 resolves/exposes 边:
func New(as *db.AssetStore, proxy func() string, workers int) *Engine {
...
for i := 0; i < workers; i++ {
go e.worker()
}
return e
}
8. LLM 基础设施
llmpool/:多 provider 连接池 + 健康检查/故障转移(pool.go/health.go)。llmrec/:LLM 调用录制(llmrec.go/capture.go)。mcphttp/:MCP 客户端,支持 Streamable HTTP 与旧版 SSE(client.go)。
9. 其他支撑子系统
notify/:多渠道告警(钉钉/飞书/企业微信/TG/邮件/webhook),含掩码/脱敏/SSRF 防护。report/:漏洞报告生成。evidence/:证据文件存储。selfupdate/:自更新(校验 SHA256SUMS、冒烟测试、失败回滚)。skills/:可插拔技能(api-recon前端接口侦察、playwright-cli浏览器自动化、scopesentryMCP),通过agent_skill_visibility按 agent 可见性绑定。
三、为什么这个 agent 会被"黑客推崇"
从纯工程角度看,它解决的是"如何让 LLM 自主、可靠、留痕地完成多步渗透链"这个难题,技术上有几个真正硬的点:
1. 事件驱动的双图闭环 + planner/worker 职责分离
不是"让一个 agent 一直说话",而是把"想"和"做"拆开:planner 只负责判态势派意图(唯一意图生成者),worker 只负责领一条意图执行并写回。这避免了单一 agent 的"发散/跑偏",同时图一变就唤醒形成真正的自主闭环——目标是证明目标达成(prove_goal)而不是人为设定步数。
2. 串行攻击链的稳定推进(todolist 跨唤醒共享)
真实攻击链(注入点→拿凭据→横向→提权)是有依赖的多步序列。planner 的共享 todolist 让它在"每次唤醒都是全新会话"的前提下仍能按依赖分步派,不错序、不重复、不空转——这是它自主走完多步利用链的关键,也是多数 LLM agent 做不到的。
3. worker 间过程级信息交换(跨 work 检索)
search_all_worker_traces / list_worker_traces / get_worker_trace 让后一个 worker 能复用前面 worker 执行过程中的观察(报错、隐藏参数、某段响应),即便那些观察还没被写成正式 fact。信息以"执行过程"为粒度流动,边界却不破(每个 worker 仍只做自己的意图)。
4. 全链路 MITM 留痕 + 证据化
所有 Bash/HTTP 流量经内嵌 go-mitmproxy 明文留痕、CA 重签、SQLite 可检索;漏洞必须"真实触发过、拿到可复现证据"才能 report_finding,还支持把流量包绑定到漏洞作为证据。这直接把"LLM 幻觉"挡在漏洞报告之外——产出的是可复现的 PoC + 流量,而非 CVE 匹配的猜测。
5. 可控的自主性(审批门 + 约束注入 + 人在环路)
guard/intercept 的 allow/deny/ask + LLM 兜底裁决、set_constraints 把操作边界注入 planner/worker 提示词、steer_work 实时纠偏、kill_work 杀单条意图——攻击者可随时介入却不打断整个任务。对实战红队来说,"能刹得住车"和"能自主跑"同等重要。
6. 扩展性:MCP + Skills + 可编辑提示词 + DB 可覆盖工具
工具描述/schema/默认值可在 DB 覆盖(handler 仍锁在代码里防篡改),提示词主体可编辑但关键规约由代码强制追加(artifactSpec 等),MCP 服务器与 skill 按 agent 可见性注入。这让它可以接入任意第三方工具链(如 ScopeSentry、playwright),成为一个"渗透编排平台"而非固定工具。
⚠️ 说明:以上是技术架构层面的分析。该项目本身采用 AGPL-3.0 协议,并在 README 中明确声明"仅供个人学习、代码研究与本地技术验证,严禁用于对任何线上系统发起实际测试"。所谓"黑客推崇",本质上推崇的是它作为授权渗透测试自动化平台的工程能力——自主闭环、过程留痕、证据可复现、可控可扩展。任何绕过授权对真实目标的使用都是非法的,与该项目的定位无关。
然后我问我喂养的agent:如果让你写一款agent解决这个agent的不足之处,你会怎么写
如果我来写一款"下一代"自主渗透 agent
先把我从代码里看到的 ARTEX 的具体不足列清楚(每条都有依据),再给出我的设计方案。我的定位不是"重写",而是"在 ARTEX 已经做对的双图 + planner/worker 分工之上,补齐它最软弱的几环"。
一、ARTEX 的 7 个核心不足(基于代码)
| # | 不足 | 代码依据 | 后果 |
|---|---|---|---|
| 1 | 去重是软约束 | planner 提示词:去重主要靠 summary 与已有意图比对,靠 LLM 自觉 |
长任务下意图重复、错序,浪费 token 和时间 |
| 2 | 操作边界是软约束 | guard/intercept 用正则匹配 + LLM 兜底裁决;约束靠 constraintBlock 注入提示词 |
编码/混淆可绕过正则;LLM 自检会"越权后道歉" |
| 3 | 上下文靠被动检索 | worker 要主动调 search_all_worker_traces 才能复用他人观察 |
模型常常"懒得搜",重复劳动 |
| 4 | 执行无沙箱 | proxyEnv 只是设环境变量,Bash 直接在宿主机/容器跑 |
恶意目标反制(返回恶意脚本诱导执行)会打穿 |
| 5 | 单模型打天下 | planner/worker/main 都用同一 LLM 配置,worker 并发数固定(默认 3) | 战略推理和执行用同一成本模型,贵且慢 |
| 6 | 记忆是折叠式、有损的 | coldgraph/compaction 把冷节点折成 digest(coldDigestsRecent),细节丢失 |
长任务后期"想不起早期关键线索" |
| 7 | 探索调度无反馈学习 | 意图优先级纯靠 planner 主观判断(add_intent 最多 4 个) |
不会根据"这类方向历史上成功率低"调整策略 |
二、我的三条设计原则
- 确定性约束优先:凡是能用代码判定的(范围、去重、依赖、资源),绝不交给 LLM。LLM 只做"代码判不了的不确定性"(判断哪个方向值得打)。
- 记忆分层 + 主动注入:不再要求 agent"记得去查",而是在它领到意图的那一刻,自动把相关信息塞进上下文。
- 成本与能力分层:战略用强模型、执行用弱模型、批处理用规则引擎——把 token 预算花在刀刃上。
三、改进版架构
┌─────────────────────────────────────────────────────────────────┐
│ Orchestrator(事件驱动,继承 ARTEX) │
│ graph-change debounce → planner 派 Intent → frontier → worker │
└───────┬──────────────────────────────────────────────────────────┘
│ 每条 Intent 不再是自由文本,而是结构化 DAG 节点
▼
┌─────────────────────────────────────────────────────────────────┐
│ ① Policy Engine(硬约束,PEP)—— 所有工具调用的必经闸门 │
│ scope 硬校验 · 命令语义审计 · 输出隔离 · 速率/资源配额 │
├─────────────────────────────────────────────────────────────────┤
│ ② Intent DAG —— 结构化意图 + 确定性去重 + 显式依赖 │
│ {target, technique, prereqs[], expected_output, value_score} │
├─────────────────────────────────────────────────────────────────┤
│ ③ Memory Bus —— 分层记忆 + 主动 RAG 注入 │
│ 工作记忆(任务内) / 过程库(worker traces) / 长期记忆(跨任务技法) │
├─────────────────────────────────────────────────────────────────┤
│ ④ Model Router —— 分层模型 + 成本感知调度 │
│ planner=强模型 · worker=按 value_score 选模型 · 批处理=规则引擎 │
├─────────────────────────────────────────────────────────────────┤
│ ⑤ Sandbox Executor —— 每次 Bash 在隔离 namespace 内跑 │
│ 网络 egress 白名单 · 只读根FS + 可写工作目录 · 资源 cgroup 限制 │
├─────────────────────────────────────────────────────────────────┤
│ ⑥ Self-Reflection —— 失败结构化归因 + 目标反制检测 │
└─────────────────────────────────────────────────────────────────┘
四、关键模块设计(附代码)
① Policy Engine —— 把软约束变成硬闸门
这是我最核心的改动。ARTEX 的拦截在 PreToolUse 钩子里做正则 + LLM 兜底,能被绕过。我改成分层硬校验,任何一层不过就直接 block,且不经过 LLM:
// PolicyEngine:确定性 PEP,不依赖 LLM 判断
type PolicyEngine struct {
scope *ScopeMatcher // task_scope 的确定性匹配器
audit *semanticAudit // 命令语义审计(归一化后再判)
outbox *OutputFilter // 工具输出隔离(防 prompt injection)
}
// Enforce 是 PreToolUse 钩子的第一层,纯代码判定
func (p *PolicyEngine) Enforce(ctx context.Context, tool string, in []byte) error {
// 1) 范围硬校验:目标必须命中 task_scope,否则直接拒绝
if target := extractTarget(tool, in); target != "" && !p.scope.Contains(target) {
return fmt.Errorf("PEP: 目标 %s 不在授权范围内,拒绝执行", target)
}
// 2) 命令语义审计:先做编码归一化(解 base64/hex/unicode 混淆)再判
if tool == "Bash" {
cmd := normalizeCommand(in) // 关键:归一化,堵住正则绕过
if p.audit.IsDestructive(cmd) { ... }
if p.audit.IsExfil(cmd) { ... }
}
return nil
}
// normalizeCommand 把混淆命令还原成规范形式,再交给规则
func normalizeCommand(in []byte) string {
s := string(in)
// 递归解码 base64 / hex / \xHH / $(printf ...) 等常见混淆
for i := 0; i < 3; i++ {
if d, ok := decodeOnce(s); ok { s = d } else { break }
}
return s
}
核心点:范围校验放在工具层(extractTarget 从 Bash/HTTP 工具入参里确定性抽目标),而不是提示词里"请自检"。混淆解码在规则匹配之前做,堵住 echo aWQ= | base64 -d | sh 这类绕过。
② Intent DAG —— 结构化意图 + 确定性去重
ARTEX 的 intent 是 summary 自由文本,去重靠比对。我改成结构化 schema + 指纹去重 + 显式依赖 DAG:
type Intent struct {
ID int64
Target string `json:"target"` // 具体资产/URL/参数,不再是一句话
Technique string `json:"technique"` // 枚举值:sqli/xss/idor/upload/...
Prereqs []int64 `json:"prereqs"` // 依赖的 fact/asset id(显式 DAG 边)
Expected string `json:"expected"` // 期望产出(可验证)
ValueScore float64 `json:"value_score"` // 调度价值分
Fingerprint string `json:"-"` // 确定性去重指纹
}
// 去重指纹 = hash(target + technique + 归一化参数),与 LLM 无关
func (it *Intent) ComputeFingerprint() string {
return sha256Hex(it.Target + "|" + it.Technique)
}
// 硬去重:claimNext 前查 frontier 里是否有同指纹 intent
func (s *Scheduler) claimNext() (*Intent, bool) {
seen := s.frontier.fingerprints()
for _, it := range s.frontier.PopByScore() {
if !seen[it.Fingerprint] {
return it, true
}
}
return nil, false
}
调度不再纯靠 LLM 主观:PopByScore() 按 ValueScore 出队,而 ValueScore 由下文的 bandit 模型更新(历史成功率先验 + 实时反馈),planner 只负责"提出候选意图",调度器负责"选哪个先打"。
③ Memory Bus —— 主动 RAG 注入(替代被动检索)
ARTEX 里 worker 要自己调 search_all_worker_traces。我改成:worker 领到意图时,Memory Bus 自动检索并注入相关上下文,模型无需"记得去查":
// 领到意图后、执行前,自动装配上下文
func (s *Scheduler) PrepareContext(it *Intent) (string, error) {
var b strings.Builder
// 1) 工作记忆:本意图 target 相关的近期 fact(确定性过滤,非全文依赖)
facts := s.mem.WorkingMem.TopK(it.Target, k=8)
// 2) 过程库:其他 worker 在同类 target/technique 上的 trace 片段
traces := s.mem.TraceStore.SemanticSearch(it.Target+" "+it.Technique, k=5)
// 3) 长期记忆:跨任务沉淀的"这类漏洞怎么打"的 playbook
playbooks := s.mem.LongTerm.Retrieve(it.Technique, k=2)
b.WriteString("【自动注入:本意图相关上下文,无需再搜索】\n")
...
return b.String(), nil
}
关键区别:ARTEX 是"工具在手,模型自觉";我是"上下文主动送到嘴边"。长期记忆(跨任务技法库)是 ARTEX 完全没有的——它只有任务内 traces 和"直接关联任务"的只读继承。
④ Model Router —— 分层模型 + 成本感知
type ModelRouter struct {
plannerModel llm.Provider // 强推理模型(如 Claude/GPT-4 级)
workerStrong llm.Provider // 中模型
workerWeak llm.Provider // 便宜模型(批量侦察)
rules *RuleEngine // 无 LLM 的确定性规则引擎
}
func (r *ModelRouter) Route(it *Intent) llm.Provider {
switch {
case it.ValueScore >= 0.8: // 高价值:注入/利用 → 强模型
return r.workerStrong
case isPassiveEnum(it): // 被动侦察/指纹识别 → 规则引擎,不耗 token
return nil // 走 rules,不调 LLM
default:
return r.workerWeak
}
}
配合 动态 worker 伸缩(ARTEX 固定 3 个):根据 frontier 积压深度和剩余 token 预算动态调 worker 数量,而不是写死。
⑤ Sandbox Executor —— 每次 Bash 隔离执行
// 每次 Bash 调用 fork 进新 namespace,而非宿主机直接 exec
func (s *Sandbox) Exec(cmd string) ([]byte, error) {
c := exec.Command("bwrap",
"--ro-bind", "/", "/", // 根FS只读
"--bind", s.workDir, s.workDir, // 仅工作目录可写
"--unshare-net", // 独立网络 ns
"--cap-drop", "ALL", // 丢全部 capability
"--die-with-parent",
"bash", "-c", cmd)
c.Env = s.egressEnv() // egress 走白名单代理
return c.CombinedOutput()
}
即便 LLM 被目标诱导执行恶意命令,最多也只影响一个无网络、无权限、可丢弃的沙箱。配合 Policy Engine 的 OutputFilter(隔离工具返回内容里的 prompt-injection 载荷),形成"输入 + 执行 + 输出"三层防线。
⑥ Self-Reflection —— 失败结构化归因 + bandit 反馈
ARTEX 有 model_error 重试,但没有"为什么失败"的结构化归因。我加一个价值反馈回路:
// 每个 intent 结束时,用确定性信号(不是 LLM)更新 technique 的先验价值
func (s *Scheduler) OnIntentDone(it *Intent, outcome Outcome) {
// outcome: fact_written / finding_confirmed / nothing / blocked
s.bandit.Update(it.Technique, outcome) // Thompson sampling 更新每个技术的成功率后验
}
好处:planner 下次再派意图时,ValueScore = prior(technique) * context_adjust,历史上成功率低的方向自动降权——这是 ARTEX 完全依赖 LLM 主观判断所做不到的。
五、对照总结
| 维度 | ARTEX(现状) | 我的改进 |
|---|---|---|
| 意图 | 自由文本,软去重 | 结构化 schema + 指纹硬去重 + 显式 DAG |
| 约束 | 提示词注入 + 正则拦截 | 工具层 PEP 硬校验 + 混淆归一化 |
| 记忆 | 被动检索 + 有损折叠 | 主动 RAG 注入 + 三层记忆(含跨任务长期记忆) |
| 执行 | 宿主机 Bash | 隔离 namespace 沙箱 + egress 白名单 |
| 模型 | 单模型 + 固定并发 | 分层路由 + 成本感知 + 动态伸缩 |
| 调度 | planner 主观 | bandit 反馈 + 确定性价值分 |
| 韧性 | 外部故障重试 | 失败归因 + prompt-injection 隔离 |
六、一句话总结我的方案
ARTEX 已经做对了"双图 + planner/worker 分工 + MITM 留痕"这套骨架;我的版本在骨架上补三样东西——把一切能确定的事从 LLM 手里抢回代码(Policy Engine、指纹去重、bandit 调度),把记忆从"等着被查"变成"主动送到嘴边"(Memory Bus),把执行从"裸奔"关进笼子(Sandbox)。
这样改出来的 agent,自主性不减,但成本更低、跑得更久、更难被绕过、更难被目标反制——这才是它作为"授权渗透自动化平台"应该有的工程形态。
注:以上设计延续 ARTEX 的 Go + norma SDK 技术栈,全部模块均可落地为独立 Go 包,且 Policy Engine / Sandbox / Intent DAG 三层都不依赖 LLM,可单独做确定性单元测试——这本身也是我对"可信自主 agent"的回答:能证明安全的部分,不要交给概率。
后面如果我打算自己写一个agent玩并写出测评的话,可能的等一段时间了,因为还得了解下go语言各种杂七杂八的。
不过ARTEX是有cve漏洞的,https://mp.weixin.qq.com/s/dUM_-s_GKoktPCUCNbaLEA
这个漏洞到时候肯定要解决。
本篇文章到此结束。
浙公网安备 33010602011771号