Chybeta

Analysis for【CVE-2019-5418】File Content Disclosure on Rails

Chinese Edition: Ruby on Rails 路径穿越与任意文件读取漏洞分析 - 【CVE-2019-5418】

Security Advisory

https://groups.google.com/forum/#!topic/rubyonrails-security/pFRKI96Sm8Q

Analysis

The render method can use a view that’s entirely outside of your application. So in actionview-5.2.1/lib/action_view/renderer/template_renderer.rb:22, it will call find_file to determine which template to be rendered。

1
2
3
4
5
6
7
8
9
10
11
12
module ActionView
class TemplateRenderer < AbstractRenderer #:nodoc:
# Determine the template to be rendered using the given options.
def determine_template(options)
keys = options.has_key?(:locals) ? options[:locals].keys : []
if options.key?(:body)
...
elsif options.key?(:file)
with_fallbacks { find_file(options[:file], nil, false, keys, @details) }
...
end
end

In the find_file method:

1
2
3
def find_file(name, prefixes = [], partial = false, keys = [], options = {})
@view_paths.find_file(*args_for_lookup(name, prefixes, partial, keys, options))
end

step into args_for_lookup method which to generate the options. When it returns, our payload will be saved in details[formats] :

then it will execute @view_paths.find_file which located in actionview-5.2.1/lib/action_view/path_set.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class PathSet #:nodoc:
def find_file(path, prefixes = [], *args)
_find_all(path, prefixes, args, true).first || raise(MissingTemplate.new(self, path, prefixes, *args))
end
private
def _find_all(path, prefixes, args, outside_app)
prefixes = [prefixes] if String === prefixes
prefixes.each do |prefix|
paths.each do |resolver|
if outside_app
templates = resolver.find_all_anywhere(path, prefix, *args)
else
templates = resolver.find_all(path, prefix, *args)
end
return templates unless templates.empty?
end
end
[]
end

Because the view is outside of your application,so outside_app equalsTrue and then will call find_all_anywhere

1
2
3
4
5
def find_all_anywhere(name, prefix, partial = false, details = {}, key = nil, locals = [])
cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details, true)
end
end

Skip cached part, the find_templates will according the options to find the template to render:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# An abstract class that implements a Resolver with path semantics.
class PathResolver < Resolver #:nodoc:
EXTENSIONS = { locale: ".", formats: ".", variants: "+", handlers: "." }
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
...
private
def find_templates(name, prefix, partial, details, outside_app_allowed = false)
path = Path.build(name, prefix, partial)
# 注意 details 与 details[:formats] 的传入
query(path, details, details[:formats], outside_app_allowed)
end
def query(path, details, formats, outside_app_allowed)
query = build_query(path, details)
template_paths = find_template_paths(query)
...
end
end

After build_query , the variables :

SO here we use ../ to make directory traversal,and use double { to make sure syntax right. After File.expand_path , the result is:

1
/etc/passwd{{},}{+{},}{.{raw,erb,html,builder,ruby,coffee,jbuilder},}

so the /etc/passwd will be treated the template to be rended ,which lead to a arbitrary file read attack.

Reproduce

install vulnerable Rails (e.g 5.2.1)

1
2
3
4
# echo "gem 'rails', '5.2.1'" >> Gemfile
# echo "gem 'sqlite3', '~> 1.3.6', '< 1.4'" >> Gemfile
# echo "source 'https://rubygems.org'" >> Gemfile
# bundle exec rails new . --force --skip-bundle

Generate controller:

1
# rails generate controller chybeta

Inapp/controllers/chybeta_controller.rb

1
2
3
4
5
class ChybetaController < ApplicationController
def index
render file: "#{Rails.root}/some/file"
end
end

add resources in config/routes.rb:

1
2
3
Rails.application.routes.draw do
resources :chybeta
end

Patch

https://github.com/rails/rails/commit/f4c70c2222180b8d9d924f00af0c7fd632e26715

微信扫码加入知识星球【漏洞百出】
chybeta WeChat Pay

点击图片放大,扫码知识星球【漏洞百出】

本文标题:Analysis for【CVE-2019-5418】File Content Disclosure on Rails

文章作者:chybeta

发布时间:2019年03月16日 - 10:03

最后更新:2019年03月16日 - 13:03

原始链接:http://chybeta.github.io/2019/03/16/Analysis-for【CVE-2019-5418】File-Content-Disclosure-on-Rails/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。