security-alert-analysis

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Alert Analysis

告警分析

Investigate one Elastic Security alert (or one group of related alerts) end to end: gather context, classify the threat, document findings, and — after confirming scope with the analyst — acknowledge.
<!-- begin-partial: preamble -->
对一条 Elastic Security 告警(或一组相关告警)进行端到端调查:收集上下文、对威胁进行分类、记录发现,并在与分析师确认范围后将其标记为已确认。
<!-- begin-partial: preamble -->

Environment Configuration

环境配置

This skill executes Elasticsearch operations through the
elastic
CLI. If the
elastic
CLI
is not installed, tell the user what it is needed for. Do not guess credentials, call the HTTP API directly, or attempt other workarounds.
This skill references operations in HTTP-shorthand form (e.g.,
GET /
,
GET /_cat/indices
,
GET /{index}/_mapping
,
GET /{index}/_settings/index.mode
,
POST /_query
). The Operations table at the end of this document maps each shorthand to the equivalent
elastic
CLI command — always use the CLI rather than calling the HTTP API directly.
<!-- end-partial: preamble -->
本 skill 通过
elastic
CLI 执行 Elasticsearch 操作。如果未安装
elastic
CLI
,请告知用户其用途。不要猜测凭据、直接调用 HTTP API 或尝试其他变通方案。
本 skill 使用 HTTP 简写形式引用各项操作(例如
GET /
、
GET /_cat/indices
、
GET /{index}/_mapping
、
GET /{index}/_settings/index.mode
、
POST /_query
)。文末的操作表将每种简写形式映射到对应的
elastic
CLI 命令——始终使用 CLI,而不是直接调用 HTTP API。
<!-- end-partial: preamble -->

Critical principles

关键原则

  • Do not classify prematurely. Gather ALL context before deciding benign, unknown, or malicious.
  • Most alerts are false positives, even when they look alarming. Rule names such as "Malicious Behavior" and severity "critical" are NOT evidence.
  • "Unknown" is an acceptable outcome and is often the correct one when evidence is insufficient.
  • Malicious requires strong corroborating evidence — persistence plus C2, credential theft, or lateral movement — not a single suspicious API call.
  • Report tool output verbatim. Copy IDs, hostnames, timestamps, and counts exactly as returned. Do not round numbers, abbreviate IDs, or paraphrase error messages.
See references/classification-guide.md for detailed evidence-weighting criteria, the behavioral weight table, and common false positive sources.
  • 不要过早分类。 在决定告警属于良性、未知还是恶意之前,应收集所有上下文信息。
  • 大多数告警都是误报,即使它们看起来令人警惕。诸如“Malicious Behavior”之类的规则名称和“critical”的严重程度都不是证据。
  • “Unknown”是可接受的结果,在证据不足时,它往往也是正确的结果。
  • 判定为恶意需要强有力的佐证——持久化与 C2、凭据窃取或横向移动——而不能仅凭一次可疑的 API 调用。
  • 逐字报告工具输出。 准确复制 ID、主机名、时间戳和计数。不要对数字进行四舍五入,不要缩写 ID,也不要转述错误消息。
有关详细的证据权重标准、行为权重表和常见误报来源,请参阅 references/classification-guide.md。

Process

流程

Step 1 — Fetch the alert

第 1 步——获取告警

Retrieve the alert document with
POST /.alerts-security.alerts-*/_search
. Filter to
kibana.alert.workflow_status
not in (
acknowledged
,
closed
), sort by
@timestamp
ascending, and take
size: 1
for the oldest unacknowledged alert — or filter by
_id
when the analyst provides a specific alert.
Capture:
  • _id
    ,
    _index
    ,
    @timestamp
  • agent.id
    ,
    host.name
    ,
    user.name
  • kibana.alert.rule.name
    ,
    kibana.alert.rule.uuid
  • kibana.alert.reason
    ,
    kibana.alert.severity
  • kibana.alert.rule.threat.tactic.name
    ,
    kibana.alert.rule.threat.technique.name
  • Key entity fields:
    source.ip
    ,
    destination.ip
    ,
    file.hash.*
    ,
    process.name
