Module: Msf::Exploit::Remote::HTTP::ManageEngineAdauditPlus::Login

Includes:
StatusCodes, TargetInfo, URIs, Msf::Exploit::Remote::HttpClient
Included in:
Msf::Exploit::Remote::HTTP::ManageEngineAdauditPlus
Defined in:
lib/msf/core/exploit/remote/http/manage_engine_adaudit_plus/login.rb

Constant Summary

Constants included from StatusCodes

StatusCodes::CONNECTION_FAILED, StatusCodes::NO_ACCESS, StatusCodes::NO_BUILD_NUMBER, StatusCodes::NO_DOMAINS, StatusCodes::SUCCESS, StatusCodes::UNEXPECTED_REPLY

Instance Attribute Summary

Attributes included from Msf::Exploit::Remote::HttpClient

#client, #cookie_jar

Instance Method Summary collapse

Methods included from URIs

#adaudit_api_alertprofiles_save_uri, #adaudit_api_js_message_uri, #adaudit_plus_configured_domains_uri, #adaudit_plus_gpo_watcher_data_uri, #adaudit_plus_jump_to_js_uri, #adaudit_plus_license_details_uri, #adaudit_plus_login_uri

Methods included from Msf::Exploit::Remote::HttpClient

#basic_auth, #cleanup, #configure_http_login_scanner, #connect, #connect_ws, #deregister_http_client_options, #disconnect, #download, #full_uri, #handler, #http_fingerprint, #initialize, #lookup_http_fingerprints, #normalize_uri, #path_from_uri, #peer, #proxies, #reconfig_redirect_opts!, #request_opts_from_url, #request_url, #rhost, #rport, #send_request_cgi, #send_request_cgi!, #send_request_raw, #service_details, #setup, #ssl, #ssl_version, #strip_tags, #target_uri, #validate_fingerprint, #vhost

Methods included from Auxiliary::Report

#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_login, #db, #db_warning_given?, #get_client, #get_host, #inside_workspace_boundary?, #invalidate_login, #mytask, #myworkspace, #myworkspace_id, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot

Methods included from Metasploit::Framework::Require

optionally, optionally_active_record_railtie, optionally_include_metasploit_credential_creation, #optionally_include_metasploit_credential_creation, optionally_require_metasploit_db_gem_engines

Methods included from TargetInfo

#adaudit_plus_grab_build, #adaudit_plus_grab_configured_domains, #adaudit_plus_grab_domain_aliases, #adaudit_plus_target_check, #gpo_watcher_data_check

Methods included from StatusCodes

#adaudit_plus_status

Instance Method Details

#adaudit_plus_login(auth_domain, user = '', pass = '', only_get_cookie = false) ⇒ Hash

Performs a ManageEngine ADAudit Plus login.

Parameters:

  • auth_domain (String)

    The authentication domain to use to log in.

  • user (String) (defaults to: '')

    The username to log in as.

  • pass (String) (defaults to: '')

    The password to log in with.

  • only_get_cookie (Boolean) (defaults to: false)

    If this is set to true, then this method will only try to obtain an 'adapcsrf' cookie that is required to perform API calls.

Returns:

  • (Hash)

    Hash containing a `status` key, which is used to hold a status value as an Integer value, a `message` key, which is used to hold a message associated with the status value as a String. May optionally contain an `adapcsrf_cookie` key which maps to a String containing the adapcsrf cookie to be used for authentication purposes, and/or a `configured_domains` key which maps to an Array of Strings, each containing a domain name that has been configured to be used by the ManageEngine ADAudit Plus target.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/msf/core/exploit/remote/http/manage_engine_adaudit_plus/login.rb', line 24

