How to fix security issue in Dradis where Ruby Rails Mini Profiler "Rack Environment" is enabled and visible when using pp=env

I'm not familiar with Ruby, Rails, Puma, or anything else in the Ruby environment, although I'm well familiar with other programming environments which are similar. Still, it took me longer to install Dradis than it should have, and I finally gave up on the Docker version entirely. I had to cobble together a systemd service knowing nothing about "bundle exec" or "puma." It was much uglier than it should be, leaving me with only one question once it was up and running: why are people who program with Ruby stuck in the 1990s? In my humble opinion, PHP is a simple and uncomplicated joy to work with compared to Ruby.

A few days later, I received an alert from my regular external security scan telling me there was a web page on my new Dradis site displaying internal network information. It was an unpleasant surprise which I would have been able to fix within minutes in my native world, but took me most of a day to track down and resolve. I'm going to skip a lot of me trawling through code, documentation, and forum discussions which got me to the heart of the matter:

  1. The current version of Dradis (CE) comes out of the box in development mode. This is fairly reasonable for a community edition.
  2. The development mode code profiler is enabled. Although appropriate for developers, this is a serious security risk in production because it displays a webpage full of internal network information and lots of information about your program.
  3. When you figure out how to get Dradis into production mode, it doesn't solve the problem.
  4. When you figure out how to configure the profiler into authorized-viewers-only mode, the problem remains.
  5. When you hack the code (profiler.rb) to outright disable the profiler within its own codebase, the problem remains.
  6. When you disable the profiler in config.rb, it finally stops printing a vast array of internal network information to the world.
  7. Yeah, yeah, I should have gone right to that file first, but remember I have no idea where things are in this environment.

Here's the problem:

After Dradis is installed, if you visit the main URL with pp=env appended, like so:

https://dradis.exampledomain.com/?pp=env

You'll get a "Rack Environment" page which shows all kinds of details about your server, your application, and your network environment. It looks like this:

Rack Environment
---------------
rack.version: [1, 6]
rack.errors: #<IO:0x00007fc17d853bd0>
rack.multithread: true
rack.multiprocess: false
rack.run_once: false
rack.url_scheme: https
SCRIPT_NAME: 
QUERY_STRING: pp=env
SERVER_SOFTWARE: puma 6.4.3 The Eagle of Durango
GATEWAY_INTERFACE: CGI/1.2
REQUEST_METHOD: GET
REQUEST_PATH: /
REQUEST_URI: /?pp=env
SERVER_PROTOCOL: HTTP/1.1
HTTP_HOST: localhost:3001
HTTP_USER_AGENT: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0
HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_LANGUAGE: en-US,en;q=0.5
HTTP_ACCEPT_ENCODING: gzip, deflate, br, zstd
HTTP_DNT: 1
HTTP_SEC_GPC: 1
HTTP_UPGRADE_INSECURE_REQUESTS: 1
HTTP_SEC_FETCH_DEST: document
HTTP_SEC_FETCH_MODE: navigate
HTTP_SEC_FETCH_SITE: none
HTTP_SEC_FETCH_USER: ?1
HTTP_PRIORITY: u=0, i
HTTP_X_FORWARDED_PROTO: https
HTTP_X_FORWARDED_SSL: on
HTTP_X_FORWARDED_FOR: 172.127.132.118
HTTP_X_FORWARDED_HOST: dradis.exampledomain.com
HTTP_X_FORWARDED_SERVER: dradis.exampledomain.com
HTTP_CONNECTION: Keep-Alive
puma.request_body_wait: 0.00633201003074646
SERVER_NAME: localhost
SERVER_PORT: 3001
PATH_INFO: /
REMOTE_ADDR: 127.0.0.1
HTTP_VERSION: HTTP/1.1
puma.socket: #<TCPSocket:0x00007fc1530c7a30>
rack.hijack?: true
rack.hijack: #<Puma::Client:0x00007fc1530c79e0>
rack.input: #<Puma::NullIO:0x00007fc17a1508b8>
rack.after_reply: []
puma.config: #<Puma::Configuration:0x00007fc17a107398>
action_dispatch.parameter_filter: [:password, :password_confirmation, :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn]
action_dispatch.redirect_filter: []
action_dispatch.secret_key_base: 0f75bcdc814e320bc54a9d0b3615081ea0850a3aa9ceffaf19fda97ad475d66bc37afc80f9fc3bccb774e3bb95f634b917b91e6997069b81faa4993421f14889
.
.
.

Environment
---------------
USER: <username>
BUNDLER_VERSION: 2.3.16
SHLVL: 0
RUBYOPT: -r/home/<username>/dradis/dradis-ce/vendor/bundle/ruby/3.1.0/gems/bundler-2.3.16/lib/bundler/setup
SERVER: puma
HOME: /home/<username>
RBENV_ROOT: /home/<username>/.rbenv
BUNDLER_ORIG_BUNDLER_VERSION: 2.3.16
SYSTEMD_EXEC_PID: 464841
LOGNAME: <username>
RUBYLIB: /home/<username>/dradis/dradis-ce/vendor/bundle/ruby/3.1.0/gems/bundler-2.3.16/lib:/home/<username>/.rbenv/rbenv.d/exec/gem-rehash
PATH: /home/<username>/dradis/dradis-ce/vendor/bundle/ruby/3.1.0/bin:/home/<username>/.rbenv/versions/3.1.2/bin:/home/<username>/.rbenv/libexec:/home/<username>/.rbenv/plugins/ruby-build/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
INVOCATION_ID: 50790fd5c0df419f83cfd8e4470f53a7
.
.
.

This example is truncated, the actual output is much longer. There are other pp= features which expose even more information. You get the idea. The fact that this is running in a user's home directory instead of /opt, the username, ruby and tool versions, all kinds of things which should not be visible to the public.

The fix:

Edit the following file: ./dradis/dradis-ce/vendor/bundle/ruby/3.1.0/gems/rack-mini-profiler-2.3.0/lib/mini_profiler/config.rb

nano ./dradis/dradis-ce/vendor/bundle/ruby/3.1.0/gems/rack-mini-profiler-2.3.0/lib/mini_profiler/config.rb

In the following section which is a few lines down in the file, comment out the "@enabled=true" line and add the line you see in red below:

# called after rack chain, to ensure we are REALLY allowed to profile
@skip_schema_queries    = false
@storage                = MiniProfiler::MemoryStore
@user_provider          = Proc.new { |env| Rack::Request.new(env).ip }
#changing to allow_authorized did not work:
#@authorization_mode     = :allow_all
@authorization_mode     = :allow_authorized
@backtrace_threshold_ms = 0
@flamegraph_sample_rate = 0.5
@storage_failure = Proc.new do |exception|
  if @logger
    @logger.warn("MiniProfiler storage failure: #{exception.message}")
  end
end
#set enabled to false
#@enabled = true
@enabled = false
.
.
.

The first section marked in bold should have worked, but it didn't. The second section marked in bold worked: set @enabled=false. Restart the application. Now when people try that URL or any of the other pp flags, the application ignores the request and takes you straight to the login page.

I know, I know, the right way to fix this is to figure out why production mode isn't working, and fix that, but I spent an enormous amount of time finding this fix, and need to get on with things. Anyone with advice on how to fix the production mode fail, lemme know in comments below.

Note there may have been a cache which was causing some of my previous legit fixes to fail, but this fix seems to bypass any cache, and it just worked.

Have fun.

 

Add a comment

Fields followed by * are mandatory

HTML code is displayed as text and web addresses are automatically converted.

Page top