腾讯云第二节黑客松自动化渗透赛开发思路文档

ATK 系统架构与开发文档

第二届腾讯云黑客松 · 智能渗透挑战赛
主语言: Go 1.25 · 依赖: AstrBot + Kali 执行环境

Intro

先总结一下,我这边比赛用minimax+gpt-5.4,个人预算严重不足就没用claude了,这一套搭配开局简单题目和赛前测试题目基本一分钟不到就解出了,后面就卡住了,再后面由于minimax tokenplan的额度限制以及一些原因就没接着打了。整场比赛看下来其实最大的决定因素就是模型能力,也就是说如果用cluade opus搭配网上的各种开源项目,其实能取得的成绩也是不错的。

在架构设计上,通过subagent来实现各类角色已经是既定的主流方案,在长任务场景下,几乎所有的架构都需要一个监督者的角色,这个角色作为独立的第三方来监督其他各agent是否存在思考过度、思维漂移等现象,并且我还引入了summary_agent这个角色,进行复盘,提取当前轮次有效信息做上下文持久化,再将持久化信息传入下一轮中。agent的通信我也采用了一种自定义的Card格式进行传输,这一点在第一届的思路中也有提到。

此外,gpt-5.4也存在一些问题(在我写这篇文章时5.5已经发布了,效果提升很不错),比如上下文腐烂速度相较于其他模型来说非常快,在上下文占用超百分之五十时,模型的性能就会大幅下降,并且5.4还存在一个很致命的点就是过度思考有些严重,并且在我的架构中,负责信息收集的agent把信息传递给5.4时,由于recon侦测到的信息很复杂,它会在众多路径中陷入一个不可行的路径越陷愈深,因此后面我设计了一套评分标准,根据侦测到信息的置信度对每个攻击路径进行打分,优先对分数最高的路径进行尝试,并且在尝试次数达到规定次数后就放弃该路径,但其实效果不是很理想。

一开始设计思路的时候,跟朋友一起聊天,想到了一个好玩的点,关于ai的上下文问题,直接用QQ群聊来作为各个subagent的上下文,我新建了很多QQ群作为不同的分工,这样就可以根据角色来隔绝上下文,并且能够将上下文在qq群中持久化,后面这个也设计成功了,但其实我思考了一会,发现根本就没必要,持久化存储使用本地存储根据目录分割就可以了,前面的尝试可能更多是一个玩着试试的想法。

然后大概说一下我的设计思路吧,我在设计项目之前参考了第一届的几个项目,比较核心的观点就是less is more,这个说法成立的前提是模型的能力够强,准确来说是当模型能力够强时,过多的干扰只会扰乱模型的解决问题的思路。用我的项目来说明,因为我用的是minimax,所以我设计了大量的core agent和swarm agent分工,并针对不同的漏洞类型进行专攻,但如果将这一结构套用在opus上,一定会导致opus的效能大大降低。因此对于后续的迭代优化,我的建议就是不要把agent的角色划分的太细,尤其是对于能力强的模型越少的过度干扰才能发挥真正的作用,要做的只是规范与监督,也就是所谓的harness engine。

后面的就是整个项目的设计思路,对于比赛中这种flag的提交,我是通过监听sse的输出,识别到flag的格式就提取出来进行提交,关于为什么没有代码开源这件事,由于某些不可抗因素,导致代码全部丢失,最后开赛前一天我剩下的只有赛方服务器上的golang编译产物,所以只剩下思路和这篇开发文档了。

目录

  1. 系统定位与总体架构
  2. 组件分层
  3. 核心数据模型
  4. Agent 体系
  5. 完整数据流
  6. Channel 路由系统
  7. HTTP API 参考
  8. 配置参考
  9. 知识系统
  10. 持久化与存储布局
  11. Go 包结构
  12. 开发规范
  13. 测试策略
  14. 部署说明

1. 系统定位与总体架构

ATK 是一个多 Agent 并发智能渗透测试系统。系统由三个独立进程组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──────────────────────────────────────────────────────────────────┐
│ AstrBot (端口 6185) │
│ LLM 编排平台,管理 14 个 persona,负责思考/推理/委派 │
│ 接受 /run /next /summary /critic 等前缀命令 │
└──────────────────┬───────────────────────────────────────────────┘
│ HTTP POST /api/v1/chat (kickoff)
│ HTTP streaming SSE (reply)
┌──────────────────▼───────────────────────────────────────────────┐
│ atk-sidecar (端口 8090) │
│ Go HTTP 服务,负责: │
│ - 与 AstrBot 的所有通信(kickoff/recovery/swarm dispatch) │
│ - Card/Event/Channel 持久化 │
│ - 自动化调度(recovery loop、swarm dispatch、challenge 管理) │
│ - Flag 提交与比赛 API 对接 │
│ - Shell/Python 执行(通过 runner) │
│ - 知识库检索(指纹阶段可主动查询;恢复轮次注入 KNOWLEDGE_HINT) │
└──────────────────┬───────────────────────────────────────────────┘
│ run_shell / run_python (via MCP → HTTP)
┌──────────────────▼───────────────────────────────────────────────┐
│ Kali Linux 执行环境 │
│ 本地模式 (mode=local) 直接执行 shell/python 命令 │
│ Docker 模式 (mode=docker) 在容器内执行 │
└──────────────────────────────────────────────────────────────────┘

