diff --git a/DATA/Investigation_Agent/Investigation_System.md b/DATA/Investigation_Agent/Investigation_System.md new file mode 100644 index 0000000..bf94976 --- /dev/null +++ b/DATA/Investigation_Agent/Investigation_System.md @@ -0,0 +1,3 @@ +你是一位顶尖的网络安全AI分析专家,深谙MITRE ATT&CK框架及安全事件关联分析. +你的核心目标是解析输入的结构化安全事件数据,进行深度的技术推理与逻辑重组,并将其提炼为高度结构化的专业事件调查报告. +你必须利用提供的工具和Schema结构化你的输出,严格遵守各个字段的描述要求进行精准的信息提取,切勿捏造未在原始数据中出现的证据. \ No newline at end of file diff --git a/MODULES/NDR-Rule-05-Suspect-C2-Communication.py b/MODULES/NDR-Rule-05-Suspect-C2-Communication.py index ad0d34c..e3597f0 100644 --- a/MODULES/NDR-Rule-05-Suspect-C2-Communication.py +++ b/MODULES/NDR-Rule-05-Suspect-C2-Communication.py @@ -98,7 +98,6 @@ class Module(LanggraphModule): analytic_type=AlertAnalyticType.BEHAVIORAL, product_category=ProductCategory.NDR, product_name="Network Detection & Response", - first_seen_time=alert_date_formatted, desc=description, data_sources=["NDR"], labels=tags + ["c2-communication"], diff --git a/PLAYBOOKS/CASE/Investigation_Agent.py b/PLAYBOOKS/CASE/Investigation_Agent.py new file mode 100644 index 0000000..e496ec5 --- /dev/null +++ b/PLAYBOOKS/CASE/Investigation_Agent.py @@ -0,0 +1,115 @@ +from typing import Any +from typing import List, Dict, Optional + +from langchain_core.messages import HumanMessage +from pydantic import BaseModel, Field +from pydantic import ConfigDict + +from Lib.baseplaybook import BasePlaybook +from PLUGINS.LLM.llmapi import LLMAPI +from PLUGINS.SIRP.sirpapi import Case +from PLUGINS.SIRP.sirpcoremodel import Severity, AttackStage, Confidence +from PLUGINS.SIRP.sirpextramodel import PlaybookModel + + +class AnalyzeResult(BaseModel): + """Structure for extracting user information from text""" + # config + model_config = ConfigDict(use_enum_values=True) + + original_severity: Severity = Field(description="Original alert severity") + new_severity: Severity = Field(description="Recommended new severity level") + confidence: Confidence = Field(description="Confidence score, only one of 'Low', 'Medium', or 'High'") + analysis_rationale: str | None = Field(description="Analysis process and reasons", default=None) + attack_stage: AttackStage = Field(description="e.g. 'Lateral Movement'", default=None) + recommended_actions: str | dict[str, Any] | None = Field(description="e.g., 'Isolate host 10.1.1.5'", default=None) + + +class AffectedAsset(BaseModel): + asset_type: str = Field(description="资产的类型, 例如Host, IP, User, File, Cloud Resource.") + asset_value: str = Field(description="资产的具体标识符, 例如IP地址, 主机名, 用户名, 资源ARN.") + + +class ArtifactAnalysis(BaseModel): + artifact_name: str = Field(description="痕迹或载荷的标识符, 例如文件名, 进程名, URL或IAM角色名.") + artifact_type: str = Field(description="痕迹的类型, 例如File, Process, Network Traffic, API Call, IAM Policy.") + properties: Dict[str, str] = Field( + description="动态属性键值对. 根据痕迹类型提取不同的关键属性, 例如文件可能包含MD5, Path; 网络请求可能包含Method, User-Agent; 云操作可能包含API Name, Parameters.") + analysis_result: str = Field(description="对该痕迹的深度技术分析, 解释其执行的恶意行为, 功能机制以及造成的威胁等级.") + threat_intelligence: Optional[str] = Field(description="与该痕迹相关的威胁情报信息或标签, 如无则为空.") + + +class AttackChainStep(BaseModel): + phase: str = Field(description="攻击链的阶段名称, 例如Initial Access, Execution, Persistence, Privilege Escalation, Lateral Movement等.") + description: str = Field(description="该阶段的具体行为说明, 描述攻击者如何利用漏洞或执行了什么操作.") + + +class TimelineEvent(BaseModel): + timestamp: str = Field(description="事件发生的时间点.") + attack_behavior: str = Field(description="在该时间点发生的具体攻击行为或检测到的动作.") + evidence_field: str = Field(description="支撑该行为判断的关键证据字段或日志内容片段.") + + +class IndicatorOfCompromise(BaseModel): + indicator_type: str = Field(description="IOC的类型, 必须从以下范围中选择: IP, Domain, URL, FileHash, FilePath, Command, RegistryKey.") + value: str = Field(description="IOC的具体值, 例如具体的IP地址, MD5字符串或恶意文件路径.") + confidence: str = Field(description="该指标的置信度或威胁等级, 例如High, Medium, Low.") + context: str = Field(description="简述该IOC在本次事件中的上下文描述, 例如作为C2服务器建立网络连接或被释放的后门文件.") + + +class RemediationRecommendation(BaseModel): + action_type: str = Field(description="处置或加固动作的类型, 例如隔离主机, 封禁IP, 清除文件, 修复漏洞等.") + description: str = Field(description="具体的处置步骤或系统加固建议说明.") + priority: str = Field(description="该建议的执行优先级, 例如High, Medium, Low.") + + +class IncidentReport(BaseModel): + digest: str = Field(description="事件的综合摘要. 需确认攻击是否真实发生, 攻击者获取的最高权限, 核心恶意行为以及总体的影响评估.") + affected_assets: List[AffectedAsset] = Field(description="受此次事件影响或作为目标的资产列表.") + attack_chain_description: List[AttackChainStep] = Field(description="按时间或逻辑顺序重建的攻击链结构化步骤列表.") + artifact_analysis: List[ArtifactAnalysis] = Field(description="对事件中的关键痕迹或恶意载荷的详细分析列表. 利用properties字段动态适应不同类型的告警特征.") + attack_timeline: List[TimelineEvent] = Field(description="按时间顺序排列的事件时间线.") + ioc_indicators: List[IndicatorOfCompromise] = Field(description="结构化提取的妥协指标列表.") + remediation_recommendations: List[RemediationRecommendation] = Field(description="针对该事件的处置与系统加固建议列表.") + + +class Playbook(BasePlaybook): + NAME = "Investigation Agent" + DESC = "Investigation Agent" + + def __init__(self): + super().__init__() # do not delete this code + + def run(self): + case = Case.get(self.param_source_row_id) + content = case.model_dump_json_for_ai() + # Load system prompt + system_message = self.load_system_prompt_template("L3_SOC_Analyst").format() + + # Run + llm_api = LLMAPI() + + llm = llm_api.get_model(tag="structured_output") + + # Construct message list + messages = [ + system_message, + HumanMessage(content=content) + ] + llm = llm.with_structured_output(IncidentReport) + response = llm.invoke(messages) + response: IncidentReport + return + + +if __name__ == "__main__": + import os + import django + + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ASP.settings") + django.setup() + model = PlaybookModel(source_row_id='b6383d3f-bbdc-432b-9ac7-debb25535617') + module = Playbook() + module._playbook_model = model + + module.run() diff --git a/PLAYBOOKS/CASE/L3_SOC_Analyst_Agent.py b/PLAYBOOKS/CASE/L3_SOC_Analyst_Agent.py index 3adda2d..5680663 100644 --- a/PLAYBOOKS/CASE/L3_SOC_Analyst_Agent.py +++ b/PLAYBOOKS/CASE/L3_SOC_Analyst_Agent.py @@ -9,8 +9,8 @@ from Lib.baseplaybook import LanggraphPlaybook from Lib.llmapi import BaseAgentState from PLUGINS.LLM.llmapi import LLMAPI from PLUGINS.SIRP.sirpapi import Case -from PLUGINS.SIRP.sirpextramodel import PlaybookJobStatus, PlaybookModel from PLUGINS.SIRP.sirpcoremodel import Severity, AttackStage, Confidence, CaseModel +from PLUGINS.SIRP.sirpextramodel import PlaybookJobStatus, PlaybookModel class AnalyzeResult(BaseModel): diff --git a/PLUGINS/ClaudeCode/skills/asp-knowledge-zh/SKILL.md b/PLUGINS/ClaudeCode/skills/asp-knowledge-zh/SKILL.md index 961f46b..7920c12 100644 --- a/PLUGINS/ClaudeCode/skills/asp-knowledge-zh/SKILL.md +++ b/PLUGINS/ClaudeCode/skills/asp-knowledge-zh/SKILL.md @@ -41,7 +41,7 @@ ASP 的内部 Knowledge 本质上是一条一条数据库记录,核心字段 ## 运行规则 - 把它视为知识检索与维护工具,而不是通用聊天记忆。 -- 如果用户是在“找相关知识内容”,尤其是给出主题、问题描述、症状、案例特征、短语或自然语言查询,优先使用 `search_knowledge`。 +- 如果用户是在“找相关知识内容”,尤其是给出主题、问题描述、症状、案件特征、短语或自然语言查询,优先使用 `search_knowledge`。 - 如果用户是在“查数据库里有哪些 knowledge 记录”或“按字段过滤某些记录”,使用 `list_knowledge`。 - 如果用户是在维护某条记录的标题、正文、标签或处理动作,使用 `update_knowledge`。 - 不要把 `using` 当作可直接更新的字段;它是系统处理后的结果状态。 diff --git a/PLUGINS/ClaudeCode/skills/asp-module-creator-zh/ARCH.md b/PLUGINS/ClaudeCode/skills/asp-module-creator-zh/ARCH.md index d8a4d01..d6f0b9e 100644 --- a/PLUGINS/ClaudeCode/skills/asp-module-creator-zh/ARCH.md +++ b/PLUGINS/ClaudeCode/skills/asp-module-creator-zh/ARCH.md @@ -84,7 +84,7 @@ self.read_stream_message() ## SIRP 数据层级 ``` -Case(调查案例,顶层) +Case(调查案件,顶层) │ 通过 correlation_uid 聚合同类告警 │ └── Alert(单次规则触发记录,二级) diff --git a/PLUGINS/MCP/llmfunc.py b/PLUGINS/MCP/llmfunc.py index a54a773..aa74a5e 100644 --- a/PLUGINS/MCP/llmfunc.py +++ b/PLUGINS/MCP/llmfunc.py @@ -160,21 +160,6 @@ def get_alert_discussions( return [json.dumps(item, ensure_ascii=False) for item in discussions] -def update_alert( - alert_id: Annotated[str, Field(description="Alert ID to update (待更新的 Alert ID)")], - severity_ai: Annotated[Severity, Field(description="Updated AI-assessed severity (更新 AI 评估严重程度)")] = None, - confidence_ai: Annotated[Optional[Confidence], Field(description="Updated AI-assessed confidence (更新 AI 评估置信度)")] = None, - comment_ai: Annotated[Optional[str], Field(description="Updated AI comment, Markdown supported (更新 AI 注释,支持 Markdown)")] = None -) -> Annotated[Optional[str], Field(description="Updated alert row ID, or None if not found (更新后的 Alert 行 ID,不存在时返回 None)")]: - """Update AI-assessed fields on an alert. (更新 Alert 的 AI 评估字段)""" - return Alert.update_by_id( - alert_id=alert_id, - severity_ai=severity_ai, - confidence_ai=confidence_ai, - comment_ai=comment_ai - ) - - # Artifact # Do not open to mcp , because we think artifact is add only by automation, not human def create_artifact( @@ -561,7 +546,6 @@ REGISTERED_MCP_TOOLS = [ # alert list_alerts, get_alert_discussions, - update_alert, # artifact list_artifacts, diff --git a/PLUGINS/SIRP/sirpapi.py b/PLUGINS/SIRP/sirpapi.py index 4ef6bf0..c2cbed8 100644 --- a/PLUGINS/SIRP/sirpapi.py +++ b/PLUGINS/SIRP/sirpapi.py @@ -512,29 +512,6 @@ class Alert(BaseWorksheetEntity[AlertModel]): else: return None - @classmethod - def update_by_id( - cls, - alert_id: str, - severity_ai: Union[Severity, None] = None, - confidence_ai: Union[Confidence, None] = None, - comment_ai: Union[str, None] = None - ) -> Union[str, None]: - alert_old = cls.get_by_id(alert_id, lazy_load=True) - if not alert_old: - return None - - alert_new = AlertModel() - alert_new.row_id = alert_old.row_id - if severity_ai is not None: - alert_new.severity_ai = severity_ai - if confidence_ai is not None: - alert_new.confidence_ai = confidence_ai - if comment_ai is not None: - alert_new.comment_ai = comment_ai - - return cls.update(alert_new) - @classmethod def get_discussions(cls, alert_id) -> Union[List[dict], None]: alert_model = cls.get_by_id(alert_id, lazy_load=True) diff --git a/PLUGINS/SIRP/sirpcoremodel.py b/PLUGINS/SIRP/sirpcoremodel.py index d28a619..da1d0a3 100644 --- a/PLUGINS/SIRP/sirpcoremodel.py +++ b/PLUGINS/SIRP/sirpcoremodel.py @@ -284,6 +284,7 @@ class CaseVerdict(StrEnum): class EnrichmentModel(BaseSystemModel): + """Artifact/Alert/Case结构化的富化信息""" _AI_EXCLUDE_FIELDS: ClassVar[set[str]] = set() id: Optional[str] = Field(default=None, init=False, description="Record ID e.g. enrichment_000001 (记录 ID e.g. enrichment_000001)") name: Optional[str] = Field(default="", description="Enrichment name (富化名称)") @@ -296,6 +297,7 @@ class EnrichmentModel(BaseSystemModel): class TicketModel(BaseSystemModel): + """关联到Case的外部工单信息""" _AI_EXCLUDE_FIELDS: ClassVar[set[str]] = {'case'} id: Optional[str] = Field(default=None, init=False, description="Record ID e.g. ticket_000001 (记录 ID e.g. ticket_000001)") @@ -308,11 +310,11 @@ class TicketModel(BaseSystemModel): src_url: Optional[str] = Field(default="", description="External ticket URL (外部工单 URL)") # 反向关联,无需手动处理 - case: Optional[List[Union[CaseModel, str]]] = Field(default=None, init=False, description="Linked case row_id (关联案例行 ID)") + case: Optional[List[Union[CaseModel, str]]] = Field(default=None, init=False, description="Linked case row_id (关联案件行 ID)") class ArtifactModel(BaseSystemModel): - """Stores entity information extracted from alerts, the minimum investigatable unit""" + """Entity information extracted from alerts, the minimum investigate unit""" _AI_EXCLUDE_FIELDS: ClassVar[set[str]] = {'alert'} # 系统自动生成字段 id: Optional[str] = Field(default=None, init=False, description="Record ID e.g. artifact_000001 (记录 ID e.g. artifact_000001)") @@ -336,7 +338,8 @@ class ArtifactModel(BaseSystemModel): class AlertModel(BaseSystemModel): - _AI_EXCLUDE_FIELDS: ClassVar[set[str]] = {'raw_data', 'severity_ai', 'confidence_ai', "impact_ai", "comment_ai", "case"} + """基础告警信息,通常与SIEM Rule 产生的告警进行映射, 是连接 SIEM 告警与 SIRP 案件的核心数据模型""" + _AI_EXCLUDE_FIELDS: ClassVar[set[str]] = {'raw_data', "case"} # 系统自动生成字段 id: Optional[str] = Field(default=None, init=False, @@ -395,47 +398,41 @@ class AlertModel(BaseSystemModel): raw_data: Optional[str] = Field(default="", description="Raw alert log JSON (原始告警日志 JSON)") - # AI字段 - severity_ai: Optional[Severity] = Field(default=None, description="AI-assessed severity (AI 评估严重程度)") - confidence_ai: Optional[Confidence] = Field(default=None, description="AI-assessed confidence (AI 评估置信度)") - impact_ai: Optional[Impact] = Field(default=Impact.UNKNOWN, description="AI-assessed potential impact (AI 评估潜在影响)") - risk_level_ai: Optional[AlertRiskLevel] = Field(default=None, description="Assessed risk level (AI 评估的风险等级)") - comment_ai: Optional[str] = Field(default="", description="AI-generated comment (AI 生成的注释)") - # 反向关联,无需手动处理 case: Optional[List[Union[CaseModel, str]]] = Field(default=None, init=False, - description="Linked case row_id, reverse association, auto-linked, no manual setting needed (关联案例行 ID,反向关联,自动化关联,无需手动设置)") + description="Linked case row_id, reverse association, auto-linked, no manual setting needed (关联案件行 ID,反向关联,自动化关联,无需手动设置)") # 关联表 artifacts: Optional[List[Union[ArtifactModel, str]]] = Field(default=None, description="Extracted artifacts (关联表, 提取的实体列表)") enrichments: Optional[List[Union[EnrichmentModel, str]]] = Field(default=None, description="Alert enrichments (关联表, 告警富化)") class CaseModel(BaseSystemModel): + """安全案件,多个告警聚合而成,安全人员与AI分析处理的核心对象""" _AI_EXCLUDE_FIELDS: ClassVar[set[str]] = {"workbook", "summary_ai", "comment_ai", "attack_stage_ai", "severity_ai", "confidence_ai", "impact_ai", "priority_ai", "verdict_ai"} # 系统自动生成字段 id: Optional[str] = Field(default=None, init=False, description="Record ID e.g. case_000001 (记录 ID e.g. case_000001,系统自动生成,无需手动赋值)") # 创建记录填写字段 - title: Optional[str] = Field(default="", description="Case title (案例标题)") + title: Optional[str] = Field(default="", description="Case title (案件标题)") severity: Optional[Severity] = Field(default=None, description="Analyst-assessed severity (严重程度)") impact: Optional[Impact] = Field(default=None, description="Analyst-assessed impact (影响)") priority: Optional[CasePriority] = Field(default=None, description="Response priority (响应优先级)") confidence: Optional[Confidence] = Field(default=None, description="Analyst-assessed confidence (分析师评估置信度)") - description: Optional[str] = Field(default="", description="Case description (案例描述)") + description: Optional[str] = Field(default="", description="Case description (案件描述)") - category: Optional[ProductCategory] = Field(default=None, description="Case category (案例类别)") - tags: Optional[List[str]] = Field(default=[], description="Case tags (案例标签)", json_schema_extra={"type": 2}) + category: Optional[ProductCategory] = Field(default=None, description="Case category (案件类别)") + tags: Optional[List[str]] = Field(default=[], description="Case tags (案件标签)", json_schema_extra={"type": 2}) - correlation_uid: Optional[str] = Field(default="", description="Case correlation ID (案例关联 ID)") + correlation_uid: Optional[str] = Field(default="", description="Case correlation ID (案件关联 ID)") # 用户手动输入字段 - status: Optional[CaseStatus] = Field(default=CaseStatus.NEW, description="Case handling status (案例处理状态)") + status: Optional[CaseStatus] = Field(default=CaseStatus.NEW, description="Case handling status (案件处理状态)") acknowledged_time: Optional[AutoDatetime] = Field(default=None, description="L1 first acknowledged time (L1 首次接手时间)") assignee_l1: Optional[AutoAccount] = Field(default=None, description="Assigned L1 analyst (分配的 L1 分析师)") - comment: Optional[str] = Field(default="", description="Case analyst comment (案例分析师注释)") + comment: Optional[str] = Field(default="", description="Case analyst comment (案件分析师注释)") assignee_l2: Optional[AutoAccount] = Field(default=None, description="Assigned or escalated L2 analyst (分配或升级的 L2 分析师)") assignee_l3: Optional[AutoAccount] = Field(default=None, description="Assigned or escalated L3 analyst (分配或升级的 L3 分析师)") @@ -444,18 +441,7 @@ class CaseModel(BaseSystemModel): verdict: Optional[CaseVerdict] = Field(default=None, description="Final verdict (最终判定结果)") summary: Optional[str] = Field(default="", description="Closure summary (结案摘要)") - # ai 字段 - severity_ai: Optional[Severity] = Field(default=None, description="AI-assessed severity (AI 评估严重程度)") - confidence_ai: Optional[Confidence] = Field(default=None, description="AI-assessed confidence (AI 评估置信度)") - impact_ai: Optional[Impact] = Field(default=None, description="AI-assessed impact (AI 评估影响)") - priority_ai: Optional[CasePriority] = Field(default=None, description="AI-assessed response priority (AI 评估响应优先级)") - attack_stage_ai: Optional[AttackStage] = Field(default="", description="AI-assessed attack stage (AI 评估攻击阶段)") - comment_ai: Optional[str] = Field(default="", description="AI-generated comment (AI 生成的注释)") - verdict_ai: Optional[CaseVerdict] = Field(default=None, description="AI-generated final verdict (AI 生成的最终判定结果)") - summary_ai: Optional[str] = Field(default="", description="AI-generated closure summary (AI 生成的结案摘要)") - # 自动计算字段,无需手动赋值 - start_time_calc: Optional[AutoDatetime] = Field(default=None, description="Calculated start time (计算的开始时间)") end_time_calc: Optional[AutoDatetime] = Field(default=None, description="Calculated end time (计算的结束时间)") detect_time_calc: Optional[Any] = Field(default=None, description="Calculated detect time (计算的检测时间)") @@ -464,5 +450,16 @@ class CaseModel(BaseSystemModel): # 关联表 tickets: Optional[List[Union[TicketModel, str]]] = Field(default=None, description="Linked external tickets (关联外部工单)") - enrichments: Optional[List[Union[EnrichmentModel, str]]] = Field(default=None, description="Case enrichments (案例富化)") + enrichments: Optional[List[Union[EnrichmentModel, str]]] = Field(default=None, description="Case enrichments (案件富化)") alerts: Optional[List[Union[AlertModel, str]]] = Field(default=None, description="Linked alerts (关联的告警)") + + # ai 字段 + severity_ai: Optional[Severity] = Field(default=None, description="AI-assessed severity (AI 评估严重程度)") + confidence_ai: Optional[Confidence] = Field(default=None, description="AI-assessed confidence (AI 评估置信度)") + impact_ai: Optional[Impact] = Field(default=None, description="AI-assessed impact (AI 评估影响)") + priority_ai: Optional[CasePriority] = Field(default=None, description="AI-assessed response priority (AI 评估响应优先级)") + attack_stage_ai: Optional[AttackStage] = Field(default="", description="AI-assessed attack stage (AI 评估攻击阶段)") + comment_ai: Optional[str] = Field(default="", description="AI-generated comment (AI 生成的注释)") + + verdict_ai: Optional[CaseVerdict] = Field(default=None, description="AI-generated final verdict (AI 生成的最终判定结果)") + summary_ai: Optional[str] = Field(default="", description="AI-generated closure summary (AI 生成的结案摘要)") diff --git a/README_ZH.md b/README_ZH.md index b162a5f..b3e07cc 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -43,8 +43,8 @@ ASP 通过简化的多阶段流程处理安全告警和事件: 2. **Webhook 转发器**:SIEM 通过 Webhook 将这些告警转发到 ASP 内置的 Webhook 接收器。 3. **Redis Stream**:接收器将告警推送到相应的 Redis Stream 中,作为持久化消息队列。每种告警类型都有自己的流。 4. **模块引擎**:ASP **模块**从其指定的流中消费告警,执行分析(通常使用 AI Agent)、丰富数据,并确定结果。 -5. **SIRP 平台**:模块的输出(现已格式化为标准化的安全记录)被发送到 **SIRP** 平台,在那里创建或更新案例、告警和 Artifact。 -6. **剧本引擎**:分析师可以从 SIRP 用户界面触发针对案例、告警或 Artifact 的 **剧本**,以执行进一步的自动化操作,例如威胁情报丰富或修复。 +5. **SIRP 平台**:模块的输出(现已格式化为标准化的安全记录)被发送到 **SIRP** 平台,在那里创建或更新案件、告警和 Artifact。 +6. **剧本引擎**:分析师可以从 SIRP 用户界面触发针对案件、告警或 Artifact 的 **剧本**,以执行进一步的自动化操作,例如威胁情报丰富或修复。 ![img_1.webp](Docker/IMG/img_20.png) ![img_2.webp](Docker/IMG/img_21.png)