跳到主要内容

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 服务
→ 连接应急端口 → 获得命令执行

操作要点:

  1. 先扫描/确认应急端口(如 8090)的存在与拉起条件。
  2. 持续投递 crafted 请求,维持压制直到 watchdog 触发。
  3. 轮询应急端口,服务起来后立即连接。

应急控制台交互技巧

应急控制台通常按 单连接串行 设计:同一时刻只服务一个连接,连接断开服务可能退出。因此交互方式与普通 shell 不同:

# 单命令短连接:每次连接执行一条命令即断开
echo 'id' | nc target 8090
echo 'cat /flag' | nc target 8090
  • 一次连接只发一条命令,不要尝试长会话。
  • 命令输出若来不及回传就断开,可以把结果落盘后下一连接再读。
  • 不要并发连接,可能把串行服务顶死。

复核要点

检查点说明
回溯确认长度递增输入的耗时曲线是否指数增长
payload 编码+ 等字符必须 URL 编码,--data-urlencode 优先
压制持续性线程数与间隔要匹配 worker 恢复速度
watchdog 触发条件超时阈值、连续失败次数决定等待时长
应急端口确认认证有无、串行特性后再连接

防御建议

  1. 正则审计:上线前检查嵌套量词与回溯风险,高风险场景换用 RE2 等无回溯引擎。
  2. 为正则执行设置回溯/超时限制,单请求正则耗时设上限。
  3. 健康检查与业务进程隔离:探针超时不应由业务解释器承担(避免 GIL 放大误判)。
  4. 应急通道必须加认证并默认关闭,禁止 nc -e 类无鉴权直接执行。
  5. watchdog 触发要有告警与人工确认环节,避免被压制行为诱导。