Ruby on Rails 远程代码执行漏洞分析 (CVE-2016-0752)

2016-01-29 +8 365884人围观 ,发现 6 个不明物体 漏洞

如果你的应用中使用了动态渲染路径(比如render params[:id]),那么很不幸,该应用目前存在通过本地文件包含而导致远程代码执行的漏洞,请迅速将你的Rails更新到最新版本或对你的控制器进行重构。

在本文中我们将演示由于Ruby on Rails框架中的缺陷而导致攻击者在某些情况下可以进行远程命令执行的场景。

Rails控制器的设计是基于所调用的方法来隐式的渲染视图文件的模式。比如当执行控制器中的show方法时,如果没有明确的指出要使用的渲染方式,框架会自动隐式的调用show.html.erb文件来进行页面渲染。

然而在大多情况下,开发者会基于文件格式(比如text, JSON, XML或者一个完全不同的视图文件)来决定所要呈现的不同的内容,这里所说的视图文件是指类似于ERB、HTML等的模板语言文件。

有很多的方法可以用来影响视图的内容,这里我们只关注render方法。Rails操作文档中列举了好几种方式来调用render方法,包括可以通过file:这个选项来显式的指定一个路径下的文件。

如果你已经阅读过解决方法的文档,(http://guides.rubyonrails.org/layouts_and_rendering.html),但是不确定你需不需要这样的功能,那么我告诉你,你不是唯一的一个。

让我们来看段代码:

def show
  render params[:template]
end

乍一看代码十分简单,你可能会猜到这个控制器中的方法的作用是指定参数”template”的值作为渲染的模板。目前尚不清楚Rails会从去哪里找指定的模板,它是在视图目录中还是在应用的根目录还是在其他地方?它需要指定一个模板名称还是一个特定的文件名还是一个完整的文件路径?还有很多这一类只有通过查看实现的细节才能解决的问题。

相关说明

动态渲染机制是一个尝试在同一函数中实现过多功能的典型例子,而这就是问题的所在。

假设渲染机制预期的行为是渲染app/views/user/#{params[:template]}文件–这似乎是一个合理的想法。如果我们将dashboard赋值给template,会使得应用尝试加载app/views/user/dashboard.{ext}模板,这里的.ext是任何Ruby允许的扩展(比如.html、.haml、.html.erb等)。

试想一下一个用户将../admin/dashboard赋值给参数template。预期的结果是什么呢?这似乎很难确定,但是当我们这么做的时候可以看到应用程序抛出了一个找不到模板的错误。

分析上面的错误,可以发现应用程序尝试在多个路径中查找用来呈现内容的视图,包括RAILS_ROOT/app/views、RAILS_ROOT和文件系统根目录。这有点让人费解, 为什么要从系统的根目录去寻找我们需要的模版文件呢? 

黑客本能促使我们将/etc/passwd赋值给参数template,然后来确认我们能够读取passwd文件的内容。这时我和小伙伴们都惊呆了。

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

别忘了是什么造成了这个漏洞,事实上是因为你选择了动态设置模版路径导致的。

def show
  render params[:template]
end

如此简单的小脚本就足以让攻击者读取我们的源代码和配置文件的值,但不幸的是这还不是最糟糕的部分。

就像Jeff Jarmoc的论文“The Anatomy of a Rails Vulnerability – CVE-2014-0130: From Directory Traversal to Shell,”(http://matasano.com/research/AnatomyOfRailsVuln-CVE-2014-0130.pdf)中说的,我们可以利用这个漏洞获取Rails应用的shell。Jeff的论文中提到了一个类似的因为渲染机制的问题而导致的目录遍历,或者更确切的说,在某些版本的Rails中能够导致本地文件包含。在这篇文章中我们关注的是显式渲染带来的问题,这是一个由开发者造成的漏洞。

在深入细节前我要指出的是我们要关注的漏洞是文件包含而不是目录遍历,区别是我们所加载的、执行的文件(ERB)是不同的。通常来说目录遍历漏洞会返回一个不可执行的内容,比如CSV文件。所以,本质上我们不仅可以读取应用程序的源代码和其他系统可读的文件,我们还可以执行Ruby代码。能够执行Ruby代码,这也意味着我们可以以服务器的身份执行系统级别的命令。

从文件包含到代码执行,我们需要采用一种叫日志污染的手法,(log file tainting)。Rails会在日志文件(比如development.log)中记录每一个请求的信息,包括参数等,而日志文件存储的内容是明文的,所以可以掺入Ruby代码。攻击者可以通过向服务器请求的参数中加上有效的Ruby代码,来让这些精心构造的Ruby代码记录到日志文件中。

在下面的例子中我们会向Web服务器发起合法的请求,然后在构造的参数中添加URL编码后的值:<%= `ls` %>。

通过查看日志我们能看到请求的参数的值已经被URL解码成为键值对,这是一段如果被应用调用就会执行的合法的Ruby代码。

我们可以利用文件包含漏洞来包含该日志文件,然后执行里面的Ruby代码。

日志被包含返回内容后,我们可以看到提交的参数的键值对,然后我们先前的值被替换成了ls执行后返回的值,从这里可以看出我们已经可以以当前服务器的权限执行任意系统级别的命令了。

解决方案

其实对于该如何降低这个漏洞的危害,最简单方法就是打对应Ruby版本的补丁。

另外如果你还没有打补丁,解决方案可以考虑不要渲染任何不可接受的文件,这可以通过创建只允许渲染的文件名的键值对,并且确保用户提交的参数在允许的键值对中实现。当然这个方法需要在应用的每一个实例中都进行配置。

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

利用Brakeman(http://brakemanscanner.org/)这个静态Rails扫描工具来扫描应用,它会报告通过动态路径渲染的实例,这可以让你发现你的应用程序中有哪些控制器受此漏洞的影响。

总结一下

Rails的渲染机制是一个神秘的函数,如果你没有深入的分析它的实现细节,那么你就很难搞懂这个函数或者想要从中挖到可利用的漏洞,非常不幸的是官方的文档也没有多大帮助。

跟CVE-2014-0130很类似,使用动态路径渲染可能导致目录遍历和代码执行。我曾经多次在评估中遇到过这种因为开发者导致的漏洞,也在很多的Rails公开项目中看到过。

而如果你还没看过Jeff Jarmoc的报告,我建议你去看一下。他对CVE-2014-0130中的漏洞研究的非常深并且擅长风险评估。这篇文章中的很多内容和他讲到的很相似,毕竟这些漏洞都很相似。

最后,我已经写了一个Metasploit的模块poc可用于在应用中发现和利用这个漏洞,你可以在这里找到该模块:https://gist.github.com/forced-request/5158759a6418e6376afb

*原文地址:nvisium,FB小编老王隔壁的白帽子编译,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

发表评论

已有 6 条评论

取消
Loading...
css.php