ReDoS 服务压制与自愈机制滥用
ReDoS(正则拒绝服务)利用 灾难性回溯 的正则:精心构造的输入让回溯次数随长度指数级增长,单个请求即可占死一个进程。当运维用无认证的应急/自愈机制兜底时,压制本身反而可能变成取得执行的手段。
仅限授权环境
本篇涉及拒绝服务测试,仅允许对自己的实验环境或明确书面授权的目标进行,并事先约定恢复流程。
原理
灾难性回溯的典型模式是嵌套量词,如 (a+)+$:对形如 aaaa...b(大量 a 结尾一个不匹配字符)的输入,正则引擎要尝试所有可能的分组切分,匹配时间为 指数级。
| 因素 | 放大效果 |
|---|---|
| 单线程/单核服务 | 一个回溯请求占满工作线程,其余请求全部排队 |
| Python GIL | 一个回溯中的正则计算占住解释器锁,整个进程内其他请求一起卡死 |
也就是说,对 Python 服务而言,一两个 crafted 请求就可能让应用整体无响应,无需大流量。
黑盒正则盲测
无源码时,用 对照输入差分 推断服务端正则的行为:
| 输入 | 形态 | 观察点 |
|---|---|---|
/event | 正常短输入 | 是否快速返回 matched: true |
aaaa...b | 长串 + 尾部不匹配字符 | 响应时间是否暴涨、matched 是否变 false |
# 提交 crafted 输入,观察 matched 布尔与耗时差分
curl -sS --data-urlencode 'pattern-input=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab' \
http://target/match
若短输入秒回 true、长 crafted 输入耗时指数增长或超时,即可确认服务端正则存在回溯缺陷。matched 布尔本身也是黑白盒之间难得的行为信号。
压制维持
单次请求只能卡住进程一小段时间或直到超时,要形成持续压制需要 多线程持续投递:
- 多线程循环发送 crafted 请求,让每个空闲 worker 立即被下一个回溯占住。
- 请求间隔与线程数按目标恢复速度调整,目标是让应用持续 100% 忙碌。
payload 中含
+号时务必用--data-urlencode提交,否则+会被解码成空格,正则形态改变导致回溯失效。
利用链:watchdog 应急服务滥用
部分部署用 watchdog/自愈脚本兜底:应用连续超时后拉起一个应急控制台或 rescue 服务便于运维抢修。这类应急服务常见形态是 无认证的直接执行:
nc -lk -p 8090 -e rescue.sh
利用链:
ReDoS 打瘫应用 → 健康检查连续超时
→ watchdog 判定故障,拉起应急控制台/rescue 服务
→ 连接应急端口 → 获得命令执行
操作要点:
- 先扫描/确认应急端口(如 8090)的存在与拉起条件。
- 持续投递 crafted 请求,维持压制直到 watchdog 触发。
- 轮询应急端口,服务起来后立即连接。
应急控制台交互技巧
应急控制台通常按 单连接串行 设计:同一时刻只服务一个连接,连接断开服务可能退出。因此交互方式与普通 shell 不同:
# 单命令短连接:每次连接执行一条命令即断开
echo 'id' | nc target 8090
echo 'cat /flag' | nc target 8090
- 一次连接只发一条命令,不要尝试长会话。
- 命令输出若来不及回传就断开,可以把结果落盘后下一连接再读。
- 不要并发连接,可能把串行服务顶死。
复核要点
| 检查点 | 说明 |
|---|---|
| 回溯确认 | 长度递增输入的耗时曲线是否指数增长 |
| payload 编码 | + 等字符必须 URL 编码,--data-urlencode 优先 |
| 压制持续性 | 线程数与间隔要匹配 worker 恢复速度 |
| watchdog 触发条件 | 超时阈值、连续失败次数决定等待时长 |
| 应急端口 | 确认认证有无、串行特性后再连接 |
防御建议
- 正则审计:上线前检查嵌套量词与回溯风险,高风险场景换用 RE2 等无回溯引擎。
- 为正则执行设置回溯/超时限制,单请求正则耗时设上限。
- 健康检查与业务进程隔离:探针超时不应由业务解释器承担(避免 GIL 放大误判)。
- 应急通道必须加认证并默认关闭,禁止
nc -e类无鉴权直接执行。 - watchdog 触发要有告警与人工确认环节,避免被压制行为诱导。