HACK80

我们很年轻,但我们有信念、有梦想!

我们坚信只有今天付出了,才有机会看到明天的太阳!
现在!加入我们,给你一个气氛优秀的技术圈子

作者: EvilGod
查看: 129|回复: 0

more +随机图赏Gallery

2017年中旬WEB渗透系列课程-19文件包含与目录遍历2017年中旬WEB渗透系列课程-19文件包含与目录遍历
2017年中旬WEB渗透系列课程-18上传绕过方法演示2017年中旬WEB渗透系列课程-18上传绕过方法演示
2017年中旬WEB渗透系列课程-17了解上传检测方式2017年中旬WEB渗透系列课程-17了解上传检测方式
2017年中旬WEB渗透系列课程-27HASH提权2017年中旬WEB渗透系列课程-27HASH提权
HACK80远控 免杀的 账号密码打包 每天都发账号 回帖要。可改密码!HACK80远控 免杀的 账号密码打包 每天都发账号 回帖要。可改密码!
2017年中旬WEB渗透系列课程-16解析漏洞演示2017年中旬WEB渗透系列课程-16解析漏洞演示
2017年中旬WEB渗透系列课程-15XSS绕过集合2017年中旬WEB渗透系列课程-15XSS绕过集合
2017年中旬WEB渗透系列课程-14XSS能干什么2017年中旬WEB渗透系列课程-14XSS能干什么
2017年中旬WEB渗透系列课程-13XSS漏洞检测2017年中旬WEB渗透系列课程-13XSS漏洞检测
2017年中旬WEB渗透系列课程-12XSS分析及演示2017年中旬WEB渗透系列课程-12XSS分析及演示

rubygems.org远程命令执行漏洞分析

[复制链接]
EvilGod 发表于 2017-10-12 17:28:04 | 显示全部楼层 |阅读模式
查看: 129|回复: 0

马上注册,加入HACK80!与我们一起交流。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
导语:通过rubygems.org上的反序列化漏洞执行远程代码, 是 Ruby 社区的一个非常受欢迎的托管服务。不过目前该漏洞的补丁已经发布,请点此升级到最新的版本。这个漏洞已被官方命名为CVE-2017-0903,关于该漏洞的详细官方介绍请点此。
1..jpg

通过rubygems.org上的反序列化漏洞执行远程代码, 是 Ruby 社区的一个非常受欢迎的托管服务。不过目前该漏洞的补丁已经发布,请点此升级到最新的版本。这个漏洞已被官方命名为CVE-2017-0903,关于该漏洞的详细官方介绍请点此

如果你曾经编写过ruby应用程序,那么很可能你已经和rubygems.org进行过交互了。甚至你可能已经把该网站设置为信任,以允许它在你的计算机上运行任意程序,例如,当用gem命令安装rails时,gem程序就会从rubygems.org获取rails gem及其所有依赖项,并将所有内容安装到相应的位置,这样任何拥有账户的人都可以在后台发布gem命令了。

Rubygems.org本身就是一个rails应用程序,它有着清楚地信息披露条例。



远程命令执行漏洞分析

Ruby gems实际上只是tar文件,所以运行tar -xvf foo.gem通常会留给你三个文件:
  1. metadata.gz
  2. data.tar.gz
  3. checksums.yaml.gz
复制代码

这三个文件都是以.gz结尾,属于被压缩的文件。 metadata.gz包含一个YAML文件,包含有关gem的信息,如名称,作者,版本等。 data.tar.gz包含了另一个tar文件,该文件包含所有源代码。 checksums.yaml.gz包含一个YAML文件,其中包含gem命令的一些哈希加密。

不过我发现,解析不信任的YAML是危险的。原来,我一直认为它是一种类似JSON的良性交换格式,但事实上,YAML允许任意对象的编码,就像利用Python pickle可以实现任意代码执行。

当你将gem上传到rubygems.org时,应用程序将调用Gem::Package.new(body).spec。该方法所用的rubygems gem使用了不安全的YAML.load调用来加载gem中的YAML文件。

不过,rubygems.org的作者是知道该方法的安全隐患的,在2013年以前,开发者利用给内置对象扩展方法(Monkey Patching)修补了YAML和gem解析库,仅允许对白名单里的对象进行反序列化。到2015年则完全采用了Psych.safe_load。

不幸的是,monkey-patching的修复还是遗留了一些漏洞,因为它只修补了Gem::Specification#from_yaml方法。如果我来看看在调用到#spec时所发生的一些情况,我就会明白#verify的调用,下面是调用中的一些关键部分:
  1. # ...
  2.   @gem.with_read_io do |io|
  3.     Gem::Package::TarReader.new io do |reader|
  4.     read_checksums reader
  5.     verify_files reader
  6.     end
  7.   end
  8.   verify_checksums @digests, @checksums
  9. # ...
复制代码
然后,在#read_checksums中会发生以下进程:
  1. # ...
  2.   Gem.load_yaml
  3.   @checksums = gem.seek 'checksums.yaml.gz' do |entry|
  4.     Zlib::GzipReader.wrap entry do |gz_io|
  5.       YAML.load gz_io.read # oops
  6.     end
  7.   end
  8. # ...