Data needed: alert core fields and key entities to anchor the investigation.
使用
POST /.alerts-security.alerts-*/_search
检索告警文档。筛选
kibana.alert.workflow_status
不属于(
acknowledged
、
closed
)的告警,按
@timestamp
升序排列,并设置
size: 1
以获取最早一条未确认的告警;如果分析师提供了特定告警,则改用
_id
进行筛选。
捕获:
  • _id
    、
    _index
    、
    @timestamp
  • agent.id
    、
    host.name
    、
    user.name
  • kibana.alert.rule.name
    、
    kibana.alert.rule.uuid
  • kibana.alert.reason
    、
    kibana.alert.severity
  • kibana.alert.rule.threat.tactic.name
    、
    kibana.alert.rule.threat.technique.name
  • 关键实体字段:
    source.ip
    、
    destination.ip
    、
    file.hash.*
    、
    process.name
所需数据:用于锚定调查的告警核心字段和关键实体。

Step 2 — Find related alerts

第 2 步——查找相关告警

Before gathering entity context, check whether other alerts share the same agent, user, or time window. Run a
POST /_query
against
.alerts-security.alerts-*
:
esql
FROM .alerts-security.alerts-* METADATA _id
| WHERE kibana.alert.workflow_status == "open"
    AND agent.id == "<agent_id>"
    AND @timestamp >= "<alert_timestamp - 2h>" AND @timestamp <= "<alert_timestamp + 2h>"
| KEEP _id, @timestamp, kibana.alert.rule.name, kibana.alert.severity, kibana.alert.workflow_status
| SORT @timestamp ASC
| LIMIT 50
Group related alerts by
agent.id
and a ~5-minute time window (one incident). Triage each group as a unit.
Also run a frequency check to assess false-positive risk:
esql
FROM .alerts-security.alerts-*
| WHERE kibana.alert.rule.name == "<rule_name>"
    AND @timestamp >= NOW() - 7 days
| STATS alert_count = COUNT(*) BY kibana.alert.workflow_status
A very high rule frequency (hundreds of alerts per day) is a strong false-positive signal.
Data needed: related alerts on the same entity and an environment-wide frequency count for the triggering rule.
在收集实体上下文之前,检查是否有其他告警共享相同的 agent、用户或时间窗口。针对
.alerts-security.alerts-*
运行
POST /_query
:
esql
FROM .alerts-security.alerts-* METADATA _id
| WHERE kibana.alert.workflow_status == "open"
    AND agent.id == "<agent_id>"
    AND @timestamp >= "<alert_timestamp - 2h>" AND @timestamp <= "<alert_timestamp + 2h>"
| KEEP _id, @timestamp, kibana.alert.rule.name, kibana.alert.severity, kibana.alert.workflow_status
| SORT @timestamp ASC
| LIMIT 50
按
agent.id
和约 5 分钟的时间窗口对相关告警进行分组(对应一个事件)。将每组作为一个整体进行分诊。
还要运行频率检查,以评估误报风险:
esql
FROM .alerts-security.alerts-*
| WHERE kibana.alert.rule.name == "<rule_name>"
    AND @timestamp >= NOW() - 7 days
| STATS alert_count = COUNT(*) BY kibana.alert.workflow_status
规则触发频率非常高(每天数百条告警)是一个强烈的误报信号。
所需数据:同一实体上的相关告警,以及整个环境中触发规则的频率计数。

Step 3 — Gather entity context

第 3 步——收集实体上下文

This is the most important step. Do not skip or shortcut it. Complete all substeps before forming any classification opinion.
Time-range rule: alerts can be days or weeks old. NEVER use relative time such as
NOW() - 1 HOUR
. Extract the alert's
@timestamp
and anchor every context query with a roughly ±1 hour window around that timestamp.
Run 2–4 focused queries with
POST /_query
(not 10+). Use
KEEP
to limit columns and avoid dumping full documents.
Process tree — who spawned what:
esql
FROM logs-endpoint.events.process-*
| WHERE agent.id == "<agent_id>"
    AND @timestamp >= "<alert_timestamp - 5min>"
    AND @timestamp <= "<alert_timestamp + 10min>"
    AND process.parent.name IS NOT NULL
    AND process.name NOT IN ("svchost.exe", "conhost.exe", "agentbeat.exe")
| KEEP @timestamp, process.name, process.command_line, process.pid,
       process.parent.name, process.parent.pid
| SORT @timestamp ASC
| LIMIT 80
Network activity — outbound connections, DNS:
esql
FROM logs-endpoint.events.network-*
| WHERE agent.id == "<agent_id>"
    AND @timestamp >= "<alert_timestamp - 15min>"
    AND @timestamp <= "<alert_timestamp + 30min>"
