Ruby on Rails 动态渲染远程代码执行漏洞 (CVE-2016-0752)

小饼仔 2016-01-27 17:03:00

原文:Rails Dynamic Render to RCE (CVE-2016-0752)

0x00 概述


如果你的应用中使用了动态渲染路径 (dynamic render paths) ,如渲染params[:id],通过本地文件包含(local file inclusion),可能会导致远程代码执行。可以通过更新到Rails的最新版本,或重构你的controllers来修复漏洞。

文章主要介绍了在特定的场景下,Ruby on Rails框架的一个缺陷导致攻击者能够远程执行代码。

Rails controllers的设计中,会根据被调用的方法,隐式的渲染对应的view文件。例如,当调用controllershow方法时,如果代码中没有明确指定要渲染的view file,框架就会隐式的渲染show.html.erb文件。

然而在很多情况下,开发者会根据请求格式,如text, JSON, XML来明确渲染的内容。此时,view file是一个模板语言文件,如ERB, HAML等。Rails框架中有多个方法能够用于影响和改变view内容。本文中重点关注渲染方法(render method)。Rails的文档中给出了多种调用渲染方法的方式,包括使用file:选项来明确指定要渲染的view file路径。

如果你阅读了该方法的文档,你会对功能的说明感到迷惑。让我们来看一段代码:

def show
  render params[:template]
end

第一眼看,这段代码非常简单,该controller action的主要功能为渲染template参数指定 view 模板。但Rails如何查找指定的模板呢?在views目录下查找? 在应用的 root 目录下查找?或是在其他位置?;该参数的值是一个模板的名称?是一个有特定后缀的文件名?或是一个完整的文件路径?这里有许许多多的疑问,我们需要查看实现的细节才能够得到答案。

0x01 细节说明


框架的渲染机制(render mechanism)是一个在单一函数内试图完成太多功能的例子,这也是导致远程代码执行的原因所在。

我们假定渲染机制的预期行为是渲染在app/views/user/#{params[:template]}文件,该假设看起来似乎是合理的。如果template参数的值为dashboard,那么就会去加载app/views/user/dashboard.{ext}文件,其中.ext是任意允许的后缀类型,如.html, .haml, .html.erb