核心设计原则

  • sidecar 是哑管道 + 自动化控制器,不包含渗透测试逻辑
  • 所有渗透决策在 AstrBot Agent 侧(通过 persona system prompt)
  • AstrBot Agent 通过 MCP server 调用 sidecar 提供的工具(run_shellrun_pythonread_artifactupsert_card
  • sidecar 观察 card 流,自动触发 recovery/swarm/flag 提交等自动化行为

2. 组件分层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
atk-sidecar 内部分层:

┌─────────────────────────────────┐
│ httpapi (HTTP 层) │ ← 21 个端点,无业务逻辑
├─────────────────────────────────┤
│ service (核心业务逻辑层) │ ← 所有自动化调度在此
│ ┌─────────────────────────────┐│
│ │ card_pipeline swarm_disp ││ ← card 处理管道 / 蜂群分发
│ │ recovery branch_ctl ││ ← 自动恢复 / 分支控制
│ │ pioneer_dispatch session_reg││ ← Pioneer 并发 / session 生命周期
│ │ challenge_manager ││ ← 多题并发管理
│ │ astrbot_postprocess ││ ← AstrBot 回复解析,提取 card
│ └─────────────────────────────┘│
├─────────────────────────────────┤
│ 基础设施包 │
│ cards channelbus events │
│ kickoff officialapi runner │
│ knowledge submissions router │
└─────────────────────────────────┘

2.1 service 包内文件职责

文件 行数 职责
service.go 1258 Service struct 定义、公共方法(UpsertCard、Publish、QueryCards 等)、初始化
astrbot_postprocess.go 1141 AstrBot 回复解析:提取 atk_card JSON block、heuristic card 检测、PlanIR 解析
branch_controller.go 1062 多路径分支管理:PathCandidateCard 追踪、dominant_branch 锁定、stall 检测
recovery.go 1189 Recovery 控制器、recovery kickoff 构建、KNOWLEDGE_HINT 注入、官方 hint 收集
card_pipeline.go 262 card upsert 管道:ObserveRecovery/Branch、FlagCandidate 自动提交、SecurityMatcher 触发
session_registry.go 265 Session 生命周期(running/stop_requested/stopped/superseded/solved)
session_control.go 214 session 状态变更:markSolved、stopAllForStage(flag 找到后取消同 stage 其他 session)
swarm_dispatcher.go 191 HypothesisCard → 对应 vuln 专项 Agent dispatch,含 per-stage 并发上限
challenge_manager.go 197 多题并发:轮询比赛 API,自动 start challenge,dispatch commander+pioneer
pioneer_dispatch.go 328 Pioneer 独立 dispatch 逻辑,使用独立 kickoff store 和 recovery controller
signal_extract.go 214 runner 输出中提取 flag(正则匹配 flag{...}),生成 FlagCandidateCard
knowledge_zone.go 66 QueryKnowledge 接口,封装 knowledge.Retriever.QueryScoped
helpers.go 117 类型转换辅助(stringFromAny、stringSliceFromAny 等)

3. 核心数据模型

3.1 Card(结构化知识单元)

所有 agent 输出的结构化信息以 Card 形式存储。Card 通过 atk_card fenced JSON block 从 AstrBot 回复中提取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// internal/sidecar/types.go
type Card struct {
CardID string // 唯一 ID,格式: "<type>-<nanoid>"
CardType string // 见下方类型表
Stage string // challenge code(多题并发隔离用)
GroupKey string // 目标 channel key
Role string // 产出此 card 的 agent 角色
Title string
Summary string
Status string // proposed/observed/completed/halt/captured/...
Priority string // low/medium/high/critical
Confidence float64 // 0.0-1.0
Tags []string // 用于 routing 规则匹配
Payload map[string]any // 结构化内容,各 card 类型有专属字段
CreatedAt time.Time
UpdatedAt time.Time
}

Card 类型完整表

CardType 生产者 关键 Payload 字段 触发的自动化行为
AssetCard recon_agent url, tech_stack, interesting_params, tool_needs → SecurityMatcher(自动假设匹配) → HypothesisCard
HypothesisCard planner_agent, SecurityMatcher vuln_type(必填), hypothesis, preconditions, validation_steps, dominant_branch → swarm_dispatcher dispatch 对应专项 Agent
ActionCard executor_agent action_type, target, selected_tool, result, failure_level, path_id, move_id → recovery ObserveCard
RiskCard critic_agent, branchController risk_type, failure_level (L0-L5), need_branch_replan, decision → recovery trigger(halt 状态优先级最高)
FlagCandidateCard executor/pioneer/swarm candidate(精确 flag 字符串), method → markSessionSolved → stopAllForStage → 自动提交比赛平台
GroupDigest summary_agent, reflector, system facts, dead_paths, retry_ready_context, dominant_branch, required_next_moves → recovery trigger
EvidenceCard reflector evidence_items, timeline, source_cards 存档,发往 evidence_review channel
PathCandidateCard planner_agent, branchController path_id, dominant_branch, required_next_moves, move_ids branchController 追踪路径状态
PlanIRCard planner_agent(经 astrbot_postprocess 解析) goal, dominant_branch, required_next_moves, stop_conditions branchController 锁定分支
BranchStateCard branchController(system 自动生成) dominant_branch, lock_state, stall_state, remaining_moves 发往 main_control
ToolCapabilityCard system(runner 探测后生成) available_tools, kali_tools 广播给 main_control
PathScoreCard branchController(内部) scores 内部状态管理

3.2 Event

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type Event struct {
ID string
Type string // "card.upserted" / "system.kickoff.created" / "system.recovery.triggered" / ...
Stage string
Source string
GroupKey string
Role string
Tags []string
Status string
Impact string
Message string
CardType string
CardID string
Payload map[string]any
CreatedAt time.Time
}

Event 通过 events.Bus 发布,存储在 runtime_data/events/*.jsonl,agent 侧不直接消费 event(agent 读 channel messages)。

3.3 Kickoff Envelope

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// internal/sidecar/kickoff/store.go
type Envelope struct {
ID string
Source string // "competition_api" / "automation" / "swarm_dispatcher"
Trigger string // 触发原因
TargetRole string // 目标 AstrBot persona ID
TargetChannel string // 期望输出的 channel
Sender string // "system://official_api"
CommandPrefix string // "/run"
CommandText string // 完整 kickoff 消息文本
Summary string
Stage string // challenge code
SnapshotHash string
Snapshot map[string]any // 官方 API 快照
Meta map[string]any
SessionIDOverride string // 非空时覆盖 AstrBot session ID(多题并发用)
CreatedAt time.Time
Status string
}

Session ID 命名约定

场景 Session ID 格式
单题 commander atk-commander-main-v3(yaml 配置)
单题 pioneer atk-pioneer-v1(yaml 配置)
多题 commander atk-cmd-{challenge_code_lowercase}
多题 pioneer atk-pio-{challenge_code_lowercase}
蜂群 agent atk-swarm-{vuln_type}-{challenge_code_lowercase}

3.4 PublishedMessage(Channel 消息)

1
2
3
4
5
6
7
8
9
10
11
// internal/sidecar/channelbus/publisher.go
type PublishedMessage struct {
ID string
ChannelKey string
Stage string // 从嵌入 card/event 自动提取(多题过滤用)
Text string
Tags []string
Card *sidecar.Card
Event *sidecar.Event
PublishedAt time.Time
}

4. Agent 体系

4.1 Core Agents(8 个)

角色 ID(AstrBot persona_id) 中文定位 主要输出 Card 工具权限
commander_main 主控、调度、fast_probe GroupDigest, HypothesisCard, RiskCard run_shell, run_python, read_artifact
recon_agent 低成本侦察、攻击面画像 AssetCard run_shell, run_python, read_artifact
planner_agent 攻击路径规划、假设建立 HypothesisCard, PathCandidateCard, PlanIRCard read_artifact
executor_agent 执行批准动作、回传结果 ActionCard, FlagCandidateCard run_shell, run_python, read_artifact
critic_agent 风险审查、失败分级(L0-L5) RiskCard read_artifact
summary_agent 上下文压缩、复盘摘要 GroupDigest read_artifact
reflector 证据归档(solved)/ 经验沉淀(unsolved) EvidenceCard, GroupDigest read_artifact
pioneer 直攻型并发解题(15 步预算) FlagCandidateCard, GroupDigest run_shell, run_python, read_artifact

4.2 Swarm Agents(6 个)

角色 ID 专项漏洞 预算 vuln_type 触发值
sqli_agent SQL 注入 10 步 sqli
ssrf_agent SSRF 10 步 ssrf
auth_bypass_agent 认证绕过/IDOR 10 步 auth_bypass
file_inclusion_agent 文件包含/路径遍历 10 步 file_inclusion
rce_agent RCE/命令注入 10 步 rce
xss_agent XSS/客户端注入 10 步 xss

蜂群 agent 与主链路并发运行,不通过 transfer_to_*,找到 flag 后 sidecar 自动取消同 stage 其他 session。

4.3 Persona 文件结构

Persona 以 persona/assembled/*.md 作为最终交付物,直接复制到 AstrBot Web。

1
2
3
4
5
6
最终交付目录:
persona/assembled/commander_main.md
persona/assembled/recon_persona.md
persona/assembled/summary_persona.md
persona/assembled/planner_persona.md
... 共 14 个完整 prompt 文件

使用方式:

1
2
# 打开对应文件并复制完整 prompt
persona/assembled/<role>.md

4.4 失败分级体系(L0-L5)

critic_agent 使用,recovery 触发优先级依据:

级别 含义 recovery 行为
L0 仅有原始现象,不足以下结论 不触发 recovery
L1 工具/命令/请求本身失败 触发 recovery,重试
L2 前提条件不成立(认证、会话缺失) 触发 recovery,补前提
L3 外部环境干扰(WAF、限速) 触发 recovery,绕过策略
L4 假设本身被证伪 触发 recovery,换分支
L5 整体路径设计错误 触发 recovery + 注入 reference layer 知识

5. 完整数据流

5.1 题目 Kickoff 流程(单题模式)

1
2
3
4
5
6
7
8
9
10
11
12
13
官方 API 变更检测(scheduler, 每 10s)

official.FetchStageSnapshot()
↓ 检测到 hash 变化
dispatchKickoff(ctx, snapshot)
├── kickoff.NewEnvelope(cfg.Automation.Kickoff, trigger, snapshot)
│ CommandText: "/run [SYSTEM][competition_api] stage=X target_url=Y ..."
│ → AstrBot POST /api/v1/chat { session_id: "atk-commander-main-v3" }
│ → SSE streaming reply → astrbot_postprocess → extractReplyCards

└── go dispatchPioneerKickoff(snapshot) ← 并发,独立 goroutine
CommandText: "/run [SYSTEM][pioneer] stage=X target_url=Y ..."
→ AstrBot POST /api/v1/chat { session_id: "atk-pioneer-v1" }

5.2 多题并发流程(challenge_manager)

1
2
3
4
5
6
7
8
9
10
11
challengeManager.tick()  ← 每 30s

official.FetchChallengeList()
↓ 发现未解且未 active 的题目(按 difficulty 升序)
official.StartChallenge(code) → entrypoints

dispatchChallenge(ch, entrypoints)
├── cmdEnv.SessionIDOverride = "atk-cmd-" + code
│ dispatchKickoffEnvelope(cmdEnv)
└── pioEnv.SessionIDOverride = "atk-pio-" + code
go dispatchPioneerEnvelope(pioEnv)

5.3 Swarm Agent 分发流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
planner_agent 输出 HypothesisCard { vuln_type: "sqli" }

AstrBot reply → astrbot_postprocess → extractReplyCards

upsertCard() → card_pipeline

swarmDisp.MaybeDispatch(card)
├── 检查 card.CardType == "HypothesisCard"
├── 检查 vuln_type 在已知 6 种之内("unknown" 不触发)
├── 检查同 stage+vuln_type 未有 active session
├── 检查 stageCount[stage] < MaxConcurrentPerStage (默认 3)
└── go dispatch(agentRole="sqli_agent", vulnType="sqli", stage, card)

swarmCfg.TargetRole = "sqli_agent"
swarmCfg.TargetChannel = "exec_feedback"
snapshot.Normalized = { stage, vuln_type, target_url, hypothesis, validation_steps, ... }
env.SessionIDOverride = "atk-swarm-sqli-" + sanitize(stage)
loadDomainSkill("sqli") → env.CommandText += "[DOMAIN_SKILL]\n..."
dispatchKickoffEnvelope(env)

另一触发路径(SecurityMatcher,专用于指纹到漏洞家族假设的自动匹配):
AssetCard 到达 card_pipeline

securityMatcher.MatchBySignals(signals)
↓ 命中高置信条目
自动生成 HypothesisCard { vuln_type: "rce", source: "security/*" }
↓ 同上 swarm dispatch 路径

5.4 Recovery 流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
agent 输出 GroupDigest(status=unsolved)或 RiskCard(status=halt)

upsertCard() → recovery.ObserveCard(card)
├── recoveryTriggerFromCard(card) → recoveryTrigger{ Stage, CardType, CardID, Reason, FailureLevel }
└── 更新 stageRecoveryState.queued(高优先级 trigger 覆盖低优先级)

dispatchKickoffEnvelope 完成回调 → maybeStartRecoveryRound

recovery.CompleteKickoff(envelope) → returns queued trigger(若有)

buildRecoveryEnvelope(trigger, previous)
├── CommandText 包含:
│ - [SYSTEM][recovery_loop] stage=X round=N failure_level=L4 reason=...
│ - 恢复策略指令(先 summary → critic → reflector → planner)
│ - [RETRY_READY_CONTEXT] 块(最近 GroupDigest 内容)
│ - [OFFICIAL_HINT] 块(来自比赛平台 /api/hint)
│ └── [KNOWLEDGE_HINT] 块(知识库检索结果,按 trigger/runbook/reference 分层)
└── 新 kickoff → AstrBot(同 session)

recovery 轮次限制:
- commander: max_rounds = 5(yaml 配置)
- pioneer: max_rounds = 2
- swarm: 无 recovery(10 步结束即结束)
- competition.auto_hint = true 时:round >= 2 且 hint_viewed=false 才请求 hint(pioneer 不请求)

5.5 Flag 提交流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
任意 agent 输出 FlagCandidateCard { candidate: "flag{xxx}", stage: "BwAMWQ..." }

upsertCard() → card_pipeline
├── markSessionSolved(sessionID, "flag candidate captured")
│ └── stopAllForStage(stage, exceptSessionID)
│ → 取消同 stage 其他 session 的 HTTP context(best-effort)
│ → 标记为 stop_requested

└── maybeAutoSubmitFlagCandidate(card)
├── extractFlagValue(candidate) ← 正则提取 flag{...}
├── 检查 confidence >= min_confidence (默认 0.85)
├── 检查未重复提交
├── [Competition 模式] official.SubmitCompetitionFlag(stage, flag)
│ → POST /api/submit { code: stage, flag: flag }
└── [Legacy 模式] official.SubmitFlag(stage, flag, sourceCardID)
→ POST {flag_submission.endpoint_path}

6. Channel 路由系统

6.1 6 个 Channel

Channel Key 默认入口 读权限角色 用途
main_control commander_main summary, planner, critic, reflector, 6 swarm 主控决策中心,沉淀摘要/计划/风险
recon_raw recon_agent summary, planner, reflector 侦察原始数据,AssetCard/OBS
exec_feedback executor_agent summary, planner, critic, reflector, 6 swarm 执行反馈,ActionCard/RESULT/FlagCandidateCard
evidence_review reflector critic, commander_main, reflector 证据归档,EvidenceCard/REPORT
direct_solve pioneer commander_main, summary Pioneer 独立解题通道
swarm_feedback sqli_agent commander_main, summary, planner 蜂群专项输出汇总

6.2 Publish 规则(tag/card_type → channel)

匹配条件 目标 Channel
tag: OBS 或 CardType: AssetCard recon_raw
tag: SUMMARY 或 CardType: GroupDigest main_control
tag: PLAN 或 CardType: HypothesisCard main_control
tag: CRITIC 或 CardType: RiskCard main_control
tag: ACTION 或 tag: RESULT exec_feedback
CardType: FlagCandidateCard exec_feedback + swarm_feedback
tag: REPORT 或 CardType: EvidenceCard evidence_review
tag: SKILL evidence_review
tag: SWARM swarm_feedback
tag: OfficialAPI main_control

6.3 Mirror 规则(额外复制到其他 channel)

匹配条件 额外发往
AssetCard + confidence >= 0.85 main_control
RiskCard + status=halt evidence_review
tag: RESULT + impact=high main_control

6.4 Ingress 模式

  • main_control: command_only,只接受以 /run /next /summary /critic 开头的命令
  • 其余 channel: none,不接受外部写入(只由 sidecar 内部 publish 写入)

6.5 Stage 过滤(多题并发隔离)

1
2
3
GET /v1/channels/messages?role=summary_agent&channel_key=main_control&stage=BwAMWQ
→ channelbus.Store.List(filter{Stage: "BwAMWQ"})
→ 只返回 PublishedMessage.Stage == "BwAMWQ" 的消息

Stage 在 filePublisher.Publish 时自动从嵌入的 Card.Stage 或 Event.Stage 提取,调用方无需显式传递。


7. HTTP API 参考

所有端点均无认证(内网部署,sidecar 信任所有请求)。

方法 路径 说明 关键参数
GET /healthz 健康检查
GET /dashboard Web Dashboard HTML
GET /v1/dashboard/progress Dashboard 数据(JSON)
GET /v1/channels 列出所有 channel(按 role 过滤) ?role=
GET /v1/channels/messages 查询 channel 消息 ?role= &channel_key= &stage= &after_id= &limit=
GET /v1/cards 查询 card 列表 ?card_type= &stage= &group_key= &tag= &limit=
GET /v1/cards/{type}/{id} 获取单张 card
POST /v1/cards/upsert 插入/更新 card(agent 通过 MCP 调用) {card, publish, publish_text, preferred_channels}
POST /v1/events 发布事件 {event}
POST /v1/publish 发布消息到 channel {text, tags, card, event, preferred_channels}
POST /v1/ingress/local 本地消息入站 {channel_key, message, sender}
GET /v1/ingress/resolve 解析 channel 入站路由 ?channel_key=
POST /v1/official-api/poll 手动触发官方 API 轮询
POST /v1/official-api/submit-flag 手动提交 flag {flag, stage, source_card_id}
GET /v1/system/kickoffs 列出 kickoff envelope ?target_role= &limit= &after_id=
POST /v1/sessions/{session_id}/stop 手动停止 session
GET /v1/knowledge/query 查询知识库 ?q= &zone= &zones= &layers= &tags= &limit=
POST /v1/runner/shell 执行 shell 命令 {command, timeout}
POST /v1/runner/python 执行 Python 代码 {code, timeout}
GET /v1/runner/artifacts/{job_id} 获取执行结果

8. 配置参考

配置文件:configs/atk-sidecar.yaml(主配置)

8.1 顶层配置段

1
2
3
4
5
6
7
8
9
10
11
12
server: # HTTP 监听地址
storage: # 持久化根目录(默认 runtime_data/)
scheduler: # 官方 API 轮询调度(interval 默认 10s)
official_api: # 比赛/题目平台 API(snapshot, hint, flag 提交)
automation: # kickoff + recovery + pioneer 配置
challenge_manager: # 多题并发管理(默认 disabled)
swarm_dispatcher: # 蜂群分发配置(默认 enabled, max 3/stage)
competition: # 比赛平台直连(独立于 official_api,默认 disabled)
knowledge: # 知识库检索(inject_on_recovery, top_k, paths)
runner: # Shell/Python 执行器(local/docker 模式)
channels: # Channel bus 配置(publisher, ingress)
routing: # Channel 路由规则(channels, publish_rules, mirror_rules)

8.2 关键配置项说明

多题并发开关

1
2
3
4
challenge_manager:
enabled: true # 启用后接管 challenge 生命周期
poll_interval: "30s"
max_concurrent: 3 # 比赛平台最大并发题目数

蜂群分发

1
2
3
4
5
swarm_dispatcher:
enabled: true
max_concurrent_per_stage: 3 # 每道题同时运行的蜂群 agent 上限
cooldown: "0s" # 同 vuln_type 重新 dispatch 的冷却时间
skills_dir: "skills/domain" # domain skill 文件路径

比赛平台直连(Competition 模式):

1
2
3
4
5
6
competition:
enabled: true
auth_token: "Agent-Token 值"
base_url: "https://competition-api.example.com"
auto_submit: true # FlagCandidateCard → POST /api/submit
auto_hint: true # recovery round >= 2 → POST /api/hint(pioneer 除外)

AstrBot 连接(kickoff 部分):

1
2
3
4
5
6
7
8
automation:
kickoff:
mode: "astrbot"
astrbot:
base_url: "http://127.0.0.1:6185"
api_key: "abk_xxx"
session_id: "atk-commander-main-v3"
enable_streaming: true # SSE 流式回复,启用实时卡片解析

8.3 配置 Struct 索引

struct 名 yaml key 文件
ServerConfig server config.go:34
OfficialAPIConfig official_api config.go:47
AutomationConfig automation config.go:93
PioneerKickoffConfig automation.pioneer_kickoff config.go:101
ChallengeManagerConfig challenge_manager config.go:107
SwarmDispatcherConfig swarm_dispatcher config.go:113
CompetitionConfig competition config.go:26
KnowledgeConfig knowledge config.go:120
KickoffConfig automation.kickoff config.go:132
AstrBotKickoffConfig automation.kickoff.astrbot config.go:142
RoutingConfig routing config.go:172

9. 知识系统

9.1 知识库结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
knowledge/
indexes/ # 索引文件(已构建,直接被 Retriever 加载)
zone1_ctf_web/
trigger.jsonl # 触发规则:看到信号 X → 建立分支 Y
runbook.jsonl # 操作手册:按步骤执行 A/B/C
reference.jsonl # 参考资料:工具、CVE、服务特性
zone2_src_component/
zone3_multi_network_oa/
zone4_ad_pentest/
raw/ # 原始知识文档(供重新构建索引用)
security/ # SecurityMatcher 数据(特征匹配型)
apache/httpd-path-traversal.md
redis/unauthorized-access.md
tomcat/manager-default-creds.md

9.2 知识层语义

Layer 作用 触发时机
trigger 信号→分支映射(看到 X,考虑 Y 漏洞) 所有 recovery 轮次
runbook 分支确认后的步骤手册(如何利用 Y) 所有 recovery 轮次
reference 工具/CVE/服务特定参考 L5_STRATEGIC_FAILURE 或 hard_stall

Recovery 注入的 [KNOWLEDGE_HINT] 格式:

1
2
3
4
5
6
7
8
9
10
11
[KNOWLEDGE_HINT]
以下为可选战术参考,不是事实。当前目标的真实观测优先级高于任何知识提示。

[TRIGGER SIGNALS] — 匹配到以下信号时触发对应漏洞分支:
- ref=... score=0.82 title=... hint=...

[RUNBOOK] — 如果确认漏洞存在,按以下步骤执行:
- ref=... score=0.71 branch=sqli hint=...

[REFERENCE] — 工具/CVE/服务特定参考(仅 L5/hard stall 时包含):
- ref=... score=0.65 hint=...

9.3 SecurityMatcher

独立于 Retriever,专门处理特征指纹匹配:

  • 加载 knowledge/security/**/*.md(YAML frontmatter + Markdown body)
  • 特征字段:signals(版本字符串、服务指纹)、category(漏洞类别)、cve
  • 触发:AssetCard 到达 card_pipeline 时,MatchBySignals(tech_stack + notes + url)
  • 命中(score >= 1.0)→ 自动生成 HypothesisCard → 触发 swarm dispatch

9.4 Domain Skills

1
2
3
4
5
6
7
8
skills/domain/
sqli/SKILL.md # SQL 注入战术手册(~107 行)
ssrf/SKILL.md
auth-bypass/SKILL.md
file-inclusion/SKILL.md
rce/SKILL.md
xss/SKILL.md
web-recon/SKILL.md # Web 侦察技术(recon_agent 使用)

Domain skill 已直接内联到对应的 assembled persona system prompt 中。
同时,swarm_dispatcher.goloadDomainSkill() 也会在 dispatch 时动态注入 CommandText(备份路径)。


10. 持久化与存储布局

所有持久化使用本地 JSONL 文件,无数据库依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
runtime_data/                          # 运行时数据根目录(yaml: storage.root_dir)
cards/
<CardType>.jsonl # 每种 card 类型一个文件,按 card_id dedup
events/
events.jsonl # 事件流,追加写入
channels/
main_control.jsonl # channel 消息,追加写入
recon_raw.jsonl
exec_feedback.jsonl
evidence_review.jsonl
direct_solve.jsonl
swarm_feedback.jsonl
system_kickoff/
commander_main.jsonl # kickoff envelope 镜像(commander)
pioneer_kickoff/
pioneer.jsonl # kickoff envelope 镜像(pioneer)
flag_submissions/
submissions.jsonl # flag 提交记录
runner_artifacts/
<job_id>.txt # shell/python 执行输出

runtime_exec/ # runner 工作目录(临时文件)

Card 存储特点

  • 同一 (CardType, CardID) 做 upsert(新版本覆盖旧版本)
  • 查询通过 CardFilter{CardType, Stage, GroupKey, Tag, Limit} 过滤
  • Channel 消息只追加,不修改;分页通过 AfterID + Limit 实现

11. Go 包结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
atk/
cmd/atk-sidecar/
main.go # 入口:初始化所有组件,启动 HTTP 服务 + 调度 goroutine

internal/sidecar/
types.go # 核心类型:Card, Event, PublishRequest, OfficialSnapshotKickoff
id.go # NewID(prefix) — nanoid 生成

cards/store.go # JSONL card 存储(Upsert, List, Get)
channelbus/
publisher.go # PublishedMessage 定义 + filePublisher(写 JSONL)
store.go # List(ListFilter) — 读 channel 消息,支持 Stage 过滤
config/config.go # 全部 Config struct + Load() + applyDefaults()
events/
bus.go # 事件总线(Publish, Subscribe)
store.go # 事件 JSONL 持久化
httpapi/
server.go # HTTP 路由注册 + 所有 handler
dashboard.go # /v1/dashboard/progress 数据聚合
kickoff/store.go # Envelope 定义 + fileStore/httpStore/astrBotStore 实现
knowledge/
retriever.go # TF-IDF 知识检索(New, Query, QueryScoped)
security_matcher.go # 特征指纹匹配(NewSecurityMatcher, MatchBySignals)
officialapi/client.go # 比赛/题目平台 API 客户端
planning/ir.go # PlanIR 解析(atk_plan XML-like 结构)
router/router.go # 路由规则引擎(ResolvePublish, ResolveIngress)
runner/
executor.go # Shell/Python 执行(local/docker 模式)
safety.go # 命令安全检查
types.go # RunRequest, RunResult
service/ # 核心业务逻辑(见 2.1 节文件职责表)
submissions/store.go # Flag 提交记录 JSONL 存储

包依赖原则

  • service 包依赖所有基础设施包,基础设施包之间不互相依赖
  • httpapi 只依赖 service,不直接操作 storage
  • config 包被所有其他包依赖,不依赖任何内部包

12. 开发规范

12.1 Card 输出规范

agent 通过 fenced code block 输出 card,sidecar 自动解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
```atk_card
{
"card_type": "HypothesisCard", // 必填,使用上方表格中的类型名
"card_id": "hypothesis-sqli-1", // 必填,格式: <type>-<描述>[-<序号>]
"group_key": "main_control", // 目标 channel(可选,有默认值)
"role": "planner_agent", // 产出角色
"summary": "...",
"status": "proposed",
"priority": "medium",
"confidence": 0.75, // 0.0-1.0
"tags": ["PLAN"],
"payload": { ... } // 类型专属字段
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

`stage` 字段由 sidecar 从 envelope 自动填充,agent 通常不需要手动指定。

### 12.2 FlagCandidateCard 规范(关键)

```json
{
"card_type": "FlagCandidateCard",
"card_id": "pioneer-flag-abc123",
"group_key": "exec_feedback",
"role": "pioneer",
"status": "captured",
"confidence": 0.95,
"tags": ["FLAG", "PIONEER"],
"payload": {
"candidate": "flag{exact_flag_string_here}", // 只放精确 flag,不加任何额外文字
"method": "SQL union-based injection",
"session_id": "atk-pioneer-v1" // 用于 markSessionSolved
}
}

session_id 字段用于 sidecar 的 markSessionSolved。如果 agent 不知道自己的 session_id,sidecar 会 fallback 到 DispatchResult.SessionID。

12.3 Recovery 上下文块规范

sidecar 在 recovery kickoff 的 CommandText 中注入以下块(按顺序):

1
2
3
4
5
6
7
8
[RETRY_READY_CONTEXT]
<最近 GroupDigest 的 retry_ready_context 字段内容>

[OFFICIAL_HINT]
<比赛平台 hint>

[KNOWLEDGE_HINT]
<知识库检索结果>

agent 应优先消费 [RETRY_READY_CONTEXT](最高优先级),[KNOWLEDGE_HINT] 视为可选参考。

12.4 Go 编码约定

  • 错误处理:公共方法返回 error,内部 helper 返回值类型或零值
  • context 传播:所有 IO 操作必须接受 ctx context.Context,async goroutine 使用 context.Background()
  • 并发安全:共享状态用 sync.Mutex(参考 sessionRegistry、swarmDispatcher)
  • 日志:使用 s.logger.Printf(),不使用全局 log;agent/component 名作前缀 [challenge_manager] ...
  • 空值防御:所有方法第一行检查 receiver 是否为 nil(参考 recoveryController 系列方法)
  • 向后兼容:新增可选字段用零值作 no-op(参考 SessionIDOverrideListFilter.Stage

12.5 Kickoff CommandText 格式

1
2
3
4
5
/run [SYSTEM][<source>] stage=<code> target_url=<url> <key1>=<val1> ...
<newline>
<optional block 1>
<newline>
<optional block 2>

键值对中不能有空格(sidecar 用空格分割为 parts)。多词值用 _ 连接。


13. 测试策略

13.1 现有测试文件

文件 覆盖内容
service/service_test.go 端到端集成测试:channel routing, card pipeline, recovery, swarm dispatch
httpapi/competition_flow_test.go 完整比赛流程:kickoff → card → flag → submit
kickoff/store_test.go AstrBot kickoff command text 构建
knowledge/retriever_test.go 知识检索:zone/layer 过滤、BM25 评分
config/config_test.go 配置加载、角色白名单验证
planning/ir_test.go PlanIR XML 解析
runner/executor_test.go Shell/Python 执行、超时、输出裁剪
runner/safety_test.go 危险命令检测

13.2 测试原则

  • service_test.go 使用 httptest.NewServer 模拟 AstrBot,不需要真实 AstrBot
  • 每个测试用 t.TempDir() 作为 storage root,测试间完全隔离
  • config_test.go 的 allowedRoles map 是新增 agent 角色的契约检查——新增角色后必须更新

13.3 运行测试

1
2
3
go build ./...                 # 编译检查
go test ./... # 全量测试
go test ./internal/sidecar/service/... -v -run TestServiceChannelMessages

14. 部署说明

14.1 进程启动顺序

1
2
3
4
5
6
1. 启动 Kali 执行环境(如 Docker 容器)
2. 启动 AstrBot(端口 6185)
- 确认 14 个 persona 已从 `persona/assembled/*.md` 粘贴到 AstrBot Web
- 确认 MCP server(atk-sidecar MCP)已配置
3. 启动 atk-sidecar(端口 8090)
ATK_SIDECAR_CONFIG=configs/atk-sidecar.yaml ./atk-sidecar

14.2 Persona 配置

1
2
3
# 直接复制 persona/assembled/<role>.md 到 AstrBot Web
# 例如:
sed -n '1,40p' persona/assembled/commander_main.md

14.3 构建

1
go build -o atk-sidecar ./cmd/atk-sidecar/

14.4 多题并发模式启用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# configs/atk-sidecar.yaml
competition:
enabled: true
auth_token: "<Agent-Token>"
base_url: "https://hackathon-api.example.com"
auto_submit: true
auto_hint: true

challenge_manager:
enabled: true
poll_interval: "30s"
max_concurrent: 3

# 单题模式的 scheduler 可以保留(用于快速测试),
# 但 challenge_manager 开启后会接管完整的题目生命周期
scheduler:
enabled: false # 建议在 challenge_manager 模式下关闭

14.5 环境变量

变量 说明 默认
ATK_SIDECAR_CONFIG 配置文件路径 configs/atk-sidecar.yaml

14.6 运行时状态检查

1
2
3
4
5
6
7
8
9
10
11
# 健康检查
curl http://localhost:8090/healthz

# 当前 session 状态
curl http://localhost:8090/v1/sessions/atk-commander-main-v3

# 最近 FlagCandidateCard
curl "http://localhost:8090/v1/cards?card_type=FlagCandidateCard&limit=10"

# Dashboard
open http://localhost:8090/dashboard

附录:完整 Card 字段速查

FlagCandidateCard.payload

1
2
3
4
candidate          string   精确 flag 字符串(仅此,不加任何额外内容)
method string 利用方式简述
session_id string 当前 AstrBot session id(用于 markSessionSolved)
vuln_type string (蜂群输出时填写)

HypothesisCard.payload

1
2
3
4
5
6
7
8
9
10
vuln_type          string   必填,触发 swarm dispatch 的关键字段
hypothesis string 假设描述
preconditions []string 前提条件
validation_steps []string 验证步骤
success_signals []string 成功信号
failure_signals []string 失败信号
dominant_branch string 当前最强主线
required_next_moves []string 必要的下一步动作
path_id string 路径 ID(稳定、跨轮复用)
tool_requirements []string 工具需求描述

GroupDigest.payload

1
2
3
4
5
6
7
8
9
10
11
facts              []string 已验证事实
unknowns []string 待确认项
priorities []string 优先级
dead_paths []string 已证伪路径
repeat_failures []string 重复失败模式
retry_ready_context []string 下一轮可直接消费的摘要
knowledge_candidates []string 候选知识提示(降噪后)
dominant_branch string
required_next_moves []string
active_move_id string
stall_state string "soft_stall" / "hard_stall" / ""

RiskCard.payload

1
2
3
4
5
6
7
risk_type          string
reason string
failure_level string L0-L5
decision string "proceed" / "require_revision_before_execute" / "halt"
need_branch_replan bool L4/L5 时通常为 true
retry_advice string
anti_pattern []string

ActionCard.payload

1
2
3
4
5
6
7
8
9
10
action_type        string
target string
selected_tool string
result string
failure_level string L1-L4(成功时为空)
failure_reason string
observed_deltas []string 与预期的差异
path_id string
move_id string
evidence_gain string