| KEEP @timestamp, process.name, destination.ip, destination.port, dns.question.name
| SORT @timestamp ASC
| LIMIT 50
Index pattern reference:
Data typeIndex pattern
Alerts
.alerts-security.alerts-*
Processes
logs-endpoint.events.process-*
Network
logs-endpoint.events.network-*
Logs
logs-*
Substeps to complete before classifying:
  • (a) Related alerts on the same agent or user (Step 2)
  • (b) Rule frequency across the environment — high frequency is false-positive-prone
  • (c) Process tree and parent-child relationships
  • (d) Network activity: DNS, connections, lateral movement indicators
  • (e) Behavioral investigation: persistence, C2, lateral movement, credential access
Data needed: enough corroborating evidence to justify a classification, or a clear finding that evidence is insufficient (→ classify unknown).
这是最重要的一步。不要跳过或走捷径。 在形成任何分类意见之前,必须完成所有子步骤。
时间范围规则: 告警可能是数天或数周之前的。绝不要使用
NOW() - 1 HOUR
等相对时间。提取告警的
@timestamp
,并围绕该时间戳构建大约正负 1 小时的查询窗口。
运行 2–4 个针对性查询,使用
POST /_query
(而不是 10 个以上)。使用
KEEP
限制列,避免导出完整文档。
进程树——谁生成了什么:
esql
FROM logs-endpoint.events.process-*
| WHERE agent.id == "<agent_id>"
    AND @timestamp >= "<alert_timestamp - 5min>"
    AND @timestamp <= "<alert_timestamp + 10min>"
    AND process.parent.name IS NOT NULL
    AND process.name NOT IN ("svchost.exe", "conhost.exe", "agentbeat.exe")
| KEEP @timestamp, process.name, process.command_line, process.pid,
       process.parent.name, process.parent.pid
| SORT @timestamp ASC
| LIMIT 80
网络活动——出站连接、DNS:
esql
FROM logs-endpoint.events.network-*
| WHERE agent.id == "<agent_id>"
    AND @timestamp >= "<alert_timestamp - 15min>"
    AND @timestamp <= "<alert_timestamp + 30min>"
| KEEP @timestamp, process.name, destination.ip, destination.port, dns.question.name
| SORT @timestamp ASC
| LIMIT 50
索引模式参考:
数据类型索引模式
告警
.alerts-security.alerts-*
进程
logs-endpoint.events.process-*
网络
logs-endpoint.events.network-*
日志
logs-*
完成分类前需要执行的子步骤:
  • (a) 同一 agent 或用户上的相关告警(第 2 步)
  • (b) 整个环境中的规则频率——高频规则更容易产生误报
  • (c) 进程树及父子关系
  • (d) 网络活动:DNS、连接和横向移动迹象
  • (e) 行为调查:持久化、C2、横向移动和凭据访问
所需数据:足以证明分类结论的佐证,或者明确说明证据不足(→ 分类为 unknown)。

Step 4 — Assess entity risk

第 4 步——评估实体风险

Before querying Entity Analytics, preflight each index with
GET /_resolve/index/{pattern}
. If an index is absent, skip that lookup and continue.
Check the Risk Engine and asset criticality for the involved hosts and users with
POST /_query
:
esql
FROM risk-score.risk-score-latest-*
| WHERE host.name IN ("<host>") OR user.name IN ("<user>")
| KEEP host.name, host.risk.calculated_level, host.risk.calculated_score_norm,
       user.name, user.risk.calculated_level, user.risk.calculated_score_norm
| LIMIT 10
esql
FROM .asset-criticality.asset-criticality-*
| WHERE (id_field == "host.name" AND id_value IN ("<host>"))
     OR (id_field == "user.name" AND id_value IN ("<user>"))
| KEEP id_field, id_value, criticality_level
| LIMIT 10
High entity risk scores (normalized >80) or
extreme_impact
/
high_impact
criticality on an involved entity are strong prioritization signals. Skip the lookups for any index the preflight reports as absent.
Data needed: entity risk level and asset criticality for the primary host or user.
查询 Entity Analytics 之前,使用
GET /_resolve/index/{pattern}
预检查每个索引。如果某个索引不存在,则跳过该查询并继续。
使用
POST /_query
通过 Risk Engine 和资产关键性检查相关主机及用户:
esql
FROM risk-score.risk-score-latest-*
| WHERE host.name IN ("<host>") OR user.name IN ("<user>")
| KEEP host.name, host.risk.calculated_level, host.risk.calculated_score_norm,
       user.name, user.risk.calculated_level, user.risk.calculated_score_norm