假设用户给出的template参数值为../admin/dashboard。 程序执行的结果会是什么? 如下图所示,执行后,程序抛出了缺少模板错误(missing template error

通过分析报错信息,我们可以知道,框架试图在多个路径下查找要渲染的view file,包括RAILS_ROOT/app/viewsRAILS_ROOT和 文件系统的根目录。

从文件系统的根目录加载并渲染文件的行为是非常危险的。如果我们将/etc/passwd作为template参数的值传入,并且能够读取到passwd文件的内容。那么将会是一个很严重的问题。

如果我们能够读取passwd文件的内容,那么也可以读取应用程序的源代码和配置文件,如config/initializers/secrettoken.rb文件。

导致该问题的原因是,在应用中使用了动态渲染路径(dynamic render paths

def show
  render params[:template]
end

这个简单的代码的例子证明了攻击者能够读取我们的源代码和应用程序配置文件。不幸的是,这不是最坏的结果。

Jeff Jarmo在他的文章The Anatomy of a Rails Vulnerability – CVE-2014-0130: From Directory Traversal to Shell中描述的,我们能够利用这个漏洞获得一个shell。Jeff的文章中描述了 在某些版本的Rails的 隐式渲染机制的一个相似漏洞,能够允许目录遍历(directory traversal),更精确的说,本地文件包含(local file inclusion)。本文中重点关注显示渲染(explicit rendering),这是一个由框架开发者所引入的漏洞。

在进入细节分析前,这里将该漏洞的分类归为文件包含而不是目录遍历,原因是我们将文件作为代码(ERB)进行加载、解释并执行。从传统意义上来说,目录遍历漏洞返回的是不可执行的(non-executable)的内容,如CSV文件。因此从本质来说,我们不仅仅能够读取应用程序的源代码和其他可读的系统文件,还能够执行Ruby代码。既然我们能够执行Ruby代码,我们也能够在web server上执行系统级别(system-level)的命令。

在从文件包含到得到shell的过程中,用到了一种关键的技术,称为 日志文件污染 (log file tainting)。Rails在运行过程中,会将请求信息记录到日志文件中,包括请求参数,如development.log。日志文件是纯文本格式,能够包含Ruby代码。日志文件污染可以通过向web应用程序发送一个恶意的请求,请求的参数中包含有效的Ruby代码来实现。

在下面的例子中,我们向web应用程序发起了一个合法的请求,但在URL中带上了包含恶意的、经过URL编码的参数 <%= `ls` %>。

通过查看日志文件,我们可以看到,日志中请求参数是以hash键值对的方式保存,并进行了 URL解码。这是有效的Ruby代码,能够在文件被渲染时执行。

因此,我们能够利用文件包含漏洞来尝试加载日志文件,执行包含的Ruby代码。

当日志文件返回时,我们能够看到参数的hash值。之前包含我们payload的部分已经被ls命令的执行结果所替换。到这里,我们已经能够以web-server对应用户权限来执行系统命令了。

0x02 修复方法 & 缓解措施


安装Rails特定版本的补丁

如果还没有安装patch,可以考虑禁止渲染白名单之外的文件。具体方法为,在action中定义允许的文件名白名单hash,对用户的参数进行校验,确保参数在文件白名单hash中。这种方法需要在应用程序中所有使用到动态渲染路径的地方都增加校验。

def show
  template = params[:id]

  valid_templates = {
    "dashboard" => "dashboard",
    "profile"   => "profile",
    "deals"   => "deals"
  }

  if valid_templates.include?(template)
    render " #{valid_templates[template]}"
  else
    # throw exception or 404
  end
end

另一种类似的方法为校验给定的文件是否在特定的目录下存在。

def show
  template = params[:id]
  d = Dir["myfolder/*.erb"]

  if d.include?("myfolder/#{template}.erb")
    render "myfolder/#{template}"
  else
    # throw exception or 404
  end
end

此外,我们可以使用Rails静态分析工具Brakeman来扫描应用程序。Brakeman检测报告会给出使用了动态渲染路径的controllers,可以根据此来分析哪些controllers会有远程代码执行的风险。

0x03 时间轴


  • 2015年2月1日漏洞被报告给Rails Team
  • 2015年2月10日Rails Team同意修复漏洞
  • Patch在2015年7月内部验证通过。-- 距离报告日期超过5个月
  • Patch在2016年1月25日发布,漏洞编号CVE-2016-0752,-- 距离验证通过超过6个月,距离报告日期将近一年

0x04 结论


Rails的渲染机制是一个比较神秘的功能,如果没有深入挖掘实现细节很难弄明白。与CVE-2014-0130类似,使用动态渲染路径会导致目录遍历和代码执行。在评估多个流行的Rails开源项目的过程中,我已经看过许多由框架开发者所引入的类似漏洞。

如果你还没有阅读过Jeff Jarmoc的文章,我建议你去阅读以下。他深入了研究了CVE-2014-0130的漏洞细节和风险点。本文中的许多内容与他的文章都很相似,事实上,这两个是非常相似的漏洞。

我已经写了MetasploitPOC模块,用于检测和攻击web应用程序的远程打码执行漏洞。

已经有白帽子在三个白帽上搭建起了相应的漏洞环境,小伙伴们感兴趣的来测试下吧,有效期一个小时:

http://b4bfc94e13431d5d4.jie.sangebaimao.com

评论

路人甲 2016-01-27 18:13:42

fsdfds

B

BeenQuiver 2016-01-27 21:10:24

666

_

_Evil 2016-01-28 09:14:38

http://www.lijiejie.com/python-django-directory-traversal/ 好像

V

vforbox 2016-04-06 14:11:18

mark

小饼仔

做有意思的事 and 分享有意思的东西~ zzz

twitter weibo github wechat

随机分类

iOS安全 文章:36 篇
无线安全 文章:27 篇
后门 文章:39 篇
前端安全 文章:29 篇
Python安全 文章:13 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Yukong

🐮皮

H

HHHeey

好的,谢谢师傅的解答

Article_kelp

a类中的变量secret_class_var = "secret"是在merge

H

HHHeey

secret_var = 1 def test(): pass

H

hgsmonkey

tql!!!

目录