def (auth_domain, user = '', pass = '', only_get_cookie = false)
  cookie_jar.clear # let's start fresh

  # Visit the default homepage to retrieve some of the baseline cookies needed to authenticate.
  res_initial_cookies = send_request_cgi({
    'uri' => normalize_uri(target_uri.path),
    'method' => 'GET',
    'keep_cookies' => true
  })

  unless res_initial_cookies
    return {
      'status' => adaudit_plus_status::CONNECTION_FAILED,
      'message' => 'Connection failed.'
    }
  end

  # Make sure the target is actually ManageEngine ADAudit Plus
  unless res_initial_cookies.code == 200 && res_initial_cookies.body =~ /<title>ADAudit Plus/
    return {
      'status' => adaudit_plus_status::UNEXPECTED_REPLY,
      'message' => 'Target does not seem to be ADAudit Plus.'
    }
  end

  # Check if we have an initial adapcsrf cookie with the expected format
  unless res_initial_cookies.headers.include?('Set-Cookie') && res_initial_cookies.get_cookies =~ /adapcsrf=[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/
    return {
      'status' => adaudit_plus_status::UNEXPECTED_REPLY,
      'message' => 'Failed to obtain the baseline cookies needed to proceed with authentication.'
    }
  end

  # Visit the adaudit_plus_jump_to_js_uri page to grab more cookies needed for authentication.
  vprint_status('Attempting to obtain the required cookies for authentication')

  res_extra_cookies = send_request_cgi({
    'uri' => adaudit_plus_jump_to_js_uri,
    'method' => 'GET',
    'keep_cookies' => true
  })

  unless res_extra_cookies
    return {
      'status' => adaudit_plus_status::CONNECTION_FAILED,
      'message' => 'Connection failed.'
    }
  end

  # check if we have a new adapcsrf cookie with the expected format, which is different
  # from the initial adapcsrf cookie format that we got before visiting the adaudit_plus_jump_to_js_uri URI.
  unless res_extra_cookies.code == 200 && res_extra_cookies.headers.include?('Set-Cookie') && res_extra_cookies.get_cookies =~ /adapcsrf=[a-f0-9]{128}/
    return {
      'status' => adaudit_plus_status::UNEXPECTED_REPLY,
      'message' => 'Failed to obtain the jump_to_js cookies required for authentication.'
    }
  end

  vprint_status('Trying to authenticate...')
  post_vars = {
    'forChecking' => '',
    'j_username' => user.to_s,
    'j_password' => pass.to_s,
    'domainName' => auth_domain.to_s,
    'AUTHRULE_NAME' => 'Authenticator'
  }

   = send_request_cgi({
    'uri' => ,
    'method' => 'POST',
    'keep_cookies' => true,
    'vars_post' => post_vars
  })

  # Check to see if the connection succeeded.
  return {
    'status' => adaudit_plus_status::CONNECTION_FAILED,
    'message' => 'Connection failed'
  } unless 

  # Check to see if we got the right response code and the expected cookies.
  unless .code == 303 && .headers.include?('Set-Cookie')
    # Matches something like JSESSIONIDADAP=50E42FBF96E820A6099A1F38FA5A4854; JSESSIONIDADAPSSO=7EB091F6BB9A7A4C4476419DFC11E2A1;
    # Or this JSESSIONIDADAP=50E42FBF96E820A6099A1F38FA5A4854; JSESSIONIDSSO=7EB091F6BB9A7A4C4476419DFC11E2A1;
    # Or even this JSESSIONIDADAP=50E42FBF96E820A6099A1F38FA5A4854; JSESSIONIDSSO=7EB091F6BB9A7A4C4476419DFC11E2A1
    unless .get_cookies =~ /(?:JSESSIONID[A-Z].*?=[0-9A-Z]{32};{0,1} {0,1}){2}/
      return {
        'status' => adaudit_plus_status::NO_ACCESS,
        'message' => 'Failed to authenticate.'
      }
    end
  end

  # Check if we are actually logged in by visiting the home page.
  res_post_auth = send_request_cgi({
    'uri' => normalize_uri(target_uri.path),
    'method' => 'GET',
    'keep_cookies' => true
  })

  return {
    'status' => adaudit_plus_status::CONNECTION_FAILED,
    'message' => 'Connection failed'
  } unless res_post_auth

  unless res_post_auth.code == 200 && res_post_auth.body.include?('ManageEngine ADAudit Plus web client is initializing')
    return {
      'status' => adaudit_plus_status::NO_ACCESS,
      'message' => 'The web app failed to load after authenticating'
    }
  end

  # Return the value of the adapcsrf cookie, which will be required for later actions.
  adapcsrf_cookie = cookie_jar.cookies.select { |k| k.name == 'adapcsrf' }&.first
  if adapcsrf_cookie.blank? || adapcsrf_cookie.value.blank?
    return {
      'status' => adaudit_plus_status::NO_ACCESS,
      'message' => 'Failed to obtain the required adapcsrf cookie'
    }
  end

  # In order to get a cookie we can actually use, we need to obtain the configured domains via the API,
  # so we will call adaudit_plus_grab_configured_domains to retrieve this information for us.
  # Note that adaudit_plus_obtain_configured_domains uses the same return format as this method.
  adaudit_plus_grab_configured_domains(adapcsrf_cookie.value, only_get_cookie)
end