| LIMIT 10
esql
FROM .asset-criticality.asset-criticality-*
| WHERE (id_field == "host.name" AND id_value IN ("<host>"))
     OR (id_field == "user.name" AND id_value IN ("<user>"))
| KEEP id_field, id_value, criticality_level
| LIMIT 10
实体风险评分较高(标准化分数 >80),或者相关实体具有
extreme_impact
/
high_impact
关键性,是强烈的优先级信号。对于预检查显示不存在的索引,跳过相应查询。
所需数据:主要主机或用户的实体风险级别和资产关键性。

Step 5 — Determine disposition

第 5 步——确定处置结论

Only after all context is gathered, decide one of:
  • True Positive — strong corroborating evidence (persistence plus C2, credential theft, lateral movement, known malware hash). Escalate and recommend containment.
  • Benign True Positive — the alert is technically correct but the activity is known-good (for example, an IT admin running PSExec). Create an exception to reduce noise.
  • False Positive — the rule needs tuning to avoid this class of alerts.
  • Unknown — insufficient evidence. This is an acceptable and often correct outcome. Document what was found and what is missing.
Use the evidence-weighting criteria in references/classification-guide.md to support the disposition call.
Summarize findings for case documentation:
  • 1–2 sentence summary
  • Attack chain (what happened and in what order)
  • IOCs: hashes, IPs, file paths
  • MITRE techniques observed
  • Behavioral findings
  • Response context: remediation steps, credentials at risk
For case creation and alert attachment, use the kibana-cases skill (the canonical cross-solution cases skill). Name the case-management operation you need.
Data needed: classification and evidence summary for documentation.
只有在收集完所有上下文之后,才能从以下选项中确定一个结论:
  • True Positive(真实告警)——存在强有力的佐证(持久化与 C2、凭据窃取、横向移动、已知恶意软件哈希)。进行升级,并建议实施遏制。
  • Benign True Positive(良性真实告警)——告警在技术层面正确,但活动属于已知正常行为(例如 IT 管理员运行 PSExec)。创建例外,以减少噪声。
  • False Positive(误报)——需要调整规则,以避免再次产生此类告警。
  • Unknown(未知)——证据不足。这是可接受且往往正确的结果。记录已发现的内容以及缺失的信息。
使用 references/classification-guide.md 中的证据权重标准来支持处置结论。
为 case 文档汇总调查发现:
  • 1–2 句话的摘要
  • 攻击链(发生了什么以及顺序如何)
  • IOCs:哈希、IP、文件路径
  • 观察到的 MITRE 技术
  • 行为发现
  • 响应上下文:修复步骤、面临风险的凭据
如需创建 case 和附加告警,请使用 kibana-cases skill(标准的跨解决方案 case skill)。指明所需的 case 管理操作。
所需数据:用于文档记录的分类结果和证据摘要。

Step 6 — Acknowledge related alerts (confirmed, irreversible)

第 6 步——确认相关告警(操作已确认且不可逆)

Before acknowledging, preview the exact scope: run
POST /{index}/_count
with the same match criteria (agent ID, host, rule name, and time window) to confirm the count. Report the count and confirm with the analyst before writing. If the count exceeds 10,000, acknowledge in batches.
To acknowledge, collect matching alert
_id
s with
POST /.alerts-security.alerts-*/_search
(use
_source: false
and
size: 10000
), then set their workflow status with
POST kbn:/api/detection_engine/signals/status
(see the Operations table for the CLI equivalent). Pass the alert IDs and target status in the request body:
json
{ "signal_ids": ["<id1>", "<id2>"], "status": "acknowledged" }
Use the Detection Engine API rather than writing to the alert index directly — direct writes are rejected on Serverless. Widen the time window for longer attack chains. Report the exact number acknowledged from the response. There is no undo for an acknowledgement — confirm scope first.
确认告警之前,先预览准确范围:使用相同的匹配条件(agent ID、主机、规则名称和时间窗口)运行
POST /{index}/_count
,以确认计数。报告计数,并在写入前与分析师确认。如果计数超过 10,000,则分批确认。
如需确认,使用
POST /.alerts-security.alerts-*/_search
收集匹配的告警
_id
(使用
_source: false
并设置
size: 10000
),然后通过
POST kbn:/api/detection_engine/signals/status
设置其工作流状态(对应的 CLI 命令见操作表)。在请求体中传入告警 ID 和目标状态:
json
{ "signal_ids": ["<id1>", "<id2>"], "status": "acknowledged" }
使用 Detection Engine API,而不是直接写入告警索引——Serverless 会拒绝直接写入。对于持续时间更长的攻击链,应扩大时间窗口。报告响应中实际确认的准确数量。确认操作无法撤销——必须先确认范围。

