cybernovaacademy/KQL-THREAT-HUNTING
GitHub: cybernovaacademy/KQL-THREAT-HUNTING
CyberNova Academy 推出的一套从零到实战的 KQL 威胁狩猎课程,帮助初学者掌握 Microsoft 安全栈下的日志分析与威胁检测技能。
Stars: 0 | Forks: 0
# CyberNova Academy — KQL 与威胁狩猎精通课程
## 课程标识
| 字段 | 详细信息 |
|---|---|
| 课程名称 | KQL 与威胁狩猎精通课程 |
| 副标题 | 从零到 SOC 分析师 |
| 提供者 | CyberNova Academy |
| 主要平台 | Microsoft Sentinel, Microsoft Defender XDR, Log Analytics Workspace |
| 主表 | DeviceLogonEvents |
| 技能路径 | 日志分析 → KQL 基础 → 威胁狩猎 → 检测工程 |
| 目标学习者 | 初级 SOC 分析师、IT 管理员、威胁猎人、网络安全理学硕士学生 |
| 预计时长 | 15–18 小时 |
## 课程介绍
### 我们在学什么?
本课程教授学习者如何使用 **Kusto Query Language (KQL)**,利用 Microsoft 安全遥测技术调查身份验证活动、检测可疑行为,并培养实用的威胁狩猎技能。
学习者将在以下方面建立信心:
- Kusto Query Language 基础知识
- 读取、过滤和分析安全日志
- Microsoft Defender 和 Sentinel 风格的调查工作流
- DeviceLogonEvents 身份验证分析
- 暴力破解检测
- 密码喷洒检测
- 外部访问调查
- 权限提升狩猎
- 横向移动分析
- 可迁移的 SIEM 查询逻辑
### 我们为什么要学这个?
| 原因 | 网络安全价值 |
|---|---|
| 就业市场需求 | 在使用 Microsoft 安全技术栈的众多 SOC 职位中都有 KQL 的要求 |
| 速度 | 手动审查日志耗时数小时;KQL 可将调查时间缩短至几秒 |
| 主动防御 | 威胁狩猎能在警报触发前发现攻击者的行为 |
| 职业发展 | KQL 技能能将初级分析师与更优秀的二级/三级分析师区分开来 |
| 现实相关性 | SOC 分析师在入职的第一周就需要查询日志 |
## 平台转换思维
| 概念 | Microsoft KQL | Splunk SPL | Elastic / OpenSearch | AWS CloudWatch |
|---|---|---|---|---|
| 过滤行 | `where` | `search` / `where` | Query filter | `filter` |
| 选择字段 | `project` | `fields` / `table` | `_source` filtering | `fields` |
| 分组结果 | `summarize` | `stats` | Aggregations | `stats` |
| 排序 | `order by` | `sort` | Sort clause | `sort` |
| 时间范围 | `ago()` / `datetime()` | `earliest=` | time picker / range | `ago()` 样式过滤器 |
逻辑是通用的。语法只是词汇。
# 模块 1 — 基础:理解日志
## 第 1.1 课 — 什么是日志?
日志是系统活动的数字凭证。它们记录了诸如登录、进程启动、网络连接、文件创建、服务更改和安全事件等操作。
### 日志回答的三个问题
| 问题 | 含义 |
|---|---|
| 发生了什么? | 记录的活动或事件 |
| 什么时候发生的? | 事件的时间戳 |
| 是谁或什么引起的? | 涉及的用户、进程、设备或来源 |
### 现实世界背景
没有日志,事件响应就变成了凭空猜测。有了日志,分析师可以重建攻击者的行为,证明时间线,识别受影响的资产,并建议遏制措施。
## 第 1.2 课 — Log Analytics Workspace
**Log Analytics Workspace** 是 Microsoft 基于云的日志存储和分析环境。
### Excel 类比
| Excel 概念 | Log Analytics 对应项 |
|---|---|
| 工作簿 | Log Analytics Workspace |
| 工作表 / 标签页 | 表 |
| 行 | 单个日志事件 |
| 列 | 字段 / 属性 |
### 常见安全表
| 表格 | 用途 |
|---|---|
| DeviceLogonEvents | 身份验证和登录活动 |
| DeviceProcessEvents | 进程执行和命令行 |
| DeviceNetworkEvents | 网络连接 |
| DeviceFileEvents | 文件创建、删除和修改 |
| SecurityAlert | 触发的警报 |
| SigninLogs | Azure AD / Entra ID 身份验证 |
## 第 1.3 课 — 什么是 KQL?
KQL 是一种用于查询大型安全数据集的语言。
```
DeviceLogonEvents
| where ActionType == "LogonFailed"
| project Timestamp, DeviceName, AccountName, RemoteIP
```
KQL 允许分析师:
- 快速过滤数百万行数据
- 仅选择有用的列
- 将事件分组为模式
- 构建检测
- 创建仪表板
- 跨表透视
# 模块 2 — KQL 基础
## 第 2.1 课 — 你的第一个查询
### 从表格开始
```
DeviceLogonEvents
```
### 提取样本
```
DeviceLogonEvents
| take 10
```
### 使用 `where` 过滤行
```
DeviceLogonEvents
| where ActionType == "LogonSuccess"
```
### 使用 `project` 选择列
```
DeviceLogonEvents
| project Timestamp, DeviceName, AccountName, ActionType
```
## 第 2.2 课 — 专业过滤
| 运算符 | 含义 | 示例 |
|---|---|---|
| `==` | 精确匹配 | `ActionType == "LogonFailed"` |
| `!=` | 不等于 | `AccountName != "guest"` |
| `contains` | 部分文本匹配 | `DeviceName contains "server"` |
| `has` | 词界匹配 | `ProcessCommandLine has "powershell"` |
| `startswith` | 以某值开头 | `AccountName startswith "adm"` |
| `endswith` | 以某值结尾 | `FileName endswith ".exe"` |
| `in` | 多个值 | `ActionType in ("LogonSuccess", "LogonFailed")` |
### 实际示例
```
DeviceLogonEvents
| where ActionType == "LogonFailed"
```
```
DeviceLogonEvents
| where AccountName == "administrator"
```
```
DeviceLogonEvents
| where LogonType == "Network"
```
```
DeviceLogonEvents
| where RemoteIPType == "Public"
```
```
DeviceLogonEvents
| where ActionType == "LogonFailed" and RemoteIPType == "Public"
```
## 第 2.3 课 — 基于时间的过滤
### 相对时间
```
DeviceLogonEvents
| where Timestamp > ago(24h)
```
```
DeviceLogonEvents
| where Timestamp > ago(7d)
```
```
DeviceLogonEvents
| where Timestamp > ago(1h)
```
### 特定时间范围
```
DeviceLogonEvents
| where Timestamp between (datetime(2026-01-13 06:00:00) .. datetime(2026-01-13 08:00:00))
```
### 专业提示
始终尽早按时间过滤以提高性能。
## 第 2.4 课 — 排序与限制
```
DeviceLogonEvents
| order by Timestamp desc
```
```
DeviceLogonEvents
| top 10 by Timestamp desc
```
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonFailed"
| order by Timestamp desc
| take 100
```
## 第 2.5 课 — 计数与聚合
### 计算行数
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| count
```
### 按操作类型汇总
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| summarize count() by ActionType
```
### 按 IP 统计失败登录
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonFailed"
| summarize FailedCount = count() by RemoteIP
| order by FailedCount desc
```
### 每台设备的唯一用户
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| summarize UniqueUsers = dcount(AccountName) by DeviceName
| order by UniqueUsers desc
```
## 第 2.6 课 — 使结果更具可读性
### 重命名列
```
DeviceLogonEvents
| project Time = Timestamp, User = AccountName, Device = DeviceName, Result = ActionType
```
### 添加计算列
```
DeviceLogonEvents
| extend HourOfDay = hourofday(Timestamp)
| extend DayOfWeek = dayofweek(Timestamp)
```
### 使用条件逻辑
```
DeviceLogonEvents
| extend Status = case(
ActionType == "LogonSuccess", "SUCCESS",
ActionType == "LogonFailed", "FAILED",
"UNKNOWN"
)
```
# 模块 3 — 深入探讨:DeviceLogonEvents
## 核心字段
| 字段类别 | 字段 | 含义 |
|---|---|---|
| 身份 | AccountName | 登录的用户 |
| 身份 | AccountDomain | 域或机器上下文 |
| 身份 | AccountSid | 唯一安全标识符 |
| 操作 | ActionType | LogonSuccess 或 LogonFailed |
| 操作 | FailureReason | 身份验证失败的原因 |
| 操作 | LogonType | 身份验证是如何发生的 |
| 设备 | DeviceName | 目标机器 |
| 设备 | DeviceId | 唯一设备 ID |
| 网络 | RemoteIP | 源 IP |
| 网络 | RemoteIPType | Public 或 Private |
| 网络 | RemotePort | 源端口 |
| 网络 | Protocol | NTLM、Kerberos 等 |
| 进程 | InitiatingProcessFileName | 触发登录的进程 |
| 进程 | InitiatingProcessCommandLine | 完整命令行 |
## 重要的登录类型
| 登录类型 | 含义 | 威胁狩猎价值 |
|---|---|---|
| Interactive | 物理控制台登录 | 本地访问基线 |
| Network | SMB / 网络资源访问 | 横向移动检测 |
| RemoteInteractive | RDP 会话 | 远程访问和勒索软件操作者活动 |
| Service | 服务账户活动 | 服务滥用或配置错误 |
## 失败原因
| 失败原因 | 解释 |
|---|---|
| InvalidUserNameOrPassword | 暴力破解或密码喷洒指标 |
| AccountLocked | 攻击可能导致账户锁定 |
| PasswordExpired | 薄弱的运维规范或账户问题 |
| AccountDisabled | 对已禁用账户的探测 |
| LogonTypeRestricted | 策略阻止了此登录类型 |
## 解析 AdditionalFields
```
DeviceLogonEvents
| extend ParsedFields = parse_json(AdditionalFields)
| extend Terminal = tostring(ParsedFields.Terminal)
| extend PosixUserId = toint(ParsedFields.PosixUserId)
| project Timestamp, DeviceName, AccountName, Terminal, PosixUserId
```
### Linux 狩猎信号
| 字段 | 可疑含义 |
|---|---|
| Terminal = ssh | 外部 SSH 活动 |
| PosixUserId = 0 | Root 级别活动 |
| InitiatingAccountName != AccountName | 可能是 sudo 或权限提升 |
# 模块 4 — 威胁狩猎模式
## 第 4.1 课 — 暴力破解检测
### 基本失败登录搜寻
```
DeviceLogonEvents
| where Timestamp > ago(1h)
| where ActionType == "LogonFailed"
```
### 按源 IP 统计失败尝试
```
DeviceLogonEvents
| where Timestamp > ago(1h)
| where ActionType == "LogonFailed"
| summarize FailedAttempts = count() by RemoteIP
| where FailedAttempts > 10
| order by FailedAttempts desc
```
### 完整暴力破解上下文
```
DeviceLogonEvents
| where Timestamp > ago(1h)
| where ActionType == "LogonFailed"
| summarize
FailedAttempts = count(),
TargetAccounts = make_set(AccountName),
TargetDevices = make_set(DeviceName),
FirstAttempt = min(Timestamp),
LastAttempt = max(Timestamp)
by RemoteIP
| where FailedAttempts > 10
| order by FailedAttempts desc
```
## 第 4.2 课 — 密码喷洒检测
```
DeviceLogonEvents
| where Timestamp > ago(1h)
| where ActionType == "LogonFailed"
| summarize
TargetedAccounts = dcount(AccountName),
TotalAttempts = count(),
AccountList = make_set(AccountName, 10)
by RemoteIP
| extend AttemptsPerAccount = todouble(TotalAttempts) / todouble(TargetedAccounts)
| where TargetedAccounts > 5 and AttemptsPerAccount < 3
| order by TargetedAccounts desc
```
### 分析师解释
| 攻击类型 | 模式 |
|---|---|
| 暴力破解 | 针对一个账户尝试多个密码 |
| 密码喷洒 | 使用一个或几个密码尝试多个账户 |
## 第 4.3 课 — 成功的外部访问
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonSuccess"
| where RemoteIPType == "Public"
```
### 添加上下文
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonSuccess"
| where RemoteIPType == "Public"
| summarize
LoginCount = count(),
Devices = make_set(DeviceName),
FirstSeen = min(Timestamp),
LastSeen = max(Timestamp)
by RemoteIP, AccountName
| order by LoginCount desc
```
## 第 4.4 课 — 权限提升狩猎
```
DeviceLogonEvents
| where AccountName in~ ("root", "Administrator")
or tostring(parse_json(AdditionalFields).PosixUserId) == "0"
| where InitiatingProcessAccountName != AccountName
| project Timestamp, DeviceName, AccountName, InitiatingProcessAccountName, InitiatingProcessFileName, AdditionalFields
| order by Timestamp desc
```
## 第 4.5 课 — 横向移动检测
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| where LogonType == "Network"
| where RemoteIPType == "Private"
| summarize
DeviceCount = dcount(DeviceName),
Devices = make_set(DeviceName)
by AccountName, RemoteIP
| where DeviceCount > 3
| order by DeviceCount desc
```
## 第 4.6 课 — 威胁狩猎工作流
| 步骤 | 操作 |
|---|---|
| 1 | 形成可测试的假设 |
| 2 | 确定相关的表和字段 |
| 3 | 构建广泛查询 |
| 4 | 根据时间、用户、设备和 IP 缩小范围 |
| 5 | 透视到相关的表 |
| 6 | 记录发现和建议 |
# 模块 5 — 高级技术
## 联结表以获取上下文
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonSuccess" and RemoteIPType == "Public"
| project LogonTime = Timestamp, DeviceName, AccountName, RemoteIP
| join kind=inner (
DeviceProcessEvents
| where Timestamp > ago(24h)
| project ProcessTime = Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine
) on DeviceName, AccountName
| where ProcessTime between (LogonTime .. LogonTime + 1h)
| order by LogonTime asc
```
## 使用 `let` 定义变量
```
let timeRange = 24h;
let failureThreshold = 10;
let suspiciousIP = "163.223.99.217";
DeviceLogonEvents
| where Timestamp > ago(timeRange)
| where RemoteIP == suspiciousIP
| where ActionType == "LogonFailed"
| summarize FailedAttempts = count()
| where FailedAttempts > failureThreshold
```
## 时间序列分析
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonFailed"
| summarize FailedLogins = count() by bin(Timestamp, 15m)
| render timechart
```
# 模块 6 — 动手实验:威胁狩猎练习
## 场景
学生使用 KQL 查询对身份验证日志进行调查,重点关注潜在的系统入侵,主要针对 DeviceLogonEvents 表。
### 学生正在搜寻的内容
- 暴力破解尝试
- 成功的未授权访问
- 权限提升活动
- 横向移动指标
## 实验练习 1 — 初始侦察
### 目标
了解环境和可用数据。
### 任务
```
DeviceLogonEvents
| summarize Devices = make_set(DeviceName)
```
```
DeviceLogonEvents
| summarize Accounts = make_set(AccountName)
```
```
DeviceLogonEvents
| summarize FirstEvent = min(Timestamp), LastEvent = max(Timestamp)
```
```
DeviceLogonEvents
| summarize Count = count() by ActionType
| order by Count desc
```
## 实验练习 2 — 搜寻暴力破解
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonFailed"
| summarize
FailedAttempts = count(),
TargetAccounts = make_set(AccountName),
TargetDevices = make_set(DeviceName)
by RemoteIP
| where FailedAttempts > 20
| order by FailedAttempts desc
```
### 关键问题
- 哪些 IP 地址正在发起攻击?
- 哪些账户被作为目标?
- 是否有尝试成功的?
- 使用了哪种身份验证协议?
## 实验练习 3 — 搜寻未授权外部访问
```
DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonSuccess"
| where RemoteIPType == "Public"
| project Timestamp, DeviceName, AccountName, RemoteIP, LogonType, Protocol
| order by Timestamp desc
```
## 实验练习 4 — 搜寻权限提升
```
DeviceLogonEvents
| where AccountName in~ ("root", "Administrator")
or tostring(parse_json(AdditionalFields).PosixUserId) == "0"
| project Timestamp, DeviceName, AccountName, InitiatingProcessAccountName, InitiatingProcessFileName, AdditionalFields
| order by Timestamp desc
```
## 实验练习 5 — 完整调查综合
### 交付物
| 交付物 | 描述 |
|---|---|
| 事件摘要 | 发生了什么以及为什么重要 |
| 时间线 | 从第一个事件到最后一个已知活动 |
| IoCs | IP、账户、设备、协议 |
| 影响评估 |影响的系统和用户 |
| 补救措施 | 封禁、重置密码、MFA、监控 |
# 模块 7 — 将技能迁移到其他平台
## KQL 到 Splunk SPL
| KQL | Splunk SPL |
|---|---|
| `where` | `search` / `where` |
| `project` | `fields` / `table` |
| `summarize count() by X` | `stats count by X` |
| `order by X desc` | `sort -X` |
| `take 10` | `head 10` |
| `ago(24h)` | `earliest=-24h` |
## KQL 到 Elastic / OpenSearch
| KQL 概念 | Elastic / OpenSearch 对应项 |
|---|---|
| 过滤 | Lucene filter 或 DSL 查询 |
| 汇总 | Aggregations |
| 时间图表 | Date histogram |
| 投影 | Source 字段选择 |
# 快速参考附录
## 基本 KQL 运算符
```
| where // Filter rows
| project // Select columns
| extend = value // Add columns
| summarize by // Aggregate
| order by desc // Sort
| take N // Limit rows
| top N by // Sorted limit
| join (table) on // Combine tables
| count // Count rows
```
## DeviceLogonEvents 关键字段
| 字段 | 含义 |
|---|---|
| Timestamp | 事件发生的时间 |
| AccountName | 登录的用户 |
| DeviceName | 目标设备 |
| ActionType | 成功或失败 |
| LogonType | 身份验证是如何发生的 |
| RemoteIP | 源 IP |
| RemoteIPType | Public 或 Private |
| FailureReason | 身份验证失败的原因 |
| Protocol | NTLM、Kerberos 等 |
| InitiatingProcessFileName | 触发身份验证的进程 |
# 常见威胁狩猎查询
## 按外部 IP 统计失败登录
```
DeviceLogonEvents
| where ActionType == "LogonFailed"
| summarize FailedCount = count() by RemoteIP
| order by FailedCount desc
```
## 成功的外部访问
```
DeviceLogonEvents
| where ActionType == "LogonSuccess"
| where RemoteIPType == "Public"
```
## Root / Administrator 活动
```
DeviceLogonEvents
| where AccountName in~ ("root", "Administrator")
```
## 暴力破解检测
```
DeviceLogonEvents
| where ActionType == "LogonFailed"
| summarize Attempts = count() by RemoteIP
| where Attempts > 20
| order by Attempts desc
```
# 讲师说明
| 模块 | 建议时长 |
|---|---:|
| 模块 1–2:基础 | 2–3 小时 |
| 模块 3:DeviceLogonEvents 深入探讨 | 2 小时 |
| 模块 4:威胁狩猎模式 | 3–4 小时 |
| 模块 5:高级技术 | 1–2 小时 |
| 模块 6:动手实验 | 3–4 小时 |
| 模块 7:迁移到其他 SIEM | 1 小时 |
| 总计 | 15–18 小时 |
## 前提条件
- 对日志的基本了解
- 熟悉电子表格概念
- 对网络安全运营感兴趣
- 拥有示例日志数据或 Microsoft 安全实验室的访问权限
# 学生作品集证据
学生应提交:
- 查询和结果的屏幕截图
- 暴力破解检测查询
- 密码喷洒检测查询
- 成功的外部访问查询
- 权限提升查询
- 书面事件摘要
- 事件时间线
- 失陷指标
- 建议的补救步骤
# CyberNova Academy 最终总结
KQL 不仅仅是一种查询语言。它是 SOC 分析师的超能力。
它帮助分析师从:
```
Alert responder → Investigator → Threat hunter → Detection engineer
```
最优秀的 SOC 分析师不仅会对警报做出反应。他们还会向数据提出更好的问题。
# 保留的原始提供内容
点击展开原始提供的文本
KQL & THREAT HUNTING
MASTERY COURSE
From Zero to SOC Analyst
CyberNova Academy
Curriculum Guide
课程介绍
我们在学什么?
• Kusto Query Language (KQL) - Microsoft 安全工具中使用的查询语言
• 如何阅读、理解和分析安全日志
• 使用真实企业数据进行威胁狩猎的方法论
• 重点:DeviceLogonEvents 表 - 身份验证和登录活动
我们为什么要学这个?
• 就业市场现实:在使用 Microsoft 技术栈的 SOC 分析师职位描述中,80%+ 都要求掌握 KQL
• 调查速度:手动日志审查 = 数小时;KQL 查询 = 数秒
• 主动防御:威胁狩猎能在警报触发之前发现攻击者
• 职业晋升:KQL 技能将 L1 分析师与 L2/L3 角色区分开来
• 现实需求:在你入职的第一周,你一定需要查询日志
这将如何帮助你?
• 更快、更准确地调查安全事件
• 从零开始构建检测规则和警报
• 通过包含 KQL 练习的技术面试
• 从警报响应者转型为主动威胁猎人
• 凭借实际的查询经验在简历中脱颖而出
这如何应用于其他平台?
• Splunk:SPL (Search Processing Language) - 类似的过滤概念,不同的语法
• Elastic/ELK:Lucene 查询和 KQL(不同的 KQL!)- 同样的逻辑适用
• Chronicle/Google SecOps:YARA-L 和 UDM 搜索 - 同样的心智模型
• AWS CloudWatch:Insights 查询 - 过滤概念是相通的
• 任何 SIEM:逻辑是通用的;语法只是词汇
核心真相:一旦你深入理解了一种查询语言,其他的就会变得更容易
模块 1:基础 - 理解日志
第 1.1 课:什么是日志?(全局概览)
要涵盖的核心概念:
• 日志 = 系统上发生的所有事情的数字凭证
• 每次点击、登录、文件访问、网络连接都会生成日志
• 类比:你 IT 环境的监控摄像头,不过是文本形式的
• 没有日志,你对发生的事情一无所知
日志回答的三个问题:
1. 发生了什么?
2. 什么时候发生的?
3. 是谁/什么导致它发生的?
现实世界背景:
• “发生了数据泄露” - 没有日志,调查是不可能的
• 没有日志的事件响应 = 凭空猜测
• 合规要求(HIPAA、PCI、SOC2)强制要求保留日志
第 1.2 课:日志是如何存储的 - Log Analytics Workspace
要涵盖的核心概念:
• Log Analytics Workspace = Microsoft 基于云的日志存储
• 把它想象成云端的一个巨大的 Excel 文件
• 数据流入来源:Defender for Endpoint、Sentinel、Azure 资源、自定义来源
• 组织成表(就像 Excel 的标签页/工作表)
Excel 电子表格类比:
• 工作簿 = Log Analytics Workspace
• 工作表/标签页 = 表(DeviceLogonEvents、DeviceProcessEvents 等)
• 行 = 单个日志条目(事件)
• 列 = 字段/属性(Timestamp、DeviceName、AccountName 等)
需要了解的常见表:
• DeviceLogonEvents - 登录/身份验证活动
• DeviceProcessEvents - 进程执行
• DeviceNetworkEvents - 网络连接
• DeviceFileEvents - 文件操作
• SecurityAlert - 触发的警报
• SigninLogs - Azure AD 身份验证
要点:
• 表有 SCHEMAS(定义的列)
• 并非每一行都有所有列的数据(稀疏数据)
• 数据有保留期(30、90、180、365 天)
• 在生产环境中存在查询成本
第 1.3 课:什么是 KQL?(最简单的解释)
核心信息:
• KQL = 一种过滤巨大电子表格的方法
• 你不是在编程;你是在向数据提问
• 从数百万行开始,准确过滤出你需要的
心智模型:
数百万条日志 → KQL 查询 → 少量相关结果
为什么不直接用 Excel?
• 规模:Excel 在大约 100 万行时会崩溃;Log Analytics 可以处理数十亿行
• 速度:查询在庞大的数据集中几秒钟即可运行完成
• 功能:内置安全函数、时间智能
• 协作:共享查询、构建仪表板、自动化警报
KQL 理念:
• 基于管道:数据通过运算符从左向右流动
• 可读性:查询读起来几乎像英语句子
• 迭代性:逐步构建查询,边构建边优化
在你继续之前
下一部分需要查询实际的日志。你有两个选择:
自己搭建 – 我会给你提供设置带有 Defender for Endpoint 的 Azure 环境的说明。完全可行,你只需处理来自你自己机器的小型数据集。
加入 CyberNova Academy– 1,000+ 名用户、数百台虚拟机、Tenable、实时威胁狩猎、完整课程库、就业证明,以及一个真正在乎的社区。我从没遇到过谁加入后说不值得的。
两种方式都行。选一个,我们继续。
模块 2:KQL 基础
第 2.1 课:你的第一个查询 - 基础知识
要教授的核心运算符:
从一个表开始:
DeviceLogonEvents
• 仅此一项就会返回所有行(不要在生产环境中这样做!)
• 总是从这里开始,然后进行过滤
take - 获取样本:
DeviceLogonEvents| take 10
• 返回前 N 行
• 使用它来探索数据结构
• 有助于了解存在哪些列
where - 过滤器:
DeviceLogonEvents| where ActionType == "LogonSuccess"
• 最重要的运算符
• 根据条件过滤行
• 想象:Excel 的筛选下拉菜单,但更强大
project - 选择列:
DeviceLogonEvents| project Timestamp, DeviceName, AccountName, ActionType
• 选择要显示的列
• 减少干扰,集中调查
• 就像在 Excel 中隐藏列一样
核心教学要点:
• 管道符 | 将数据从一个操作传递到下一个操作
• 顺序很重要:尽早过滤以提高性能
• 区分大小写:值中 "LogonSuccess" ≠ "logonsuccess"
• 双等号 == 用于精确匹配
第 2.2 课:专业级过滤
比较运算符:
• == 精确匹配
• != 不等于
• > < >= <= 用于数字和日期
• contains 部分文本匹配(不区分大小写)
• has 词界匹配(比 contains 更快)
• startswith / endswith 用于模式匹配
• in 用于多个值
使用 DeviceLogonEvents 的实际示例:
查找失败登录:
| where ActionType == "LogonFailed"
查找特定用户:
| where AccountName == "administrator"
查找网络登录:
| where LogonType == "Network"
查找外部 IP:
| where RemoteIPType == "Public"
组合条件 (AND):
| where ActionType == "LogonFailed" and RemoteIPType == "Public"
多值 (OR):
| where ActionType in ("LogonSuccess", "LogonFailed")
教学要点:
• AND 缩小结果范围(更具限制性)
• OR 扩大结果范围(限制较少)
• 圆括号用于分组逻辑:(A and B) or C
• 首先对索引列进行过滤以提高性能
第 2.3 课:基于时间的过滤
为什么时间很重要:
• 安全调查几乎总是受时间限制的
• “昨天下午 2 点到 4 点之间发生了什么?”
• 减少数据量以加快查询速度
时间运算符:
相对时间(最常见):
DeviceLogonEvents
| where Timestamp > ago(24h)
| where Timestamp > ago(7d)
| where Timestamp > ago(1h)
时间范围:
| where Timestamp between (datetime(2026-01-13 06:00:00) .. datetime(2026-01-13 08:00:00))
特定日期:
| where Timestamp > datetime(2026-01-13)
时间单位:
• m = 分钟
• h = 小时
• d = 天
专业提示:
• 始终在查询的早期按时间过滤(提高性能)
• 注意 UTC 与本地时间的区别(日志通常为 UTC)
• 使用 ago() 表示相对时间,datetime() 表示特定时间
第 2.4 课:排序和限制结果
使用 order by 排序:
| order by Timestamp desc
• desc = 最新优先(在安全领域最常见)
• asc = 最旧优先
多列排序:
| order by AccountName asc, Timestamp desc
使用 take 和 top 限制:
| take 100| top 10 by Timestamp desc
• take = 任意样本
• top = 排序限制(结合了 order by + take)
实用模式:
DeviceLogonEvents| where Timestamp > ago(24h)| where ActionType == "LogonFailed"| order by Timestamp desc| take 100
第 2.5 课:计数和聚合
为什么要聚合?
• 原始日志显示单个事件
• 聚合揭示了模式
• “给我看计数,而不是细节”
count - 总行数:
DeviceLogonEvents| where Timestamp > ago(24h)| count
summarize -组和聚合:
DeviceLogonEvents| where Timestamp > ago(24h)| summarize count() by ActionType
常见聚合函数:
• count() - 行数
• dcount() - 去重计数
• sum() - 数字字段的总和
• avg() - 平均值
• min() / max() - 极值
强大的模式:
按 IP 统计失败登录:
| summarize FailedCount = count() by RemoteIP| order by FailedCount desc
每台设备的唯一用户:
| summarize UniqueUsers = dcount(AccountName) by DeviceName
第 2.6 课:使结果具有可读性
重命名列:
| project TimeStamp = Timestamp, User = AccountName, Device = DeviceName
extend - 添加计算列:
| extend HourOfDay = hourofday(Timestamp)| extend DayOfWeek = dayofweek(Timestamp)
使用 case 的条件逻辑:
| extend Status = case( ActionType == "LogonSuccess", "SUCCESS", ActionType == "LogonFailed", "FAILED", "UNKNOWN")
模块 3:深入探讨 - DeviceLogonEvents 表
第 3.1 课:理解 Schema
需要了解的核心字段:
身份字段:
• AccountName - 登录的用户名
• AccountDomain - 账户的域/机器
• AccountSid - 安全标识符(唯一 ID)
操作字段:
• ActionType - LogonSuccess 或 LogonFailed
• FailureReason - 失败原因(InvalidUserNameOrPassword 等)
• LogonType - 登录方式(Interactive、Network、RemoteInteractive 等)
设备字段:
• DeviceName - 目标机器
• DeviceId - 唯一设备标识符
网络字段:
• RemoteIP - 源 IP 地址
• RemoteIPType - Public 或 Private
• RemotePort - 源端口
• Protocol - 身份验证协议(NTLM、Kerberos 等)
进程字段:
• InitiatingProcessFileName - 触发登录的进程
• InitiatingProcessCommandLine - 完整命令行
• InitiatingProcessAccountName - 运行进程的用户
重要区别:
• AccountName = 谁正在登录
• InitiatingProcessAccountName = 谁启动了登录过程
它们可能不同(权限提升、runas 等)
第 3.2 课:LogonType - 关键字段
什么是 LogonType?
• 身份验证是如何发生的
• 对于理解攻击媒介至关重要
• 不同的类型 = 不同的威胁含义
需要了解的 LogonTypes:
Interactive (类型 2):
• 物理键盘登录
• 坐在机器前的人
Network (类型 3):
• 通过网络访问
• SMB 文件共享、Web 应用等
• 常用于横向移动
RemoteInteractive (类型 10):
• 远程桌面 (RDP)
• 攻击者的高价值目标
Service (类型 5):
• 服务账户启动
• 通常是自动化的/预期的
威胁狩猎相关性:
• 意外的 Network 登录 = 潜在的横向移动
• 来自异常 IP 的 RemoteInteractive = 可能的 RDP 攻击
• 多次失败的 Network 登录 = 暴力破解尝试
第 3.3 课:失败原因 - 哪里出了问题
常见 FailureReason 值:
• InvalidUserNameOrPassword - 错误的凭据(暴力破解指标)
• AccountLocked - 失败尝试过多
• PasswordExpired - 凭据需要更新
• AccountDisabled - 尝试访问已禁用的账户
• LogonTypeRestricted - 策略阻止登录类型
威胁狩猎价值:
• InvalidUserNameOrPassword 集群 = 密码喷洒/暴力破解
• AccountDisabled 尝试 = 有人在探测已禁用的账户
• AccountLocked = 攻击造成的账户成功锁定
第 3.4 课:协议 - 身份验证是如何发生的
关键协议:
NTLM:
• 传统身份验证
• 容易受到哈希传递攻击
• 在现代环境中应该较少见到
• 如果在期望 Kerberos 的地方看到了 NTLM,则是危险信号
Kerberos:
• 现代、更安全的身份验证
• 使用票据而不是密码
• 域身份验证的预期协议
第 3.5 课:AdditionalFields 列
这里包含了什么:
• 带有扩展信息的 JSON 数据块
• 特定于平台的详细信息
• Linux:POSIX 用户 ID、组信息、终端信息
• Windows:扩展的身份验证详细信息
如何解析:
| extend Terminal = tostring(parse_json(AdditionalFields).Terminal)| extend PosixUserId = toint(parse_json(AdditionalFields).PosixUserId)
特定于 Linux 的字段:
• Terminal(ssh、cron、pts/0 等)
• PosixUserId(0 = root!)
• InitiatingAccountName(谁运行了 sudo 等)
威胁狩猎价值:
• 来自公共 IP 的 Terminal = "ssh" = 外部 SSH 访问
• PosixUserId = 0 = root 活动(权限提升?)
• InitiatingAccountName 不同于 AccountName = sudo/权限提升
模块 4:威胁狩猎模式
第 4.1 课:检测暴力破解攻击
模式:
• 多次失败的登录
• 相同的目标账户或设备
• 短时间窗口
• 通常来自相同的源 IP
检测查询逻辑:
步骤 1 - 查找失败登录:
| where ActionType == "LogonFailed"| where Timestamp > ago(1h)
步骤 2 - 按来源分组:
| summarize FailedAttempts = count() by RemoteIP| where FailedAttempts > 10
步骤 3 - 添加上下文:
| summarize FailedAttempts = count(), TargetAccounts = make_set(AccountName), TargetDevices = make_set(DeviceName), FirstAttempt = min(Timestamp), LastAttempt = max(Timestamp) by RemoteIP| where FailedAttempts > 10
来自实验室数据的真实示例:
• IP 163.223.99.217 攻击 Windows 目标
• 针对 administrator 账户
• 在一段时间内进行多次尝试
• NTLM 协议 - 经典的暴力破解特征
教学要点:
• 阈值调优(10?50?100?)
• 时间窗口考量
• 误报来源(用户忘记密码)
• make_set() 用于查看被针对的账户
第 4.2 课:检测密码喷洒攻击
模式:
• 跨多个账户的失败登录
• 到处尝试相同的密码
• 通常来自单个 IP 或小范围 IP
• 每个账户的尝试次数少(逃避锁定)
与暴力破解的区别:
• 暴力破解:多个密码 → 一个账户
• 密码喷洒:一个密码 → 多个账户
检测查询逻辑:
| where ActionType == "LogonFailed"| where Timestamp > ago(1h)| summarize TargetedAccounts = dcount(AccountName), AttemptsPerAccount = count() / dcount(AccountName), AccountList = make_set(AccountName, 10) by RemoteIP| where TargetedAccounts > 5 and AttemptsPerAccount < 3
第 4.3 课:检测成功的外部访问
关注点:
• 来自公共 IP 的成功登录
• 可能是合法的远程工作,或者是初始访问
检测查询逻辑:
| where ActionType == "LogonSuccess"| where RemoteIPType == "Public"| where Timestamp > ago(24h)
添加上下文:
| summarize LoginCount = count(), Devices = make_set(DeviceName), FirstSeen = min(Timestamp), LastSeen = max(Timestamp) by RemoteIP, AccountName| order by LoginCount desc
要注意什么:
• 以前未见过的新 IP
• 异常的地理位置
• 非工作时间访问
• 对敏感系统的访问
第 4.4 课:检测权限提升
Linux 重点 - sudo 检测:
模式:
• 以普通用户登录
• 由低权限用户启动的进程
• 账户变为 root(AccountName = root,PosixUserId = 0)
查询方法:
| where AccountName == "root" or tostring(parse_json(AdditionalFields).PosixUserId) == "0"| where InitiatingProcessAccountName != "root"| where InitiatingProcessFileName == "sudo"
来自实验室数据的真实示例:
• labuser → sudo → root on linux-scan-agent-test-josh
• 命令:sudo -i(交互式 root shell)
• labuser 是否被授权执行此操作?上下文很重要!
第 4.5 课:检测横向移动
概念:
• 攻击者从受感染机器移动到其他机器
• 使用被盗的凭据
• 网络登录类型
• 内部 IP 到内部 IP
指标:
• 工作站之间的网络登录(异常)
• 管理员账户登录多台机器
• 内部的 NTLM 身份验证
• 不同机器上登录之间的时间很短
查询方法:
| where LogonType == "Network"| where RemoteIPType == "Private"| where Timestamp > ago(24h)| summarize DeviceCount = dcount(DeviceName), Devices = make_set(DeviceName) by AccountName, RemoteIP| where DeviceCount > 3
教学要点:
• 正常:服务账户跨多台机器
• 异常:用户账户快速在多台服务器上登录
• 需要环境“正常”的基线
第 4.6 课:构建威胁狩猎工作流
过程:
1. 形成假设:
• “我认为可能存在针对 RDP 的暴力破解攻击”
• 从一个具体的、可测试的想法开始
2. 确定相关数据:
• 哪些表?哪些字段?什么时间范围?
3. 构建初始查询:
• 从宽泛开始,迭代缩小
• 先测试较小的时间范围
4. 分析结果:
• 寻找模式、异常值
• 什么是正常与异常?
5. 透视和扩展:
• 发现了可疑 IP?在其他地方搜索它
• 使用发现来指导下一次查询
6. 记录发现:
• 发现了什么?有什么影响?需要采取什么行动?
模块 5:高级技术
第 5.1 课:联结表以获取上下文
为什么要联结?
• DeviceLogonEvents 显示了谁登录了
• DeviceProcessEvents 显示了他们之后运行了什么
• 相关性揭示了完整的攻击故事
基本联结:
DeviceLogonEvents| where ActionType == "LogonSuccess" and RemoteIPType == "Public"| project LogonTime = Timestamp, DeviceName, AccountName, RemoteIP| join kind=inner ( DeviceProcessEvents | where Timestamp > ago(24h) | project ProcessTime = Timestamp, DeviceName, AccountName, FileName) on DeviceName, AccountName| where ProcessTime between (LogonTime .. LogonTime + 1h)
教学要点:
• 联结键必须完全匹配
• Kind 参数:inner、left、right、full
• 时间限制对性能至关重要
• 这正是调查变得强大的地方
第 5.2 课:使用 let 定义变量
为什么要使用变量?
• 可重用的值
• 更简洁的查询
• 易于调整阈值
语法:
let timeRange = 24h;let failureThreshold = 10;let suspiciousIP = "163.223.99.217";DeviceLogonEvents| where Timestamp > ago(timeRange)| where RemoteIP == suspiciousIP| where ActionType == "LogonFailed"| summarize count()
实际用途:
• 在顶部定义调查范围
• 更改一次,到处适用
• 共享带有可调参数的查询
第 5.3 课:时间序列分析
bin() - 时间分桶:
| summarize count() by bin(Timestamp, 1h)
可视化攻击模式:
DeviceLogonEvents| where ActionType == "LogonFailed"| where Timestamp > ago(24h)| summarize FailedLogins = count() by bin(Timestamp, 15m)| render timechart
这揭示了什么:
• 失败登录的激增
• 攻击定时模式
• 正常与异常量
• 工作时间与非工作时间
模块 6:动手实验 - 威胁狩猎练习
实验室概述
场景:
学生可以访问带有真实日志数据的 CyberNova Academy 环境。他们必须仅使用针对 DeviceLogonEvents 的 KQL 查询来调查潜在的入侵。
他们正在搜寻什么:
• 暴力破解尝试的证据
• 成功的未授权访问
• 权限提升活动
• 横向移动指标
实验练习 1:初始侦察
目标:
了解环境和可用数据
任务:
4. 查询以查看日志中的所有唯一设备
5. 查询以查看所有唯一账户
6. 查询以查看可用数据的时间范围
7. 查询以查看 ActionType 的分类
预期输出:
范围内的设备列表
• 需要关注的账户列表
• 对数据量的了解
实验练习 2:搜寻暴力破解
目标:
识别活跃的暴力破解攻击
任务:
8. 查找过去 24 小时内的所有失败登录
9. 按源 IP 对失败进行分组
10. 识别具有超过 20 次失败的 IP
11. 记录被针对的账户和设备
12. 确定是否有任何在失败后成功的尝试
要回答的关键问题:
• 哪个(些)IP 正在攻击?
• 哪些账户被作为目标?
• 是否有攻击成功?
• 正在使用什么协议?
交付物:
• 攻击来源摘要
• 攻击活动时间线
• 成功/失败的评估
实验练习 3:搜寻未授权访问
目标:
找到可能表明系统被入侵的成功登录
任务:
13. 查找来自公共 IP 的所有成功登录
14. 识别哪些账户从外部登录
15. 确定此访问是否是预期的
16. 查找失败尝试后的成功登录(突破)
要回答的关键问题:
• 在此环境中,外部登录是否是预期的?
• 源 IP 在地理位置上是否合理?
• 是否有来自已知恶意 IP 的成功登录?
实验练习 4:搜寻权限提升
目标:
找到用户获得提升权限的证据
任务:
17. 查找所有导致 root/administrator 访问的登录
18. 识别启动用户(谁运行了 sudo)
19. 确定权限提升是否经过授权
20. 寻找不寻常的权限提升模式
关键问题:
• 谁正在提升到 root?
• 这是预期的管理员活动吗?
• 是否有来自受损账户的提升?
实验练习 5:完整调查综合
目标:
将所有发现结合成事件叙述
任务:
21. 建立从第一个攻击迹象到现在的时间线
22. 映射攻击者的进展(如果成功)
23. 识别所有受影响的系统和账户
24. 记录 IoC(IP、账户等)
25. 建议响应行动
交付物:
• 书面事件摘要
• 事件时间线
• 失陷指标
• 建议的补救措施
模块 7:将技能迁移到其他平台
第 7.1 课:KQL 概念 → Splunk SPL
概念映射:
KQL Splunk SPL
| where | search 或 | where
| project | fields 或 | table
| summarize count() by X | stats count by X
| order by X desc | sort -X
| take 10 | head 10
ago(24h) earliest=-24h
主要区别:
• Splunk 使用 index= 来指定数据源(而在 KQL 中是表名)
• Splunk 时间语法:earliest=-24h@h latest=now
• Splunk 中的字段提取更偏手动
核心逻辑是一致的:
• 过滤数据 → 选择列 → 分组计数 → 排序结果
第 7.2 课:KQL 概念 → Elastic/OpenSearch
相似理念:
• Lucene 查询语法用于过滤
• 聚合用于分组
• Kibana 用于可视化
概念映射:
• KQL where → Lucene filters 或 DSL 查询
• KQL summarize → Aggregations (terms, date_histogram)
• KQL project → _source 过滤
示例转换:
KQL: DeviceLogonEvents | where ActionType == "LogonFailed"
Elastic: 搜索栏中的 action_type: "LogonFailed"
第 7.3 课:通用的威胁狩猎技能
随处可迁移的技能:
26. 过滤思维 - 将庞大数据缩小到相关子集
27. 基于时间的分析 - 事情何时发生?
28. 聚合思维 - 通过分组显现模式
29. 关联技能 - 联结数据源以获取上下文
30. 基线意识 - 了解正常以发现异常
这些是与 SIEM 无关的:
• 理解身份验证日志
• 识别暴力破解模式
• 发现横向移动
• 检测权限提升
平台只是工具:
• 锤子与螺丝刀 - 都能建造东西
• KQL 与 SPL - 都能狩猎威胁
• 掌握概念,适应语法
课程总结
核心要点
技术技能:
• 从基础到高级的 KQL 查询构建
• DeviceLogonEvents 表精通
• 基于时间的过滤和聚合
• 联结表进行关联
• 构建可重用查询
威胁狩猎技能:
• 暴力破解检测
• 密码喷洒识别
• 权限提升狩猎
• 横向移动检测
• 调查工作流方法论
职业价值:
• 具备面试能力的 KQL 技能
• 使用真实数据的动手经验
• 适用于任何 SIEM 的可迁移概念
• 检测工程的基础
附录:快速参考
基本 KQL 运算符备忘单
| where <condition> // 过滤行| project <columns> // 选择列| extend <new_column> = value // 添加列| summarize <agg> by <group> // 聚合| order by <column> desc/asc // 排序| take N // 限制行| top N by <column> // 排序限制| join (table) on <key> // 联结表| count // 计算所有行
DeviceLogonEvents 关键字段
Timestamp // 何时AccountName // 谁登录了DeviceName // 在哪里(目标)ActionType // 成功或失败LogonType // 如何(Network、Interactive 等)RemoteIP // 源 IPRemoteIPType // Public 或 PrivateFailureReason // 为什么失败Protocol // NTLM、Kerberos 等InitiatingProcess* // 什么进程触发了登录
常见威胁狩猎查询
按外部 IP 统计失败登录:
| where ActionType == "LogonFailed"| summarize count() by RemoteIP| order by count_ desc
成功的外部访问:
| where ActionType == "LogonSuccess"| where RemoteIPType == "Public"
Root/admin 活动:
| where AccountName in ("root", "Administrator")
暴力破解检测:
| where ActionType == "LogonFailed"| summarize Attempts = count() by RemoteIP| where Attempts > 20
讲师说明
进度建议
• 模块 1-2:2-3 小时(基础)
• 模块 3:2 小时(表格深入探讨)
• 模块 4:3-4 小时(威胁模式)
• 模块 5:1-2 小时(高级)
• 模块 6:3-4 小时(动手实验)
• 模块 7:1 小时(迁移)
总课程时长:约 15-18 小时
前提条件
• 对什么是日志有基本了解
• 熟悉电子表格概念(Excel)
• 对网络安全感兴趣
所需材料
• 访问 CyberNova Academy Log Analytics 环境
• 示例 DeviceLogonEvents 数据(已提供)
• KQL 查询界面
标签:BurpSuite集成, KQL, Kusto查询语言, Log Analytics, Microsoft Defender XDR, Microsoft Sentinel, SOAR, SOC分析师, Threat Hunting, 培训课程, 安全教育, 安全运营, 扫描框架, 端点遥测, 红队行动, 网络安全, 防御矩阵, 隐私保护, 零基础入门