复制代码

现在,我就可以用我控制的输入调用YAML.load。最初,我试图在YAML.load调用时运行漏洞利用代码。但事实比我想得更复杂,虽然我可以反序列化任意对象,但其实对这些对象进行调用的方法却非常有限。是我可以对这些对象做出的唯一实际方法是非常有限的。 这时,就要在python上使用yaml解析库库,这可以让我多一些调用方法的选择,比如#[]=, #init_with,和#marshal_load(请注意不是Marshal.load)。但是对于大多数对象来说,这些方法并不会给攻击带来什么灵活性,因为他们通常的做法只是初始化几个变量并返回。在一些标准的rails库中存在一些危险的#[]=方法(如过去一样),但目前,我还没有找到一个能够攻击的对象。

于是,我又重新检查了rubygems.org应用程序,对其中的@checksums变量的作用进行重新评估,发现可以将其设置为任何类实例变量,在#verify_checksums中的情况如下:
  1. # ...
  2.   checksums.sort.each do |algorithm, gem_digests|
  3.     gem_digests.sort.each do |file_name, gem_hexdigest|
  4.       computed_digest = digests[algorithm][file_name]
  5. # ...
复制代码

如果我可以构建一个调用#sort的对象,那就可以实施一些攻击,触发漏洞。这样,我就有了以下的POC。实际得到评估的有效载荷包含在底层64位编码的DEFLATE压缩的编组部分,在本例中,它只是负责运行echo "oops"。
  1. SHA1: !ruby/object:Gem::Package::TarReader
  2.   io: !ruby/object:Gem::Package::TarReader::Entry
  3.     closed: false
  4.     header: 'foo'
  5.     read: 0
  6.     io: !ruby/object:ActiveSupport::Cache::MemoryStore
  7.       options: {}
  8.       monitor: !ruby/object:ActiveSupport::Cache::Strategy::LocalCache::LocalStore
  9.         registry: {}
  10.       key_access: {}
  11.       data:
  12.         '3': !ruby/object:ActiveSupport::Cache::Entry
  13.           compressed: true
  14.           value: !binary '
  15.           eJx1jrsKAjEQRbeQNT4QwQ9Q8hlTRXGL7UTFemMysIGYCZNZ0b/XYsHK8nIO
  16.           nDtRBGbvJDzxMuRMLABHzIzOSqD0G+jbVMQmhzfLwd4jnphebwUrE0ZAoJrz
  17.           YQpLE0PCRKGCmSnsWr3p0PW000S56G5eQ91cv9oDpScPC8YyRIG18WOMmGD7
  18.           /1X1AV+XPlQ='
复制代码

可以看出,从最后一步才开始逆向进行#sort调用。

在底部,我有一个ActiveSupport::Cache::Entry对象。这个对象的重要之处在于,当#value方法被调用并且@compressed为true时,它将在攻击者提供的DEFLATE压缩的数据上调用Marshal.load。解组的对象的构造方式是这样的,只要调用其上的任何方法就可以执行攻击者的代码。该方法是我以前写的,工作原理请点击这里。不幸的是,我不能在实现代码执行时,只用YAML来反序列化这个对象,因为它几乎对所有的方法都进行了undef,包括允许我设置实例变量的方法。因此,在使用时,要对Marshal.load进行加载才可以。

我会利用ActiveSupport::Cache::MemoryStore对象在@data哈希中解组我的恶意对象。它的父类ActiveSupport::Cache::Store定义了一个在MemoryStore中调用#read_entry的#read方法,#read_entry基本上只是抓取@data中的条目并将其返回。

由于MemoryStore以反序列化后的数组或者序列化后的字节缓存(ByteBuffer)形式将代码块存储到内存中,所以对MemoryStore#read的调用来自对Gem::Package::TarReader::Entry#read的调用,而Gem::Package::TarReader::Entry#read本身是由Gem::Package::TarReader#each调用的。读取返回后,对返回的值调用#size,由于我的恶意解组对象未定义,所以这会导致我的有效载荷开始执行。

最后,因为Gem::Package::TarReader对可枚举性(enumerable)进行了指定,所以调用其#sort方法将会调用其#each方法,这样整个攻击链就被启动了。

总结

在本文中,我介绍了 YAML的强大功能,有时它也可以在表现力较弱但很安全的交换格式(如JSON)中使用。也许在将来,YAML.load可以被修改为将类的白名单作为可选参数,使复杂对象的反序列化成为选择性行为。其实,目前的YAML.load实际上应该被命名为类似YAML.unsafe_load这样的名称,这样用户就知道他们何时用YAML.safe_load了。


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|手机版|HACK80 ( 沪ICP备15007512号 )

GMT+8, 2017-12-17 12:13 , Processed in 0.062598 second(s), 27 queries .

Powered by Discuz! X3.4 © 2001-2013 Comsenz Inc.