Guidelines

指南

  • Do not classify prematurely. All five substeps in Step 3 must complete before any opinion is formed.
  • Report only tool output. Do not invent IDs, hostnames, IPs, or details not in the tool response.
  • Preserve identifiers. Use exact values the analyst provides; copy
    _id
    s, timestamps, and hostnames verbatim.
  • Distinguish facts from inference. Label conclusions beyond tool output explicitly as your assessment.
  • Keep context gathering focused. Run 2–4 targeted queries per alert group, not 10+. Use
    KEEP
    to limit columns.
  • Acknowledgement is irreversible. Always preview with a count and prefer a narrow, well-scoped match. Pass the count to the analyst and wait for confirmation before writing.
  • Absolute timestamps only. Extract
    @timestamp
    from the alert and build all queries around it. Never use
    NOW() - 1 HOUR
    or other relative expressions on old alerts.
  • 不要过早分类。 在形成任何意见之前,必须完成第 3 步中的五个子步骤。
  • 仅报告工具输出。 不要编造工具响应中不存在的 ID、主机名、IP 或其他详细信息。
  • 保留标识符。 准确使用分析师提供的值;逐字复制
    _id
    、时间戳和主机名。
  • 区分事实与推断。 对超出工具输出范围的结论,明确标注为你的评估。
  • 保持上下文收集有针对性。 每个告警组运行 2–4 个精准查询,而不是 10 个以上。使用
    KEEP
    限制列。
  • 确认操作不可逆。 始终先使用 count 预览范围,并优先采用范围狭窄且定义明确的匹配。将计数告知分析师,并等待确认后再写入。
  • 仅使用绝对时间戳。 从告警中提取
    @timestamp
    ,并围绕它构建所有查询。对于旧告警,绝不要使用
    NOW() - 1 HOUR
    或其他相对时间表达式。

Examples

示例

  • "Fetch the next unacknowledged alert and triage it."
  • "Investigate alert ID abc-123 — gather context, classify, and create a case if malicious."
  • "Process the top 5 critical alerts from the last 24 hours."
  • "Triage the alert group on host WIN-SRV01 from 2025-09-14."
  • “获取下一个未确认的告警并对其进行分诊。”
  • “调查告警 ID abc-123——收集上下文、进行分类,并在发现恶意行为时创建 case。”
  • “处理过去 24 小时内最紧急的 5 条严重告警。”
  • “对来自 2025-09-14 的主机 WIN-SRV01 上的告警组进行分诊。”

References

参考资料

Operations

操作

HTTP API (shorthand)
elastic
CLI command
POST /.alerts-security.alerts-*/_search
elastic es search --index '.alerts-security.alerts-*' --input-file '<query.json>'
POST /{index}/_search
elastic es search --index '<index>' --input-file '<query.json>'
POST /{index}/_count
elastic es count --index '<index>' --input-file '<query.json>'
POST /_query
elastic es esql query --format tsv --query '<esql>'
GET /_resolve/index/{pattern}
elastic es indices resolve-index --name '<pattern>'
POST kbn:/api/detection_engine/signals/status
elastic kb security-detections-api set-alerts-status --signal-ids '<ids>' --status 'acknowledged'
HTTP API(简写)
elastic
CLI 命令
POST /.alerts-security.alerts-*/_search
elastic es search --index '.alerts-security.alerts-*' --input-file '<query.json>'
POST /{index}/_search
elastic es search --index '<index>' --input-file '<query.json>'
POST /{index}/_count
elastic es count --index '<index>' --input-file '<query.json>'
POST /_query
elastic es esql query --format tsv --query '<esql>'
GET /_resolve/index/{pattern}
elastic es indices resolve-index --name '<pattern>'
POST kbn:/api/detection_engine/signals/status
elastic kb security-detections-api set-alerts-status --signal-ids '<ids>' --status 'acknowledged'