- AI 技能
- AI 插件
- 人工智能
【免费下载链接】agentic-awesome-skills
AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,600+ agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.
本篇技术指南围绕开源仓库 agentic-awesome-skills(AAS)中attack-tree-construction技能及其配套的implementation-playbook.md实施手册展开,系统讲解如何以 AND/OR 逻辑树对攻击场景建模、对叶子节点标注成本/技能/时间/可探测性等属性,并借助数据模型、Fluent 构建器、可视化导出器与路径分析器四套可直接运行的 Python 模板,完成攻击路径枚举、缓解覆盖率计算与防御投资优先级排序。读完本文,你将掌握从"攻击者目标"到"可量化防御决策"的完整建模闭环,并能将攻击树直接用于安全评审、红队范围划定与利益相关方风险沟通。
一、技能定位与适用场景
在 AAS 仓库中,该技能以两个组件形式存在,内容同源:
- 技能定义文件(仓库根目录下亦有 同名技能),其 frontmatter 标注
risk: offensive、source: community,描述为"构建全面的攻击树以可视化威胁路径,用于映射攻击场景、识别防御缺口、向利益相关方沟通安全风险"; - 实施手册,即本文核心依据,收录了本技能引用的全部模式、清单与代码样例。
根据 SKILL.md,适合使用本技能的典型任务包括:
- 可视化复杂攻击场景(多步骤、多分支的攻击链);
- 识别防御缺口并确定优先级;
- 向利益相关方(管理层、开发团队、客户)沟通风险;
- 规划防御投入或测试范围(例如渗透测试、红队行动的作用域划定)。
SKILL.md 同时明确了不适用的情形:缺乏授权或未定义建模范围、任务仅是一般性风险评审而无攻击路径建模需求、请求与安全评估或设计无关时,均不应启用本技能。
二、攻击树核心概念
攻击树是一种将攻击者达成目标的多种途径系统化建模的结构化方法。根节点代表攻击者想要达成的根目标(Root Goal),向下递归分解为子目标与具体攻击步骤。
1. 攻击树结构
[Root Goal] | ┌────────────┴────────────┐ │ │ [Sub-goal 1] [Sub-goal 2] (OR node) (AND node) │ │ ┌─────┴─────┐ ┌─────┴─────┐ │ │ │ │ [Attack] [Attack] [Attack] [Attack] (leaf) (leaf) (leaf) (leaf)2. 节点类型
| Type | Symbol | Description |
|---|---|---|
| OR | Oval | Any child achieves goal |
| AND | Rectangle | All children required |
| Leaf | Box | Atomic attack step |
理解节点语义是建模正确性的前提:OR 节点表示任意一个子目标达成即可使父目标成立(攻击者选最有利的路径);AND 节点表示所有子步骤都执行完毕才能推进(例如先收集个人信息、再拨打客服电话的账号恢复攻击);Leaf 叶子节点是不可再分的原子攻击步骤,也是所有属性标注与缓解措施挂载的载体。
3. 攻击属性
| Attribute | Description | Values |
|---|---|---|
| Cost | Resources needed | $, $$, $$$ |
| Time | Duration to execute | Hours, Days, Weeks |
| Skill | Expertise required | Low, Medium, High |
| Detection | Likelihood of detection | Low, Medium, High |
属性标注使攻击树从"定性图形"升级为"可比较、可量化"的决策工具:成本(Cost)描述执行攻击所需资源量级,时间(Time)描述执行周期,技能(Skill)描述所需专业水平,可探测性(Detection)描述被防御方发现的可能性。这些属性最终将支撑后续章节中的路径难度、路径成本与隐蔽性计算。
三、模板 1:攻击树数据模型(AttackTree 数据模型)
实施手册提供了完整的数据模型实现,它定义了攻击树的所有基础类型:节点类型枚举、难度/成本/检测风险枚举、攻击属性与攻击节点、攻击树容器类。
from dataclasses import dataclass, field from enum import Enum from typing import List, Dict, Optional, Union import json class NodeType(Enum): OR = "or" AND = "and" LEAF = "leaf" class Difficulty(Enum): TRIVIAL = 1 LOW = 2 MEDIUM = 3 HIGH = 4 EXPERT = 5 class Cost(Enum): FREE = 0 LOW = 1 MEDIUM = 2 HIGH = 3 VERY_HIGH = 4 class DetectionRisk(Enum): NONE = 0 LOW = 1 MEDIUM = 2 HIGH = 3 CERTAIN = 4 @dataclass class AttackAttributes: difficulty: Difficulty = Difficulty.MEDIUM cost: Cost = Cost.MEDIUM detection_risk: DetectionRisk = DetectionRisk.MEDIUM time_hours: float = 8.0 requires_insider: bool = False requires_physical: bool = False @dataclass class AttackNode: id: str name: str description: str node_type: NodeType attributes: AttackAttributes = field(default_factory=AttackAttributes) children: List['AttackNode'] = field(default_factory=list) mitigations: List[str] = field(default_factory=list) cve_refs: List[str] = field(default_factory=list) def add_child(self, child: 'AttackNode') -> None: self.children.append(child) def calculate_path_difficulty(self) -> float: """Calculate aggregate difficulty for this path.""" if self.node_type == NodeType.LEAF: return self.attributes.difficulty.value if not self.children: return 0 child_difficulties = [c.calculate_path_difficulty() for c in self.children] if self.node_type == NodeType.OR: return min(child_difficulties) else: # AND return max(child_difficulties) def calculate_path_cost(self) -> float: """Calculate aggregate cost for this path.""" if self.node_type == NodeType.LEAF: return self.attributes.cost.value if not self.children: return 0 child_costs = [c.calculate_path_cost() for c in self.children] if self.node_type == NodeType.OR: return min(child_costs) else: # AND return sum(child_costs) def to_dict(self) -> Dict: """Convert to dictionary for serialization.""" return { "id": self.id, "name": self.name, "description": self.description, "type": self.node_type.value, "attributes": { "difficulty": self.attributes.difficulty.name, "cost": self.attributes.cost.name, "detection_risk": self.attributes.detection_risk.name, "time_hours": self.attributes.time_hours, }, "mitigations": self.mitigations, "children": [c.to_dict() for c in self.children] } @dataclass class AttackTree: name: str description: str root: AttackNode version: str = "1.0" def find_easiest_path(self) -> List[AttackNode]: """Find the path with lowest difficulty.""" return self._find_path(self.root, minimize="difficulty") def find_cheapest_path(self) -> List[AttackNode]: """Find the path with lowest cost.""" return self._find_path(self.root, minimize="cost") def find_stealthiest_path(self) -> List[AttackNode]: """Find the path with lowest detection risk.""" return self._find_path(self.root, minimize="detection") def _find_path( self, node: AttackNode, minimize: str ) -> List[AttackNode]: """Recursive path finding.""" if node.node_type == NodeType.LEAF: return [node] if not node.children: return [node] if node.node_type == NodeType.OR: # Pick the best child path best_path = None best_score = float('inf') for child in node.children: child_path = self._find_path(child, minimize) score = self._path_score(child_path, minimize) if score < best_score: best_score = score best_path = child_path return [node] + (best_path or []) else: # AND # Must traverse all children path = [node] for child in node.children: path.extend(self._find_path(child, minimize)) return path def _path_score(self, path: List[AttackNode], metric: str) -> float: """Calculate score for a path.""" if metric == "difficulty": return sum(n.attributes.difficulty.value for n in path if n.node_type == NodeType.LEAF) elif metric == "cost": return sum(n.attributes.cost.value for n in path if n.node_type == NodeType.LEAF) elif metric == "detection": return sum(n.attributes.detection_risk.value for n in path if n.node_type == NodeType.LEAF) return 0 def get_all_leaf_attacks(self) -> List[AttackNode]: """Get all leaf attack nodes.""" leaves = [] self._collect_leaves(self.root, leaves) return leaves def _collect_leaves(self, node: AttackNode, leaves: List[AttackNode]) -> None: if node.node_type == NodeType.LEAF: leaves.append(node) for child in node.children: self._collect_leaves(child, leaves) def get_unmitigated_attacks(self) -> List[AttackNode]: """Find attacks without mitigations.""" return [n for n in self.get_all_leaf_attacks() if not n.mitigations] def export_json(self) -> str: """Export tree to JSON.""" return json.dumps({ "name": self.name, "description": self.description, "version": self.version, "root": self.root.to_dict() }, indent=2)该模型蕴含的聚合语义值得留意:
- 枚举是有序标量:
Difficulty取值 1(TRIVIAL)到 5(EXPERT)、Cost取值 0(FREE)到 4(VERY_HIGH)、DetectionRisk取值 0(NONE)到 4(CERTAIN),这是后续数值计算的基石; - OR 取最优、AND 取最差:
calculate_path_difficulty中 OR 节点取子节点难度最小值(攻击者总选最容易的路径),AND 节点取最大值(最难的必经环节决定整体难度);calculate_path_cost中 OR 取最小成本,AND 则对子节点成本求和(所有步骤的成本都要付出)——这正是对攻击者理性行为的建模; - 原生支持防御视角:
AttackNode自带mitigations(缓解措施列表)与cve_refs(关联 CVE 引用)字段,get_unmitigated_attacks()可以直接列出尚未挂接任何缓解措施的叶子攻击,是防御缺口扫描的快捷入口; - 序列化友好:
to_dict()与export_json()使得攻击树可以导出为 JSON,便于后续持久化、工具链交换或在 Web 前端渲染。
四、模板 2:Fluent 构建器(AttackTreeBuilder)
直接手工构造嵌套AttackNode冗长易错。实施手册提供了链式(Fluent)构建器,以goal → or_node → and_node → attack → end → build的调用序列描述树形结构,并附带了完整的"账号接管"实战示例。
class AttackTreeBuilder: """Fluent builder for attack trees.""" def __init__(self, name: str, description: str): self.name = name self.description = description self._node_stack: List[AttackNode] = [] self._root: Optional[AttackNode] = None def goal(self, id: str, name: str, description: str = "") -> 'AttackTreeBuilder': """Set the root goal (OR node by default).""" self._root = AttackNode( id=id, name=name, description=description, node_type=NodeType.OR ) self._node_stack = [self._root] return self def or_node(self, id: str, name: str, description: str = "") -> 'AttackTreeBuilder': """Add an OR sub-goal.""" node = AttackNode( id=id, name=name, description=description, node_type=NodeType.OR ) self._current().add_child(node) self._node_stack.append(node) return self def and_node(self, id: str, name: str, description: str = "") -> 'AttackTreeBuilder': """Add an AND sub-goal (all children required).""" node = AttackNode( id=id, name=name, description=description, node_type=NodeType.AND ) self._current().add_child(node) self._node_stack.append(node) return self def attack( self, id: str, name: str, description: str = "", difficulty: Difficulty = Difficulty.MEDIUM, cost: Cost = Cost.MEDIUM, detection: DetectionRisk = DetectionRisk.MEDIUM, time_hours: float = 8.0, mitigations: List[str] = None ) -> 'AttackTreeBuilder': """Add a leaf attack node.""" node = AttackNode( id=id, name=name, description=description, node_type=NodeType.LEAF, attributes=AttackAttributes( difficulty=difficulty, cost=cost, detection_risk=detection, time_hours=time_hours ), mitigations=mitigations or [] ) self._current().add_child(node) return self def end(self) -> 'AttackTreeBuilder': """Close current node, return to parent.""" if len(self._node_stack) > 1: self._node_stack.pop() return self def build(self) -> AttackTree: """Build the attack tree.""" if not self._root: raise ValueError("No root goal defined") return AttackTree( name=self.name, description=self.description, root=self._root ) def _current(self) -> AttackNode: if not self._node_stack: raise ValueError("No current node") return self._node_stack[-1]构建器通过内部_node_stack(节点栈)维护当前父节点:goal()建立根节点并压栈;or_node()/and_node()将新节点挂到当前节点下再压栈;attack()直接添加叶子节点(不入栈);end()弹出栈顶返回父级;build()校验根节点存在后组装AttackTree。若未定义根目标即调用build(),会抛出ValueError("No root goal defined")。
实战示例:账号接管攻击树
以下示例完整展示如何用构建器建模一个"获取用户账号未授权访问"的攻击场景——它同时包含 OR 分支(窃取凭据 / 绕过认证 / 社工)和嵌套 AND 子目标(账号恢复攻击必须先"收集个人信息"再"致电支持台"):
# Example usage def build_account_takeover_tree() -> AttackTree: """Build attack tree for account takeover scenario.""" return ( AttackTreeBuilder("Account Takeover", "Gain unauthorized access to user account") .goal("G1", "Take Over User Account") .or_node("S1", "Steal Credentials") .attack( "A1", "Phishing Attack", difficulty=Difficulty.LOW, cost=Cost.LOW, detection=DetectionRisk.MEDIUM, mitigations=["Security awareness training", "Email filtering"] ) .attack( "A2", "Credential Stuffing", difficulty=Difficulty.TRIVIAL, cost=Cost.LOW, detection=DetectionRisk.HIGH, mitigations=["Rate limiting", "MFA", "Password breach monitoring"] ) .attack( "A3", "Keylogger Malware", difficulty=Difficulty.MEDIUM, cost=Cost.MEDIUM, detection=DetectionRisk.MEDIUM, mitigations=["Endpoint protection", "MFA"] ) .end() .or_node("S2", "Bypass Authentication") .attack( "A4", "Session Hijacking", difficulty=Difficulty.MEDIUM, cost=Cost.LOW, detection=DetectionRisk.LOW, mitigations=["Secure session management", "HTTPS only"] ) .attack( "A5", "Authentication Bypass Vulnerability", difficulty=Difficulty.HIGH, cost=Cost.LOW, detection=DetectionRisk.LOW, mitigations=["Security testing", "Code review", "WAF"] ) .end() .or_node("S3", "Social Engineering") .and_node("S3.1", "Account Recovery Attack") .attack( "A6", "Gather Personal Information", difficulty=Difficulty.LOW, cost=Cost.FREE, detection=DetectionRisk.NONE ) .attack( "A7", "Call Support Desk", difficulty=Difficulty.MEDIUM, cost=Cost.FREE, detection=DetectionRisk.MEDIUM, mitigations=["Support verification procedures", "Security questions"] ) .end() .end() .build() )注意 A6"收集个人信息"未挂接缓解措施且检测风险为 NONE——这正是后面get_unmitigated_attacks()与coverage_analysis()会识别的防御盲区;而 A2"撞库"被标注为 TRIVIAL 难度 + HIGH 检测风险,说明它门槛极低但较易被察觉,缓解项(限流、MFA、泄露口令监控)应作为高优先级投入。
五、模板 3:可视化导出器(Mermaid 与 PlantUML)
攻击树最终需要呈现给利益相关方。实施手册提供了两个导出器:MermaidExporter生成 Mermaid flowchart(支持直接在 GitHub、许多文档站与编辑器内渲染),PlantUMLExporter生成 PlantUML mindmap。
class MermaidExporter: """Export attack trees to Mermaid diagram format.""" def __init__(self, tree: AttackTree): self.tree = tree self._lines: List[str] = [] self._node_count = 0 def export(self) -> str: """Export tree to Mermaid flowchart.""" self._lines = ["flowchart TD"] self._export_node(self.tree.root, None) return "\n".join(self._lines) def _export_node(self, node: AttackNode, parent_id: Optional[str]) -> str: """Recursively export nodes.""" node_id = f"N{self._node_count}" self._node_count += 1 # Node shape based on type if node.node_type == NodeType.OR: shape = f"{node_id}(({node.name}))" elif node.node_type == NodeType.AND: shape = f"{node_id}[{node.name}]" else: # LEAF # Color based on difficulty style = self._get_leaf_style(node) shape = f"{node_id}[/{node.name}/]" self._lines.append(f" style {node_id} {style}") self._lines.append(f" {shape}") if parent_id: connector = "-->" if node.node_type != NodeType.AND else "==>" self._lines.append(f" {parent_id} {connector} {node_id}") for child in node.children: self._export_node(child, node_id) return node_id def _get_leaf_style(self, node: AttackNode) -> str: """Get style based on attack attributes.""" colors = { Difficulty.TRIVIAL: "fill:#ff6b6b", # Red - easy attack Difficulty.LOW: "fill:#ffa06b", Difficulty.MEDIUM: "fill:#ffd93d", Difficulty.HIGH: "fill:#6bcb77", Difficulty.EXPERT: "fill:#4d96ff", # Blue - hard attack } color = colors.get(node.attributes.difficulty, "fill:#gray") return color class PlantUMLExporter: """Export attack trees to PlantUML format.""" def __init__(self, tree: AttackTree): self.tree = tree def export(self) -> str: """Export tree to PlantUML.""" lines = [ "@startmindmap", f"* {self.tree.name}", ] self._export_node(self.tree.root, lines, 1) lines.append("@endmindmap") return "\n".join(lines) def _export_node(self, node: AttackNode, lines: List[str], depth: int) -> None: """Recursively export nodes.""" prefix = "*" * (depth + 1) if node.node_type == NodeType.OR: marker = "[OR]" elif node.node_type == NodeType.AND: marker = "[AND]" else: diff = node.attributes.difficulty.name marker = f"<<{diff}>>" lines.append(f"{prefix} {marker} {node.name}") for child in node.children: self._export_node(child, depth + 1)两个导出器的设计差异值得注意:
- Mermaid 导出器将难度可视化:OR 用圆形
((...))、AND 用矩形[...]、叶子用平行四边形[/.../],且叶子节点按Difficulty上色——红色(TRIVIAL)到蓝色(EXPERT),让"最容易被利用的薄弱点"在图中一眼可辨; - 连接线区分语义:OR 分支用普通箭头
-->,AND 分支用粗箭头==>,进一步强化"必经步骤"的视觉权重; - PlantUML 导出器以文本标注类型:每个节点前缀
[OR]/[AND],叶子标注<<难度名>>,适合在纯文本/邮件中快速传达树结构。
六、模板 4:攻击路径分析(AttackPathAnalyzer)
建模的最终目的是分析。AttackPathAnalyzer提供了五类核心能力:全路径枚举、路径指标计算、关键节点识别、缓解覆盖率分析、缓解优先级排序——这是把攻击树从"画图"推向"决策"的关键模块。
from typing import Set, Tuple class AttackPathAnalyzer: """Analyze attack paths and coverage.""" def __init__(self, tree: AttackTree): self.tree = tree def get_all_paths(self) -> List[List[AttackNode]]: """Get all possible attack paths.""" paths = [] self._collect_paths(self.tree.root, [], paths) return paths def _collect_paths( self, node: AttackNode, current_path: List[AttackNode], all_paths: List[List[AttackNode]] ) -> None: """Recursively collect all paths.""" current_path = current_path + [node] if node.node_type == NodeType.LEAF: all_paths.append(current_path) return if not node.children: all_paths.append(current_path) return if node.node_type == NodeType.OR: # Each child is a separate path for child in node.children: self._collect_paths(child, current_path, all_paths) else: # AND # Must combine all children child_paths = [] for child in node.children: child_sub_paths = [] self._collect_paths(child, [], child_sub_paths) child_paths.append(child_sub_paths) # Combine paths from all AND children combined = self._combine_and_paths(child_paths) for combo in combined: all_paths.append(current_path + combo) def _combine_and_paths( self, child_paths: List[List[List[AttackNode]]] ) -> List[List[AttackNode]]: """Combine paths from AND node children.""" if not child_paths: return [[]] if len(child_paths) == 1: return [path for paths in child_paths for path in paths] # Cartesian product of all child path combinations result = [[]] for paths in child_paths: new_result = [] for existing in result: for path in paths: new_result.append(existing + path) result = new_result return result def calculate_path_metrics(self, path: List[AttackNode]) -> Dict: """Calculate metrics for a specific path.""" leaves = [n for n in path if n.node_type == NodeType.LEAF] total_difficulty = sum(n.attributes.difficulty.value for n in leaves) total_cost = sum(n.attributes.cost.value for n in leaves) total_time = sum(n.attributes.time_hours for n in leaves) max_detection = max((n.attributes.detection_risk.value for n in leaves), default=0) return { "steps": len(leaves), "total_difficulty": total_difficulty, "avg_difficulty": total_difficulty / len(leaves) if leaves else 0, "total_cost": total_cost, "total_time_hours": total_time, "max_detection_risk": max_detection, "requires_insider": any(n.attributes.requires_insider for n in leaves), "requires_physical": any(n.attributes.requires_physical for n in leaves), } def identify_critical_nodes(self) -> List[Tuple[AttackNode, int]]: """Find nodes that appear in the most paths.""" paths = self.get_all_paths() node_counts: Dict[str, Tuple[AttackNode, int]] = {} for path in paths: for node in path: if node.id not in node_counts: node_counts[node.id] = (node, 0) node_counts[node.id] = (node, node_counts[node.id][1] + 1) return sorted( node_counts.values(), key=lambda x: x[1], reverse=True ) def coverage_analysis(self, mitigated_attacks: Set[str]) -> Dict: """Analyze how mitigations affect attack coverage.""" all_paths = self.get_all_paths() blocked_paths = [] open_paths = [] for path in all_paths: path_attacks = {n.id for n in path if n.node_type == NodeType.LEAF} if path_attacks & mitigated_attacks: blocked_paths.append(path) else: open_paths.append(path) return { "total_paths": len(all_paths), "blocked_paths": len(blocked_paths), "open_paths": len(open_paths), "coverage_percentage": len(blocked_paths) / len(all_paths) * 100 if all_paths else 0, "open_path_details": [ {"path": [n.name for n in p], "metrics": self.calculate_path_metrics(p)} for p in open_paths[:5] # Top 5 open paths ] } def prioritize_mitigations(self) -> List[Dict]: """Prioritize mitigations by impact.""" critical_nodes = self.identify_critical_nodes() paths = self.get_all_paths() total_paths = len(paths) recommendations = [] for node, count in critical_nodes: if node.node_type == NodeType.LEAF and node.mitigations: recommendations.append({ "attack": node.name, "attack_id": node.id, "paths_blocked": count, "coverage_impact": count / total_paths * 100, "difficulty": node.attributes.difficulty.name, "mitigations": node.mitigations, }) return sorted(recommendations, key=lambda x: x["coverage_impact"], reverse=True)各分析方法的工程含义:
get_all_paths():枚举从根到每个叶子的全部路径。OR 节点每个子节点构成独立路径;AND 节点则对子路径做笛卡尔积组合(_combine_and_paths),正确体现"所有分支都要走"的语义——这是最容易出错的递归逻辑,手册用显式组合函数规避了理解偏差;calculate_path_metrics():对单条路径聚合指标,包括步骤数、总难度/平均难度、总成本、总耗时、最大检测风险,以及是否涉及内部人员(requires_insider)或物理接触(requires_physical)——后两者专门用于识别内部威胁与物理攻击路径;identify_critical_nodes():统计每个节点出现在多少条路径中并降序排列。出现在最多路径中的节点(通常是 AND 必经节点)就是"一击致命"的杠杆点;coverage_analysis(mitigated_attacks):输入已缓解攻击的 id 集合,输出阻断/未阻断路径数与覆盖率百分比,并给出前 5 条仍开放的路径及其指标详情——这是衡量"当前防御投入覆盖了多少攻击面"的直接量化工具;prioritize_mitigations():结合关键节点与已挂接缓解项,按"阻断路径占比(coverage_impact)"降序输出建议清单,即:优先修复那些出现在最多路径中、且已有缓解措施的叶子攻击,用最少的动作闭合最多的攻击面。
七、最佳实践:Do's 与 Don'ts
实施手册以清单形式给出了攻击树建模的行为准则:
Do's(应当)
- Start with clear goals—— 定义攻击者到底想得到什么,根目标必须明确;
- Be exhaustive—— 穷尽所有攻击向量,不要遗漏明显或冷门的途径;
- Attribute attacks—— 为每个攻击标注成本、技能与可探测性,使其可比较;
- Update regularly—— 新威胁不断出现,树必须持续维护;
- Validate with experts—— 由红队或安全专家评审校验,防止盲区。
Don'ts(不应)
- Don't oversimplify—— 真实攻击复杂,过度简化会误导决策;
- Don't ignore dependencies—— AND 节点的依赖关系决定路径可行性,忽略会导致错误结论;
- Don't forget insider threats—— 并非所有攻击者都来自外部,务必纳入内部人员路径;
- Don't skip mitigations—— 攻击树是为防御规划服务的,脱离缓解措施就失去了意义;
- Don't make it static—— 威胁态势不断演化,树必须随之更新。
八、仓库中的授权边界与安全护栏
由于本技能在 SKILL.md 中标记为risk: offensive,AAS 仓库对其使用设定了严格的授权边界,这也是将本技能投入实际使用前必须遵守的前提:
- 仅限授权使用:技能仅用于教育目的或经授权的安全评估;使用者必须获得系统所有者的明确书面许可,滥用该技能属违法行为;
- 强制确认门(Mandatory confirmation gate):在执行任何探测、利用、更改、持久化、数据提取或凭据访问类命令之前,必须依次完成四步:① 要求用户明确目标 URL/IP/账号/资源;② 确认书面授权与允许范围;③ 展示确切命令并解释预期效果;④ 等待当前对话中的明确确认。未获确认时保持只读、仅提供防御性建议,并优先使用沙箱、一次性虚拟机或受控实验环境;
- 安全使用:攻击树只与授权利益相关方共享,除非必要避免包含敏感利用细节。
这与仓库的全局安全护栏一脉相承——安全护栏文档 规定每个 offensive 技能都必须在 SKILL.md 中以该免责声明与确认门开头,而 技能解剖文档 要求涉及命令/网络/攻击类技能必须提供安全与合规说明(包括确认门与环境预期,如local-only、authorized test environment)。此外,SKILL.md 还明确了三点局限:仅在任务与上述范围匹配时使用;输出不能替代环境专属的验证、测试与专家评审;当所需输入、权限、安全边界或成功标准缺失时,应停下来询问澄清。
九、从模型到决策的完整工作流
综合实施手册的四个模板,攻击树建模在安全评估中的完整工作流可以归纳为一条可复用的流水线:
- 定目标:确认范围、资产与攻击者根目标,以此建立根节点;
- 分解:按 AND/OR 结构递归分解子目标,直到原子攻击步骤(叶子);
- 标注属性:为每个叶子填充难度、成本、检测风险、耗时,以及内部/物理前提;
- 挂缓解:为叶子挂接已实施的缓解措施,必要时关联 CVE 引用;
- 分析:用
AttackPathAnalyzer枚举全部路径、计算路径指标、识别关键节点与未缓解攻击; - 量化防御:用
coverage_analysis()计算当前缓解覆盖率,用prioritize_mitigations()输出按影响排序的修复建议; - 呈现与评审:用
MermaidExporter/PlantUMLExporter生成可视化图交付给利益相关方,并交由安全专家复核。
从源码结构看,实施手册的模板设计(数据模型 → 构建器 → 导出器 → 分析器)正是为这条流水线逐环配套的:每一环都有明确输入输出,既可以整体串用,也可以单独抽取(例如只取分析器对已有 JSON 树做覆盖评估)。对安全团队而言,这套模板可直接复制进项目作为攻击树建模的基础设施;对安全评审与红队行动而言,它提供了把"威胁路径"翻译成"防御投入优先级"的可重复方法。
延伸阅读:本文依据的实施手册与技能定义均位于 AAS 仓库plugins/agentic-awesome-skills-claude/skills/attack-tree-construction/目录下;仓库根目录的 skills/attack-tree-construction/SKILL.md 为同一技能的镜像副本。关于 offensive 技能的通用授权要求,可参考 docs/contributors/security-guardrails.md;关于技能元数据(risk、source、date_added)与安全章节的写作规范,可参考 docs/contributors/skill-anatomy.md。
- AI 技能
- AI 插件
- 人工智能
【免费下载链接】agentic-awesome-skills
AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,600+ agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.
相关推荐
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考